~martijnbraam/python-pinecamera

bbee468ba9fc7dbab75382c4937140f4e9c72bf0 — Martijn Braam 3 months ago
Initial commit
1 files changed, 192 insertions(+), 0 deletions(-)

A camera.py
A  => camera.py +192 -0
@@ 1,192 @@
#!/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):
    speed = 30
    if '@' in res:
        res, speed = res.split('@')

    command = ['sudo', 'media-ctl', '-d', '/dev/media1', '--set-v4l2',
               '"{}":0[fmt:UYVY8_2X8/{}@1/{}]'.format(node, 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=UYVY']
    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):
    setup(node, res, debug=debug)

    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

    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)

    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',
        '720p': '1280x720@30',
        '720p60': '1280x720@60',
        '720p50': '1280x720@50',
        '720p30': '1280x720@30',
        '720p25': '1280x720@25',
        '720p24': '1280x720@24',
    }
    modes_gc2145 = {
        '1080p': '1920x1080@30',
        '720p': '1280x720@30',
        '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('filename')
    args = parser.parse_args()

    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'
        angle = '270'
        modes = modes_gc2145

    mode = modes[args.resolution]

    if args.action == "still":
        take_snapshot(node, mode, args.filename, angle, debug=args.debug)
    elif args.action == "movie":
        record(node, mode, args.filename, angle, debug=args.debug)
    else:
        print("Unsupported action")
        exit(1)


if __name__ == "__main__":
    main()