<> == Overview == The Go''''''Pro cameras are small, robust, shake-compensating and offer a wide field of view. Thus they are fine devices for recording or sharing your physical adventures. Sadly the cameras are rather hard to integrate into a streaming environment. The following description should help fellow users along that path. It is tested with the Go''''''Pro Hero7 Silver, but should work for many other models, too. == Camera interfaces == === Wireless network === The device can create a wireless network: * the connection details can be found in the device's preference menu (via the touchscreen) * SSID and password can be changed via the API (see {{{goprocom}}}) * only "ap" (accesspoint) mode is supported (not "sta" (station)) * thus it is not possible to simply join an existing local network * clients are isolated (i.e. you cannot reach your router, if these two devices are connected to the camera network) * 2,4 and 5 GHz are supported * the camera enters sleep mode after a period of inactivity, but it keeps its wireless network open, as long as there is another client connected to it * this allows a "wakeup" packet to be received (based on the MAC address of the camera) * thus clients should be disconnected after usage * the camera always uses the IP {{{10.5.5.9}}} === HTTP API === The camera supports an HTTP interface for reading and modifying the device's properties: * inspect the meaning of the device's control registers: {{{curl -sS http://10.5.5.9/gp/gpControl}}} * sadly many control registers are not really writable (or some values are not accepted) * settings can be written with an HTTP GET request, e.g. `curl -sS http://10.5.5.9/gp/gpControl/setting/2/9` (change the resolution to 1080p) * sometimes changes are outright rejected (with a malformed JSON response containing unescaped special characters; no reason is given) * sometimes changes are accepted, but not applied (the previous value stays unchanged) * the current state of all settings can be inspected: {{{ curl -sS http://10.5.5.9/gp/gpControl/status }}} === Streaming format === A video stream can be started by sending a packet to the camera: {{{ echo '_GPHD_:0:0:2:0.000000'; done | nc -u -q 0 10.5.5.9 8554 }}} (other cameras may need a different packet, e.g. {{{_GPHD_:1:0:2:0.000000}}} for Hero8/9 Black) The UDP stream points is sent towards port 8554 of the sender (of the above packet). The stream must be kept awake by sending the above packet repeatedly. A period of 2.5 seconds seems to be a good choice. The stream is probably only meant to be used for a preview within the mobile app. But it can also be used for other purposes, of course. At least for the Hero7 Silver the stream was not easy to access properly (e.g. VLC, mplayer of ffplay produced only partial and distorted frames). See below for details. Technically (according to {{{ffprobe}}}), the stream seems to contain the following channels: {{{ Stream #0:0[0x1011]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuvj420p(pc, smpte170m, progressive), 640x480 [SAR 1:1 DAR 4:3], 30 fps, 30 tbr, 90k tbn, 60 tbc Stream #0:1[0x1100]: Audio: aac ([15][0][0][0] / 0x000F), 0 channels, fltp Stream #0:2[0x200]: Unknown: none ([128][0][0][0] / 0x0080) Stream #0:3[0x201]: Audio: ac3 ([129][0][0][0] / 0x0081), 0 channels, fltp }}} == Remote Streaming == Please note, that you need to start the stream (and keep it awake) by sending the following packet periodically to the camera: {{{ while sleep 2.5; do echo '_GPHD_:0:0:2:0.000000'; done | nc -u -q 0 10.5.5.9 8554 }}} You can easily watch the amount of bandwidth used by the stream via {{{bwm-ng}}} or similar tools. === ffmpeg === For unknown reasons {{{ffmpeg}}} / {{{ffplay}}} produced only distorted and partial frames (at least for my Hero7 Silver): {{{ ffplay -fflags nobuffer -f:v mpegts -probesize 8192 udp://:8554 }}} After quite a good amount of playing around with different ffmpeg's and ffplay's options, I gave up. === gstreamer === [[https://gstreamer.freedesktop.org/|gstreamer]] is a great pipeline-based processor for audio and video data. For unknown reasons it was quite trivial (compared to {{{ffmpeg}}}) to extract a beautiful video stream with its [[https://gstreamer.freedesktop.org/documentation/udp/udpsrc.html|udpsrc]] module. * watch the stream locally: {{{ gst-launch-1.0 udpsrc uri=udp://0.0.0.0:8554 \! tsdemux latency=100 \! h264parse \! avdec_h264 \! videoconvert \! gtksink }}} * send the stream to a remote RTMP server (see below for its setup): {{{ gst-launch-1.0 udpsrc uri=udp://0.0.0.0:8554 \! tsdemux latency=100 \! "video/x-h264,profile=baseline,framerate=10/1" \! h264parse \! flvmux streamable=true \! queue \! rtmpsink location="rtmp://example.org/camera/foo live=1" }}} * serve a stream for local access (e.g. via [[https://obsproject.com/|OBS Studio]]): {{{ gst-launch-1.0 udpsrc uri=udp://0.0.0.0:8554 \! tsdemux latency=100 \! "video/x-h264,profile=baseline,framerate=10/1" \! queue \! udpsink host=127.0.0.1 port=1234 }}} * since v26.1 OBS supports the ''Virtual CAM'' feature (providing a {{{/dev/videoX}}} device to be used in a browser and other desktop programs - see below for a similar approach directly via gstreamer) * create a local video source (similar to a local webcam): {{{ gst-launch-1.0 udpsrc uri=udp://0.0.0.0:8554 \! tsdemux latency=100 \! "video/x-h264, profile=baseline, framerate=30/1, width=640, height=480, format=(string)YUY2" \! h264parse \! avdec_h264 \! queue \! videoconvert \! v4l2sink device=/dev/video2 sync=false }}} * adjust the {{{/dev/videoX}}} selection according to your locally accessible devices * the {{{/dev/videoX}}} device can be used by your local programs (e.g. join a [[https://jit.si|jitsi]]] meeting with your browser) * the {{{v4l2loopback}}} module is required ({{{apt install v4l2loopback-dkms}}}) * Chromium: in order to use this virtual ''camera'' (e.g. for video conferences), you need to add a specific parameter when loading the module (see [[https://github.com/umlaeute/v4l2loopback/issues/274|v4l2loopback#274]]): {{{ modprobe v4l2loopback exclusive_caps=1 }}} * Firefox: only the very first frame is shown (no further updates) - see [[https://github.com/umlaeute/v4l2loopback/issues/190|v4l2loopback#190]] for details == Stream server (nginx) == The {{{rtmpsink}}} pipeline above can be used for sending the stream to an rtmp server. An easily accessible setup can be accomplished with nginx in combination with the [[https://github.com/arut/nginx-rtmp-module/|rtmp module]]: * {{{apt install libnginx-mod-rtmp}}} * module configuration for receiving a stream (to be placed below {{{/etc/nginx/modules-enabled/}}}, since it operates above the {{{http}}} level): {{{ rtmp_auto_push on; rtmp { server { listen 1935; chunk_size 4000; application camera { live on; dash on; dash_nested; dash_path /run/nginx-rtmp/dash; dash_fragment 3; dash_playlist_length 3; hls on; hls_nested on; hls_fragment_naming system; hls_path /run/nginx-rtmp/hls; hls_fragment 3; hls_playlist_length 6; allow publish 127.0.0.1; deny publish all; allow play 192.168.0.0/24; deny play all; } } } }}} * site configuration for delivering the streams via HTTP (to be located below {{{/etc/nginx/sites-enabled/}}}: {{{ server { listen 80; server_name stream.example.org; root /var/www/html; # Source: https://gist.github.com/spiermar/b389b96642fb973d0cc9766ee7b559ac location /play/ { add_header Cache-Control no-cache; # CORS setup add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Expose-Headers' 'Content-Length'; # Allow CORS preflight requests if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain charset=UTF-8'; add_header 'Content-Length' 0; return 204; } location /play/hls/ { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } alias /run/nginx-rtmp/hls/; } location /play/dash/ { alias /run/nginx-rtmp/dash/; types { application/dash+xml mpd; video/mp4 mp4; } } } } }}} The streams are accessible in a variety of ways: * RTMP stream (if the source IP address is allowed - see above): {{{vlc rtmp://stream.example.org/camera/foo}}} * HTTP stream: * dash: {{{http://stream.example.org/play/dash/foo/index.mpd}}} * hls: {{{http://stream.example.org/play/hls/foo/index.m3u8}}} The stream obviously only contains data while your are sending an input stream (see above: {{{rtmpsink}}}). == Useful ressources == * [[https://github.com/KonradIT/gopro-py-api/|gopro-py-api]]: a python library ({{{goprocam}}}) for controlling some Go''''''Pro devices * [[https://github.com/KonradIT/goprowifihack|goprowifihack]]: documentation of some HTTP API calls for Go''''''Pro devices * [[https://gstreamer.freedesktop.org/documentation/|gstreamer documentation]]: fine documentation of all the gstreamer modules * [[https://github.com/arut/nginx-rtmp-module/wiki/Directives|nginx-rtmp directives]]: documentation of the directives provided by the nginx module {{{rtmp}}}