#!/bin/env python3 import argparse import logging import sys import signal import subprocess import datetime import os.path import time import urllib.request, urllib.parse from goprocam import GoProCamera, constants # setup logging facilities logging.basicConfig(level=logging.INFO) def setup_cam(dev='gp0'): logging.info(f"Connecting to gopro camera at {dev}") return GoProCamera.GoPro(ip_address=GoProCamera.GoPro.getWebcamIP(dev), mac_address='0e:7c:80:b5:82:0e', camera=constants.gpcontrol, webcam_device=dev) def configure_cam(gp, resolution="720p", fps="60", fov="0", autooff=constants.Setup.AutoOff.Never): gp.video_settings(resolution, fps) gp.gpControlSet(constants.Setup.AUTO_OFF, autooff) def take_photo(gp, outdir='.'): logging.info('Snapping picture!') mediauri = gp.take_photo() medianame = os.path.basename(urllib.parse.urlparse(mediauri).path) outpath = os.path.join(outdir, medianame) logging.info(f"Saving to {outpath}") urllib.request.urlretrieve(mediauri, filename=outpath) def webcam(gp, resolution=constants.Webcam.Resolution.R720p, fov=constants.Webcam.FOV.Wide): # prepare v4l2 device logging.info('Creating v4l2 device node') v4l2p = subprocess.run([ 'doas -n modprobe v4l2loopback && doas -n v4l2loopback-ctl add -n GoPro -x 1 || true' ], shell=True, check=True, capture_output=True) vnode = v4l2p.stdout.decode('utf-8').strip() logging.info(f'Starting webcam mode ({vnode})') gp.webcamFOV(fov=fov) gp.startWebcam(resolution=resolution) try: # gracefull handle sigterm def sigterm_handler(sig, frame): raise SystemExit('SIGTERM') signal.signal(signal.SIGTERM, sigterm_handler) # pipe video from camera to v4l2 device udp_stream = f"udp://{gp.getWebcamIP()}:8554" ffmpeg_args = [ f"ffmpeg -hide_banner -loglevel warning -vsync 2 -fflags nobuffer -flags low_delay -probesize 3072 -nostdin -i {udp_stream} -ar 44100 -f v4l2 -vcodec rawvideo -pix_fmt yuv420p {vnode}" ] logging.info('Running ffmpeg') subprocess.run(ffmpeg_args, shell=True, check=True) except (KeyboardInterrupt, SystemExit, Exception): # allow camera to shut down time.sleep(1) # stop webcam mode logging.info('Stopping webcam mode') gp.stopWebcam() subprocess.run([f"doas -n v4l2loopback-ctl del {vnode}"], shell=True, check=True) # done logging.info('Exiting...') parser = argparse.ArgumentParser(description='gopro cli utility') parser.add_argument('-o', '--output-directory', type=str, default=['.'], help='Directory to save files to.') parser.add_argument('-r', '--resolution', nargs=1, type=str, choices=('1080p', '720p', '420p'), default='1080p', help='Video resolution') # parser.add_argument('-f', # '--fps', # type=str, # default=['60'], # help='Video frame rate') parser.add_argument('-v', '--fov', type=str, choices=('wide', 'linear', 'narrow'), default=['wide'], help='Video field of view') group = parser.add_mutually_exclusive_group() group.add_argument('--take-photo', action='store_true', help='Take a photo and save it to OUTPUT.') group.add_argument('--webcam', action='store_true', help='Start webcam mode and register as v4l2 device') args = parser.parse_args() res = constants.Webcam.Resolution.R720p if args.resolution: if args.resolution == '1080p': res = constants.Webcam.Resolution.R1080p elif args.resolution == '720p': res = constants.Webcam.Resolution.R720p elif args.resolution == '480p': res = constants.Webcam.Resolution.R480p fov = constants.Webcam.FOV.Wide if args.fov: if args.fov == 'wide': fov = constants.Webcam.FOV.Wide elif args.fov == 'linear': fov = constants.Webcam.FOV.Linear elif args.fov == 'narrow': fov = constants.Webcam.FOV.Narrow # get camera network device from environment # should be up and connected (with an ip address assigned) gp = setup_cam(os.getenv('GOPRO_DEVICE', 'gp0')) # configure_cam(gp, resolution=args.resolution, fps=args.fps, fov=args.fov) try: if args.take_photo: take_photo(gp, outdir=args.output_directory[0]) if args.webcam: webcam(gp, resolution=res, fov=fov) except Exception as err: logging.error(f"error: {err}") exit(1)