1 // Copyright 2014 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.
7 #include <CoreVideo/CoreVideo.h>
8 #include <OpenGL/CGLIOSurface.h>
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/mac/mac_logging.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/sys_byteorder.h"
17 #include "base/sys_info.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/version.h"
20 #include "content/common/gpu/media/vt_video_decode_accelerator.h"
21 #include "content/public/common/content_switches.h"
22 #include "media/base/limits.h"
23 #include "ui/gl/gl_context.h"
24 #include "ui/gl/gl_image_io_surface.h"
25 #include "ui/gl/scoped_binders.h"
27 using content_common_gpu_media::kModuleVt
;
28 using content_common_gpu_media::InitializeStubs
;
29 using content_common_gpu_media::IsVtInitialized
;
30 using content_common_gpu_media::StubPathMap
;
32 #define NOTIFY_STATUS(name, status, session_failure) \
34 OSSTATUS_DLOG(ERROR, status) << name; \
35 NotifyError(PLATFORM_FAILURE, session_failure); \
40 // Only H.264 with 4:2:0 chroma sampling is supported.
41 static const media::VideoCodecProfile kSupportedProfiles
[] = {
42 media::H264PROFILE_BASELINE
,
43 media::H264PROFILE_MAIN
,
44 media::H264PROFILE_EXTENDED
,
45 media::H264PROFILE_HIGH
,
46 media::H264PROFILE_HIGH10PROFILE
,
47 media::H264PROFILE_SCALABLEBASELINE
,
48 media::H264PROFILE_SCALABLEHIGH
,
49 media::H264PROFILE_STEREOHIGH
,
50 media::H264PROFILE_MULTIVIEWHIGH
,
53 // Size to use for NALU length headers in AVC format (can be 1, 2, or 4).
54 static const int kNALUHeaderLength
= 4;
56 // We request 5 picture buffers from the client, each of which has a texture ID
57 // that we can bind decoded frames to. We need enough to satisfy preroll, and
58 // enough to avoid unnecessary stalling, but no more than that. The resource
59 // requirements are low, as we don't need the textures to be backed by storage.
60 static const int kNumPictureBuffers
= media::limits::kMaxVideoFrames
+ 1;
62 // Maximum number of frames to queue for reordering before we stop asking for
63 // more. (NotifyEndOfBitstreamBuffer() is called when frames are moved into the
65 static const int kMaxReorderQueueSize
= 16;
67 // When set to false, always create a new decoder instead of reusing the
68 // existing configuration when the configuration changes. This works around a
69 // bug in VideoToolbox that results in corruption before Mac OS X 10.10.3. The
70 // value is set in InitializeVideoToolbox().
71 static bool g_enable_compatible_configuration_reuse
= true;
73 // Build an |image_config| dictionary for VideoToolbox initialization.
74 static base::ScopedCFTypeRef
<CFMutableDictionaryRef
>
75 BuildImageConfig(CMVideoDimensions coded_dimensions
) {
76 base::ScopedCFTypeRef
<CFMutableDictionaryRef
> image_config
;
78 // 4:2:2 is used over the native 4:2:0 because only 4:2:2 can be directly
79 // bound to a texture by CGLTexImageIOSurface2D().
80 int32_t pixel_format
= kCVPixelFormatType_422YpCbCr8
;
81 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i)
82 base::ScopedCFTypeRef
<CFNumberRef
> cf_pixel_format(CFINT(pixel_format
));
83 base::ScopedCFTypeRef
<CFNumberRef
> cf_width(CFINT(coded_dimensions
.width
));
84 base::ScopedCFTypeRef
<CFNumberRef
> cf_height(CFINT(coded_dimensions
.height
));
86 if (!cf_pixel_format
.get() || !cf_width
.get() || !cf_height
.get())
90 CFDictionaryCreateMutable(
93 &kCFTypeDictionaryKeyCallBacks
,
94 &kCFTypeDictionaryValueCallBacks
));
95 if (!image_config
.get())
98 CFDictionarySetValue(image_config
, kCVPixelBufferPixelFormatTypeKey
,
100 CFDictionarySetValue(image_config
, kCVPixelBufferWidthKey
, cf_width
);
101 CFDictionarySetValue(image_config
, kCVPixelBufferHeightKey
, cf_height
);
102 CFDictionarySetValue(image_config
, kCVPixelBufferOpenGLCompatibilityKey
,
108 // Create a VTDecompressionSession using the provided |pps| and |sps|. If
109 // |require_hardware| is true, the session must uses real hardware decoding
110 // (as opposed to software decoding inside of VideoToolbox) to be considered
113 // TODO(sandersd): Merge with ConfigureDecoder(), as the code is very similar.
114 static bool CreateVideoToolboxSession(const uint8_t* sps
, size_t sps_size
,
115 const uint8_t* pps
, size_t pps_size
,
116 bool require_hardware
) {
117 const uint8_t* data_ptrs
[] = {sps
, pps
};
118 const size_t data_sizes
[] = {sps_size
, pps_size
};
120 base::ScopedCFTypeRef
<CMFormatDescriptionRef
> format
;
121 OSStatus status
= CMVideoFormatDescriptionCreateFromH264ParameterSets(
123 2, // parameter_set_count
124 data_ptrs
, // ¶meter_set_pointers
125 data_sizes
, // ¶meter_set_sizes
126 kNALUHeaderLength
, // nal_unit_header_length
127 format
.InitializeInto());
129 OSSTATUS_DLOG(WARNING
, status
)
130 << "Failed to create CMVideoFormatDescription.";
134 base::ScopedCFTypeRef
<CFMutableDictionaryRef
> decoder_config(
135 CFDictionaryCreateMutable(
138 &kCFTypeDictionaryKeyCallBacks
,
139 &kCFTypeDictionaryValueCallBacks
));
140 if (!decoder_config
.get())
143 if (require_hardware
) {
144 CFDictionarySetValue(
146 // kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder
147 CFSTR("RequireHardwareAcceleratedVideoDecoder"),
151 base::ScopedCFTypeRef
<CFMutableDictionaryRef
> image_config(
152 BuildImageConfig(CMVideoFormatDescriptionGetDimensions(format
)));
153 if (!image_config
.get())
156 VTDecompressionOutputCallbackRecord callback
= {0};
158 base::ScopedCFTypeRef
<VTDecompressionSessionRef
> session
;
159 status
= VTDecompressionSessionCreate(
161 format
, // video_format_description
162 decoder_config
, // video_decoder_specification
163 image_config
, // destination_image_buffer_attributes
164 &callback
, // output_callback
165 session
.InitializeInto());
167 OSSTATUS_DLOG(WARNING
, status
) << "Failed to create VTDecompressionSession";
174 // The purpose of this function is to preload the generic and hardware-specific
175 // libraries required by VideoToolbox before the GPU sandbox is enabled.
176 // VideoToolbox normally loads the hardware-specific libraries lazily, so we
177 // must actually create a decompression session. If creating a decompression
178 // session fails, hardware decoding will be disabled (Initialize() will always
180 static bool InitializeVideoToolboxInternal() {
181 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
182 switches::kDisableAcceleratedVideoDecode
)) {
186 if (!IsVtInitialized()) {
187 // CoreVideo is also required, but the loader stops after the first path is
188 // loaded. Instead we rely on the transitive dependency from VideoToolbox to
190 // TODO(sandersd): Fallback to PrivateFrameworks to support OS X < 10.8.
192 paths
[kModuleVt
].push_back(FILE_PATH_LITERAL(
193 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox"));
194 if (!InitializeStubs(paths
)) {
195 LOG(WARNING
) << "Failed to initialize VideoToolbox framework. "
196 << "Hardware accelerated video decoding will be disabled.";
201 // Create a hardware decoding session.
202 // SPS and PPS data are taken from a 480p sample (buck2.mp4).
203 const uint8_t sps_normal
[] = {0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x80, 0xd4,
204 0x3d, 0xa1, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00,
205 0x00, 0x03, 0x00, 0x30, 0x8f, 0x16, 0x2d, 0x9a};
206 const uint8_t pps_normal
[] = {0x68, 0xe9, 0x7b, 0xcb};
207 if (!CreateVideoToolboxSession(sps_normal
, arraysize(sps_normal
), pps_normal
,
208 arraysize(pps_normal
), true)) {
209 LOG(WARNING
) << "Failed to create hardware VideoToolbox session. "
210 << "Hardware accelerated video decoding will be disabled.";
214 // Create a software decoding session.
215 // SPS and PPS data are taken from a 18p sample (small2.mp4).
216 const uint8_t sps_small
[] = {0x67, 0x64, 0x00, 0x0a, 0xac, 0xd9, 0x89, 0x7e,
217 0x22, 0x10, 0x00, 0x00, 0x3e, 0x90, 0x00, 0x0e,
218 0xa6, 0x08, 0xf1, 0x22, 0x59, 0xa0};
219 const uint8_t pps_small
[] = {0x68, 0xe9, 0x79, 0x72, 0xc0};
220 if (!CreateVideoToolboxSession(sps_small
, arraysize(sps_small
), pps_small
,
221 arraysize(pps_small
), false)) {
222 LOG(WARNING
) << "Failed to create software VideoToolbox session. "
223 << "Hardware accelerated video decoding will be disabled.";
227 // Set |g_enable_compatible_configuration_reuse| to false on
228 // Mac OS X < 10.10.3.
229 base::Version
os_x_version(base::SysInfo::OperatingSystemVersion());
230 if (os_x_version
.IsOlderThan("10.10.3"))
231 g_enable_compatible_configuration_reuse
= false;
236 bool InitializeVideoToolbox() {
237 // InitializeVideoToolbox() is called only from the GPU process main thread;
238 // once for sandbox warmup, and then once each time a VTVideoDecodeAccelerator
240 static bool attempted
= false;
241 static bool succeeded
= false;
245 succeeded
= InitializeVideoToolboxInternal();
251 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator.
252 static void OutputThunk(
253 void* decompression_output_refcon
,
254 void* source_frame_refcon
,
256 VTDecodeInfoFlags info_flags
,
257 CVImageBufferRef image_buffer
,
258 CMTime presentation_time_stamp
,
259 CMTime presentation_duration
) {
260 VTVideoDecodeAccelerator
* vda
=
261 reinterpret_cast<VTVideoDecodeAccelerator
*>(decompression_output_refcon
);
262 vda
->Output(source_frame_refcon
, status
, image_buffer
);
265 VTVideoDecodeAccelerator::Task::Task(TaskType type
) : type(type
) {
268 VTVideoDecodeAccelerator::Task::~Task() {
271 VTVideoDecodeAccelerator::Frame::Frame(int32_t bitstream_id
)
272 : bitstream_id(bitstream_id
), pic_order_cnt(0), reorder_window(0) {
275 VTVideoDecodeAccelerator::Frame::~Frame() {
278 VTVideoDecodeAccelerator::PictureInfo::PictureInfo(uint32_t client_texture_id
,
279 uint32_t service_texture_id
)
280 : client_texture_id(client_texture_id
),
281 service_texture_id(service_texture_id
) {}
283 VTVideoDecodeAccelerator::PictureInfo::~PictureInfo() {
285 gl_image
->Destroy(false);
288 bool VTVideoDecodeAccelerator::FrameOrder::operator()(
289 const linked_ptr
<Frame
>& lhs
,
290 const linked_ptr
<Frame
>& rhs
) const {
291 if (lhs
->pic_order_cnt
!= rhs
->pic_order_cnt
)
292 return lhs
->pic_order_cnt
> rhs
->pic_order_cnt
;
293 // If |pic_order_cnt| is the same, fall back on using the bitstream order.
294 // TODO(sandersd): Assign a sequence number in Decode() and use that instead.
295 // TODO(sandersd): Using the sequence number, ensure that frames older than
296 // |kMaxReorderQueueSize| are ordered first, regardless of |pic_order_cnt|.
297 return lhs
->bitstream_id
> rhs
->bitstream_id
;
300 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(
301 const base::Callback
<bool(void)>& make_context_current
,
302 const base::Callback
<void(uint32
, uint32
, scoped_refptr
<gfx::GLImage
>)>&
304 : make_context_current_(make_context_current
),
305 bind_image_(bind_image
),
307 state_(STATE_DECODING
),
312 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()),
313 decoder_thread_("VTDecoderThread"),
314 weak_this_factory_(this) {
315 DCHECK(!make_context_current_
.is_null());
316 callback_
.decompressionOutputCallback
= OutputThunk
;
317 callback_
.decompressionOutputRefCon
= this;
318 weak_this_
= weak_this_factory_
.GetWeakPtr();
321 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() {
322 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
325 bool VTVideoDecodeAccelerator::Initialize(
326 media::VideoCodecProfile profile
,
328 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
331 if (!InitializeVideoToolbox())
334 bool profile_supported
= false;
335 for (const auto& supported_profile
: kSupportedProfiles
) {
336 if (profile
== supported_profile
) {
337 profile_supported
= true;
341 if (!profile_supported
)
344 // Spawn a thread to handle parsing and calling VideoToolbox.
345 if (!decoder_thread_
.Start())
348 // Count the session as successfully initialized.
349 UMA_HISTOGRAM_ENUMERATION("Media.VTVDA.SessionFailureReason",
350 SFT_SUCCESSFULLY_INITIALIZED
,
355 bool VTVideoDecodeAccelerator::FinishDelayedFrames() {
356 DCHECK(decoder_thread_
.task_runner()->BelongsToCurrentThread());
358 OSStatus status
= VTDecompressionSessionWaitForAsynchronousFrames(session_
);
360 NOTIFY_STATUS("VTDecompressionSessionWaitForAsynchronousFrames()",
361 status
, SFT_PLATFORM_ERROR
);
368 bool VTVideoDecodeAccelerator::ConfigureDecoder() {
369 DCHECK(decoder_thread_
.task_runner()->BelongsToCurrentThread());
370 DCHECK(!last_sps_
.empty());
371 DCHECK(!last_pps_
.empty());
373 // Build the configuration records.
374 std::vector
<const uint8_t*> nalu_data_ptrs
;
375 std::vector
<size_t> nalu_data_sizes
;
376 nalu_data_ptrs
.reserve(3);
377 nalu_data_sizes
.reserve(3);
378 nalu_data_ptrs
.push_back(&last_sps_
.front());
379 nalu_data_sizes
.push_back(last_sps_
.size());
380 if (!last_spsext_
.empty()) {
381 nalu_data_ptrs
.push_back(&last_spsext_
.front());
382 nalu_data_sizes
.push_back(last_spsext_
.size());
384 nalu_data_ptrs
.push_back(&last_pps_
.front());
385 nalu_data_sizes
.push_back(last_pps_
.size());
387 // Construct a new format description from the parameter sets.
388 // TODO(sandersd): Replace this with custom code to support OS X < 10.9.
390 OSStatus status
= CMVideoFormatDescriptionCreateFromH264ParameterSets(
392 nalu_data_ptrs
.size(), // parameter_set_count
393 &nalu_data_ptrs
.front(), // ¶meter_set_pointers
394 &nalu_data_sizes
.front(), // ¶meter_set_sizes
395 kNALUHeaderLength
, // nal_unit_header_length
396 format_
.InitializeInto());
398 NOTIFY_STATUS("CMVideoFormatDescriptionCreateFromH264ParameterSets()",
399 status
, SFT_PLATFORM_ERROR
);
403 // Store the new configuration data.
404 CMVideoDimensions coded_dimensions
=
405 CMVideoFormatDescriptionGetDimensions(format_
);
406 coded_size_
.SetSize(coded_dimensions
.width
, coded_dimensions
.height
);
408 // If the session is compatible, there's nothing else to do.
409 if (g_enable_compatible_configuration_reuse
&& session_
&&
410 VTDecompressionSessionCanAcceptFormatDescription(session_
, format_
)) {
414 // Prepare VideoToolbox configuration dictionaries.
415 base::ScopedCFTypeRef
<CFMutableDictionaryRef
> decoder_config(
416 CFDictionaryCreateMutable(
419 &kCFTypeDictionaryKeyCallBacks
,
420 &kCFTypeDictionaryValueCallBacks
));
421 if (!decoder_config
.get()) {
422 DLOG(ERROR
) << "Failed to create CFMutableDictionary.";
423 NotifyError(PLATFORM_FAILURE
, SFT_PLATFORM_ERROR
);
427 CFDictionarySetValue(
429 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder
430 CFSTR("EnableHardwareAcceleratedVideoDecoder"),
433 base::ScopedCFTypeRef
<CFMutableDictionaryRef
> image_config(
434 BuildImageConfig(coded_dimensions
));
435 if (!image_config
.get()) {
436 DLOG(ERROR
) << "Failed to create decoder image configuration.";
437 NotifyError(PLATFORM_FAILURE
, SFT_PLATFORM_ERROR
);
441 // Ensure that the old decoder emits all frames before the new decoder can
443 if (!FinishDelayedFrames())
447 status
= VTDecompressionSessionCreate(
449 format_
, // video_format_description
450 decoder_config
, // video_decoder_specification
451 image_config
, // destination_image_buffer_attributes
452 &callback_
, // output_callback
453 session_
.InitializeInto());
455 NOTIFY_STATUS("VTDecompressionSessionCreate()", status
,
456 SFT_UNSUPPORTED_STREAM_PARAMETERS
);
460 // Report whether hardware decode is being used.
461 bool using_hardware
= false;
462 base::ScopedCFTypeRef
<CFBooleanRef
> cf_using_hardware
;
463 if (VTSessionCopyProperty(
465 // kVTDecompressionPropertyKey_UsingHardwareAcceleratedVideoDecoder
466 CFSTR("UsingHardwareAcceleratedVideoDecoder"),
468 cf_using_hardware
.InitializeInto()) == 0) {
469 using_hardware
= CFBooleanGetValue(cf_using_hardware
);
471 UMA_HISTOGRAM_BOOLEAN("Media.VTVDA.HardwareAccelerated", using_hardware
);
476 void VTVideoDecodeAccelerator::DecodeTask(
477 const media::BitstreamBuffer
& bitstream
,
479 DCHECK(decoder_thread_
.task_runner()->BelongsToCurrentThread());
481 // Map the bitstream buffer.
482 base::SharedMemory
memory(bitstream
.handle(), true);
483 size_t size
= bitstream
.size();
484 if (!memory
.Map(size
)) {
485 DLOG(ERROR
) << "Failed to map bitstream buffer";
486 NotifyError(PLATFORM_FAILURE
, SFT_PLATFORM_ERROR
);
489 const uint8_t* buf
= static_cast<uint8_t*>(memory
.memory());
491 // NALUs are stored with Annex B format in the bitstream buffer (start codes),
492 // but VideoToolbox expects AVC format (length headers), so we must rewrite
495 // Locate relevant NALUs and compute the size of the rewritten data. Also
496 // record any parameter sets for VideoToolbox initialization.
497 std::vector
<uint8_t> sps
;
498 std::vector
<uint8_t> spsext
;
499 std::vector
<uint8_t> pps
;
500 bool has_slice
= false;
501 size_t data_size
= 0;
502 std::vector
<media::H264NALU
> nalus
;
503 parser_
.SetStream(buf
, size
);
504 media::H264NALU nalu
;
506 media::H264Parser::Result result
= parser_
.AdvanceToNextNALU(&nalu
);
507 if (result
== media::H264Parser::kEOStream
)
509 if (result
== media::H264Parser::kUnsupportedStream
) {
510 DLOG(ERROR
) << "Unsupported H.264 stream";
511 NotifyError(PLATFORM_FAILURE
, SFT_UNSUPPORTED_STREAM
);
514 if (result
!= media::H264Parser::kOk
) {
515 DLOG(ERROR
) << "Failed to parse H.264 stream";
516 NotifyError(UNREADABLE_INPUT
, SFT_INVALID_STREAM
);
519 switch (nalu
.nal_unit_type
) {
520 case media::H264NALU::kSPS
:
521 result
= parser_
.ParseSPS(&last_sps_id_
);
522 if (result
== media::H264Parser::kUnsupportedStream
) {
523 DLOG(ERROR
) << "Unsupported SPS";
524 NotifyError(PLATFORM_FAILURE
, SFT_UNSUPPORTED_STREAM
);
527 if (result
!= media::H264Parser::kOk
) {
528 DLOG(ERROR
) << "Could not parse SPS";
529 NotifyError(UNREADABLE_INPUT
, SFT_INVALID_STREAM
);
532 sps
.assign(nalu
.data
, nalu
.data
+ nalu
.size
);
536 case media::H264NALU::kSPSExt
:
537 // TODO(sandersd): Check that the previous NALU was an SPS.
538 spsext
.assign(nalu
.data
, nalu
.data
+ nalu
.size
);
541 case media::H264NALU::kPPS
:
542 result
= parser_
.ParsePPS(&last_pps_id_
);
543 if (result
== media::H264Parser::kUnsupportedStream
) {
544 DLOG(ERROR
) << "Unsupported PPS";
545 NotifyError(PLATFORM_FAILURE
, SFT_UNSUPPORTED_STREAM
);
548 if (result
!= media::H264Parser::kOk
) {
549 DLOG(ERROR
) << "Could not parse PPS";
550 NotifyError(UNREADABLE_INPUT
, SFT_INVALID_STREAM
);
553 pps
.assign(nalu
.data
, nalu
.data
+ nalu
.size
);
556 case media::H264NALU::kSliceDataA
:
557 case media::H264NALU::kSliceDataB
:
558 case media::H264NALU::kSliceDataC
:
559 case media::H264NALU::kNonIDRSlice
:
560 // TODO(sandersd): Check that there has been an IDR slice since the
562 case media::H264NALU::kIDRSlice
:
563 // Compute the |pic_order_cnt| for the picture from the first slice.
564 // TODO(sandersd): Make sure that any further slices are part of the
565 // same picture or a redundant coded picture.
567 media::H264SliceHeader slice_hdr
;
568 result
= parser_
.ParseSliceHeader(nalu
, &slice_hdr
);
569 if (result
== media::H264Parser::kUnsupportedStream
) {
570 DLOG(ERROR
) << "Unsupported slice header";
571 NotifyError(PLATFORM_FAILURE
, SFT_UNSUPPORTED_STREAM
);
574 if (result
!= media::H264Parser::kOk
) {
575 DLOG(ERROR
) << "Could not parse slice header";
576 NotifyError(UNREADABLE_INPUT
, SFT_INVALID_STREAM
);
580 // TODO(sandersd): Maintain a cache of configurations and reconfigure
581 // when a slice references a new config.
582 DCHECK_EQ(slice_hdr
.pic_parameter_set_id
, last_pps_id_
);
583 const media::H264PPS
* pps
=
584 parser_
.GetPPS(slice_hdr
.pic_parameter_set_id
);
586 DLOG(ERROR
) << "Mising PPS referenced by slice";
587 NotifyError(UNREADABLE_INPUT
, SFT_INVALID_STREAM
);
591 DCHECK_EQ(pps
->seq_parameter_set_id
, last_sps_id_
);
592 const media::H264SPS
* sps
= parser_
.GetSPS(pps
->seq_parameter_set_id
);
594 DLOG(ERROR
) << "Mising SPS referenced by PPS";
595 NotifyError(UNREADABLE_INPUT
, SFT_INVALID_STREAM
);
599 if (!poc_
.ComputePicOrderCnt(sps
, slice_hdr
, &frame
->pic_order_cnt
)) {
600 DLOG(ERROR
) << "Unable to compute POC";
601 NotifyError(UNREADABLE_INPUT
, SFT_INVALID_STREAM
);
605 if (sps
->vui_parameters_present_flag
&&
606 sps
->bitstream_restriction_flag
) {
607 frame
->reorder_window
= std::min(sps
->max_num_reorder_frames
,
608 kMaxReorderQueueSize
- 1);
613 nalus
.push_back(nalu
);
614 data_size
+= kNALUHeaderLength
+ nalu
.size
;
619 // Initialize VideoToolbox.
620 bool config_changed
= false;
621 if (!sps
.empty() && sps
!= last_sps_
) {
623 last_spsext_
.swap(spsext
);
624 config_changed
= true;
626 if (!pps
.empty() && pps
!= last_pps_
) {
628 config_changed
= true;
630 if (config_changed
) {
631 if (last_sps_
.empty()) {
632 DLOG(ERROR
) << "Invalid configuration; no SPS";
633 NotifyError(INVALID_ARGUMENT
, SFT_INVALID_STREAM
);
636 if (last_pps_
.empty()) {
637 DLOG(ERROR
) << "Invalid configuration; no PPS";
638 NotifyError(INVALID_ARGUMENT
, SFT_INVALID_STREAM
);
641 if (!ConfigureDecoder())
645 // If there are no image slices, drop the bitstream buffer by returning an
648 if (!FinishDelayedFrames())
650 gpu_task_runner_
->PostTask(FROM_HERE
, base::Bind(
651 &VTVideoDecodeAccelerator::DecodeDone
, weak_this_
, frame
));
655 // If the session is not configured by this point, fail.
657 DLOG(ERROR
) << "Cannot decode without configuration";
658 NotifyError(INVALID_ARGUMENT
, SFT_INVALID_STREAM
);
662 // Update the frame metadata with configuration data.
663 frame
->coded_size
= coded_size_
;
665 // Create a memory-backed CMBlockBuffer for the translated data.
666 // TODO(sandersd): Pool of memory blocks.
667 base::ScopedCFTypeRef
<CMBlockBufferRef
> data
;
668 OSStatus status
= CMBlockBufferCreateWithMemoryBlock(
670 nullptr, // &memory_block
671 data_size
, // block_length
672 kCFAllocatorDefault
, // block_allocator
673 nullptr, // &custom_block_source
675 data_size
, // data_length
677 data
.InitializeInto());
679 NOTIFY_STATUS("CMBlockBufferCreateWithMemoryBlock()", status
,
684 // Make sure that the memory is actually allocated.
685 // CMBlockBufferReplaceDataBytes() is documented to do this, but prints a
686 // message each time starting in Mac OS X 10.10.
687 status
= CMBlockBufferAssureBlockMemory(data
);
689 NOTIFY_STATUS("CMBlockBufferAssureBlockMemory()", status
,
694 // Copy NALU data into the CMBlockBuffer, inserting length headers.
696 for (size_t i
= 0; i
< nalus
.size(); i
++) {
697 media::H264NALU
& nalu
= nalus
[i
];
698 uint32_t header
= base::HostToNet32(static_cast<uint32_t>(nalu
.size
));
699 status
= CMBlockBufferReplaceDataBytes(
700 &header
, data
, offset
, kNALUHeaderLength
);
702 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status
,
706 offset
+= kNALUHeaderLength
;
707 status
= CMBlockBufferReplaceDataBytes(nalu
.data
, data
, offset
, nalu
.size
);
709 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status
,
716 // Package the data in a CMSampleBuffer.
717 base::ScopedCFTypeRef
<CMSampleBufferRef
> sample
;
718 status
= CMSampleBufferCreate(
722 nullptr, // make_data_ready_callback
723 nullptr, // make_data_ready_refcon
724 format_
, // format_description
726 0, // num_sample_timing_entries
727 nullptr, // &sample_timing_array
728 1, // num_sample_size_entries
729 &data_size
, // &sample_size_array
730 sample
.InitializeInto());
732 NOTIFY_STATUS("CMSampleBufferCreate()", status
, SFT_PLATFORM_ERROR
);
736 // Send the frame for decoding.
737 // Asynchronous Decompression allows for parallel submission of frames
738 // (without it, DecodeFrame() does not return until the frame has been
739 // decoded). We don't enable Temporal Processing so that frames are always
740 // returned in decode order; this makes it easier to avoid deadlock.
741 VTDecodeFrameFlags decode_flags
=
742 kVTDecodeFrame_EnableAsynchronousDecompression
;
743 status
= VTDecompressionSessionDecodeFrame(
745 sample
, // sample_buffer
746 decode_flags
, // decode_flags
747 reinterpret_cast<void*>(frame
), // source_frame_refcon
748 nullptr); // &info_flags_out
750 NOTIFY_STATUS("VTDecompressionSessionDecodeFrame()", status
,
756 // This method may be called on any VideoToolbox thread.
757 void VTVideoDecodeAccelerator::Output(
758 void* source_frame_refcon
,
760 CVImageBufferRef image_buffer
) {
762 NOTIFY_STATUS("Decoding", status
, SFT_DECODE_ERROR
);
766 // The type of |image_buffer| is CVImageBuffer, but we only handle
767 // CVPixelBuffers. This should be guaranteed as we set
768 // kCVPixelBufferOpenGLCompatibilityKey in |image_config|.
770 // Sometimes, for unknown reasons (http://crbug.com/453050), |image_buffer| is
771 // NULL, which causes CFGetTypeID() to crash. While the rest of the code would
772 // smoothly handle NULL as a dropped frame, we choose to fail permanantly here
773 // until the issue is better understood.
774 if (!image_buffer
|| CFGetTypeID(image_buffer
) != CVPixelBufferGetTypeID()) {
775 DLOG(ERROR
) << "Decoded frame is not a CVPixelBuffer";
776 NotifyError(PLATFORM_FAILURE
, SFT_DECODE_ERROR
);
780 Frame
* frame
= reinterpret_cast<Frame
*>(source_frame_refcon
);
781 frame
->image
.reset(image_buffer
, base::scoped_policy::RETAIN
);
782 gpu_task_runner_
->PostTask(FROM_HERE
, base::Bind(
783 &VTVideoDecodeAccelerator::DecodeDone
, weak_this_
, frame
));
786 void VTVideoDecodeAccelerator::DecodeDone(Frame
* frame
) {
787 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
788 DCHECK_EQ(1u, pending_frames_
.count(frame
->bitstream_id
));
789 Task
task(TASK_FRAME
);
790 task
.frame
= pending_frames_
[frame
->bitstream_id
];
791 pending_frames_
.erase(frame
->bitstream_id
);
792 task_queue_
.push(task
);
796 void VTVideoDecodeAccelerator::FlushTask(TaskType type
) {
797 DCHECK(decoder_thread_
.task_runner()->BelongsToCurrentThread());
798 FinishDelayedFrames();
800 // Always queue a task, even if FinishDelayedFrames() fails, so that
801 // destruction always completes.
802 gpu_task_runner_
->PostTask(FROM_HERE
, base::Bind(
803 &VTVideoDecodeAccelerator::FlushDone
, weak_this_
, type
));
806 void VTVideoDecodeAccelerator::FlushDone(TaskType type
) {
807 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
808 task_queue_
.push(Task(type
));
812 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer
& bitstream
) {
813 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
814 DCHECK_EQ(0u, assigned_bitstream_ids_
.count(bitstream
.id()));
815 assigned_bitstream_ids_
.insert(bitstream
.id());
816 Frame
* frame
= new Frame(bitstream
.id());
817 pending_frames_
[frame
->bitstream_id
] = make_linked_ptr(frame
);
818 decoder_thread_
.task_runner()->PostTask(
819 FROM_HERE
, base::Bind(&VTVideoDecodeAccelerator::DecodeTask
,
820 base::Unretained(this), bitstream
, frame
));
823 void VTVideoDecodeAccelerator::AssignPictureBuffers(
824 const std::vector
<media::PictureBuffer
>& pictures
) {
825 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
827 for (const media::PictureBuffer
& picture
: pictures
) {
828 DCHECK(!picture_info_map_
.count(picture
.id()));
829 assigned_picture_ids_
.insert(picture
.id());
830 available_picture_ids_
.push_back(picture
.id());
831 picture_info_map_
.insert(picture
.id(), make_scoped_ptr(new PictureInfo(
832 picture
.internal_texture_id(),
833 picture
.texture_id())));
836 // Pictures are not marked as uncleared until after this method returns, and
837 // they will be broken if they are used before that happens. So, schedule
838 // future work after that happens.
839 gpu_task_runner_
->PostTask(FROM_HERE
, base::Bind(
840 &VTVideoDecodeAccelerator::ProcessWorkQueues
, weak_this_
));
843 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id
) {
844 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
845 DCHECK(picture_info_map_
.count(picture_id
));
846 PictureInfo
* picture_info
= picture_info_map_
.find(picture_id
)->second
;
847 DCHECK_EQ(CFGetRetainCount(picture_info
->cv_image
), 1);
848 picture_info
->cv_image
.reset();
849 picture_info
->gl_image
->Destroy(false);
850 picture_info
->gl_image
= nullptr;
852 if (assigned_picture_ids_
.count(picture_id
) != 0) {
853 available_picture_ids_
.push_back(picture_id
);
856 client_
->DismissPictureBuffer(picture_id
);
860 void VTVideoDecodeAccelerator::ProcessWorkQueues() {
861 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
864 // TODO(sandersd): Batch where possible.
865 while (state_
== STATE_DECODING
) {
866 if (!ProcessReorderQueue() && !ProcessTaskQueue())
872 // Do nothing until Destroy() is called.
875 case STATE_DESTROYING
:
876 // Drop tasks until we are ready to destruct.
877 while (!task_queue_
.empty()) {
878 if (task_queue_
.front().type
== TASK_DESTROY
) {
888 bool VTVideoDecodeAccelerator::ProcessTaskQueue() {
889 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
890 DCHECK_EQ(state_
, STATE_DECODING
);
892 if (task_queue_
.empty())
895 const Task
& task
= task_queue_
.front();
898 // TODO(sandersd): Signal IDR explicitly (not using pic_order_cnt == 0).
899 if (reorder_queue_
.size() < kMaxReorderQueueSize
&&
900 (task
.frame
->pic_order_cnt
!= 0 || reorder_queue_
.empty())) {
901 assigned_bitstream_ids_
.erase(task
.frame
->bitstream_id
);
902 client_
->NotifyEndOfBitstreamBuffer(task
.frame
->bitstream_id
);
903 reorder_queue_
.push(task
.frame
);
910 DCHECK_EQ(task
.type
, pending_flush_tasks_
.front());
911 if (reorder_queue_
.size() == 0) {
912 pending_flush_tasks_
.pop();
913 client_
->NotifyFlushDone();
920 DCHECK_EQ(task
.type
, pending_flush_tasks_
.front());
921 if (reorder_queue_
.size() == 0) {
925 last_spsext_
.clear();
928 pending_flush_tasks_
.pop();
929 client_
->NotifyResetDone();
936 NOTREACHED() << "Can't destroy while in STATE_DECODING.";
937 NotifyError(ILLEGAL_STATE
, SFT_PLATFORM_ERROR
);
942 bool VTVideoDecodeAccelerator::ProcessReorderQueue() {
943 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
944 DCHECK_EQ(state_
, STATE_DECODING
);
946 if (reorder_queue_
.empty())
949 // If the next task is a flush (because there is a pending flush or becuase
950 // the next frame is an IDR), then we don't need a full reorder buffer to send
952 bool flushing
= !task_queue_
.empty() &&
953 (task_queue_
.front().type
!= TASK_FRAME
||
954 task_queue_
.front().frame
->pic_order_cnt
== 0);
956 size_t reorder_window
= std::max(0, reorder_queue_
.top()->reorder_window
);
957 if (flushing
|| reorder_queue_
.size() > reorder_window
) {
958 if (ProcessFrame(*reorder_queue_
.top())) {
959 reorder_queue_
.pop();
967 bool VTVideoDecodeAccelerator::ProcessFrame(const Frame
& frame
) {
968 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
969 DCHECK_EQ(state_
, STATE_DECODING
);
971 // If the next pending flush is for a reset, then the frame will be dropped.
972 bool resetting
= !pending_flush_tasks_
.empty() &&
973 pending_flush_tasks_
.front() == TASK_RESET
;
975 if (!resetting
&& frame
.image
.get()) {
976 // If the |coded_size| has changed, request new picture buffers and then
978 // TODO(sandersd): If GpuVideoDecoder didn't specifically check the size of
979 // textures, this would be unnecessary, as the size is actually a property
980 // of the texture binding, not the texture. We rebind every frame, so the
981 // size passed to ProvidePictureBuffers() is meaningless.
982 if (picture_size_
!= frame
.coded_size
) {
983 // Dismiss current pictures.
984 for (int32_t picture_id
: assigned_picture_ids_
)
985 client_
->DismissPictureBuffer(picture_id
);
986 assigned_picture_ids_
.clear();
987 available_picture_ids_
.clear();
989 // Request new pictures.
990 picture_size_
= frame
.coded_size
;
991 client_
->ProvidePictureBuffers(
992 kNumPictureBuffers
, coded_size_
, GL_TEXTURE_RECTANGLE_ARB
);
995 if (!SendFrame(frame
))
1002 bool VTVideoDecodeAccelerator::SendFrame(const Frame
& frame
) {
1003 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
1004 DCHECK_EQ(state_
, STATE_DECODING
);
1006 if (available_picture_ids_
.empty())
1009 int32_t picture_id
= available_picture_ids_
.back();
1010 DCHECK(picture_info_map_
.count(picture_id
));
1011 PictureInfo
* picture_info
= picture_info_map_
.find(picture_id
)->second
;
1012 DCHECK(!picture_info
->cv_image
);
1013 DCHECK(!picture_info
->gl_image
);
1015 if (!make_context_current_
.Run()) {
1016 DLOG(ERROR
) << "Failed to make GL context current";
1017 NotifyError(PLATFORM_FAILURE
, SFT_PLATFORM_ERROR
);
1021 IOSurfaceRef surface
= CVPixelBufferGetIOSurface(frame
.image
.get());
1022 glEnable(GL_TEXTURE_RECTANGLE_ARB
);
1023 gfx::ScopedTextureBinder
texture_binder(GL_TEXTURE_RECTANGLE_ARB
,
1024 picture_info
->service_texture_id
);
1025 CGLContextObj cgl_context
=
1026 static_cast<CGLContextObj
>(gfx::GLContext::GetCurrent()->GetHandle());
1027 CGLError status
= CGLTexImageIOSurface2D(
1029 GL_TEXTURE_RECTANGLE_ARB
, // target
1030 GL_RGB
, // internal_format
1031 frame
.coded_size
.width(), // width
1032 frame
.coded_size
.height(), // height
1033 GL_YCBCR_422_APPLE
, // format
1034 GL_UNSIGNED_SHORT_8_8_APPLE
, // type
1035 surface
, // io_surface
1037 glDisable(GL_TEXTURE_RECTANGLE_ARB
);
1038 if (status
!= kCGLNoError
) {
1039 NOTIFY_STATUS("CGLTexImageIOSurface2D()", status
, SFT_PLATFORM_ERROR
);
1043 bool allow_overlay
= false;
1044 scoped_refptr
<gfx::GLImageIOSurface
> gl_image(new gfx::GLImageIOSurface(
1045 gfx::GenericSharedMemoryId(), frame
.coded_size
, GL_BGRA_EXT
));
1046 if (gl_image
->Initialize(surface
, gfx::BufferFormat::BGRA_8888
)) {
1047 allow_overlay
= true;
1051 bind_image_
.Run(picture_info
->client_texture_id
, GL_TEXTURE_RECTANGLE_ARB
,
1054 // Assign the new image(s) to the the picture info.
1055 picture_info
->gl_image
= gl_image
;
1056 picture_info
->cv_image
= frame
.image
;
1057 available_picture_ids_
.pop_back();
1059 // TODO(sandersd): Currently, the size got from
1060 // CMVideoFormatDescriptionGetDimensions is visible size. We pass it to
1061 // GpuVideoDecoder so that GpuVideoDecoder can use correct visible size in
1062 // resolution changed. We should find the correct API to get the real
1063 // coded size and fix it.
1064 client_
->PictureReady(media::Picture(picture_id
, frame
.bitstream_id
,
1065 gfx::Rect(frame
.coded_size
),
1070 void VTVideoDecodeAccelerator::NotifyError(
1071 Error vda_error_type
,
1072 VTVDASessionFailureType session_failure_type
) {
1073 DCHECK_LT(session_failure_type
, SFT_MAX
+ 1);
1074 if (!gpu_thread_checker_
.CalledOnValidThread()) {
1075 gpu_task_runner_
->PostTask(FROM_HERE
, base::Bind(
1076 &VTVideoDecodeAccelerator::NotifyError
, weak_this_
, vda_error_type
,
1077 session_failure_type
));
1078 } else if (state_
== STATE_DECODING
) {
1079 state_
= STATE_ERROR
;
1080 UMA_HISTOGRAM_ENUMERATION("Media.VTVDA.SessionFailureReason",
1081 session_failure_type
,
1083 client_
->NotifyError(vda_error_type
);
1087 void VTVideoDecodeAccelerator::QueueFlush(TaskType type
) {
1088 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
1089 pending_flush_tasks_
.push(type
);
1090 decoder_thread_
.task_runner()->PostTask(
1091 FROM_HERE
, base::Bind(&VTVideoDecodeAccelerator::FlushTask
,
1092 base::Unretained(this), type
));
1094 // If this is a new flush request, see if we can make progress.
1095 if (pending_flush_tasks_
.size() == 1)
1096 ProcessWorkQueues();
1099 void VTVideoDecodeAccelerator::Flush() {
1100 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
1101 QueueFlush(TASK_FLUSH
);
1104 void VTVideoDecodeAccelerator::Reset() {
1105 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
1106 QueueFlush(TASK_RESET
);
1109 void VTVideoDecodeAccelerator::Destroy() {
1110 DCHECK(gpu_thread_checker_
.CalledOnValidThread());
1112 // In a forceful shutdown, the decoder thread may be dead already.
1113 if (!decoder_thread_
.IsRunning()) {
1118 // For a graceful shutdown, return assigned buffers and flush before
1119 // destructing |this|.
1120 // TODO(sandersd): Make sure the decoder won't try to read the buffers again
1121 // before discarding them.
1122 for (int32_t bitstream_id
: assigned_bitstream_ids_
)
1123 client_
->NotifyEndOfBitstreamBuffer(bitstream_id
);
1124 assigned_bitstream_ids_
.clear();
1125 state_
= STATE_DESTROYING
;
1126 QueueFlush(TASK_DESTROY
);
1129 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() {
1134 media::VideoDecodeAccelerator::SupportedProfiles
1135 VTVideoDecodeAccelerator::GetSupportedProfiles() {
1136 SupportedProfiles profiles
;
1137 for (const auto& supported_profile
: kSupportedProfiles
) {
1138 SupportedProfile profile
;
1139 profile
.profile
= supported_profile
;
1140 profile
.min_resolution
.SetSize(16, 16);
1141 profile
.max_resolution
.SetSize(4096, 2160);
1142 profiles
.push_back(profile
);
1147 } // namespace content