| /* Copyright 2016 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 "hal/usb/stream_format.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <tuple> |
| |
| #include <linux/videodev2.h> |
| #include <system/graphics.h> |
| |
| #include "cros-camera/common.h" |
| #include "hal/usb/quirks.h" |
| |
| namespace cros { |
| |
| namespace { |
| |
| constexpr int kSupportedHalFormats[] = { |
| HAL_PIXEL_FORMAT_BLOB, HAL_PIXEL_FORMAT_YCbCr_420_888, |
| HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}; |
| |
| std::vector<uint32_t> GetSupportedFourCCs(bool prefer_mjpeg) { |
| // The preference of supported fourccs in the list is from high to low. |
| std::vector<uint32_t> formats = { |
| V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV420, |
| V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_Z16, V4L2_PIX_FMT_INVZ, |
| // JPEG works as MJPEG on some gspca webcams from field reports, see |
| // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the |
| // least preferred format. |
| V4L2_PIX_FMT_JPEG}; |
| if (prefer_mjpeg) { |
| auto it = std::find(formats.begin(), formats.end(), V4L2_PIX_FMT_MJPEG); |
| formats.erase(it); |
| formats.insert(formats.begin(), V4L2_PIX_FMT_MJPEG); |
| } |
| return formats; |
| } |
| |
| } // namespace |
| |
| // Return corresponding format by matching resolution |width|x|height| in |
| // |formats|. |
| const SupportedFormat* FindFormatByResolution(const SupportedFormats& formats, |
| uint32_t width, |
| uint32_t height) { |
| for (const auto& format : formats) { |
| if (format.width == width && format.height == height) { |
| return &format; |
| } |
| } |
| return NULL; |
| } |
| |
| SupportedFormat GetMaximumFormat(const SupportedFormats& supported_formats) { |
| SupportedFormat max_format; |
| memset(&max_format, 0, sizeof(max_format)); |
| for (const auto& supported_format : supported_formats) { |
| if (supported_format.width >= max_format.width && |
| supported_format.height >= max_format.height) { |
| max_format = supported_format; |
| } |
| } |
| return max_format; |
| } |
| |
| std::vector<int32_t> GetJpegAvailableThumbnailSizes( |
| const SupportedFormats& supported_formats) { |
| // This list will include at least one non-zero resolution, plus (0,0) for |
| // indicating no thumbnail should be generated. |
| std::vector<Size> sizes = {{0, 0}}; |
| |
| // Each output JPEG size in android.scaler.availableStreamConfigurations will |
| // have at least one corresponding size that has the same aspect ratio in |
| // availableThumbnailSizes, and vice versa. |
| for (auto& supported_format : supported_formats) { |
| double aspect_ratio = |
| static_cast<double>(supported_format.width) / supported_format.height; |
| |
| if (supported_format.width < 192) { |
| // Use the same resolution as the thumbnail size. |
| sizes.push_back({supported_format.width, supported_format.height}); |
| continue; |
| } |
| // Note that we only support to generate thumbnails with (width % 8 == 0) |
| // and (height % 2 == 0) for now, so set width as multiple of 48 is good for |
| // the common ratios 4:3, 16:9, and 3:2. When width is 192, the thumbnail |
| // sizes would be 192x144, 192x108, and 192x128 respectively. |
| uint32_t thumbnail_width = 192; |
| uint32_t thumbnail_height = round(thumbnail_width / aspect_ratio); |
| |
| // It's still possible that some resolutions that make thumbnail_height odd, |
| // such as 11:9 (352x288). Ensure it's even by masking out LSB. |
| thumbnail_height &= ~1; |
| |
| sizes.push_back({thumbnail_width, thumbnail_height}); |
| } |
| |
| // The sizes will be sorted by increasing pixel area (width x height). If |
| // several resolutions have the same area, they will be sorted by increasing |
| // width. |
| std::sort(sizes.begin(), sizes.end()); |
| sizes.erase(std::unique(sizes.begin(), sizes.end()), sizes.end()); |
| |
| // The aspect ratio of the largest thumbnail size will be same as the aspect |
| // ratio of largest JPEG output size in |
| // android.scaler.availableStreamConfigurations. The largest size is defined |
| // as the size that has the largest pixel area in a given size list. |
| auto max_format = GetMaximumFormat(supported_formats); |
| double aspect_ratio = |
| static_cast<double>(max_format.width) / max_format.height; |
| for (uint32_t thumbnail_width = 240; true; thumbnail_width += 48) { |
| uint32_t thumbnail_height = round(thumbnail_width / aspect_ratio); |
| thumbnail_height &= ~1; |
| Size size(thumbnail_width, thumbnail_height); |
| if (sizes.back() < size) { |
| sizes.push_back(size); |
| break; |
| } |
| } |
| |
| std::vector<int32_t> ret; |
| for (auto& size : sizes) { |
| ret.push_back(size.width); |
| ret.push_back(size.height); |
| } |
| return ret; |
| } |
| |
| SupportedFormats GetQualifiedFormats(const SupportedFormats& supported_formats, |
| uint32_t quirks) { |
| // The preference of supported fourccs in the list is from high to low. |
| bool prefer_mjpeg = quirks & kQuirkPreferMjpeg; |
| const std::vector<uint32_t> supported_fourccs = |
| GetSupportedFourCCs(prefer_mjpeg); |
| SupportedFormats qualified_formats; |
| for (const auto& supported_fourcc : supported_fourccs) { |
| for (const auto& supported_format : supported_formats) { |
| if (supported_format.fourcc != supported_fourcc) { |
| continue; |
| } |
| |
| // For the same resolution, prefer the format which has larger frame rate. |
| // For the same frame rate, choose preferred fourcc first. |
| auto it = qualified_formats.cbegin(); |
| for (; it != qualified_formats.cend(); it++) { |
| if (it->width == supported_format.width && |
| it->height == supported_format.height) { |
| break; |
| } |
| } |
| if (it != qualified_formats.cend()) { |
| float max_fps_existed_format = GetMaximumFrameRate(*it); |
| float max_fps_current_format = GetMaximumFrameRate(supported_format); |
| if (max_fps_existed_format < max_fps_current_format) { |
| qualified_formats.erase(it); |
| } else { |
| continue; |
| } |
| } |
| qualified_formats.push_back(supported_format); |
| } |
| } |
| |
| // Sort the resolution from high to low for easier reading and consistent of |
| // different camera modules. |
| // CTS uses the first 2 resolutions to create streams. It also let CTS choose |
| // high resolutions. |
| std::sort(qualified_formats.begin(), qualified_formats.end()); |
| std::reverse(qualified_formats.begin(), qualified_formats.end()); |
| return qualified_formats; |
| } |
| |
| bool IsFormatSupported(const SupportedFormats& supported_formats, |
| const camera3_stream_t& stream) { |
| if (std::find(std::begin(kSupportedHalFormats), |
| std::end(kSupportedHalFormats), |
| stream.format) == std::end(kSupportedHalFormats)) { |
| return false; |
| } |
| for (const auto& supported_format : supported_formats) { |
| if (stream.width == supported_format.width && |
| stream.height == supported_format.height) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| float GetMaximumFrameRate(const SupportedFormat& format) { |
| float max_fps = 0; |
| for (const auto& frame_rate : format.frame_rates) { |
| if (frame_rate > max_fps) { |
| max_fps = frame_rate; |
| } |
| } |
| |
| return max_fps; |
| } |
| |
| } // namespace cros |