chromium/content/browser/renderer_host/media/fuchsia_media_codec_provider_impl.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/media/fuchsia_media_codec_provider_impl.h"

#include <fuchsia/media/cpp/fidl.h>
#include <fuchsia/mediacodec/cpp/fidl.h>
#include <lib/fit/function.h>
#include <lib/sys/cpp/component_context.h>

#include <optional>

#include "base/command_line.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/process_context.h"
#include "media/base/supported_video_decoder_config.h"
#include "media/base/video_codecs.h"

namespace content {

namespace {

std::optional<std::string> GetMimeTypeForVideoCodec(media::VideoCodec codec) {
  switch (codec) {
    case media::VideoCodec::kH264:
      return "video/h264";
    case media::VideoCodec::kVP8:
      return "video/vp8";
    case media::VideoCodec::kVP9:
      return "video/vp9";
    case media::VideoCodec::kHEVC:
      return "video/hevc";
    case media::VideoCodec::kAV1:
      return "video/av1";

    case media::VideoCodec::kVC1:
    case media::VideoCodec::kMPEG2:
    case media::VideoCodec::kMPEG4:
    case media::VideoCodec::kTheora:
    case media::VideoCodec::kDolbyVision:
      return std::nullopt;

    case media::VideoCodec::kUnknown:
      break;
  }

  NOTREACHED();
}

media::VideoCodecProfile ConvertToVideoCodecProfile(
    const fuchsia::media::CodecProfile& profile) {
  switch (profile) {
    case fuchsia::media::CodecProfile::H264PROFILE_BASELINE:
      return media::VideoCodecProfile::H264PROFILE_BASELINE;
    case fuchsia::media::CodecProfile::H264PROFILE_MAIN:
      return media::VideoCodecProfile::H264PROFILE_MAIN;
    case fuchsia::media::CodecProfile::H264PROFILE_EXTENDED:
      return media::VideoCodecProfile::H264PROFILE_EXTENDED;
    case fuchsia::media::CodecProfile::H264PROFILE_HIGH:
      return media::VideoCodecProfile::H264PROFILE_HIGH;
    case fuchsia::media::CodecProfile::H264PROFILE_HIGH10PROFILE:
      return media::VideoCodecProfile::H264PROFILE_HIGH10PROFILE;
    case fuchsia::media::CodecProfile::H264PROFILE_HIGH422PROFILE:
      return media::VideoCodecProfile::H264PROFILE_HIGH422PROFILE;
    case fuchsia::media::CodecProfile::H264PROFILE_HIGH444PREDICTIVEPROFILE:
      return media::VideoCodecProfile::H264PROFILE_HIGH444PREDICTIVEPROFILE;
    case fuchsia::media::CodecProfile::H264PROFILE_SCALABLEBASELINE:
      return media::VideoCodecProfile::H264PROFILE_SCALABLEBASELINE;
    case fuchsia::media::CodecProfile::H264PROFILE_SCALABLEHIGH:
      return media::VideoCodecProfile::H264PROFILE_SCALABLEHIGH;
    case fuchsia::media::CodecProfile::H264PROFILE_STEREOHIGH:
      return media::VideoCodecProfile::H264PROFILE_STEREOHIGH;
    case fuchsia::media::CodecProfile::H264PROFILE_MULTIVIEWHIGH:
      return media::VideoCodecProfile::H264PROFILE_MULTIVIEWHIGH;
    case fuchsia::media::CodecProfile::VP8PROFILE_ANY:
      return media::VideoCodecProfile::VP8PROFILE_ANY;
    case fuchsia::media::CodecProfile::VP9PROFILE_PROFILE0:
      return media::VideoCodecProfile::VP9PROFILE_PROFILE0;
    case fuchsia::media::CodecProfile::VP9PROFILE_PROFILE1:
      return media::VideoCodecProfile::VP9PROFILE_PROFILE1;
    case fuchsia::media::CodecProfile::VP9PROFILE_PROFILE2:
      return media::VideoCodecProfile::VP9PROFILE_PROFILE2;
    case fuchsia::media::CodecProfile::VP9PROFILE_PROFILE3:
      return media::VideoCodecProfile::VP9PROFILE_PROFILE3;
    case fuchsia::media::CodecProfile::HEVCPROFILE_MAIN:
      return media::VideoCodecProfile::HEVCPROFILE_MAIN;
    case fuchsia::media::CodecProfile::HEVCPROFILE_MAIN10:
      return media::VideoCodecProfile::HEVCPROFILE_MAIN10;
    case fuchsia::media::CodecProfile::HEVCPROFILE_MAIN_STILL_PICTURE:
      return media::VideoCodecProfile::HEVCPROFILE_MAIN_STILL_PICTURE;
    default:
      NOTIMPLEMENTED() << "Unknown codec profile: " << profile;
      return media::VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN;
  }
}

media::SupportedVideoDecoderConfigs GetSupportedVideoDecoderConfigsForCodecList(
    const std::vector<fuchsia::mediacodec::DetailedCodecDescription>&
        detailed_codec_list) {
  media::SupportedVideoDecoderConfigs configs;

  for (const auto& codec_description : detailed_codec_list) {
    if (!codec_description.has_codec_type() || !codec_description.has_is_hw() ||
        !codec_description.has_mime_type() ||
        !codec_description.has_profile_descriptions()) {
      LOG(WARNING) << "Missing required fields when parsing the "
                      "DetailedCodecDescription. Skipped.";
      continue;
    }

    if (
        // Only use platform codecs that are accelerated.
        !codec_description.is_hw() ||
        // Exclude non-video codecs.
        codec_description.mime_type().find("video") != 0 ||
        // Exclude non-decoder codecs.
        codec_description.codec_type() !=
            fuchsia::mediacodec::CodecType::DECODER ||
        !codec_description.profile_descriptions()
             .is_decoder_profile_descriptions()) {
      continue;
    }

    for (const auto& profile_description :
         codec_description.profile_descriptions()
             .decoder_profile_descriptions()) {
      if (!profile_description.has_profile() ||
          !profile_description.has_min_image_size() ||
          !profile_description.has_max_image_size()) {
        LOG(WARNING) << "Missing required fields when parsing the "
                        "DecoderProfileDescription. Skipped.";
        continue;
      }

      const auto video_codec_profile =
          ConvertToVideoCodecProfile(profile_description.profile());
      if (video_codec_profile ==
          media::VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN) {
        continue;
      }

      configs.emplace_back(
          video_codec_profile, video_codec_profile,
          gfx::Size(profile_description.min_image_size().width,
                    profile_description.min_image_size().height),
          gfx::Size(profile_description.max_image_size().width,
                    profile_description.max_image_size().height),
          profile_description.has_allow_encryption()
              ? profile_description.allow_encryption()
              : false,
          profile_description.has_require_encryption()
              ? profile_description.require_encryption()
              : false);
    }
  }

  return configs;
}

}  // namespace

FuchsiaMediaCodecProviderImpl::FuchsiaMediaCodecProviderImpl() = default;
FuchsiaMediaCodecProviderImpl::~FuchsiaMediaCodecProviderImpl() = default;

void FuchsiaMediaCodecProviderImpl::AddReceiver(
    mojo::PendingReceiver<media::mojom::FuchsiaMediaCodecProvider> receiver) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  receivers_.Add(this, std::move(receiver));
}

