1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.h"
6
7 #include <array>
8 #include <memory>
9
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/sequenced_task_runner.h"
13 #include "build/build_config.h"
14 #include "media/base/media_util.h"
15 #include "media/base/video_codecs.h"
16 #include "media/video/gpu_video_accelerator_factories.h"
17 #include "third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h"
18 #include "third_party/webrtc/media/base/h264_profile_level_id.h"
19 #include "third_party/webrtc/media/base/media_constants.h"
20 #include "third_party/webrtc/media/base/vp9_profile.h"
21 #include "ui/gfx/color_space.h"
22 #include "ui/gfx/geometry/size.h"
23
24 namespace blink {
25 namespace {
26
27 const int kDefaultFps = 30;
28 // Any reasonable size, will be overridden by the decoder anyway.
29 const gfx::Size kDefaultSize(640, 480);
30
31 struct CodecConfig {
32 media::VideoCodec codec;
33 media::VideoCodecProfile profile;
34 };
35
36 constexpr std::array<CodecConfig, 7> kCodecConfigs = {{
37 {media::kCodecVP8, media::VP8PROFILE_ANY},
38 {media::kCodecVP9, media::VP9PROFILE_PROFILE0},
39 {media::kCodecVP9, media::VP9PROFILE_PROFILE1},
40 {media::kCodecVP9, media::VP9PROFILE_PROFILE2},
41 {media::kCodecH264, media::H264PROFILE_BASELINE},
42 {media::kCodecH264, media::H264PROFILE_MAIN},
43 {media::kCodecH264, media::H264PROFILE_HIGH},
44 }};
45
46 // Translate from media::VideoDecoderConfig to webrtc::SdpVideoFormat, or return
47 // nothing if the profile isn't supported.
VdcToWebRtcFormat(const media::VideoDecoderConfig & config)48 base::Optional<webrtc::SdpVideoFormat> VdcToWebRtcFormat(
49 const media::VideoDecoderConfig& config) {
50 switch (config.codec()) {
51 case media::VideoCodec::kCodecVP8:
52 return webrtc::SdpVideoFormat("VP8");
53 case media::VideoCodec::kCodecVP9: {
54 webrtc::VP9Profile vp9_profile;
55 switch (config.profile()) {
56 case media::VP9PROFILE_PROFILE0:
57 vp9_profile = webrtc::VP9Profile::kProfile0;
58 break;
59 case media::VP9PROFILE_PROFILE1:
60 vp9_profile = webrtc::VP9Profile::kProfile1;
61 break;
62 case media::VP9PROFILE_PROFILE2:
63 vp9_profile = webrtc::VP9Profile::kProfile2;
64 break;
65 default:
66 // Unsupported profile in WebRTC.
67 return base::nullopt;
68 }
69 return webrtc::SdpVideoFormat(
70 "VP9", {{webrtc::kVP9FmtpProfileId,
71 webrtc::VP9ProfileToString(vp9_profile)}});
72 }
73 case media::VideoCodec::kCodecH264: {
74 webrtc::H264::Profile h264_profile;
75 switch (config.profile()) {
76 case media::H264PROFILE_BASELINE:
77 h264_profile = webrtc::H264::kProfileBaseline;
78 break;
79 case media::H264PROFILE_MAIN:
80 h264_profile = webrtc::H264::kProfileMain;
81 break;
82 case media::H264PROFILE_HIGH:
83 h264_profile = webrtc::H264::kProfileHigh;
84 break;
85 default:
86 // Unsupported H264 profile in WebRTC.
87 return base::nullopt;
88 }
89
90 const int width = config.visible_rect().width();
91 const int height = config.visible_rect().height();
92
93 const absl::optional<webrtc::H264::Level> h264_level =
94 webrtc::H264::SupportedLevel(width * height, kDefaultFps);
95 const webrtc::H264::ProfileLevelId profile_level_id(
96 h264_profile, h264_level.value_or(webrtc::H264::kLevel1));
97
98 webrtc::SdpVideoFormat format("H264");
99 format.parameters = {
100 {cricket::kH264FmtpProfileLevelId,
101 *webrtc::H264::ProfileLevelIdToString(profile_level_id)},
102 {cricket::kH264FmtpLevelAsymmetryAllowed, "1"},
103 {cricket::kH264FmtpPacketizationMode, "1"}};
104 return format;
105 }
106 default:
107 return base::nullopt;
108 }
109 }
110
111 // Due to https://crbug.com/345569, HW decoders do not distinguish between
112 // Constrained Baseline(CBP) and Baseline(BP) profiles. Since CBP is a subset of
113 // BP, we can report support for both. It is safe to do so when SW fallback is
114 // available.
115 // TODO(emircan): Remove this when the bug referred above is fixed.
MapBaselineProfile(std::vector<webrtc::SdpVideoFormat> * supported_formats)116 void MapBaselineProfile(
117 std::vector<webrtc::SdpVideoFormat>* supported_formats) {
118 for (const auto& format : *supported_formats) {
119 const absl::optional<webrtc::H264::ProfileLevelId> profile_level_id =
120 webrtc::H264::ParseSdpProfileLevelId(format.parameters);
121 if (profile_level_id &&
122 profile_level_id->profile == webrtc::H264::kProfileBaseline) {
123 webrtc::SdpVideoFormat cbp_format = format;
124 webrtc::H264::ProfileLevelId cbp_profile = *profile_level_id;
125 cbp_profile.profile = webrtc::H264::kProfileConstrainedBaseline;
126 cbp_format.parameters[cricket::kH264FmtpProfileLevelId] =
127 *webrtc::H264::ProfileLevelIdToString(cbp_profile);
128 supported_formats->push_back(cbp_format);
129 return;
130 }
131 }
132 }
133
134 // This extra indirection is needed so that we can delete the decoder on the
135 // correct thread.
136 class ScopedVideoDecoder : public webrtc::VideoDecoder {
137 public:
ScopedVideoDecoder(const scoped_refptr<base::SequencedTaskRunner> & task_runner,std::unique_ptr<webrtc::VideoDecoder> decoder)138 ScopedVideoDecoder(
139 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
140 std::unique_ptr<webrtc::VideoDecoder> decoder)
141 : task_runner_(task_runner), decoder_(std::move(decoder)) {}
142
InitDecode(const webrtc::VideoCodec * codec_settings,int32_t number_of_cores)143 int32_t InitDecode(const webrtc::VideoCodec* codec_settings,
144 int32_t number_of_cores) override {
145 return decoder_->InitDecode(codec_settings, number_of_cores);
146 }
RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback * callback)147 int32_t RegisterDecodeCompleteCallback(
148 webrtc::DecodedImageCallback* callback) override {
149 return decoder_->RegisterDecodeCompleteCallback(callback);
150 }
Release()151 int32_t Release() override { return decoder_->Release(); }
Decode(const webrtc::EncodedImage & input_image,bool missing_frames,int64_t render_time_ms)152 int32_t Decode(const webrtc::EncodedImage& input_image,
153 bool missing_frames,
154 int64_t render_time_ms) override {
155 return decoder_->Decode(input_image, missing_frames, render_time_ms);
156 }
PrefersLateDecoding() const157 bool PrefersLateDecoding() const override {
158 return decoder_->PrefersLateDecoding();
159 }
ImplementationName() const160 const char* ImplementationName() const override {
161 return decoder_->ImplementationName();
162 }
163
164 // Runs on Chrome_libJingle_WorkerThread. The child thread is blocked while
165 // this runs.
~ScopedVideoDecoder()166 ~ScopedVideoDecoder() override {
167 task_runner_->DeleteSoon(FROM_HERE, decoder_.release());
168 }
169
170 private:
171 scoped_refptr<base::SequencedTaskRunner> task_runner_;
172 std::unique_ptr<webrtc::VideoDecoder> decoder_;
173 };
174
175 } // namespace
176
RTCVideoDecoderFactory(media::GpuVideoAcceleratorFactories * gpu_factories)177 RTCVideoDecoderFactory::RTCVideoDecoderFactory(
178 media::GpuVideoAcceleratorFactories* gpu_factories)
179 : gpu_factories_(gpu_factories), gpu_codec_support_waiter_(gpu_factories) {
180 DVLOG(2) << __func__;
181 }
182
CheckAndWaitDecoderSupportStatusIfNeeded() const183 void RTCVideoDecoderFactory::CheckAndWaitDecoderSupportStatusIfNeeded() const {
184 if (!gpu_codec_support_waiter_.IsDecoderSupportKnown()) {
185 DLOG(WARNING) << "Decoder support is unknown. Timeout "
186 << gpu_codec_support_waiter_.wait_timeout_ms()
187 .value_or(base::TimeDelta())
188 .InMilliseconds()
189 << "ms. Decoders might not be available.";
190 }
191 }
192
193 std::vector<webrtc::SdpVideoFormat>
GetSupportedFormats() const194 RTCVideoDecoderFactory::GetSupportedFormats() const {
195 CheckAndWaitDecoderSupportStatusIfNeeded();
196
197 std::vector<webrtc::SdpVideoFormat> supported_formats;
198 for (auto& codec_config : kCodecConfigs) {
199 media::VideoDecoderConfig config(
200 codec_config.codec, codec_config.profile,
201 media::VideoDecoderConfig::AlphaMode::kIsOpaque,
202 media::VideoColorSpace(), media::kNoTransformation, kDefaultSize,
203 gfx::Rect(kDefaultSize), kDefaultSize, media::EmptyExtraData(),
204 media::EncryptionScheme::kUnencrypted);
205 for (auto impl : RTCVideoDecoderAdapter::SupportedImplementations()) {
206 if (gpu_factories_->IsDecoderConfigSupported(impl, config) ==
207 media::GpuVideoAcceleratorFactories::Supported::kTrue) {
208 base::Optional<webrtc::SdpVideoFormat> format =
209 VdcToWebRtcFormat(config);
210 if (format) {
211 supported_formats.push_back(*format);
212 }
213 break;
214 }
215 }
216 }
217 MapBaselineProfile(&supported_formats);
218 return supported_formats;
219 }
220
~RTCVideoDecoderFactory()221 RTCVideoDecoderFactory::~RTCVideoDecoderFactory() {
222 DVLOG(2) << __func__;
223 }
224
225 std::unique_ptr<webrtc::VideoDecoder>
CreateVideoDecoder(const webrtc::SdpVideoFormat & format)226 RTCVideoDecoderFactory::CreateVideoDecoder(
227 const webrtc::SdpVideoFormat& format) {
228 DVLOG(2) << __func__;
229 CheckAndWaitDecoderSupportStatusIfNeeded();
230
231 std::unique_ptr<webrtc::VideoDecoder> decoder =
232 RTCVideoDecoderAdapter::Create(gpu_factories_, format);
233 // ScopedVideoDecoder uses the task runner to make sure the decoder is
234 // destructed on the correct thread.
235 return decoder ? std::make_unique<ScopedVideoDecoder>(
236 gpu_factories_->GetTaskRunner(), std::move(decoder))
237 : nullptr;
238 }
239
240 } // namespace blink
241