1 // Copyright 2019 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 "media/gpu/test/video_player/test_vda_video_decoder.h"
6 
7 #include <utility>
8 #include <vector>
9 
10 #include <GLES2/gl2.h>
11 #include <GLES2/gl2ext.h>
12 
13 #include "base/bind.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/task/post_task.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "build/build_config.h"
18 #include "media/base/media_log.h"
19 #include "media/base/video_frame.h"
20 #include "media/base/video_util.h"
21 #include "media/base/waiting.h"
22 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
23 #include "media/gpu/macros.h"
24 #include "media/gpu/test/video.h"
25 #include "media/gpu/test/video_player/frame_renderer.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 
28 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
29 #include "media/gpu/chromeos/chromeos_video_decoder_factory.h"
30 #include "media/gpu/chromeos/platform_video_frame_utils.h"
31 #include "media/gpu/chromeos/vd_video_decode_accelerator.h"
32 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
33 
34 namespace media {
35 namespace test {
36 
37 namespace {
38 // Size of the timestamp cache, needs to be large enough for frame-reordering.
39 constexpr size_t kTimestampCacheSize = 128;
40 }  // namespace
41 
TestVDAVideoDecoder(AllocationMode allocation_mode,bool use_vd_vda,const gfx::ColorSpace & target_color_space,FrameRenderer * const frame_renderer,gpu::GpuMemoryBufferFactory * gpu_memory_buffer_factory)42 TestVDAVideoDecoder::TestVDAVideoDecoder(
43     AllocationMode allocation_mode,
44     bool use_vd_vda,
45     const gfx::ColorSpace& target_color_space,
46     FrameRenderer* const frame_renderer,
47     gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory)
48     : output_mode_(allocation_mode == AllocationMode::kAllocate
49                        ? VideoDecodeAccelerator::Config::OutputMode::ALLOCATE
50                        : VideoDecodeAccelerator::Config::OutputMode::IMPORT),
51       use_vd_vda_(use_vd_vda),
52       target_color_space_(target_color_space),
53       frame_renderer_(frame_renderer),
54 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
55       gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
56 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
57       decode_start_timestamps_(kTimestampCacheSize) {
58   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
59 
60   // TODO(crbug.com/933632) Remove support for allocate mode, and always use
61   // import mode. Support for allocate mode is temporary maintained for older
62   // platforms that don't support import mode.
63 
64   vda_wrapper_task_runner_ = base::ThreadTaskRunnerHandle::Get();
65 
66   weak_this_ = weak_this_factory_.GetWeakPtr();
67 }
68 
~TestVDAVideoDecoder()69 TestVDAVideoDecoder::~TestVDAVideoDecoder() {
70   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
71 
72   // Invalidate all scheduled tasks.
73   weak_this_factory_.InvalidateWeakPtrs();
74 
75   decoder_ = nullptr;
76 
77   // Delete all video frames and related textures and the decoder.
78   video_frames_.clear();
79 }
80 
GetDisplayName() const81 std::string TestVDAVideoDecoder::GetDisplayName() const {
82   return "TestVDAVideoDecoder";
83 }
84 
IsPlatformDecoder() const85 bool TestVDAVideoDecoder::IsPlatformDecoder() const {
86   return true;
87 }
88 
Initialize(const VideoDecoderConfig & config,bool low_delay,CdmContext * cdm_context,InitCB init_cb,const OutputCB & output_cb,const WaitingCB & waiting_cb)89 void TestVDAVideoDecoder::Initialize(const VideoDecoderConfig& config,
90                                      bool low_delay,
91                                      CdmContext* cdm_context,
92                                      InitCB init_cb,
93                                      const OutputCB& output_cb,
94                                      const WaitingCB& waiting_cb) {
95   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
96 
97   output_cb_ = output_cb;
98 
99   // Create decoder factory.
100   std::unique_ptr<GpuVideoDecodeAcceleratorFactory> decoder_factory;
101   bool hasGLContext = frame_renderer_->GetGLContext() != nullptr;
102   GpuVideoDecodeGLClient gl_client;
103   if (hasGLContext) {
104     gl_client.get_context = base::BindRepeating(
105         &FrameRenderer::GetGLContext, base::Unretained(frame_renderer_));
106     gl_client.make_context_current = base::BindRepeating(
107         &FrameRenderer::AcquireGLContext, base::Unretained(frame_renderer_));
108     gl_client.bind_image = base::BindRepeating(
109         [](uint32_t, uint32_t, const scoped_refptr<gl::GLImage>&, bool) {
110           return true;
111         });
112   }
113   decoder_factory = GpuVideoDecodeAcceleratorFactory::Create(gl_client);
114 
115   if (!decoder_factory) {
116     ASSERT_TRUE(decoder_) << "Failed to create VideoDecodeAccelerator factory";
117     std::move(init_cb).Run(StatusCode::kCodeOnlyForTesting);
118     return;
119   }
120 
121   // Create Decoder.
122   VideoDecodeAccelerator::Config vda_config(config.profile());
123   vda_config.output_mode = output_mode_;
124   vda_config.encryption_scheme = config.encryption_scheme();
125   vda_config.is_deferred_initialization_allowed = false;
126   vda_config.initial_expected_coded_size = config.coded_size();
127   vda_config.container_color_space = config.color_space_info();
128   vda_config.target_color_space = target_color_space_;
129   vda_config.hdr_metadata = config.hdr_metadata();
130 
131   gpu::GpuDriverBugWorkarounds gpu_driver_bug_workarounds;
132   gpu::GpuPreferences gpu_preferences;
133   if (use_vd_vda_) {
134 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
135     DVLOGF(2) << "Use VdVideoDecodeAccelerator";
136     vda_config.is_deferred_initialization_allowed = true;
137     decoder_ = media::VdVideoDecodeAccelerator::Create(
138         base::BindRepeating(&media::ChromeosVideoDecoderFactory::Create), this,
139         vda_config, base::SequencedTaskRunnerHandle::Get());
140 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
141   } else {
142     DVLOGF(2) << "Use original VDA";
143     decoder_ = decoder_factory->CreateVDA(
144         this, vda_config, gpu_driver_bug_workarounds, gpu_preferences);
145   }
146 
147   if (!decoder_) {
148     ASSERT_TRUE(decoder_) << "Failed to create VideoDecodeAccelerator factory";
149     std::move(init_cb).Run(StatusCode::kCodeOnlyForTesting);
150     return;
151   }
152 
153   if (!vda_config.is_deferred_initialization_allowed)
154     std::move(init_cb).Run(OkStatus());
155   else
156     init_cb_ = std::move(init_cb);
157 }
158 
NotifyInitializationComplete(Status status)159 void TestVDAVideoDecoder::NotifyInitializationComplete(Status status) {
160   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
161   DCHECK(init_cb_);
162 
163   std::move(init_cb_).Run(status);
164 }
165 
Decode(scoped_refptr<DecoderBuffer> buffer,DecodeCB decode_cb)166 void TestVDAVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
167                                  DecodeCB decode_cb) {
168   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
169 
170   // If the |buffer| is an EOS buffer the decoder must be flushed.
171   if (buffer->end_of_stream()) {
172     flush_cb_ = std::move(decode_cb);
173     decoder_->Flush();
174     return;
175   }
176 
177   int32_t bitstream_buffer_id = GetNextBitstreamBufferId();
178   decode_cbs_[bitstream_buffer_id] = std::move(decode_cb);
179 
180   // Record picture buffer decode start time. A cache is used because not each
181   // bitstream buffer decode will result in a call to PictureReady(). Pictures
182   // can be delivered in a different order than the decode operations, so we
183   // don't know when it's safe to purge old decode timestamps. Instead we use
184   // a cache with a large enough size to account for frame reordering.
185   decode_start_timestamps_.Put(bitstream_buffer_id, buffer->timestamp());
186 
187   decoder_->Decode(std::move(buffer), bitstream_buffer_id);
188 }
189 
Reset(base::OnceClosure reset_cb)190 void TestVDAVideoDecoder::Reset(base::OnceClosure reset_cb) {
191   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
192 
193   reset_cb_ = std::move(reset_cb);
194   decoder_->Reset();
195 }
196 
NeedsBitstreamConversion() const197 bool TestVDAVideoDecoder::NeedsBitstreamConversion() const {
198   return false;
199 }
200 
CanReadWithoutStalling() const201 bool TestVDAVideoDecoder::CanReadWithoutStalling() const {
202   return true;
203 }
204 
GetMaxDecodeRequests() const205 int TestVDAVideoDecoder::GetMaxDecodeRequests() const {
206   return 4;
207 }
208 
ProvidePictureBuffers(uint32_t requested_num_of_buffers,VideoPixelFormat format,uint32_t textures_per_buffer,const gfx::Size & dimensions,uint32_t texture_target)209 void TestVDAVideoDecoder::ProvidePictureBuffers(
210     uint32_t requested_num_of_buffers,
211     VideoPixelFormat format,
212     uint32_t textures_per_buffer,
213     const gfx::Size& dimensions,
214     uint32_t texture_target) {
215   NOTIMPLEMENTED() << "VDA must call ProvidePictureBuffersWithVisibleRect()";
216 }
217 
ProvidePictureBuffersWithVisibleRect(uint32_t requested_num_of_buffers,VideoPixelFormat format,uint32_t textures_per_buffer,const gfx::Size & dimensions,const gfx::Rect & visible_rect,uint32_t texture_target)218 void TestVDAVideoDecoder::ProvidePictureBuffersWithVisibleRect(
219     uint32_t requested_num_of_buffers,
220     VideoPixelFormat format,
221     uint32_t textures_per_buffer,
222     const gfx::Size& dimensions,
223     const gfx::Rect& visible_rect,
224     uint32_t texture_target) {
225   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
226   ASSERT_EQ(textures_per_buffer, 1u);
227   DVLOGF(4) << "Requested " << requested_num_of_buffers
228             << " picture buffers with size " << dimensions.width() << "x"
229             << dimensions.height();
230 
231   // If using allocate mode the format requested here might be
232   // PIXEL_FORMAT_UNKNOWN.
233   if (format == PIXEL_FORMAT_UNKNOWN)
234     format = PIXEL_FORMAT_ARGB;
235 
236   std::vector<PictureBuffer> picture_buffers;
237 
238   switch (output_mode_) {
239     case VideoDecodeAccelerator::Config::OutputMode::IMPORT:
240       // If using import mode, create a set of DMABuf-backed video frames.
241       for (uint32_t i = 0; i < requested_num_of_buffers; ++i) {
242         picture_buffers.emplace_back(GetNextPictureBufferId(), dimensions);
243       }
244       decoder_->AssignPictureBuffers(picture_buffers);
245 
246       // Create a video frame for each of the picture buffers and provide memory
247       // handles to the video frame's data to the decoder.
248       for (const PictureBuffer& picture_buffer : picture_buffers) {
249         scoped_refptr<VideoFrame> video_frame;
250 
251 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
252         video_frame = CreatePlatformVideoFrame(
253             gpu_memory_buffer_factory_, format, dimensions, visible_rect,
254             visible_rect.size(), base::TimeDelta(),
255             gfx::BufferUsage::SCANOUT_VDA_WRITE);
256 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
257 
258         ASSERT_TRUE(video_frame) << "Failed to create video frame";
259         video_frames_.emplace(picture_buffer.id(), video_frame);
260         gfx::GpuMemoryBufferHandle handle;
261 
262 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
263         handle = CreateGpuMemoryBufferHandle(video_frame.get());
264         DCHECK(!handle.is_null());
265 #else
266         NOTREACHED();
267 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
268 
269         ASSERT_TRUE(!handle.is_null()) << "Failed to create GPU memory handle";
270         decoder_->ImportBufferForPicture(picture_buffer.id(), format,
271                                          std::move(handle));
272       }
273       break;
274     case VideoDecodeAccelerator::Config::OutputMode::ALLOCATE: {
275       // If using allocate mode, request a set of texture-backed video frames
276       // from the renderer.
277       const gfx::Size texture_dimensions =
278           texture_target == GL_TEXTURE_EXTERNAL_OES
279               ? GetRectSizeFromOrigin(visible_rect)
280               : dimensions;
281       for (uint32_t i = 0; i < requested_num_of_buffers; ++i) {
282         uint32_t texture_id;
283         auto video_frame = frame_renderer_->CreateVideoFrame(
284             format, texture_dimensions, texture_target, &texture_id);
285         ASSERT_TRUE(video_frame) << "Failed to create video frame";
286         int32_t picture_buffer_id = GetNextPictureBufferId();
287         PictureBuffer::TextureIds texture_ids(1, texture_id);
288         picture_buffers.emplace_back(picture_buffer_id, texture_dimensions,
289                                      texture_ids, texture_ids, texture_target,
290                                      format);
291         video_frames_.emplace(picture_buffer_id, std::move(video_frame));
292       }
293       // The decoder requires an active GL context to allocate memory.
294       decoder_->AssignPictureBuffers(picture_buffers);
295       break;
296     }
297     default:
298       LOG(ERROR) << "Unsupported output mode "
299                  << static_cast<size_t>(output_mode_);
300   }
301 }
302 
DismissPictureBuffer(int32_t picture_buffer_id)303 void TestVDAVideoDecoder::DismissPictureBuffer(int32_t picture_buffer_id) {
304   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
305 
306   // Drop reference to the video frame associated with the picture buffer, so
307   // the video frame and related texture are automatically destroyed once the
308   // renderer and video frame processors are done using them.
309   ASSERT_EQ(video_frames_.erase(picture_buffer_id), 1u);
310 }
311 
PictureReady(const Picture & picture)312 void TestVDAVideoDecoder::PictureReady(const Picture& picture) {
313   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
314   DVLOGF(4) << "Picture buffer ID: " << picture.picture_buffer_id();
315 
316   auto it = video_frames_.find(picture.picture_buffer_id());
317   ASSERT_TRUE(it != video_frames_.end());
318   scoped_refptr<VideoFrame> video_frame = it->second;
319 
320   // Look up the time at which the decode started.
321   auto timestamp_it =
322       decode_start_timestamps_.Peek(picture.bitstream_buffer_id());
323   ASSERT_NE(timestamp_it, decode_start_timestamps_.end());
324   video_frame->set_timestamp(timestamp_it->second);
325 
326   scoped_refptr<VideoFrame> wrapped_video_frame = nullptr;
327 
328   // Wrap the video frame in another frame that calls ReusePictureBufferTask()
329   // upon destruction. When the renderer and video frame processors are done
330   // using the video frame, the associated picture buffer will automatically be
331   // flagged for reuse. WrapVideoFrame() is not supported for texture-based
332   // video frames (see http://crbug/362521) so we work around this by creating a
333   // new video frame using the same mailbox.
334   if (!picture.visible_rect().IsEmpty()) {
335     if (!video_frame->HasTextures()) {
336       wrapped_video_frame = VideoFrame::WrapVideoFrame(
337           video_frame, video_frame->format(), picture.visible_rect(),
338           picture.visible_rect().size());
339     } else {
340       gpu::MailboxHolder mailbox_holders[media::VideoFrame::kMaxPlanes];
341       mailbox_holders[0] = video_frame->mailbox_holder(0);
342       wrapped_video_frame = VideoFrame::WrapNativeTextures(
343           video_frame->format(), mailbox_holders,
344           VideoFrame::ReleaseMailboxCB(), video_frame->coded_size(),
345           picture.visible_rect(), picture.visible_rect().size(),
346           video_frame->timestamp());
347     }
348   } else {
349     // This occurs in bitstream buffer in webrtc scenario. WrapNativeTexture()
350     // fails if visible_rect() is empty. Although the client of
351     // TestVdaVideoDecoder should ignore this frame, it is necessary to output
352     // the dummy frame to count up the number of output video frames.
353     wrapped_video_frame =
354         VideoFrame::CreateFrame(PIXEL_FORMAT_UNKNOWN, gfx::Size(), gfx::Rect(),
355                                 gfx::Size(), video_frame->timestamp());
356   }
357 
358   DCHECK(wrapped_video_frame);
359 
360   // Flag that the video frame was decoded in a power efficient way.
361   wrapped_video_frame->metadata()->power_efficient = true;
362 
363   // It's important to bind the original video frame to the destruction callback
364   // of the wrapped frame, to avoid deleting it before rendering of the wrapped
365   // frame is done. A reference to the video frame is already stored in
366   // |video_frames_| to map between picture buffers and frames, but that
367   // reference will be released when the decoder calls DismissPictureBuffer()
368   // (e.g. on a resolution change).
369   base::OnceClosure reuse_cb =
370       base::BindOnce(&TestVDAVideoDecoder::ReusePictureBufferThunk, weak_this_,
371                      vda_wrapper_task_runner_, picture.picture_buffer_id());
372   wrapped_video_frame->AddDestructionObserver(std::move(reuse_cb));
373   output_cb_.Run(std::move(wrapped_video_frame));
374 }
375 
376 // static
ReusePictureBufferThunk(base::Optional<base::WeakPtr<TestVDAVideoDecoder>> vda_video_decoder,scoped_refptr<base::SequencedTaskRunner> task_runner,int32_t picture_buffer_id)377 void TestVDAVideoDecoder::ReusePictureBufferThunk(
378     base::Optional<base::WeakPtr<TestVDAVideoDecoder>> vda_video_decoder,
379     scoped_refptr<base::SequencedTaskRunner> task_runner,
380     int32_t picture_buffer_id) {
381   DCHECK(vda_video_decoder);
382   task_runner->PostTask(
383       FROM_HERE, base::BindOnce(&TestVDAVideoDecoder::ReusePictureBufferTask,
384                                 *vda_video_decoder, picture_buffer_id));
385 }
386 
387 // Called when a picture buffer is ready to be re-used.
ReusePictureBufferTask(int32_t picture_buffer_id)388 void TestVDAVideoDecoder::ReusePictureBufferTask(int32_t picture_buffer_id) {
389   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
390   DCHECK(decoder_);
391   DVLOGF(4) << "Picture buffer ID: " << picture_buffer_id;
392 
393   // Notify the decoder the picture buffer can be reused. The decoder will only
394   // request a limited set of picture buffers, when it runs out it will wait
395   // until picture buffers are flagged for reuse.
396   decoder_->ReusePictureBuffer(picture_buffer_id);
397 }
398 
NotifyEndOfBitstreamBuffer(int32_t bitstream_buffer_id)399 void TestVDAVideoDecoder::NotifyEndOfBitstreamBuffer(
400     int32_t bitstream_buffer_id) {
401   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
402 
403   auto it = decode_cbs_.find(bitstream_buffer_id);
404   ASSERT_TRUE(it != decode_cbs_.end())
405       << "Couldn't find decode callback for picture buffer with id "
406       << bitstream_buffer_id;
407 
408   std::move(it->second).Run(DecodeStatus::OK);
409   decode_cbs_.erase(it);
410 }
411 
NotifyFlushDone()412 void TestVDAVideoDecoder::NotifyFlushDone() {
413   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
414   DCHECK(flush_cb_);
415 
416   std::move(flush_cb_).Run(DecodeStatus::OK);
417 }
418 
NotifyResetDone()419 void TestVDAVideoDecoder::NotifyResetDone() {
420   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
421   DCHECK(reset_cb_);
422 
423   std::move(reset_cb_).Run();
424 }
425 
NotifyError(VideoDecodeAccelerator::Error error)426 void TestVDAVideoDecoder::NotifyError(VideoDecodeAccelerator::Error error) {
427   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
428 
429   switch (error) {
430     case VideoDecodeAccelerator::ILLEGAL_STATE:
431       LOG(ERROR) << "ILLEGAL_STATE";
432       break;
433     case VideoDecodeAccelerator::INVALID_ARGUMENT:
434       LOG(ERROR) << "INVALID_ARGUMENT";
435       break;
436     case VideoDecodeAccelerator::UNREADABLE_INPUT:
437       LOG(ERROR) << "UNREADABLE_INPUT";
438       break;
439     case VideoDecodeAccelerator::PLATFORM_FAILURE:
440       LOG(ERROR) << "PLATFORM_FAILURE";
441       break;
442     default:
443       LOG(ERROR) << "Unknown error " << error;
444       break;
445   }
446 }
447 
GetNextBitstreamBufferId()448 int32_t TestVDAVideoDecoder::GetNextBitstreamBufferId() {
449   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
450   // The bitstream buffer ID should always be positive, negative values are
451   // reserved for uninitialized buffers.
452   next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x7FFFFFFF;
453   return next_bitstream_buffer_id_;
454 }
455 
GetNextPictureBufferId()456 int32_t TestVDAVideoDecoder::GetNextPictureBufferId() {
457   DCHECK_CALLED_ON_VALID_SEQUENCE(vda_wrapper_sequence_checker_);
458   // The picture buffer ID should always be positive, negative values are
459   // reserved for uninitialized buffers.
460   next_picture_buffer_id_ = (next_picture_buffer_id_ + 1) & 0x7FFFFFFF;
461   return next_picture_buffer_id_;
462 }
463 
464 }  // namespace test
465 }  // namespace media
466