// media::mojom::FuchsiaMediaCodecProvider implementation.
void FuchsiaMediaCodecProviderImpl::CreateVideoDecoder(
    media::VideoCodec codec,
    media::mojom::VideoDecoderSecureMemoryMode secure_mode,
    fidl::InterfaceRequest<fuchsia::media::StreamProcessor>
        stream_processor_request) {
  fuchsia::mediacodec::CreateDecoder_Params decoder_params;

  // Set format details ordinal to 0. Decoder doesn't change the format, so
  // the value doesn't matter.
  decoder_params.mutable_input_details()->set_format_details_version_ordinal(0);

  auto mime_type = GetMimeTypeForVideoCodec(codec);
  if (!mime_type) {
    // Drop `stream_processor_request` if the codec is not supported.
    return;
  }
  decoder_params.mutable_input_details()->set_mime_type(mime_type.value());

  switch (secure_mode) {
    case media::mojom::VideoDecoderSecureMemoryMode::CLEAR:
      // Use defaults for non-secure mode.
      break;

    case media::mojom::VideoDecoderSecureMemoryMode::SECURE:
      decoder_params.set_secure_input_mode(
          fuchsia::mediacodec::SecureMemoryMode::ON);
      decoder_params.set_secure_output_mode(
          fuchsia::mediacodec::SecureMemoryMode::ON);
      break;

    case media::mojom::VideoDecoderSecureMemoryMode::SECURE_OUTPUT:
      decoder_params.set_secure_output_mode(
          fuchsia::mediacodec::SecureMemoryMode::ON);
      break;
  }

  // Video demuxers return each video frame in a separate packet. This field
  // must be set to get frame timestamps on the decoder output.
  decoder_params.set_promise_separate_access_units_on_input(true);

  // We use `fuchsia.mediacodec` only for hardware decoders. Renderer will
  // handle software decoding if hardware decoder is not available.
  decoder_params.set_require_hw(true);

  auto decoder_factory = base::ComponentContextForProcess()
                             ->svc()
                             ->Connect<fuchsia::mediacodec::CodecFactory>();
  decoder_factory->CreateDecoder(std::move(decoder_params),
                                 std::move(stream_processor_request));
}

