├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── main.go └── server.go /.gitignore: -------------------------------------------------------------------------------- 1 | ios_video_stream -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 David Helkowski 2 | 3 | Free Use Anti-Corruption License 4 | 5 | ======================================================================= 6 | 7 | The content of this project is licensed for free conditional use by those 8 | not employed by THE RESTRICTED PARTY. The conditional aspect is that the use 9 | does not benefit THE RESTRICTED PARTY. 10 | 11 | THE RESTRICTED PARTY is any person or legal entity meeting any of the 12 | following conditions: 13 | 1. A person who is employed directly or transitively by a RESTRICTED ENTITY. 14 | 2. A legal entity engaged in contractual business for a RESTRICTED ENTITY. 15 | 16 | A DIRECT RESTRICTED ENTITY is defined to be any of the following: 17 | - Accenture https://en.wikipedia.org/wiki/Accenture 18 | - Amazon https://en.wikipedia.org/wiki/Amazon_(company) 19 | - Apple 20 | - AptEdge https://aptedge.io 21 | - Avalara https://www.avalara.com 22 | - Baltimore Sun 23 | - BCG https://en.wikipedia.org/wiki/Boston_Consulting_Group 24 | - BrowserStack https://www.browserstack.com 25 | - The Canton Group https://cantongroup.com 26 | - Comcast 27 | - Cruise LLC https://en.wikipedia.org/wiki/Cruise_(autonomous_vehicle) 28 | - Disney 29 | - Ebay 30 | - Epic Games 31 | - EQT Partners https://en.wikipedia.org/wiki/EQT_Partners 32 | - Equifax 33 | - Experian 34 | - Extrahop https://www.extrahop.com 35 | - Facebook 36 | - Fox Entertainment Group 37 | - Headspin https://www.headspin.io 38 | - IBM 39 | - Insight Global https://www.insightglobal.com 40 | - Jamf https://www.jamf.com 41 | - Kobiton https://www.kobiton.com 42 | - Logic 20/20 https://www.logic2020.com 43 | - Micro Focus https://en.wikipedia.org/wiki/Micro_Focus 44 | - Mobica https://mobica.com 45 | - NBC 46 | - Nintendo https://en.wikipedia.org/wiki/Nintendo 47 | - Oracle Corporation https://en.wikipedia.org/wiki/Oracle_Corporation 48 | - Palantir 49 | - Perfecto https://www.perfecto.io 50 | - ProKarma https://pkglobal.com 51 | - Sauce Labs https://saucelabs.com 52 | - Sinclair https://en.wikipedia.org/wiki/Sinclair_Broadcast_Group 53 | - Sony Corporation https://en.wikipedia.org/wiki/Sony 54 | - Sprint Corporation https://en.wikipedia.org/wiki/Sprint_Corporation 55 | - Systems Alliance https://www.systemsalliance.com 56 | - SUSE https://en.wikipedia.org/wiki/SUSE 57 | - TEKsystems https://www.teksystems.com 58 | - TMobile https://en.wikipedia.org/wiki/T-Mobile 59 | - TransUnion 60 | - T. Rowe Price https://en.wikipedia.org/wiki/T._Rowe_Price 61 | - Verizon 62 | - Wells Fargo 63 | - Zulily 64 | 65 | The links given are to clarify which companies exactly are meant. Should 66 | those links become invalid the original content of those links on the 67 | date 11/24/2020 shall apply. 68 | 69 | A RESTRICTED ENTITY is defined to be any of the following: 70 | 1. A DIRECT RESTRICTED ENTITY 71 | 2. An owner of a RESTRICTED ENTITY 72 | 3. A subsidiary or any functional business unit of a RESTRICTED ENTITY 73 | 4. Any entity of which 50% of its annual revenue comes from work done for 74 | a RESTRICTED ENTITY 75 | 5. An entity that broke off from a RESTRICTED ENTITY ( such as a company 76 | splitting up into different legal entities ) 77 | 6. An international counterpart of a RESTRICTED ENTITY. 78 | 79 | LICENSEE refers to any party ( person or legal entity ) accepting this 80 | license and hence making use of THE CONTENT or utilizing any of the 81 | permissions provided by the license. By definition a LICENSEE cannot be 82 | a RESTRICTED ENTITY. 83 | 84 | THE CONTENT refers to the content in full, any portion of it, and anything 85 | derived from it. 86 | 87 | LICENSEE agrees not to provide THE CONTENT to THE RESTRICTED PARTY, nor to 88 | allow THE CONTENT to be used in any way by THE RESTRICTED PARTY. 89 | 90 | THE RESTRICTION is defined to be this license in full, specifically to 91 | refer to the way the license restricts use of THE CONTENT in any way by 92 | THE RESTRICTED PARTY. 93 | 94 | THE RESTRICTION shall apply to all projects with Copyright 95 | ownership by THE RESTRICTED PARTY. This shall remain true even if the 96 | project contributions themselves are done by a person not in THE 97 | RESTRICTED PARTY. Example: No project with a copyright ownership by Amazon 98 | may use this project in any way. 99 | 100 | To those wondering why THE RESTRICTION has been applied it is due to the 101 | author's personal experience with these companies and their blatant disregard 102 | for the open source community as well as their illegal methods of interacting 103 | with their employees, clients, and customers. 104 | 105 | Some additional reasons for your consideration: 106 | - Amazon: Refused to release a rewrite of Apache Avro as open source when 107 | the sole developer requested it. Additionally Amazon has extensive modifications 108 | to tens of thousands of open source libraries that they refuse to contribute 109 | back to the upstream creators. Amazon is a plague on the earth. Amazon also 110 | holds many software patents which they claim are only used "in defense", but it 111 | is a complete lie. See their various suits over their ridiculous "One Click" 112 | patent. 113 | 114 | - Apple: Consistently ignores developers reaching out to them to attempt to 115 | get information / support on their products. 116 | 117 | - Avalara: Illegal employment practices. Specfically around reasons for 118 | termination ( past history ) 119 | 120 | - Disney: Copyright should not be eternal. "The vault" 121 | 122 | - Ebay: "Buy it now" patent; https://patents.google.com/patent/US5845265 123 | 124 | - Epic Games: Paying off game companies to make themselves into an 125 | intentional illegal monopoly on offering those games. 126 | 127 | - Headspin: Refused to release iOS support for STF. Abandoned most support 128 | for STF and left it to languish. 129 | 130 | - Micro Focus / SUSE / EQT: Refused to release build process information for 131 | their k8s distribution, only offering the raw source code. This was an 132 | intentional effort to skirt open source requirements and prevent usage of 133 | their k8s offering without paying for support. When directly told that this 134 | was wrong, the leaders at the highest levels of the company responded that 135 | this was intentional and they want to prevent fully open source usage. 136 | 137 | - Baltimore Sun / NBC: Refusal to acknowledge and comply with "Right to forget" 138 | 139 | - Boston Consulting Group: You know "The Bobs" from Office Space? That's them. 140 | 141 | - Nintendo: Threatened to sue David Helkowski at the age of 15 for creating 142 | the fan site N64.org. Eventually they realized that was nonsense and 143 | purchased the domain for the measly sum of $100. Nintendo is/are really 144 | horrible in the way they handle fair usage of their IP. Other examples are 145 | their takedowns of emulators of their systems. RIP UltraHLE. 146 | 147 | - Oracle: Due to their ridiculous attempts to Copyright an API and their 148 | general behavior with Java. 149 | 150 | - Palantir: Spying on US citizens. 151 | 152 | - Sinclair Broadcast Group: Deliberate license breaking/abuse. Refused to 153 | correct their practices and continues to use software illegally. 154 | 155 | - Sony: Due to revoking OtherOS on the PS3 despite it being a labelled 156 | feature on the box we bought the PS3 in. Most never received their $55 for 157 | this, and few wantthat $55. They want OtherOS back. Also due to their 158 | constant updates to device firmware that in no way enhance the devices and 159 | are purely reactions to attempt to prevent fair usage of their devices in an 160 | open source fashion. Sony also released malware to attempt to prevent copying 161 | of their CDs 162 | ( See https://en.wikipedia.org/wiki/Sony_BMG_copy_protection_rootkit_scandal ) 163 | 164 | - Sprint: Because they are now owned by TMobile. 165 | 166 | - TMobile / Insight Global / Logic 20/20: Refusal to give reasonable 167 | accomodation for Autistic engineers. ( and termination for being Autistic ) 168 | 169 | - T.Rowe Price: Terrible internal security practices. 170 | 171 | - Comcast / Verizon: Unethical "overage charges" / violation of Net Neutrality 172 | 173 | - Wells Fargo: Unethical late fees 174 | 175 | - Kobiton / Perfecto / BrowserStack / Sauce Labs: All reverse engineered 176 | Apple APIs and refuse to share the information gained to the open source 177 | community automating iOS devices. 178 | 179 | Considering their disregard for law and common morality it is likely this 180 | license will not prevent their illegal usage of the software, but it is an 181 | attempt to prevent it none the less. 182 | 183 | For an understanding of the moral framework these judgements are made from 184 | see https://github.com/nanoscopic/beliefN 185 | ( specifically the points that information should be free, non-cooperation 186 | with immoral people, and the legal framework available should be used as 187 | possible to do so ) 188 | 189 | ======================================================================= 190 | 191 | Permission is hereby granted, free of charge, to LICENSEE, to conditionally 192 | use, modify, publish, distribute, or sell copies of THE CONTENT. 193 | 194 | These permissions are granted with the following conditions: 195 | 196 | 1. THE CONTENT may not be distributed or sold to THE RESTRICTED PARTY. 197 | 198 | 2. Access to a service containing or utilizing THE CONTENT may not be 199 | given, sold, or licensed to THE RESTRICTED PARTY. 200 | 201 | 3. Any service containing or utilizing THE CONTENT shall be considered a 202 | derivative of this license and therefore by bound by THE RESTRICTION. 203 | This includes use of THE CONTENT by way of dynamic libraries, indirect calls, 204 | scripting, virtualization, containerization, or instantiation as a remote 205 | callable service. Such restricted software must be prevented from being 206 | used by THE RESTRICTED PARTY. 207 | 208 | 4. THE CONTENT cannot be sold to THE RESTRICTED PARTY. 209 | 210 | 5. THE CONTENT may not be published to or distributed through a system 211 | owned or controlled by THE RESTRICTED PARTY. 212 | 213 | 6. Any derivatives of THE CONTENT must be licensed under this same license. 214 | By same license this means the contents of this LICENSE file in its 215 | entirety. No modifications may be made to the license. 216 | 217 | 7. The above copyright notice and this LICENSE file must be included in 218 | all copies and derivative forms of THE CONTENT. 219 | 220 | 8. LICENSEE understands and agrees that this content is provided "as is", 221 | without warranty of any kind, express or implied, including but not limited 222 | to the warranties of merchantability, fitness for a particular purpose and 223 | noninfringement. In no event shall the authors or copyright holders be liable 224 | for any claim, damages or other liability, whether in an action of contract, 225 | tort or otherwise, arising from, out of or in connection with THE CONTENT or 226 | the use or other dealings of it. 227 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET = ios_video_stream 2 | 3 | all: $(TARGET) 4 | 5 | $(TARGET): main.go server.go go.sum 6 | go build -o $(TARGET) . 7 | 8 | go.sum: 9 | go get 10 | go get . 11 | 12 | clean: 13 | $(RM) $(TARGET) go.sum 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 1. What is this? 2 | This is a frame server for use with stf_ios_support. 3 | 4 | ## 2. Installation 5 | 6 | 1. `make` 7 | 8 | ## 3. Usage 9 | 10 | 1. `./ios_video_stream stream [zmq spec to send to] [zmq spec to recv from] [--udid=] 11 | 1. Start a frame source ( such as ios_video_app ) 12 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/nanoscopic/ios_video_stream 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/danielpaulus/go-ios v0.0.0-20190926190740-cc977db05eea // indirect 7 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 // indirect 8 | github.com/google/gousb v0.0.0-20190812193832-18f4c1d8a750 // indirect 9 | github.com/gorilla/websocket v1.4.2 10 | github.com/nanoscopic/ujsonin v1.5.0 11 | github.com/pebbe/zmq4 v1.2.0 // indirect 12 | github.com/pion/rtp v1.1.4 // indirect 13 | github.com/pion/webrtc/v2 v2.1.11 // indirect 14 | github.com/sirupsen/logrus v1.4.2 15 | github.com/stretchr/testify v1.4.0 // indirect 16 | go.nanomsg.org/mangos/v3 v3.0.1 17 | ) 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= 2 | github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= 3 | github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= 4 | github.com/danielpaulus/go-ios v0.0.0-20190926190740-cc977db05eea/go.mod h1:/I8WMN9JIALHl5vtg939USw/gDASCwlC9D+K10QdGB4= 5 | github.com/danielpaulus/gst v0.0.0-20200201205042-e6d2974fceb8/go.mod h1:JbhjLST5AaUXpKQK65g9144BK8QHftbpuFoYuhDuONw= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 9 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 10 | github.com/gdamore/optopia v0.2.0/go.mod h1:YKYEwo5C1Pa617H7NlPcmQXl+vG6YnSSNB44n8dNL0Q= 11 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 12 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 13 | github.com/google/gousb v0.0.0-20190812193832-18f4c1d8a750 h1:DVKHLo3yE4psTjD9aM2pY7EHoicaQbgmaxxvvHC6ZSM= 14 | github.com/google/gousb v0.0.0-20190812193832-18f4c1d8a750/go.mod h1:Tl4HdAs1ThE3gECkNwz+1MWicX6FXddhJEw7L8jRDiI= 15 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 16 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 17 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 18 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 19 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 20 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= 21 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 22 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 23 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 24 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 25 | github.com/lijo-jose/glib v0.0.0-20191012030101-93ee72d7d646/go.mod h1:wzypjnJX+g/LKnKDVvJni/u0gNlQestTwv6Kt/Qf3fk= 26 | github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= 27 | github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= 28 | github.com/nanoscopic/ujsonin v1.1.0 h1:w2IoDngW3p7K1Dj2xqrSoX/G+3sGEmczSicL750nzVI= 29 | github.com/nanoscopic/ujsonin v1.1.0/go.mod h1:Q/8hEeydgTJwpaZyL3UREl3U44P01CI5VAL6ueB47dI= 30 | github.com/nanoscopic/ujsonin v1.5.0 h1:pnbQ6jHsfDm9O/wWLkszM3n73ETeHrsutf7LV3gTTro= 31 | github.com/nanoscopic/ujsonin v1.5.0/go.mod h1:FyHvuWes/DhijYGBTtQB74enYOLiHodd3M3waNV3gWU= 32 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 33 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 34 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 35 | github.com/pebbe/zmq4 v1.2.0/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= 36 | github.com/pion/datachannel v1.4.12/go.mod h1:Ulrx2j4r8c0Za5ltWFv/hZvSpc3ZpvOvcz46tvnt+PY= 37 | github.com/pion/dtls v1.5.2/go.mod h1:v4ULmyyV65geAZQBBckCjgMhmngTqz7HQVsQVYnfkGo= 38 | github.com/pion/ice v0.7.1/go.mod h1:fPnWLWO3B83fJmO6Sci5Mv3ypN4Vd956Py4JlbJfVwU= 39 | github.com/pion/logging v0.2.1/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= 40 | github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= 41 | github.com/pion/mdns v0.0.3/go.mod h1:VrN3wefVgtfL8QgpEblPUC46ag1reLIfpqekCnKunLE= 42 | github.com/pion/quic v0.1.1/go.mod h1:zEU51v7ru8Mp4AUBJvj6psrSth5eEFNnVQK5K48oV3k= 43 | github.com/pion/rtcp v1.2.1/go.mod h1:a5dj2d6BKIKHl43EnAOIrCczcjESrtPuMgfmL6/K6QM= 44 | github.com/pion/rtp v1.1.3/go.mod h1:/l4cvcKd0D3u9JLs2xSVI95YkfXW87a3br3nqmVtSlE= 45 | github.com/pion/rtp v1.1.4/go.mod h1:/l4cvcKd0D3u9JLs2xSVI95YkfXW87a3br3nqmVtSlE= 46 | github.com/pion/sctp v1.7.2/go.mod h1:HlTD+15FeLYYQTTDO35uKEeRLVq5L2AY/ef6ZSvpIXc= 47 | github.com/pion/sdp/v2 v2.3.1/go.mod h1:jccXVYW0fuK6ds2pwKr89SVBDYlCjhgMI6nucl5R5rA= 48 | github.com/pion/srtp v1.2.6/go.mod h1:rd8imc5htjfs99XiEoOjLMEOcVjME63UHx9Ek9IGst0= 49 | github.com/pion/stun v0.3.3/go.mod h1:xrCld6XM+6GWDZdvjPlLMsTU21rNxnO6UO8XsAvHr/M= 50 | github.com/pion/transport v0.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE= 51 | github.com/pion/transport v0.8.9/go.mod h1:lpeSM6KJFejVtZf8k0fgeN7zE73APQpTF83WvA1FVP8= 52 | github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8= 53 | github.com/pion/turn v1.4.0/go.mod h1:aDSi6hWX/hd1+gKia9cExZOR0MU95O7zX9p3Gw/P2aU= 54 | github.com/pion/webrtc/v2 v2.1.11/go.mod h1:spr7E544asF+Y3ajLasHi9fSJTiDjUERRBeTeRimlfE= 55 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 56 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 57 | github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 58 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 59 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 60 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 61 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 62 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 63 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 64 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 65 | github.com/tmobile/stf_ios_mirrorfeed/mirrorfeed/mods/mjpeg v0.0.0-20200109223406-fc86e617c1c5/go.mod h1:g2Z8fnPWJ5xiSDsZieY3fzGGcAQPJI6gO8fSWAIx+q8= 66 | go.nanomsg.org/mangos/v3 v3.0.1 h1:xR8nca0ZeAvwsoRWjeEHuR2/B0N+Po/ZJpGNCpDz6To= 67 | go.nanomsg.org/mangos/v3 v3.0.1/go.mod h1:RxVwsn46YtfJ74mF8MeVo+MFjg545KCI50NuZrFXmzc= 68 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 69 | golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 70 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 71 | golang.org/x/crypto v0.0.0-20191001170739-f9e2070545dc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 72 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 73 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 74 | golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 75 | golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 76 | golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 77 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 78 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 79 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 80 | golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 81 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 82 | golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 83 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 84 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 85 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 86 | golang.org/x/sys v0.0.0-20191023151326-f89234f9a2c2 h1:I7efaDQAsIQmkTF+WSdcydwVWzK07Yuz8IFF8rNkDe0= 87 | golang.org/x/sys v0.0.0-20191023151326-f89234f9a2c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 88 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 89 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 90 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 91 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 92 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 93 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 94 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 95 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 96 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 97 | howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= 98 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "time" 9 | 10 | "go.nanomsg.org/mangos/v3" 11 | "go.nanomsg.org/mangos/v3/protocol/pull" 12 | // register transports 13 | _ "go.nanomsg.org/mangos/v3/transport/all" 14 | 15 | log "github.com/sirupsen/logrus" 16 | ) 17 | 18 | func main() { 19 | var udid = flag.String( "udid" , "" , "Device UDID" ) 20 | var streamCmd = flag.Bool( "stream" , false , "Stream jpegs" ) 21 | var pullSpec = flag.String( "pullSpec" , "tcp://127.0.0.1:7879", "NanoMsg spec to pull jpeg frames from" ) 22 | var iface = flag.String( "interface", "none" , "Network interface to listen on" ) 23 | var port = flag.String( "port" , "8000" , "Network port to listen on" ) 24 | var verbose = flag.Bool( "v" , false , "Verbose Debugging" ) 25 | var secure = flag.Bool( "secure" , false , "Secure HTTPS mode" ) 26 | var cert = flag.String( "cert" , "https-server.crt" , "Cert for HTTP" ) 27 | var key = flag.String( "key" , "https-server.key" , "Key for HTTPS" ) 28 | var coordinator= flag.String( "coordinator", "127.0.0.1:8027" , "Coordinator control address" ) 29 | 30 | flag.Parse() 31 | 32 | log.SetFormatter(&log.JSONFormatter{}) 33 | 34 | if *verbose { 35 | log.Info("Set Debug mode") 36 | log.SetLevel(log.DebugLevel) 37 | } 38 | 39 | if *streamCmd { 40 | stream( *pullSpec, *udid, *iface, *port, *secure, *cert, *key, *coordinator ) 41 | } else { 42 | flag.Usage() 43 | } 44 | } 45 | 46 | func stream( pullSpec string, udid string, tunName string, port string, secure bool, cert string, key string, coordinator string ) { 47 | pullSock := setup_nanomsg_sockets( pullSpec ) 48 | 49 | stopChannel := make( chan bool ) 50 | stopChannel2 := make( chan bool ) 51 | waitForSigInt( stopChannel, stopChannel2 ) 52 | 53 | startJpegServer( pullSock, stopChannel2, port, tunName, secure, cert, key, coordinator, udid ) 54 | 55 | <- stopChannel 56 | } 57 | 58 | func setup_nanomsg_sockets( pullSpec string ) ( pullSock mangos.Socket ) { 59 | var err error 60 | 61 | if pullSock, err = pull.NewSocket(); err != nil { 62 | log.WithFields( log.Fields{ 63 | "type": "err_socket_new", 64 | "zmq_spec": pullSpec, 65 | "err": err, 66 | } ).Fatal("Socket new error") 67 | } 68 | pullSock.SetOption( mangos.OptionRecvDeadline, time.Duration.Seconds( 1 ) ) 69 | if err = pullSock.Listen(pullSpec); err != nil { 70 | log.WithFields( log.Fields{ 71 | "type": "err_socket_bind", 72 | "spec": pullSpec, 73 | "err": err, 74 | } ).Fatal("Socket bind error") 75 | } 76 | return pullSock 77 | } 78 | 79 | func waitForSigInt( stopChannel chan bool, stopChannel2 chan bool ) { 80 | c := make(chan os.Signal, 1) 81 | signal.Notify(c, os.Interrupt) 82 | go func() { 83 | for sig := range c { 84 | fmt.Printf("Got signal %s\n", sig) 85 | go func() { stopChannel2 <- true }() 86 | stopChannel <- true 87 | } 88 | }() 89 | } 90 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "html/template" 7 | //"image" 8 | _ "image/jpeg" 9 | //"io" 10 | "log" 11 | "net" 12 | "net/http" 13 | "os" 14 | "strings" 15 | "sync" 16 | //"time" 17 | 18 | "github.com/gorilla/websocket" 19 | 20 | "go.nanomsg.org/mangos/v3" 21 | // register transports 22 | _ "go.nanomsg.org/mangos/v3/transport/all" 23 | uj "github.com/nanoscopic/ujsonin/mod" 24 | ) 25 | 26 | func callback( r *http.Request ) bool { 27 | return true 28 | } 29 | 30 | var upgrader = websocket.Upgrader { 31 | CheckOrigin: callback, 32 | } 33 | 34 | type ImgMsg struct { 35 | imgNum int 36 | msg string 37 | data []byte 38 | } 39 | 40 | type ImgType struct { 41 | rawData []byte 42 | } 43 | 44 | const ( 45 | WriterStop = iota 46 | ) 47 | 48 | type WriterMsg struct { 49 | msg int 50 | } 51 | 52 | type MainMsg struct { 53 | msg int 54 | } 55 | 56 | const ( 57 | BeginDiscard = iota 58 | EndDiscard 59 | ) 60 | 61 | type Stats struct { 62 | recv int 63 | dumped int 64 | sent int 65 | socketConnected bool 66 | waitCnt int 67 | } 68 | 69 | func startJpegServer( inSock mangos.Socket, stopChannel chan bool, mirrorPort string, tunName string, secure bool, cert string, key string, coordinator string, udid string ) { 70 | var err error 71 | 72 | ifaces, err := net.Interfaces() 73 | if err != nil { 74 | fmt.Printf( err.Error() ) 75 | os.Exit( 1 ) 76 | } 77 | 78 | var listen_addr string 79 | if tunName == "none" { 80 | fmt.Printf("No tunnel specified; listening on all interfaces\n") 81 | listen_addr = ":" + mirrorPort 82 | } else { 83 | foundInterface := false 84 | for _, iface := range ifaces { 85 | addrs, err := iface.Addrs() 86 | if err != nil { 87 | fmt.Printf( err.Error() ) 88 | os.Exit( 1 ) 89 | } 90 | for _, addr := range addrs { 91 | var ip net.IP 92 | switch v := addr.(type) { 93 | case *net.IPNet: 94 | ip = v.IP 95 | case *net.IPAddr: 96 | ip = v.IP 97 | default: 98 | fmt.Printf("Unknown type\n") 99 | } 100 | if iface.Name == tunName { 101 | str := ip.String() 102 | if !strings.Contains(str,":") { 103 | listen_addr = str + ":" + mirrorPort 104 | foundInterface = true 105 | } 106 | } 107 | } 108 | } 109 | if foundInterface == false { 110 | fmt.Printf( "Could not find interface %s\n", tunName ) 111 | os.Exit( 1 ) 112 | } 113 | } 114 | 115 | imgCh := make(chan ImgMsg, 60) 116 | mainCh := make( chan MainMsg, 4 ) 117 | 118 | lock := sync.RWMutex{} 119 | var stats Stats = Stats{} 120 | stats.recv = 0 121 | stats.dumped = 0 122 | stats.socketConnected = false 123 | 124 | statLock := sync.RWMutex{} 125 | 126 | sentSize := false 127 | discard := true 128 | 129 | go func() { 130 | imgnum := 1 131 | 132 | LOOP: 133 | for { 134 | select { 135 | case <- stopChannel: 136 | fmt.Printf("Server channel got stop message\n") 137 | break LOOP 138 | case msg := <- mainCh: 139 | if msg.msg == BeginDiscard { 140 | discard = true 141 | } else if msg.msg == EndDiscard { 142 | discard = false 143 | } 144 | default: // this makes the above read from stopChannel non-blocking 145 | } 146 | 147 | msg, err := inSock.RecvMsg() 148 | 149 | fmt.Printf("Got incoming frame\n") 150 | 151 | if err != nil && err == mangos.ErrRecvTimeout { 152 | continue 153 | } 154 | 155 | if discard && sentSize { 156 | statLock.Lock() 157 | stats.dumped++ 158 | statLock.Unlock() 159 | 160 | msg.Free() 161 | continue 162 | } 163 | 164 | clickScale := 1000 165 | 166 | imgMsg := ImgMsg{} 167 | 168 | // image is prepended by some JSON metadata 169 | if msg.Body[0] == '{' { 170 | endi := strings.Index( string(msg.Body), "}" ) 171 | //fmt.Printf( "out:[" + string( msg.Body[:endi+1] ) + "]" ) 172 | root, left := uj.Parse( msg.Body ) 173 | if ( len(msg.Body ) - len( left ) - 1 ) != endi { 174 | fmt.Printf( "size mistmatched what was parsed: %d != %d\n", endi, len( msg.Body ) - len(left) - 1 ) 175 | } 176 | imgMsg.data = left 177 | 178 | ow := root.Get("ow").Int() 179 | oh := root.Get("oh").Int() 180 | dw := root.Get("dw").Int() 181 | dh := root.Get("dh").Int() 182 | 183 | fmt.Printf("ow=%d, oh=%d, dw=%d, dh=%d\n", ow, oh, dw, dh ) 184 | 185 | if ow != dw { 186 | clickScale = ow / dw * 1000 187 | } 188 | 189 | imgMsg.msg = fmt.Sprintf("Width: %d, Height: %d, Clickscale: %d, Size: %d\n", dw, dh, clickScale, len( msg.Body ) ) 190 | 191 | if !sentSize { 192 | dataReader := bytes.NewBuffer( []byte( fmt.Sprintf( `{"type":"frame1","width":%d,"height":%d,"clickScale":%d,"uuid":"%s"}`, dw, dh, clickScale, udid ) ) ) 193 | http.Post("http://"+coordinator+"/frame", "application/json", dataReader ) 194 | sentSize = true 195 | } 196 | } else { 197 | imgMsg.data = msg.Body 198 | } 199 | 200 | if !discard { 201 | imgMsg.imgNum = imgnum 202 | imgCh <- imgMsg 203 | msg.Free() 204 | } else { 205 | msg.Free() 206 | } 207 | 208 | statLock.Lock() 209 | stats.recv++ 210 | statLock.Unlock() 211 | 212 | imgnum++ 213 | } 214 | }() 215 | 216 | startServer( imgCh, mainCh, &lock, &statLock, &stats, listen_addr, secure, cert, key ) 217 | } 218 | 219 | func startWriter(ws *websocket.Conn,imgCh <-chan ImgMsg,writerCh <-chan WriterMsg,lock *sync.RWMutex, statLock *sync.RWMutex, stats *Stats) { 220 | go func() { 221 | var running bool = true 222 | statLock.Lock() 223 | stats.socketConnected = true 224 | statLock.Unlock() 225 | for { 226 | select { 227 | case imgMsg := <- imgCh: 228 | // Keep receiving images till there are no more to receive 229 | stop := false 230 | for { 231 | select { 232 | case imgMsg = <- imgCh: 233 | 234 | default: 235 | stop = true 236 | } 237 | if stop { break } 238 | } 239 | 240 | msg := []byte(imgMsg.msg) 241 | imgBytes := imgMsg.data 242 | 243 | lock.Lock() 244 | ws.WriteMessage(websocket.TextMessage, msg) 245 | ws.WriteMessage(websocket.BinaryMessage, imgBytes ) 246 | lock.Unlock() 247 | case controlMsg := <- writerCh: 248 | if controlMsg.msg == WriterStop { 249 | running = false 250 | } 251 | } 252 | if running == false { 253 | break 254 | } 255 | } 256 | statLock.Lock() 257 | stats.socketConnected = false 258 | statLock.Unlock() 259 | }() 260 | } 261 | 262 | func startServer( imgCh <-chan ImgMsg, mainCh chan<- MainMsg, lock *sync.RWMutex, statLock *sync.RWMutex, stats *Stats, listen_addr string, secure bool, cert string, key string ) (*http.Server) { 263 | fmt.Printf("Listening on %s\n", listen_addr ) 264 | 265 | echoClosure := func( w http.ResponseWriter, r *http.Request ) { 266 | handleEcho( w, r, imgCh, mainCh, lock, statLock, stats ) 267 | } 268 | statsClosure := func( w http.ResponseWriter, r *http.Request ) { 269 | handleStats( w, r, statLock, stats ) 270 | } 271 | rootClosure := func( w http.ResponseWriter, r *http.Request ) { 272 | handleRoot( w, r, secure ) 273 | } 274 | 275 | server := &http.Server{ Addr: listen_addr } 276 | http.HandleFunc( "/echo", echoClosure ) 277 | http.HandleFunc( "/echo/", echoClosure ) 278 | http.HandleFunc( "/", rootClosure ) 279 | http.HandleFunc( "/stats", statsClosure ) 280 | go func() { 281 | if secure { 282 | server.ListenAndServeTLS( cert, key ); 283 | } else { 284 | server.ListenAndServe() 285 | } 286 | }() 287 | return server 288 | } 289 | 290 | func handleStats( w http.ResponseWriter, r *http.Request, statLock *sync.RWMutex, stats *Stats ) { 291 | statLock.Lock() 292 | recv := stats.recv 293 | dumped := stats.dumped 294 | socketConnected := stats.socketConnected 295 | statLock.Unlock() 296 | waitCnt := stats.waitCnt 297 | 298 | var socketStr string = "no" 299 | if socketConnected { 300 | socketStr = "yes" 301 | } 302 | 303 | fmt.Fprintf( w, "Received: %d
\nDumped: %d
\nSocket Connected: %s
\nWait Count: %d
\n", recv, dumped, socketStr, waitCnt ) 304 | } 305 | 306 | func handleEcho( w http.ResponseWriter, r *http.Request,imgCh <-chan ImgMsg,mainCh chan<- MainMsg, lock *sync.RWMutex, statLock *sync.RWMutex, stats *Stats ) { 307 | c, err := upgrader.Upgrade(w, r, nil) 308 | if err != nil { 309 | log.Print("Upgrade error:", err) 310 | return 311 | } 312 | defer c.Close() 313 | fmt.Printf("Received connection\n") 314 | welcome(c) 315 | 316 | writerCh := make(chan WriterMsg, 2) 317 | 318 | // stop discarding images 319 | mainCh <- MainMsg{ msg: EndDiscard } 320 | 321 | stopped := false 322 | 323 | c.SetCloseHandler( func( code int, text string ) error { 324 | stopped = true 325 | writerCh <- WriterMsg{ msg: WriterStop } 326 | mainCh <- MainMsg{ msg: BeginDiscard } 327 | return nil 328 | } ) 329 | 330 | 331 | startWriter(c,imgCh,writerCh,lock,statLock,stats) 332 | for { 333 | mt, message, err := c.ReadMessage() 334 | if err != nil { 335 | log.Println("read:", err) 336 | break 337 | } 338 | log.Printf("recv: %s", message) 339 | lock.Lock() 340 | err = c.WriteMessage(mt, message) 341 | lock.Unlock() 342 | if err != nil { 343 | log.Println("write:", err) 344 | break 345 | } 346 | } 347 | 348 | // send WriterMsg to terminate writer 349 | if !stopped { 350 | writerCh <- WriterMsg{ msg: WriterStop } 351 | mainCh <- MainMsg{ msg: BeginDiscard } 352 | } 353 | } 354 | 355 | func welcome( c *websocket.Conn ) ( error ) { 356 | msg := ` 357 | { 358 | "version":1, 359 | "length":24, 360 | "pid":12733, 361 | "realWidth":750, 362 | "realHeight":1334, 363 | "virtualWidth":375, 364 | "virtualHeight":667, 365 | "orientation":0, 366 | "quirks":{ 367 | "dumb":false, 368 | "alwaysUpright":true, 369 | "tear":false 370 | } 371 | }` 372 | return c.WriteMessage( websocket.TextMessage, []byte(msg) ) 373 | } 374 | 375 | func handleRoot( w http.ResponseWriter, r *http.Request, secure bool ) { 376 | if secure { 377 | rootTpl.Execute( w, "wss://"+r.Host+"/echo" ) 378 | } else { 379 | rootTpl.Execute( w, "ws://"+r.Host+"/echo" ) 380 | } 381 | } 382 | 383 | var rootTpl = template.Must(template.New("").Parse(` 384 | 385 | 386 | 387 | 388 | 393 | 496 | 497 | 498 | 499 | 500 | 503 | 512 | 515 | 516 |
501 | 502 | 504 |
505 | 506 | 507 |
508 | 509 | 510 |
511 |
513 |
514 |
517 | 518 | 519 | `)) --------------------------------------------------------------------------------