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