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