185 lines
5.5 KiB
Python
185 lines
5.5 KiB
Python
|
|
# Copyright 2023 The Android Open Source Project
|
||
|
|
#
|
||
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
# you may not use this file except in compliance with the License.
|
||
|
|
# You may obtain a copy of the License at
|
||
|
|
#
|
||
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
#
|
||
|
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
# See the License for the specific language governing permissions and
|
||
|
|
# limitations under the License.
|
||
|
|
"""Verifies advertised FPS from device as webcam."""
|
||
|
|
|
||
|
|
import logging
|
||
|
|
import time
|
||
|
|
|
||
|
|
import AVFoundation
|
||
|
|
import Cocoa
|
||
|
|
from libdispatch import dispatch_queue_create
|
||
|
|
import objc
|
||
|
|
|
||
|
|
|
||
|
|
objc.loadBundle('AVFoundation',
|
||
|
|
bundle_path=objc.pathForFramework('AVFoundation.framework'),
|
||
|
|
module_globals=globals())
|
||
|
|
|
||
|
|
_TOTAL_NUM_FRAMES = 0
|
||
|
|
_DEVICE_NAME = 'android' # TODO b/277159494
|
||
|
|
_FPS_TEST_DURATION = 10 # seconds
|
||
|
|
|
||
|
|
|
||
|
|
class SampleBufferDelegate(Cocoa.NSObject):
|
||
|
|
"""Notified upon every frame. Used to help calculate FPS.
|
||
|
|
"""
|
||
|
|
|
||
|
|
def captureOutput_didOutputSampleBuffer_fromConnection_(self,
|
||
|
|
output,
|
||
|
|
sample_buffer,
|
||
|
|
connection):
|
||
|
|
global _TOTAL_NUM_FRAMES
|
||
|
|
|
||
|
|
if sample_buffer:
|
||
|
|
_TOTAL_NUM_FRAMES += 1
|
||
|
|
|
||
|
|
|
||
|
|
def initialize_device():
|
||
|
|
"""Initializes android webcam device.
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Returns the device if an android device is found, None otherwise
|
||
|
|
"""
|
||
|
|
devices = AVFoundation.AVCaptureDevice.devices()
|
||
|
|
|
||
|
|
res_device = None
|
||
|
|
for device in devices:
|
||
|
|
if (device.hasMediaType_(AVFoundation.AVMediaTypeVideo) and
|
||
|
|
(_DEVICE_NAME in device.localizedName().lower())):
|
||
|
|
res_device = device
|
||
|
|
logging.info('Using webcam %s', res_device.localizedName())
|
||
|
|
break
|
||
|
|
|
||
|
|
return res_device
|
||
|
|
|
||
|
|
|
||
|
|
def initialize_formats_and_resolutions(device):
|
||
|
|
"""Initializes list of device advertised formats, resolutions and FPS.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
device: the device from which the info will be retrieved
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Returns a list of the formats, resolutions, and frame rates
|
||
|
|
[ (format, [resolution, [frame rates] ] )]
|
||
|
|
"""
|
||
|
|
supported_formats = device.formats()
|
||
|
|
formats_and_resolutions = []
|
||
|
|
|
||
|
|
for format_index, frmt in enumerate(supported_formats):
|
||
|
|
formats_and_resolutions.append((frmt, []))
|
||
|
|
frame_rate_ranges = frmt.videoSupportedFrameRateRanges()
|
||
|
|
|
||
|
|
for frame_rate_range in frame_rate_ranges:
|
||
|
|
min_frame_rate = frame_rate_range.minFrameRate()
|
||
|
|
max_frame_rate = frame_rate_range.maxFrameRate()
|
||
|
|
default_frame_rate = (
|
||
|
|
device.activeVideoMinFrameDuration().timescale /
|
||
|
|
device.activeVideoMinFrameDuration().value)
|
||
|
|
|
||
|
|
formats_and_resolutions[format_index][1].append(min_frame_rate + 1)
|
||
|
|
formats_and_resolutions[format_index][1].append(max_frame_rate)
|
||
|
|
formats_and_resolutions[format_index][1].append(default_frame_rate)
|
||
|
|
|
||
|
|
return formats_and_resolutions
|
||
|
|
|
||
|
|
|
||
|
|
def setup_for_test_fps(device, supported_formats_and_resolutions):
|
||
|
|
"""Configures device with format, resolution, and FPS to be tested.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
device: device under test
|
||
|
|
supported_formats_and_resolutions: list containing supported device
|
||
|
|
configurations
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Returns a list of tuples, where the first element is the frame
|
||
|
|
rate that was being tested and the second element is the actual fps
|
||
|
|
"""
|
||
|
|
res = []
|
||
|
|
|
||
|
|
delegate = SampleBufferDelegate.alloc().init()
|
||
|
|
session = AVFoundation.AVCaptureSession.alloc().init()
|
||
|
|
|
||
|
|
input_for_session = (
|
||
|
|
AVFoundation.AVCaptureDeviceInput.deviceInputWithDevice_error_(device,
|
||
|
|
None))
|
||
|
|
session.addInput_(input_for_session[0])
|
||
|
|
|
||
|
|
video_output = AVFoundation.AVCaptureVideoDataOutput.alloc().init()
|
||
|
|
queue = dispatch_queue_create(b'1', None)
|
||
|
|
video_output.setSampleBufferDelegate_queue_(delegate, queue)
|
||
|
|
session.addOutput_(video_output)
|
||
|
|
|
||
|
|
session.startRunning()
|
||
|
|
|
||
|
|
for elem in supported_formats_and_resolutions:
|
||
|
|
frmt = elem[0]
|
||
|
|
frame_rates = elem[1]
|
||
|
|
|
||
|
|
for frame_rate in frame_rates:
|
||
|
|
global _TOTAL_NUM_FRAMES
|
||
|
|
_TOTAL_NUM_FRAMES = 0 # reset total num frames for every test
|
||
|
|
device.lockForConfiguration_(None)
|
||
|
|
device.setActiveFormat_(frmt)
|
||
|
|
device.setActiveVideoMinFrameDuration_(
|
||
|
|
AVFoundation.CMTimeMake(1, frame_rate))
|
||
|
|
device.setActiveVideoMaxFrameDuration_(
|
||
|
|
AVFoundation.CMTimeMake(1, frame_rate))
|
||
|
|
device.unlockForConfiguration()
|
||
|
|
|
||
|
|
res.append((frame_rate, test_fps()))
|
||
|
|
|
||
|
|
session.removeInput_(input_for_session)
|
||
|
|
session.removeOutput_(video_output)
|
||
|
|
session.stopRunning()
|
||
|
|
input_for_session[0].release()
|
||
|
|
video_output.release()
|
||
|
|
return res
|
||
|
|
|
||
|
|
|
||
|
|
def test_fps():
|
||
|
|
"""Tests and returns test estimated fps.
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Test estimated fps
|
||
|
|
"""
|
||
|
|
start_time = time.time()
|
||
|
|
time.sleep(_FPS_TEST_DURATION)
|
||
|
|
end_time = time.time()
|
||
|
|
fps = _TOTAL_NUM_FRAMES / (end_time - start_time)
|
||
|
|
return fps
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
device = initialize_device()
|
||
|
|
|
||
|
|
if not device:
|
||
|
|
logging.error('Supported device not found!')
|
||
|
|
return []
|
||
|
|
|
||
|
|
supported_formats_and_resolutions = initialize_formats_and_resolutions(device)
|
||
|
|
|
||
|
|
if not supported_formats_and_resolutions:
|
||
|
|
logging.error('Error retrieving formats and resolutions')
|
||
|
|
return []
|
||
|
|
|
||
|
|
res = setup_for_test_fps(device, supported_formats_and_resolutions)
|
||
|
|
|
||
|
|
return res
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
main()
|