void FuchsiaMediaCodecProviderImpl::GetSupportedVideoDecoderConfigs(
    GetSupportedVideoDecoderConfigsCallback callback) {
  EnsureCodecFactory();

  pending_get_supported_vd_configs_callbacks_.emplace_back(std::move(callback));
  if (supported_video_decoder_configs_) {
    RunPendingGetSupportedVideoDecoderConfigsCallbacks();
  }
}

// End of media::mojom::FuchsiaMediaCodecProvider implementation.

void FuchsiaMediaCodecProviderImpl::EnsureCodecFactory() {
  if (codec_factory_)
    return;

  base::ComponentContextForProcess()
      ->svc()
      ->Connect<fuchsia::mediacodec::CodecFactory>(codec_factory_.NewRequest());

  codec_factory_.set_error_handler(fit::bind_member(
      this, &FuchsiaMediaCodecProviderImpl::OnCodecFactoryDisconnected));
  codec_factory_->GetDetailedCodecDescriptions(fit::bind_member(
      this, &FuchsiaMediaCodecProviderImpl::OnGetDetailedCodecDescriptions));
}

void FuchsiaMediaCodecProviderImpl::OnCodecFactoryDisconnected(
    zx_status_t status) {
  ZX_LOG(ERROR, status) << "fuchsia.mediacodec.CodecFactory disconnected.";

  supported_video_decoder_configs_.reset();
  RunPendingGetSupportedVideoDecoderConfigsCallbacks();
}

void FuchsiaMediaCodecProviderImpl::OnGetDetailedCodecDescriptions(
    fuchsia::mediacodec::CodecFactoryGetDetailedCodecDescriptionsResponse
        response) {
  if (response.has_codecs()) {
    supported_video_decoder_configs_.emplace(
        GetSupportedVideoDecoderConfigsForCodecList(response.codecs()));
  }
  RunPendingGetSupportedVideoDecoderConfigsCallbacks();
}

void FuchsiaMediaCodecProviderImpl::
    RunPendingGetSupportedVideoDecoderConfigsCallbacks() {
  media::SupportedVideoDecoderConfigs configs =
      supported_video_decoder_configs_.value_or(
          std::vector<media::SupportedVideoDecoderConfig>{});

  for (auto& callback :
       std::exchange(pending_get_supported_vd_configs_callbacks_, {})) {
    std::move(callback).Run(configs);
  }
}

}  // namespace content