summaryrefslogtreecommitdiff
path: root/src/android/jpeg/exif.h
AgeCommit message (Expand)Author
2021-06-25libcamera/base: Move span to base libraryKieran Bingham
2021-03-10android: jpeg: exif: Fix and expand setRationalPaul Elder
2021-01-27android: jpeg: exif: Add functions for setting various valuesPaul Elder
2021-01-27android: jpeg: exif: Expand setString to support different encodingsPaul Elder
2020-10-28android: jpeg: post_processor_jpeg: Embed thumbnail into Exif metadataUmang Jain
2020-10-10android: jpeg: Use LGPL-2.1 licenseUmang Jain
2020-10-02src: android: exif: Set the class byte orderingKieran Bingham
2020-09-20android: jpeg: exif: Use reentrant localtime_r()Laurent Pinchart
2020-09-10android: jpeg: Support an initial set of EXIF metadata tagsUmang Jain
2020-09-10android: jpeg: Add EXIF infrastructureKieran Bingham
9 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2020, Google Inc.
#
# Author: Paul Elder <paul.elder@ideasonboard.com>
#
# v4l2_compat_test.py - Test the V4L2 compatibility layer

import argparse
import glob
import os
from packaging import version
import re
import shutil
import signal
import subprocess
import sys

MIN_V4L_UTILS_VERSION = version.parse("1.21.0")

TestPass = 0
TestFail = -1
TestSkip = 77


supported_pipelines = [
    'bcm2835-isp',
    'uvcvideo',
    'vimc',
]


def grep(exp, arr):
    return [s for s in arr if re.search(exp, s)]


def run_with_stdout(*args, env={}):
    try:
        with open(os.devnull, 'w') as devnull:
            output = subprocess.check_output(args, env=env, stderr=devnull)
        ret = 0
    except subprocess.CalledProcessError as err:
        output = err.output
        ret = err.returncode
    return ret, output.decode('utf-8').split('\n')


def extract_result(result):
    res = result.split(', ')
    ret = {}
    ret['total']     = int(res[0].split(': ')[-1])
    ret['succeeded'] = int(res[1].split(': ')[-1])
    ret['failed']    = int(res[2].split(': ')[-1])
    ret['warnings']  = int(res[3].split(': ')[-1])
    ret['device']    = res[0].split()[4].strip(':')
    ret['driver']    = res[0].split()[2]
    return ret


def test_v4l2_compliance(v4l2_compliance, v4l2_compat, device, base_driver):
    ret, output = run_with_stdout(v4l2_compliance, '-s', '-d', device, env={'LD_PRELOAD': v4l2_compat})
    if ret < 0:
        output.append(f'Test for {device} terminated due to signal {signal.Signals(-ret).name}')
        return TestFail, output

    result = extract_result(output[-2])
    if result['failed'] == 0:
        return TestPass, None

    # vimc will fail s_fmt because it only supports framesizes that are
    # multiples of 3
    if base_driver == 'vimc' and result['failed'] == 1:
        failures = grep('fail', output)
        if re.search('S_FMT cannot handle an invalid format', failures[0]) is None:
            return TestFail, output
        return TestPass, None

    return TestFail, output


def main(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument('-a', '--all', action='store_true',
                        help='Test all available cameras')
    parser.add_argument('v4l2_compat', type=str,
                        help='Path to v4l2-compat.so')
    args = parser.parse_args(argv[1:])

    v4l2_compat = args.v4l2_compat

    v4l2_compliance = shutil.which('v4l2-compliance')
    if v4l2_compliance is None:
        print('v4l2-compliance is not available')
        return TestSkip

    ret, out = run_with_stdout(v4l2_compliance, '--version')
    if ret != 0 or version.parse(out[0].split()[1].replace(',', '')) < MIN_V4L_UTILS_VERSION:
        print('v4l2-compliance version >= 1.21.0 required')
        return TestSkip

    v4l2_ctl = shutil.which('v4l2-ctl')
    if v4l2_ctl is None:
        print('v4l2-ctl is not available')
        return TestSkip

    ret, out = run_with_stdout(v4l2_ctl, '--version')
    if ret != 0 or version.parse(out[0].split()[-1]) < MIN_V4L_UTILS_VERSION:
        print('v4l2-ctl version >= 1.21.0 required')
        return TestSkip

    dev_nodes = glob.glob('/dev/video*')
    if len(dev_nodes) == 0:
        print('no video nodes available to test with')
        return TestSkip

    failed = []
    drivers_tested = {}
    for device in dev_nodes:
        ret, out = run_with_stdout(v4l2_ctl, '-D', '-d', device, env={'LD_PRELOAD': v4l2_compat})
        if ret < 0:
            failed.append(device)
            print(f'v4l2-ctl failed on {device} with v4l2-compat')
            continue
        driver = grep('Driver name', out)[0].split(':')[-1].strip()
        if driver != "libcamera":
            continue

        ret, out = run_with_stdout(v4l2_ctl, '-D', '-d', device)
        if ret < 0:
            failed.append(device)
            print(f'v4l2-ctl failed on {device} without v4l2-compat')
            continue
        driver = grep('Driver name', out)[0].split(':')[-1].strip()
        if driver not in supported_pipelines:
            continue

        # TODO: Add kernel version check when vimc supports scaling
        if driver == "vimc":
            continue

        if not args.all and driver in drivers_tested:
            continue

        print(f'Testing {device} with {driver} driver... ', end='')
        ret, msg = test_v4l2_compliance(v4l2_compliance, v4l2_compat, device, driver)
        if ret == TestFail:
            failed.append(device)
            print('failed')
            print('\n'.join(msg))
        else:
            print('success')
        drivers_tested[driver] = True

    if len(drivers_tested) == 0:
        print(f'No compatible drivers found')
        return TestSkip

    if len(failed) > 0:
        print(f'Failed {len(failed)} tests:')
        for device in failed:
            print(f'- {device}')

    return TestPass if not failed else TestFail


if __name__ == '__main__':
    sys.exit(main(sys.argv))