blob: db9e4a41704edceb7ff8004531e34077106df7e8 [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_decoder_factory.h"
#include <memory>
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "content/renderer/media/webrtc/rtc_video_decoder.h"
#include "content/renderer/media/webrtc/rtc_video_decoder_adapter.h"
#include "media/base/media_switches.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"
#include "third_party/webrtc/media/base/vp9_profile.h"
namespace content {
namespace {
const int kDefaultFps = 30;
// Translate from media::VideoDecodeAccelerator::SupportedProfile to
// webrtc::SdpVideoFormat, or return nothing if the profile isn't supported.
base::Optional<webrtc::SdpVideoFormat> VDAToWebRTCFormat(
const media::VideoDecodeAccelerator::SupportedProfile& profile) {
if (profile.profile >= media::VP8PROFILE_MIN &&
profile.profile <= media::VP8PROFILE_MAX) {
return webrtc::SdpVideoFormat("VP8");
} else 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 H264 profile in WebRTC.
return base::nullopt;
}
return webrtc::SdpVideoFormat(
"VP9",
{{webrtc::kVP9FmtpProfileId, webrtc::VP9ProfileToString(vp9_profile)}});
} else if (profile.profile >= media::H264PROFILE_MIN &&
profile.profile <= media::H264PROFILE_MAX) {
webrtc::H264::Profile h264_profile;
switch (profile.profile) {
case media::H264PROFILE_BASELINE:
h264_profile = webrtc::H264::kProfileBaseline;
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 absl::optional<webrtc::H264::Level> h264_level =
webrtc::H264::SupportedLevel(width * height, kDefaultFps);
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;
}
return base::nullopt;
}
// Due to https://crbug.com/345569, HW decoders do not distinguish between
// Constrained Baseline(CBP) and Baseline(BP) profiles. Since CBP is a subset of
// BP, we can report support for both. It is safe to do so when SW fallback is
// available.
// TODO(emircan): Remove this when the bug referred above is fixed.
void MapBaselineProfile(
std::vector<webrtc::SdpVideoFormat>* supported_formats) {
for (const auto& format : *supported_formats) {
const absl::optional<webrtc::H264::ProfileLevelId> profile_level_id =
webrtc::H264::ParseSdpProfileLevelId(format.parameters);
if (profile_level_id &&
profile_level_id->profile == webrtc::H264::kProfileBaseline) {
webrtc::SdpVideoFormat cbp_format = format;
webrtc::H264::ProfileLevelId cbp_profile = *profile_level_id;
cbp_profile.profile = webrtc::H264::kProfileConstrainedBaseline;
cbp_format.parameters[cricket::kH264FmtpProfileLevelId] =
*webrtc::H264::ProfileLevelIdToString(cbp_profile);
supported_formats->push_back(cbp_format);
return;
}
}
}
// This extra indirection is needed so that we can delete the decoder on the
// correct thread.
class ScopedVideoDecoder : public webrtc::VideoDecoder {
public:
ScopedVideoDecoder(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
std::unique_ptr<webrtc::VideoDecoder> decoder)
: task_runner_(task_runner), decoder_(std::move(decoder)) {}
int32_t InitDecode(const webrtc::VideoCodec* codec_settings,
int32_t number_of_cores) override {
return decoder_->InitDecode(codec_settings, number_of_cores);
}
int32_t RegisterDecodeCompleteCallback(
webrtc::DecodedImageCallback* callback) override {
return decoder_->RegisterDecodeCompleteCallback(callback);
}
int32_t Release() override { return decoder_->Release(); }
int32_t Decode(const webrtc::EncodedImage& input_image,
bool missing_frames,
const webrtc::CodecSpecificInfo* codec_specific_info,
int64_t render_time_ms) override {
return decoder_->Decode(input_image, missing_frames, codec_specific_info,
render_time_ms);
}
bool PrefersLateDecoding() const override {
return decoder_->PrefersLateDecoding();
}
const char* ImplementationName() const override {
return decoder_->ImplementationName();
}
// Runs on Chrome_libJingle_WorkerThread. The child thread is blocked while
// this runs.
~ScopedVideoDecoder() override {
task_runner_->DeleteSoon(FROM_HERE, decoder_.release());
}
private:
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
std::unique_ptr<webrtc::VideoDecoder> decoder_;
};
} // namespace
RTCVideoDecoderFactory::RTCVideoDecoderFactory(
media::GpuVideoAcceleratorFactories* gpu_factories)
: gpu_factories_(gpu_factories) {
DVLOG(2) << __func__;
// RTCVideoDecoderAdapter does not use |supported_formats_|.
if (base::FeatureList::IsEnabled(media::kRTCVideoDecoderAdapter))
return;
const media::VideoDecodeAccelerator::SupportedProfiles profiles =
gpu_factories_->GetVideoDecodeAcceleratorCapabilities()
.supported_profiles;
for (const auto& profile : profiles) {
base::Optional<webrtc::SdpVideoFormat> format = VDAToWebRTCFormat(profile);
if (format)
supported_formats_.push_back(std::move(*format));
}
MapBaselineProfile(&supported_formats_);
}
std::vector<webrtc::SdpVideoFormat>
RTCVideoDecoderFactory::GetSupportedFormats() const {
DCHECK(!base::FeatureList::IsEnabled(media::kRTCVideoDecoderAdapter));
return supported_formats_;
}
RTCVideoDecoderFactory::~RTCVideoDecoderFactory() {
DVLOG(2) << __func__;
}
std::unique_ptr<webrtc::VideoDecoder>
RTCVideoDecoderFactory::CreateVideoDecoder(
const webrtc::SdpVideoFormat& format) {
DVLOG(2) << __func__;
std::unique_ptr<webrtc::VideoDecoder> decoder;
if (base::FeatureList::IsEnabled(media::kRTCVideoDecoderAdapter)) {
decoder = RTCVideoDecoderAdapter::Create(gpu_factories_, format);
} else {
decoder = RTCVideoDecoder::Create(format, gpu_factories_);
}
// ScopedVideoDecoder uses the task runner to make sure the decoder is
// destructed on the correct thread.
return decoder ? std::make_unique<ScopedVideoDecoder>(
gpu_factories_->GetTaskRunner(), std::move(decoder))
: nullptr;
}
} // namespace content