123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- // Copyright 2018 The Chromium 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 "media/gpu/chromeos/image_processor_factory.h"
- #include <stddef.h>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/containers/contains.h"
- #include "base/feature_list.h"
- #include "base/memory/scoped_refptr.h"
- #include "media/base/media_switches.h"
- #include "media/base/video_types.h"
- #include "media/gpu/buildflags.h"
- #include "media/gpu/chromeos/image_processor.h"
- #include "media/gpu/chromeos/libyuv_image_processor_backend.h"
- #include "media/gpu/macros.h"
- #if BUILDFLAG(USE_VAAPI)
- #include "media/gpu/vaapi/vaapi_image_processor_backend.h"
- #include "media/gpu/vaapi/vaapi_wrapper.h"
- #endif // BUILDFLAG(USE_VAAPI)
- #if BUILDFLAG(USE_V4L2_CODEC)
- #include "media/gpu/v4l2/v4l2_device.h"
- #include "media/gpu/v4l2/v4l2_image_processor_backend.h"
- #include "media/gpu/v4l2/v4l2_vda_helpers.h"
- #endif // BUILDFLAG(USE_V4L2_CODEC)
- namespace media {
- namespace {
- using PixelLayoutCandidate = ImageProcessor::PixelLayoutCandidate;
- #if BUILDFLAG(USE_VAAPI)
- std::unique_ptr<ImageProcessor> CreateVaapiImageProcessorWithInputCandidates(
- const std::vector<PixelLayoutCandidate>& input_candidates,
- const gfx::Rect& input_visible_rect,
- const gfx::Size& output_size,
- scoped_refptr<base::SequencedTaskRunner> client_task_runner,
- ImageProcessorFactory::PickFormatCB out_format_picker,
- ImageProcessor::ErrorCB error_cb) {
- std::vector<Fourcc> vpp_supported_formats =
- VaapiWrapper::GetVppSupportedFormats();
- absl::optional<PixelLayoutCandidate> chosen_input_candidate;
- for (const auto& input_candidate : input_candidates) {
- if (base::Contains(vpp_supported_formats, input_candidate.fourcc) &&
- VaapiWrapper::IsVppResolutionAllowed(input_candidate.size)) {
- chosen_input_candidate = input_candidate;
- break;
- }
- }
- if (!chosen_input_candidate)
- return nullptr;
- // Note that we pick the first input candidate as the preferred output format.
- // The reason is that in practice, the VaapiVideoDecoder will make
- // |input_candidates| either {NV12} or {P010} depending on the bitdepth. So
- // choosing the first (and only) element will keep the bitdepth of the frame
- // which is needed to display HDR content.
- auto chosen_output_format = out_format_picker.Run(
- /*candidates=*/vpp_supported_formats, input_candidates[0].fourcc);
- if (!chosen_output_format)
- return nullptr;
- // Note: the VaapiImageProcessorBackend doesn't use the ColorPlaneLayouts in
- // the PortConfigs, so we just pass an empty list of plane layouts.
- ImageProcessor::PortConfig input_config(
- chosen_input_candidate->fourcc, chosen_input_candidate->size,
- /*planes=*/{}, input_visible_rect,
- {VideoFrame::STORAGE_GPU_MEMORY_BUFFER});
- ImageProcessor::PortConfig output_config(
- /*fourcc=*/*chosen_output_format, /*size=*/output_size, /*planes=*/{},
- /*visible_rect=*/gfx::Rect(output_size),
- {VideoFrame::STORAGE_GPU_MEMORY_BUFFER});
- return ImageProcessor::Create(
- base::BindRepeating(&VaapiImageProcessorBackend::Create), input_config,
- output_config, ImageProcessor::OutputMode::IMPORT, VIDEO_ROTATION_0,
- std::move(error_cb), std::move(client_task_runner));
- }
- #endif // BUILDFLAG(USE_VAAPI)
- #if BUILDFLAG(USE_V4L2_CODEC) && !BUILDFLAG(USE_VAAPI)
- std::unique_ptr<ImageProcessor> CreateV4L2ImageProcessorWithInputCandidates(
- const std::vector<PixelLayoutCandidate>& input_candidates,
- const gfx::Rect& visible_rect,
- size_t num_buffers,
- scoped_refptr<base::SequencedTaskRunner> client_task_runner,
- ImageProcessorFactory::PickFormatCB out_format_picker,
- ImageProcessor::ErrorCB error_cb) {
- // Pick a renderable output format, and try each available input format.
- // TODO(akahuang): let |out_format_picker| return a list of supported output
- // formats, and try all combination of input/output format, if any platform
- // fails to create ImageProcessor via current approach.
- const std::vector<uint32_t> supported_output_formats =
- V4L2ImageProcessorBackend::GetSupportedOutputFormats();
- std::vector<Fourcc> supported_fourccs;
- for (const auto& format : supported_output_formats) {
- const auto fourcc = Fourcc::FromV4L2PixFmt(format);
- if (fourcc.has_value())
- supported_fourccs.push_back(*fourcc);
- }
- const auto output_fourcc = out_format_picker.Run(
- /*candidates=*/supported_fourccs, /*preferred_fourcc=*/absl::nullopt);
- if (!output_fourcc) {
- #if DCHECK_IS_ON()
- std::string output_fourccs_string;
- for (const auto fourcc : supported_fourccs) {
- output_fourccs_string += fourcc.ToString();
- output_fourccs_string += " ";
- }
- DVLOGF(1) << "None of " << output_fourccs_string << "formats is supported.";
- #endif
- return nullptr;
- }
- const auto supported_input_pixfmts =
- V4L2ImageProcessorBackend::GetSupportedInputFormats();
- for (const auto& input_candidate : input_candidates) {
- const Fourcc input_fourcc = input_candidate.fourcc;
- const gfx::Size& input_size = input_candidate.size;
- if (!base::Contains(supported_input_pixfmts, input_fourcc.ToV4L2PixFmt()))
- continue;
- // Ideally the ImageProcessor would be able to scale and crop |input_size|
- // to the |visible_rect| area of |output_size|. TryOutputFormat() below is
- // called to verify that a given combination of fourcc values and input/
- // output sizes are indeed supported (the driver can potentially return a
- // different supported |output_size|). Some Image Processors(e.g. MTK8183)
- // are not able to crop/scale correctly -- but TryOutputFormat() doesn't
- // return a "corrected" |output_size|. To avoid troubles (and, in general,
- // low performance), we set |output_size| to be equal to |input_size|; the
- // |visible_rect| will carry the information of exactly what part of the
- // video frame contains valid pixels, and the media/compositor pipeline
- // will take care of it.
- gfx::Size output_size = input_size;
- size_t num_planes = 0;
- if (!V4L2ImageProcessorBackend::TryOutputFormat(
- input_fourcc.ToV4L2PixFmt(), output_fourcc->ToV4L2PixFmt(),
- input_size, &output_size, &num_planes)) {
- VLOGF(2) << "Failed to get output size and plane count of IP";
- continue;
- }
- // This is very restrictive because it assumes the IP has the same alignment
- // criteria as the video decoder that will produce the input video frames.
- // In practice, this applies to all Image Processors, i.e. Mediatek devices.
- DCHECK_EQ(input_size, output_size);
- // |visible_rect| applies equally to both |input_size| and |output_size|.
- DCHECK(gfx::Rect(output_size).Contains(visible_rect));
- return v4l2_vda_helpers::CreateImageProcessor(
- input_fourcc, *output_fourcc, input_size, output_size, visible_rect,
- VideoFrame::StorageType::STORAGE_GPU_MEMORY_BUFFER, num_buffers,
- V4L2Device::Create(), ImageProcessor::OutputMode::IMPORT,
- std::move(client_task_runner), std::move(error_cb));
- }
- return nullptr;
- }
- std::unique_ptr<ImageProcessor> CreateLibYUVImageProcessorWithInputCandidates(
- const std::vector<PixelLayoutCandidate>& input_candidates,
- const gfx::Rect& input_visible_rect,
- const gfx::Size& output_size,
- size_t num_buffers,
- scoped_refptr<base::SequencedTaskRunner> client_task_runner,
- ImageProcessorFactory::PickFormatCB out_format_picker,
- ImageProcessor::ErrorCB error_cb) {
- if (input_candidates.size() != 1)
- return nullptr;
- if (input_candidates[0].fourcc != Fourcc(Fourcc::MM21))
- return nullptr;
- std::vector<Fourcc> supported_output_formats =
- LibYUVImageProcessorBackend::GetSupportedOutputFormats(
- Fourcc(Fourcc::MM21));
- auto output_format =
- out_format_picker.Run(supported_output_formats, Fourcc(Fourcc::NV12));
- if (!output_format)
- return nullptr;
- ImageProcessor::PortConfig input_config(
- Fourcc(Fourcc::MM21), input_candidates[0].size, /*planes=*/{},
- input_visible_rect, {VideoFrame::STORAGE_DMABUFS});
- ImageProcessor::PortConfig output_config(
- *output_format, output_size, /*planes=*/{}, gfx::Rect(output_size),
- {VideoFrame::STORAGE_GPU_MEMORY_BUFFER});
- return ImageProcessor::Create(
- base::BindRepeating(&LibYUVImageProcessorBackend::Create), input_config,
- output_config, ImageProcessor::OutputMode::IMPORT, VIDEO_ROTATION_0,
- std::move(error_cb), std::move(client_task_runner));
- }
- #endif // BUILDFLAG(USE_V4L2_CODEC) && !BUILDFLAG(USE_VAAPI)
- } // namespace
- // static
- std::unique_ptr<ImageProcessor> ImageProcessorFactory::Create(
- const ImageProcessor::PortConfig& input_config,
- const ImageProcessor::PortConfig& output_config,
- ImageProcessor::OutputMode output_mode,
- size_t num_buffers,
- VideoRotation relative_rotation,
- scoped_refptr<base::SequencedTaskRunner> client_task_runner,
- ImageProcessor::ErrorCB error_cb) {
- std::vector<ImageProcessor::CreateBackendCB> create_funcs;
- #if BUILDFLAG(USE_VAAPI)
- create_funcs.push_back(
- base::BindRepeating(&VaapiImageProcessorBackend::Create));
- #elif BUILDFLAG(USE_V4L2_CODEC)
- create_funcs.push_back(base::BindRepeating(
- &V4L2ImageProcessorBackend::Create, V4L2Device::Create(), num_buffers));
- #endif // BUILDFLAG(USE_V4L2_CODEC)
- create_funcs.push_back(
- base::BindRepeating(&LibYUVImageProcessorBackend::Create));
- std::unique_ptr<ImageProcessor> image_processor;
- for (auto& create_func : create_funcs) {
- image_processor = ImageProcessor::Create(
- std::move(create_func), input_config, output_config, output_mode,
- relative_rotation, error_cb, client_task_runner);
- if (image_processor)
- return image_processor;
- }
- return nullptr;
- }
- // static
- std::unique_ptr<ImageProcessor>
- ImageProcessorFactory::CreateWithInputCandidates(
- const std::vector<PixelLayoutCandidate>& input_candidates,
- const gfx::Rect& input_visible_rect,
- const gfx::Size& output_size,
- size_t num_buffers,
- scoped_refptr<base::SequencedTaskRunner> client_task_runner,
- PickFormatCB out_format_picker,
- ImageProcessor::ErrorCB error_cb) {
- #if BUILDFLAG(USE_VAAPI)
- auto processor = CreateVaapiImageProcessorWithInputCandidates(
- input_candidates, input_visible_rect, output_size, client_task_runner,
- out_format_picker, error_cb);
- if (processor)
- return processor;
- #elif BUILDFLAG(USE_V4L2_CODEC)
- if (base::FeatureList::IsEnabled(media::kPreferLibYuvImageProcessor)) {
- auto processor = CreateLibYUVImageProcessorWithInputCandidates(
- input_candidates, input_visible_rect, output_size, num_buffers,
- client_task_runner, out_format_picker, error_cb);
- if (processor)
- return processor;
- }
- auto processor = CreateV4L2ImageProcessorWithInputCandidates(
- input_candidates, input_visible_rect, num_buffers, client_task_runner,
- out_format_picker, error_cb);
- if (processor)
- return processor;
- #endif // BUILDFLAG(USE_V4L2_CODEC)
- // TODO(crbug.com/1004727): Implement LibYUVImageProcessorBackend. When doing
- // so, we must keep in mind that it might not be desirable to fallback to
- // libyuv if the hardware image processor fails (e.g., in the case of
- // protected content).
- return nullptr;
- }
- } // namespace media
|