| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <getopt.h> |
| |
| #include <memory> |
| #include <string> |
| #include <unordered_map> |
| |
| #include "camera_characteristics.h" |
| #include "common_types.h" |
| #include "media_v4l2_device.h" |
| |
| static void PrintUsage(int argc, char** argv) { |
| printf("Usage: %s [options]\n\n" |
| "Options:\n" |
| "--help Print usage\n" |
| "--device=DEVICE_NAME Video device name [/dev/video]\n" |
| "--usb-info=VID:PID Device vendor id and product id\n", |
| argv[0]); |
| } |
| |
| static const char short_options[] = "?d:u:"; |
| static const struct option |
| long_options[] = { |
| { "help", no_argument, NULL, '?' }, |
| { "device", required_argument, NULL, 'd' }, |
| { "usb-info", required_argument, NULL, 'u' }, |
| { 0, 0, 0, 0 } |
| }; |
| |
| int RunTest(V4L2Device* device, V4L2Device::IOMethod io, |
| uint32_t buffers, uint32_t capture_time_in_sec, uint32_t width, |
| uint32_t height, uint32_t pixfmt, uint32_t fps) { |
| int32_t retcode = 0; |
| if (!device->InitDevice(io, width, height, pixfmt, fps)) |
| retcode = 1; |
| |
| if (!retcode && !device->StartCapture()) |
| retcode = 2; |
| |
| if (!retcode && !device->Run(capture_time_in_sec)) |
| retcode = 3; |
| |
| if (!retcode && !device->StopCapture()) |
| retcode = 4; |
| |
| if (!retcode && !device->UninitDevice()) |
| retcode = 5; |
| |
| return retcode; |
| } |
| |
| bool GetSupportedFormats( |
| V4L2Device* device, SupportedFormats* supported_formats) { |
| supported_formats->clear(); |
| |
| SupportedFormat format; |
| uint32_t num_format = 0; |
| device->EnumFormat(&num_format, false); |
| for (uint32_t i = 0; i < num_format; ++i) { |
| if (!device->GetPixelFormat(i, &format.fourcc)) { |
| printf("[Error] Get format error\n"); |
| return false; |
| } |
| uint32_t num_frame_size; |
| if (!device->EnumFrameSize(format.fourcc, &num_frame_size, false)) { |
| printf("[Error] Enumerate frame size error\n"); |
| return false; |
| }; |
| |
| for (uint32_t j = 0; j < num_frame_size; ++j) { |
| if (!device->GetFrameSize(j, format.fourcc, &format.width, |
| &format.height)) { |
| printf("[Error] Get frame size error\n"); |
| return false; |
| }; |
| uint32_t num_frame_rate; |
| if (!device->EnumFrameInterval(format.fourcc, format.width, |
| format.height, &num_frame_rate, false)) { |
| printf("[Error] Enumerate frame interval error\n"); |
| return false; |
| }; |
| |
| format.frame_rates.clear(); |
| uint32_t frame_rate; |
| for (uint32_t k = 0; k < num_frame_rate; ++k) { |
| if (!device->GetFrameInterval(k, format.fourcc, format.width, |
| format.height, &frame_rate)) { |
| printf("[Error] Get frame interval error\n"); |
| return false; |
| }; |
| // All supported resolution should have at least 1 fps. |
| if (frame_rate == 0) { |
| printf("[Error] Frame rate should be at least 1.\n"); |
| return false; |
| } |
| format.frame_rates.push_back(frame_rate); |
| } |
| supported_formats->push_back(format); |
| } |
| } |
| return true; |
| } |
| |
| SupportedFormat GetMaximumResolution(const SupportedFormats& formats) { |
| SupportedFormat max_format; |
| memset(&max_format, 0, sizeof(max_format)); |
| for (const auto& format : formats) { |
| if (format.width >= max_format.width && |
| format.height >= max_format.height) { |
| max_format = format; |
| } |
| } |
| return max_format; |
| } |
| |
| // Find format according to width and height. If multiple formats support the |
| // same resolution, choose V4L2_PIX_FMT_MJPEG first. |
| const SupportedFormat* FindFormatByResolution(const SupportedFormats& formats, |
| uint32_t width, |
| uint32_t height) { |
| const SupportedFormat* result_format = nullptr; |
| for (const auto& format : formats) { |
| if (format.width == width && format.height == height) { |
| if (!result_format || format.fourcc == V4L2_PIX_FMT_MJPEG) { |
| result_format = &format; |
| } |
| } |
| } |
| return result_format; |
| } |
| |
| bool TestIO(const std::string& dev_name) { |
| uint32_t buffers = 4; |
| uint32_t width = 640; |
| uint32_t height = 480; |
| uint32_t pixfmt = V4L2_PIX_FMT_YUYV; |
| uint32_t fps = 30; |
| uint32_t time_to_capture = 3; // The unit is second. |
| bool check_1280x960 = false; |
| |
| std::unique_ptr<V4L2Device> device( |
| new V4L2Device(dev_name.c_str(), buffers)); |
| |
| if (!device->OpenDevice()) |
| return false; |
| |
| v4l2_capability cap; |
| if (!device->ProbeCaps(&cap)) |
| return false; |
| |
| if (cap.capabilities & V4L2_CAP_STREAMING) { |
| int mmap_ret = RunTest(device.get(), V4L2Device::IO_METHOD_MMAP, buffers, |
| time_to_capture, width, height, pixfmt, fps); |
| int userp_ret = RunTest(device.get(), V4L2Device::IO_METHOD_USERPTR, |
| buffers, time_to_capture, width, height, pixfmt, fps); |
| if (mmap_ret && userp_ret) { |
| printf("[Error] Stream I/O failed.\n"); |
| return false; |
| } |
| } else { |
| printf("[Error] Streaming capability is mandatory.\n"); |
| return false; |
| } |
| |
| device->CloseDevice(); |
| return true; |
| } |
| |
| // Test all required resolutions with all fps which are larger than 30. |
| bool TestResolutions(const std::string& dev_name, bool check_1280x960) { |
| uint32_t buffers = 4; |
| uint32_t time_to_capture = 3; |
| V4L2Device::IOMethod io = V4L2Device::IO_METHOD_MMAP; |
| std::unique_ptr<V4L2Device> device( |
| new V4L2Device(dev_name.c_str(), buffers)); |
| |
| if (!device->OpenDevice()) |
| return false; |
| |
| SupportedFormats supported_formats; |
| if (!GetSupportedFormats(device.get(), &supported_formats)) { |
| printf("[Error] Get supported formats failed in %s.\n", dev_name.c_str()); |
| return false; |
| } |
| SupportedFormat max_resolution = GetMaximumResolution(supported_formats); |
| |
| SupportedFormats required_resolutions; |
| required_resolutions.push_back(SupportedFormat(320, 240, 0, 30)); |
| required_resolutions.push_back(SupportedFormat(640, 480, 0, 30)); |
| required_resolutions.push_back(SupportedFormat(1280, 720, 0, 30)); |
| required_resolutions.push_back(SupportedFormat(1920, 1080, 0, 30)); |
| required_resolutions.push_back(SupportedFormat(1600, 1200, 0, 30)); |
| if (check_1280x960) { |
| required_resolutions.push_back(SupportedFormat(1280, 960, 0, 30)); |
| } |
| |
| for (const auto& test_resolution : required_resolutions) { |
| // Skip the resolution that is larger than the maximum. |
| if (max_resolution.width < test_resolution.width || |
| max_resolution.height < test_resolution.height) { |
| continue; |
| } |
| |
| const SupportedFormat* test_format = FindFormatByResolution( |
| supported_formats, test_resolution.width, test_resolution.height); |
| if (test_format == nullptr) { |
| printf("[Error] %dx%d not found in %s\n", test_resolution.width, |
| test_resolution.height, dev_name.c_str()); |
| return false; |
| } |
| |
| bool frame_rate_tested = false; |
| for (const auto& frame_rate : test_format->frame_rates) { |
| // If the fps is less than the fps that we want to test, skip it. |
| if (frame_rate < test_resolution.frame_rates[0]) { |
| continue; |
| } |
| frame_rate_tested = true; |
| if (RunTest(device.get(), io, buffers, time_to_capture, |
| test_format->width, test_format->height, test_format->fourcc, |
| frame_rate)) { |
| printf("[Error] Could not capture frames for %dx%d (%08X) in %s\n", |
| test_format->width, test_format->height, test_format->fourcc, |
| dev_name.c_str()); |
| return false; |
| } |
| |
| // Make sure the driver didn't adjust the format. |
| v4l2_format fmt = device->GetActualPixelFormat(); |
| if (test_format->width != fmt.fmt.pix.width || |
| test_format->height != fmt.fmt.pix.height || |
| test_format->fourcc != fmt.fmt.pix.pixelformat || |
| frame_rate != device->GetFrameRate()) { |
| printf("[Error] Capture test %dx%d (%08X) failed in %s\n", |
| test_format->width, test_format->height, test_format->fourcc, |
| dev_name.c_str()); |
| } |
| } |
| if (!frame_rate_tested) { |
| printf("[Error] Cannot test frame rate for %dx%d (%08X) failed in %s\n", |
| test_format->width, test_format->height, test_format->fourcc, |
| dev_name.c_str()); |
| return false; |
| } |
| } |
| device->CloseDevice(); |
| |
| return true; |
| } |
| |
| int main(int argc, char** argv) { |
| std::string dev_name = "/dev/video"; |
| std::string usb_info = ""; |
| |
| for (;;) { |
| int32_t index; |
| int32_t c = getopt_long(argc, argv, short_options, long_options, &index); |
| if (-1 == c) |
| break; |
| switch (c) { |
| case 0: // getopt_long() flag. |
| break; |
| case '?': |
| PrintUsage(argc, argv); |
| exit (EXIT_SUCCESS); |
| case 'd': |
| // Initialize default v4l2 device name. |
| dev_name = strdup(optarg); |
| break; |
| case 'u': |
| usb_info = strdup(optarg); |
| break; |
| default: |
| PrintUsage(argc, argv); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| std::unordered_map<std::string, std::string> mapping = {{usb_info, dev_name}}; |
| CameraCharacteristics characteristics; |
| DeviceInfos device_infos = |
| characteristics.GetCharacteristicsFromFile(mapping); |
| |
| bool check_1280x960 = false; |
| if (device_infos.size() > 1) { |
| printf("[Error] One device should not have multiple configs.\n"); |
| exit(EXIT_FAILURE); |
| } |
| if (device_infos.size() == 1) { |
| check_1280x960 = !device_infos[0].resolution_1280x960_unsupported; |
| } |
| printf("[Info] check 1280x960: %d\n", check_1280x960); |
| |
| if (!TestIO(dev_name)) |
| exit(EXIT_FAILURE); |
| |
| if (!TestResolutions(dev_name, check_1280x960)) |
| exit(EXIT_FAILURE); |
| |
| return EXIT_SUCCESS; |
| } |