blob: e39a1f0dc163a56ea73f62aae45e1500dbefac73 [file] [log] [blame]
// Copyright 2013 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 "content/renderer/media/webrtc/rtc_video_encoder_factory.h"
#include <memory>
#include "base/command_line.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/feature_h264_with_openh264_ffmpeg.h"
#include "content/renderer/media/webrtc/rtc_video_encoder.h"
#include "media/media_buildflags.h"
#include "media/video/gpu_video_accelerator_factories.h"
#include "third_party/webrtc/api/video_codecs/sdp_video_format.h"
#include "third_party/webrtc/common_video/h264/profile_level_id.h"
#include "third_party/webrtc/media/base/codec.h"
namespace content {
namespace {
// Translate from media::VideoEncodeAccelerator::SupportedProfile to
// webrtc::SdpVideoFormat, or return nothing if the profile isn't supported.
base::Optional<webrtc::SdpVideoFormat> VEAToWebRTCFormat(
const media::VideoEncodeAccelerator::SupportedProfile& profile) {
DCHECK_EQ(profile.max_framerate_denominator, 1U);
if (profile.profile >= media::VP8PROFILE_MIN &&
profile.profile <= media::VP8PROFILE_MAX) {
if (base::FeatureList::IsEnabled(features::kWebRtcHWVP8Encoding)) {
return webrtc::SdpVideoFormat("VP8");
}
} else if (profile.profile >= media::H264PROFILE_MIN &&
profile.profile <= media::H264PROFILE_MAX) {
// Enable H264 HW encode for WebRTC when SW fallback is available, which is
// checked by kWebRtcH264WithOpenH264FFmpeg flag. This check should be
// removed when SW implementation is fully enabled.
bool webrtc_h264_sw_enabled = false;
#if BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
webrtc_h264_sw_enabled =
base::FeatureList::IsEnabled(kWebRtcH264WithOpenH264FFmpeg);
#endif // BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
if (webrtc_h264_sw_enabled ||
base::FeatureList::IsEnabled(features::kWebRtcHWH264Encoding)) {
webrtc::H264::Profile h264_profile;
switch (profile.profile) {
case media::H264PROFILE_BASELINE:
#if defined(OS_ANDROID)
// Force HW H264 on Android to be CBP for most compatibility, since:
// - Only HW H264 is available on Android at present.
// - MediaCodec only advise BP, which works same as CBP in most cases.
// - Some peers only expect CBP in negotiation.
h264_profile = webrtc::H264::kProfileConstrainedBaseline;
#else
h264_profile = webrtc::H264::kProfileBaseline;
#endif
break;
case media::H264PROFILE_MAIN:
h264_profile = webrtc::H264::kProfileMain;
break;
case media::H264PROFILE_HIGH:
h264_profile = webrtc::H264::kProfileHigh;
break;
default:
// Unsupported H264 profile in WebRTC.
return base::nullopt;
}
const int width = profile.max_resolution.width();
const int height = profile.max_resolution.height();
const int fps = profile.max_framerate_numerator;
DCHECK_EQ(1u, profile.max_framerate_denominator);
const absl::optional<webrtc::H264::Level> h264_level =
webrtc::H264::SupportedLevel(width * height, fps);
const webrtc::H264::ProfileLevelId profile_level_id(
h264_profile, h264_level.value_or(webrtc::H264::kLevel1));
webrtc::SdpVideoFormat format("H264");
format.parameters = {
{cricket::kH264FmtpProfileLevelId,
*webrtc::H264::ProfileLevelIdToString(profile_level_id)},
{cricket::kH264FmtpLevelAsymmetryAllowed, "1"},
{cricket::kH264FmtpPacketizationMode, "1"}};
return format;
}
} else if (profile.profile >= media::VP9PROFILE_MIN &&
profile.profile <= media::VP9PROFILE_MAX) {
if (base::FeatureList::IsEnabled(features::kWebRtcHWVP9Encoding)) {
return webrtc::SdpVideoFormat("VP9");
}
}
return base::nullopt;
}
bool IsSameFormat(const webrtc::SdpVideoFormat& format1,
const webrtc::SdpVideoFormat& format2) {
return cricket::IsSameCodec(format1.name, format2.parameters, format2.name,
format2.parameters);
}
} // anonymous namespace
RTCVideoEncoderFactory::RTCVideoEncoderFactory(
media::GpuVideoAcceleratorFactories* gpu_factories)
: gpu_factories_(gpu_factories) {
const media::VideoEncodeAccelerator::SupportedProfiles& profiles =
gpu_factories_->GetVideoEncodeAcceleratorSupportedProfiles();
for (const auto& profile : profiles) {
base::Optional<webrtc::SdpVideoFormat> format = VEAToWebRTCFormat(profile);
if (format) {
supported_formats_.push_back(std::move(*format));
profiles_.push_back(profile.profile);
}
}
}
RTCVideoEncoderFactory::~RTCVideoEncoderFactory() {}
std::unique_ptr<webrtc::VideoEncoder>
RTCVideoEncoderFactory::CreateVideoEncoder(
const webrtc::SdpVideoFormat& format) {
for (size_t i = 0; i < supported_formats_.size(); ++i) {
if (IsSameFormat(format, supported_formats_[i])) {
return std::make_unique<RTCVideoEncoder>(profiles_[i], gpu_factories_);
}
}
return nullptr;
}
std::vector<webrtc::SdpVideoFormat>
RTCVideoEncoderFactory::GetSupportedFormats() const {
return supported_formats_;
}
webrtc::VideoEncoderFactory::CodecInfo
RTCVideoEncoderFactory::QueryVideoEncoder(
const webrtc::SdpVideoFormat& format) const {
CodecInfo info;
info.has_internal_source = false;
info.is_hardware_accelerated = true;
return info;
}
} // namespace content