You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
6.3 KiB
Python
226 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
import subprocess
|
|
import os
|
|
import glob
|
|
|
|
|
|
def get_orientation():
|
|
for device in glob.glob('/sys/bus/iio/devices/iio:device*'):
|
|
if os.path.isfile(os.path.join(device, 'in_accel_x_raw')):
|
|
break
|
|
else:
|
|
return 0
|
|
|
|
with open(os.path.join(device, 'in_accel_x_raw')) as handle:
|
|
x = int(handle.read().strip())
|
|
with open(os.path.join(device, 'in_accel_y_raw')) as handle:
|
|
y = int(handle.read().strip())
|
|
with open(os.path.join(device, 'in_accel_z_raw')) as handle:
|
|
z = int(handle.read().strip())
|
|
|
|
max_abs = max(abs(x), abs(y), abs(z))
|
|
if x == max_abs:
|
|
# portrait
|
|
return "portrait"
|
|
if -x == max_abs:
|
|
# upside down portrait
|
|
return "portrait"
|
|
if y == max_abs:
|
|
# landscale counterclockwise
|
|
return "ccw"
|
|
if -y == max_abs:
|
|
# landscape clockwise
|
|
return "cw"
|
|
if z == max_abs:
|
|
# flat on back
|
|
return "flat"
|
|
if -z == max_abs:
|
|
# flat on screen
|
|
return "flat"
|
|
return "cw"
|
|
|
|
|
|
def setup(node, res, debug=False, pixfmt=None):
|
|
speed = 30
|
|
if '@' in res:
|
|
res, speed = res.split('@')
|
|
|
|
busfmts = {
|
|
'UYVY': 'UYVY8_2X8',
|
|
'BA81': 'SBGGR8_1X8',
|
|
'GBRG': 'SGBRG8_1X8',
|
|
'GRBG': 'SGRBG8_1X8',
|
|
'RGGB': 'SRGGB8_1X8',
|
|
}
|
|
if pixfmt in busfmts:
|
|
busfmt = busfmts[pixfmt]
|
|
else:
|
|
busfmt = 'UYVY8_2X8'
|
|
|
|
command = ['sudo', 'media-ctl', '-d', '/dev/media1', '--set-v4l2',
|
|
'"{}":0[fmt:{}/{}@1/{}]'.format(node, busfmt, res, speed)]
|
|
if debug:
|
|
print(command)
|
|
p = subprocess.run(command, timeout=5)
|
|
if p.returncode != 0:
|
|
return False
|
|
|
|
width, height = res.split('x')
|
|
fmt = ['width=' + width, 'height=' + height, 'pixelformat={}'.format(pixfmt)]
|
|
command = ['sudo', 'v4l2-ctl', '--device', '/dev/video1', '--set-fmt-video={}'.format(','.join(fmt))]
|
|
if debug:
|
|
print(command)
|
|
p = subprocess.run(command, timeout=5)
|
|
if p.returncode != 0:
|
|
return False
|
|
|
|
|
|
def take_snapshot(node, res, name, rotate, skip=5, debug=False, pixfmt=None, raw=False):
|
|
if pixfmt is None:
|
|
pixfmt = 'uyvy'
|
|
setup(node, res, debug=debug, pixfmt=pixfmt)
|
|
|
|
speed = 30
|
|
if '@' in res:
|
|
res, speed = res.split('@')
|
|
command = ['sudo', 'v4l2-ctl', '--device', '/dev/video1', '--stream-mmap', '--stream-to=/tmp/frame.raw',
|
|
'--stream-count=1', '--stream-skip={}'.format(skip)]
|
|
if debug:
|
|
print(command)
|
|
p = subprocess.run(command, timeout=10)
|
|
if p.returncode != 0:
|
|
return False
|
|
|
|
if raw:
|
|
os.rename('/tmp/frame.raw', name)
|
|
return True
|
|
|
|
command = ['convert', '-size', res, 'uyvy:/tmp/frame.raw', '-rotate', rotate, name]
|
|
if debug:
|
|
print(command)
|
|
p = subprocess.run(command, timeout=15)
|
|
if p.returncode != 0:
|
|
return False
|
|
|
|
command = ['sudo', 'rm', '-rf', '/tmp/frame.raw']
|
|
if debug:
|
|
print(command)
|
|
subprocess.run(command, timeout=5)
|
|
|
|
return True
|
|
|
|
|
|
def record(node, res, name, rotate, debug=False):
|
|
setup(node, res, debug=debug, pixfmt='uyvy')
|
|
|
|
speed = 30
|
|
if '@' in res:
|
|
res, speed = res.split('@')
|
|
|
|
command = ['ffmpeg', '-f', 'v4l2', '-framerate', str(speed), '-video_size', res, '-i', '/dev/video1', '-preset',
|
|
'ultrafast', name]
|
|
if debug:
|
|
print(command)
|
|
p = subprocess.run(command)
|
|
if p.returncode != 0:
|
|
return False
|
|
|
|
|
|
def set_route(camera):
|
|
if camera == 'ov5640':
|
|
links = [
|
|
'"gc2145 3-003c":0->"sun6i-csi":0[0]',
|
|
'"ov5640 3-004c":0->"sun6i-csi":0[1]'
|
|
]
|
|
elif camera == 'gc2145':
|
|
links = [
|
|
'"ov5640 3-004c":0->"sun6i-csi":0[0]',
|
|
'"gc2145 3-003c":0->"sun6i-csi":0[1]'
|
|
]
|
|
else:
|
|
raise Exception("Something wrong")
|
|
for link in links:
|
|
subprocess.run(['sudo', 'media-ctl', '-d', '/dev/media1', '--links', link])
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
modes_ov5640 = {
|
|
'max': '1920x1080@20',
|
|
'1080p': '1920x1080@20',
|
|
'1080p20': '1920x1080@20',
|
|
'1080p15': '1920x1080@15',
|
|
'1080p5': '1920x1080@5',
|
|
'720p': '1280x720@30',
|
|
'720p60': '1280x720@60',
|
|
'720p50': '1280x720@50',
|
|
'720p30': '1280x720@30',
|
|
'720p25': '1280x720@25',
|
|
'720p24': '1280x720@24',
|
|
}
|
|
modes_gc2145 = {
|
|
'max': '1600x1200@15',
|
|
'1200p': '1600x1200@15',
|
|
'1200p15': '1600x1200@15',
|
|
'1200p1': '1600x1200@1',
|
|
'720p': '1280x720@30',
|
|
'720p1': '1280x720@1',
|
|
'1080p15': '1920x1080@15',
|
|
}
|
|
|
|
options = set(modes_ov5640.keys())
|
|
options.update(modes_gc2145.keys())
|
|
|
|
parser = argparse.ArgumentParser(description="PinePhone camera tool")
|
|
parser.add_argument('action', choices=['still', 'movie'])
|
|
parser.add_argument('--resolution', '-r', choices=options, default='1080p')
|
|
parser.add_argument('--camera', '-c', choices=['rear', 'front'], default='rear')
|
|
parser.add_argument('--debug', '-d', action="store_true")
|
|
parser.add_argument('--raw', action="store_true", help="store raw frame")
|
|
parser.add_argument('--pixfmt', '-p', help="pixelformat for raw frame", default="UYVY")
|
|
parser.add_argument('filename')
|
|
args = parser.parse_args()
|
|
|
|
skip = 5
|
|
orientation = get_orientation()
|
|
if args.camera == "rear":
|
|
set_route("ov5640")
|
|
node = 'ov5640 3-004c'
|
|
if orientation == "portrait":
|
|
angle = '90'
|
|
elif orientation == "cw":
|
|
angle = '0'
|
|
elif orientation == "ccw":
|
|
angle = '180'
|
|
else:
|
|
angle = '0'
|
|
modes = modes_ov5640
|
|
else:
|
|
set_route("gc2145")
|
|
node = 'gc2145 3-003c'
|
|
if orientation == "portrait":
|
|
angle = '270'
|
|
elif orientation == "cw":
|
|
angle = '0'
|
|
elif orientation == "ccw":
|
|
angle = '180'
|
|
else:
|
|
angle = '0'
|
|
modes = modes_gc2145
|
|
skip = 0
|
|
|
|
mode = modes[args.resolution]
|
|
|
|
if args.action == "still":
|
|
take_snapshot(node, mode, args.filename, angle, skip=skip, debug=args.debug, pixfmt=args.pixfmt, raw=args.raw)
|
|
elif args.action == "movie":
|
|
record(node, mode, args.filename, angle, debug=args.debug)
|
|
else:
|
|
print("Unsupported action")
|
|
exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|