blob: 7730dfabd9348918bfc87789535079e3a3b6513e [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 "third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.h"
#include <memory>
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "media/media_buildflags.h"
#include "media/video/gpu_video_accelerator_factories.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.h"
#include "third_party/webrtc/api/video_codecs/sdp_video_format.h"
#include "third_party/webrtc/api/video_codecs/video_encoder.h"
#include "third_party/webrtc/common_video/h264/profile_level_id.h"
#include "third_party/webrtc/media/base/codec.h"
#include "third_party/webrtc/media/base/vp9_profile.h"
namespace blink {
namespace {
base::Optional<media::VideoCodecProfile> WebRTCFormatToCodecProfile(
const webrtc::SdpVideoFormat& sdp) {
if (sdp.name == "H264") {
#if !defined(OS_ANDROID)
// 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(
blink::features::kWebRtcH264WithOpenH264FFmpeg);
#endif // BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
if (!webrtc_h264_sw_enabled)
return base::nullopt;
#endif
return media::VideoCodecProfile::H264PROFILE_MIN;
} else if (sdp.name == "VP8") {
return media::VideoCodecProfile::VP8PROFILE_MIN;
} else if (sdp.name == "VP9") {
return media::VideoCodecProfile::VP9PROFILE_MIN;
}
return base::nullopt;
}
// 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) {
return webrtc::SdpVideoFormat("VP8");
}
if (profile.profile >= media::H264PROFILE_MIN &&
profile.profile <= media::H264PROFILE_MAX) {
#if !defined(OS_ANDROID)
// 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(
blink::features::kWebRtcH264WithOpenH264FFmpeg);
#endif // BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
if (!webrtc_h264_sw_enabled)
return base::nullopt;
#endif
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 // defined(OS_ANDROID)
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;
}
if (profile.profile >= media::VP9PROFILE_MIN &&
profile.profile <= media::VP9PROFILE_MAX) {
webrtc::VP9Profile vp9_profile;
switch (profile.profile) {
case media::VP9PROFILE_PROFILE0:
vp9_profile = webrtc::VP9Profile::kProfile0;
break;
case media::VP9PROFILE_PROFILE2:
vp9_profile = webrtc::VP9Profile::kProfile2;
break;
default:
// Unsupported VP9 profiles (profile1 & profile3) in WebRTC.
return base::nullopt;
}
webrtc::SdpVideoFormat format("VP9");
format.parameters = {
{webrtc::kVP9FmtpProfileId,
webrtc::VP9ProfileToString(vp9_profile)}};
return format;
}
return base::nullopt;
} // namespace
bool IsSameFormat(const webrtc::SdpVideoFormat& format1,
const webrtc::SdpVideoFormat& format2) {
return cricket::IsSameCodec(format1.name, format2.parameters, format2.name,
format2.parameters);
}
struct SupportedFormats {
bool unknown = true;
std::vector<media::VideoCodecProfile> profiles;
std::vector<webrtc::SdpVideoFormat> sdp_formats;
};
SupportedFormats GetSupportedFormatsInternal(
media::GpuVideoAcceleratorFactories* gpu_factories) {
SupportedFormats supported_formats;
auto profiles = gpu_factories->GetVideoEncodeAcceleratorSupportedProfiles();
if (!profiles)
return supported_formats;
// |profiles| are either the info at GpuInfo instance or the info got by
// querying GPU process.
supported_formats.unknown = false;
for (const auto& profile : *profiles) {
base::Optional<webrtc::SdpVideoFormat> format = VEAToWebRTCFormat(profile);
if (format) {
supported_formats.profiles.push_back(profile.profile);
supported_formats.sdp_formats.push_back(std::move(*format));
}
}
return supported_formats;
}
bool IsConstrainedH264(const webrtc::SdpVideoFormat& format) {
bool is_constrained_h264 = false;
if (format.name == cricket::kH264CodecName) {
const absl::optional<webrtc::H264::ProfileLevelId> profile_level_id =
webrtc::H264::ParseSdpProfileLevelId(format.parameters);
if (profile_level_id && profile_level_id->profile ==
webrtc::H264::kProfileConstrainedBaseline) {
is_constrained_h264 = true;
}
}
return is_constrained_h264;
}
} // anonymous namespace
RTCVideoEncoderFactory::RTCVideoEncoderFactory(
media::GpuVideoAcceleratorFactories* gpu_factories)
: gpu_factories_(gpu_factories), gpu_codec_support_waiter_(gpu_factories) {}
RTCVideoEncoderFactory::~RTCVideoEncoderFactory() {}
void RTCVideoEncoderFactory::CheckAndWaitEncoderSupportStatusIfNeeded() const {
if (!gpu_codec_support_waiter_.IsEncoderSupportKnown()) {
DLOG(WARNING) << "Encoder support is unknown. Timeout "
<< gpu_codec_support_waiter_.wait_timeout_ms()
.value_or(base::TimeDelta())
.InMilliseconds()
<< "ms. Encoders might not be available.";
}
}
std::unique_ptr<webrtc::VideoEncoder>
RTCVideoEncoderFactory::CreateVideoEncoder(
const webrtc::SdpVideoFormat& format) {
CheckAndWaitEncoderSupportStatusIfNeeded();
std::unique_ptr<webrtc::VideoEncoder> encoder;
bool is_constrained_h264 = IsConstrainedH264(format);
auto supported_formats = GetSupportedFormatsInternal(gpu_factories_);
if (!supported_formats.unknown) {
for (size_t i = 0; i < supported_formats.sdp_formats.size(); ++i) {
if (IsSameFormat(format, supported_formats.sdp_formats[i])) {
encoder = std::make_unique<RTCVideoEncoder>(
supported_formats.profiles[i], is_constrained_h264, gpu_factories_);
break;
}
}
} else {
auto profile = WebRTCFormatToCodecProfile(format);
if (profile) {
encoder = std::make_unique<RTCVideoEncoder>(*profile, is_constrained_h264,
gpu_factories_);
}
}
return encoder;
}
std::vector<webrtc::SdpVideoFormat>
RTCVideoEncoderFactory::GetSupportedFormats() const {
CheckAndWaitEncoderSupportStatusIfNeeded();
return GetSupportedFormatsInternal(gpu_factories_).sdp_formats;
}
} // namespace blink