OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <CoreVideo/CoreVideo.h> | 5 #include <CoreVideo/CoreVideo.h> |
6 #include <OpenGL/CGLIOSurface.h> | 6 #include <OpenGL/CGLIOSurface.h> |
7 | 7 |
8 #include "base/bind.h" | 8 #include "base/bind.h" |
9 #include "base/thread_task_runner_handle.h" | 9 #include "base/thread_task_runner_handle.h" |
10 #include "content/common/gpu/media/vt_video_decode_accelerator.h" | 10 #include "content/common/gpu/media/vt_video_decode_accelerator.h" |
11 #include "media/filters/h264_parser.h" | 11 #include "media/filters/h264_parser.h" |
12 #include "ui/gl/scoped_binders.h" | |
12 | 13 |
13 using content_common_gpu_media::kModuleVt; | 14 using content_common_gpu_media::kModuleVt; |
14 using content_common_gpu_media::InitializeStubs; | 15 using content_common_gpu_media::InitializeStubs; |
15 using content_common_gpu_media::IsVtInitialized; | 16 using content_common_gpu_media::IsVtInitialized; |
16 using content_common_gpu_media::StubPathMap; | 17 using content_common_gpu_media::StubPathMap; |
17 | 18 |
18 namespace content { | 19 namespace content { |
19 | 20 |
20 // Size of length headers prepended to NALUs in MPEG-4 framing. (1, 2, or 4.) | 21 // Size of NALU length headers in AVCC/MPEG-4 format (can be 1, 2, or 4). |
21 static const int kNALUHeaderLength = 4; | 22 static const int kNALUHeaderLength = 4; |
22 | 23 |
24 // We only request 5 picture buffers from the client which are used to hold the | |
25 // decoded samples. These buffers are then reused when the client tells us that | |
26 // it is done with the buffer. | |
27 static const int kNumPictureBuffers = 5; | |
28 | |
23 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | 29 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
24 static void OutputThunk( | 30 static void OutputThunk( |
25 void* decompression_output_refcon, | 31 void* decompression_output_refcon, |
26 void* source_frame_refcon, | 32 void* source_frame_refcon, |
27 OSStatus status, | 33 OSStatus status, |
28 VTDecodeInfoFlags info_flags, | 34 VTDecodeInfoFlags info_flags, |
29 CVImageBufferRef image_buffer, | 35 CVImageBufferRef image_buffer, |
30 CMTime presentation_time_stamp, | 36 CMTime presentation_time_stamp, |
31 CMTime presentation_duration) { | 37 CMTime presentation_duration) { |
38 // TODO(sandersd): Implement flush-before-delete to guarantee validity. | |
32 VTVideoDecodeAccelerator* vda = | 39 VTVideoDecodeAccelerator* vda = |
33 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); | 40 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); |
34 int32_t* bitstream_id_ptr = reinterpret_cast<int32_t*>(source_frame_refcon); | 41 intptr_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon); |
Pawel Osciak
2014/08/02 00:28:14
Would it work to cast to int32_t directly?
sandersd (OOO until July 31)
2014/08/02 01:43:13
Done.
| |
35 int32_t bitstream_id = *bitstream_id_ptr; | 42 vda->Output(bitstream_id, status, image_buffer); |
36 delete bitstream_id_ptr; | 43 } |
37 CFRetain(image_buffer); | 44 |
38 vda->Output(bitstream_id, status, info_flags, image_buffer); | 45 VTVideoDecodeAccelerator::DecodedFrame::DecodedFrame( |
46 uint32_t bitstream_id, | |
47 CVImageBufferRef image_buffer) | |
48 : bitstream_id(bitstream_id), | |
49 image_buffer(image_buffer) { | |
50 } | |
51 | |
52 VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() { | |
39 } | 53 } |
40 | 54 |
41 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) | 55 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
42 : cgl_context_(cgl_context), | 56 : cgl_context_(cgl_context), |
43 client_(NULL), | 57 client_(NULL), |
44 decoder_thread_("VTDecoderThread"), | |
45 format_(NULL), | 58 format_(NULL), |
46 session_(NULL), | 59 session_(NULL), |
47 weak_this_factory_(this) { | 60 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
61 weak_this_factory_(this), | |
62 decoder_thread_("VTDecoderThread") { | |
48 callback_.decompressionOutputCallback = OutputThunk; | 63 callback_.decompressionOutputCallback = OutputThunk; |
49 callback_.decompressionOutputRefCon = this; | 64 callback_.decompressionOutputRefCon = this; |
50 } | 65 } |
51 | 66 |
52 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 67 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
53 } | 68 } |
54 | 69 |
55 bool VTVideoDecodeAccelerator::Initialize( | 70 bool VTVideoDecodeAccelerator::Initialize( |
56 media::VideoCodecProfile profile, | 71 media::VideoCodecProfile profile, |
57 Client* client) { | 72 Client* client) { |
58 DCHECK(CalledOnValidThread()); | 73 DCHECK(CalledOnValidThread()); |
59 client_ = client; | 74 client_ = client; |
60 | 75 |
61 // Only H.264 is supported. | 76 // Only H.264 is supported. |
62 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) | 77 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
63 return false; | 78 return false; |
64 | 79 |
65 // TODO(sandersd): Move VideoToolbox library loading to sandbox startup; | 80 // TODO(sandersd): Move VideoToolbox library loading to sandbox startup; |
66 // until then, --no-sandbox is required. | 81 // until then, --no-sandbox is required. |
67 if (!IsVtInitialized()) { | 82 if (!IsVtInitialized()) { |
68 StubPathMap paths; | |
69 // CoreVideo is also required, but the loader stops after the first | 83 // CoreVideo is also required, but the loader stops after the first |
70 // path is loaded. Instead we rely on the transitive dependency from | 84 // path is loaded. Instead we rely on the transitive dependency from |
71 // VideoToolbox to CoreVideo. | 85 // VideoToolbox to CoreVideo. |
72 // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox. | 86 // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox. |
87 StubPathMap paths; | |
73 paths[kModuleVt].push_back(FILE_PATH_LITERAL( | 88 paths[kModuleVt].push_back(FILE_PATH_LITERAL( |
74 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); | 89 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); |
75 if (!InitializeStubs(paths)) | 90 if (!InitializeStubs(paths)) |
76 return false; | 91 return false; |
77 } | 92 } |
78 | 93 |
79 // Spawn a thread to handle parsing and calling VideoToolbox. | 94 // Spawn a thread to handle parsing and calling VideoToolbox. |
80 if (!decoder_thread_.Start()) | 95 if (!decoder_thread_.Start()) |
81 return false; | 96 return false; |
82 | 97 |
83 // Note that --ignore-gpu-blacklist is still required to get here. | 98 // Note that --ignore-gpu-blacklist is still required to get here. |
84 return true; | 99 return true; |
85 } | 100 } |
86 | 101 |
87 // TODO(sandersd): Proper error reporting instead of CHECKs. | 102 // TODO(sandersd): Proper error reporting instead of CHECKs. |
88 void VTVideoDecodeAccelerator::ConfigureDecoder( | 103 void VTVideoDecodeAccelerator::ConfigureDecoder( |
89 const std::vector<const uint8_t*>& nalu_data_ptrs, | 104 const std::vector<const uint8_t*>& nalu_data_ptrs, |
90 const std::vector<size_t>& nalu_data_sizes) { | 105 const std::vector<size_t>& nalu_data_sizes) { |
106 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
107 // Construct a new format description from the parameter sets. | |
108 // TODO(sandersd): Replace this with custom code to support OS X < 10.9. | |
91 format_.reset(); | 109 format_.reset(); |
92 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( | 110 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( |
93 kCFAllocatorDefault, | 111 kCFAllocatorDefault, |
94 nalu_data_ptrs.size(), // parameter_set_count | 112 nalu_data_ptrs.size(), // parameter_set_count |
95 &nalu_data_ptrs.front(), // ¶meter_set_pointers | 113 &nalu_data_ptrs.front(), // ¶meter_set_pointers |
96 &nalu_data_sizes.front(), // ¶meter_set_sizes | 114 &nalu_data_sizes.front(), // ¶meter_set_sizes |
97 kNALUHeaderLength, // nal_unit_header_length | 115 kNALUHeaderLength, // nal_unit_header_length |
98 format_.InitializeInto() | 116 format_.InitializeInto())); |
99 )); | 117 CMVideoDimensions coded_dimensions = |
118 CMVideoFormatDescriptionGetDimensions(format_); | |
100 | 119 |
101 // TODO(sandersd): Check if the size has changed and handle picture requests. | 120 // Prepare VideoToolbox configuration dictionaries. |
102 CMVideoDimensions coded_size = CMVideoFormatDescriptionGetDimensions(format_); | |
103 coded_size_.SetSize(coded_size.width, coded_size.height); | |
104 | |
105 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( | 121 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( |
106 CFDictionaryCreateMutable( | 122 CFDictionaryCreateMutable( |
107 kCFAllocatorDefault, | 123 kCFAllocatorDefault, |
108 1, // capacity | 124 1, // capacity |
109 &kCFTypeDictionaryKeyCallBacks, | 125 &kCFTypeDictionaryKeyCallBacks, |
110 &kCFTypeDictionaryValueCallBacks)); | 126 &kCFTypeDictionaryValueCallBacks)); |
111 | 127 |
112 CFDictionarySetValue( | 128 CFDictionarySetValue( |
113 decoder_config, | 129 decoder_config, |
114 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder | 130 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder |
115 CFSTR("EnableHardwareAcceleratedVideoDecoder"), | 131 CFSTR("EnableHardwareAcceleratedVideoDecoder"), |
116 kCFBooleanTrue); | 132 kCFBooleanTrue); |
117 | 133 |
118 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | 134 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
119 CFDictionaryCreateMutable( | 135 CFDictionaryCreateMutable( |
120 kCFAllocatorDefault, | 136 kCFAllocatorDefault, |
121 4, // capacity | 137 4, // capacity |
122 &kCFTypeDictionaryKeyCallBacks, | 138 &kCFTypeDictionaryKeyCallBacks, |
123 &kCFTypeDictionaryValueCallBacks)); | 139 &kCFTypeDictionaryValueCallBacks)); |
124 | 140 |
125 // TODO(sandersd): ARGB for video that is not 4:2:0. | |
126 int32_t pixel_format = '2vuy'; | |
127 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | 141 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) |
142 // TODO(sandersd): RGBA option for 4:4:4 video. | |
143 int32_t pixel_format = kCVPixelFormatType_422YpCbCr8; | |
128 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); | 144 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); |
129 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_size.width)); | 145 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); |
130 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_size.height)); | 146 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); |
131 #undef CFINT | 147 #undef CFINT |
132 CFDictionarySetValue( | 148 CFDictionarySetValue( |
133 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | 149 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
134 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | 150 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
135 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | 151 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
136 CFDictionarySetValue( | 152 CFDictionarySetValue( |
137 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | 153 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
138 | 154 |
139 // TODO(sandersd): Skip if the session is compatible. | 155 // TODO(sandersd): Check if the session is already compatible. |
140 // TODO(sandersd): Flush frames when resetting. | 156 // TODO(sandersd): Flush. |
141 session_.reset(); | 157 session_.reset(); |
142 CHECK(!VTDecompressionSessionCreate( | 158 CHECK(!VTDecompressionSessionCreate( |
143 kCFAllocatorDefault, | 159 kCFAllocatorDefault, |
144 format_, // video_format_description | 160 format_, // video_format_description |
145 decoder_config, // video_decoder_specification | 161 decoder_config, // video_decoder_specification |
146 image_config, // destination_image_buffer_attributes | 162 image_config, // destination_image_buffer_attributes |
147 &callback_, // output_callback | 163 &callback_, // output_callback |
148 session_.InitializeInto() | 164 session_.InitializeInto())); |
149 )); | 165 |
150 DVLOG(2) << "Created VTDecompressionSession"; | 166 // If the size has changed, trigger a request for new picture buffers. |
Pawel Osciak
2014/08/02 00:28:14
Nit: I'd CHECK(coded_size_.empty()) in case you ge
sandersd (OOO until July 31)
2014/08/02 01:43:13
This should be the correct behavior for a mid-stre
Pawel Osciak
2014/08/03 04:26:30
coded_size_.empty() would mean not mid-stream, but
sandersd (OOO until July 31)
2014/08/04 19:24:20
Right, but my intention is that this code will exe
| |
167 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); | |
168 if (coded_size_ != new_coded_size) { | |
169 coded_size_ = new_coded_size; | |
170 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
171 &VTVideoDecodeAccelerator::SizeChangedTask, | |
172 weak_this_factory_.GetWeakPtr(), | |
173 coded_size_));; | |
174 } | |
151 } | 175 } |
152 | 176 |
153 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 177 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
154 DCHECK(CalledOnValidThread()); | 178 DCHECK(CalledOnValidThread()); |
155 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | 179 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
156 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), | 180 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), |
157 bitstream)); | 181 bitstream)); |
158 } | 182 } |
159 | 183 |
160 void VTVideoDecodeAccelerator::DecodeTask( | 184 void VTVideoDecodeAccelerator::DecodeTask( |
161 const media::BitstreamBuffer bitstream) { | 185 const media::BitstreamBuffer bitstream) { |
162 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 186 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
163 | 187 |
164 // Map the bitstream buffer. | 188 // Map the bitstream buffer. |
165 base::SharedMemory memory(bitstream.handle(), true); | 189 base::SharedMemory memory(bitstream.handle(), true); |
166 size_t size = bitstream.size(); | 190 size_t size = bitstream.size(); |
167 CHECK(memory.Map(size)); | 191 CHECK(memory.Map(size)); |
168 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); | 192 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); |
169 | 193 |
170 // Locate relevant NALUs in the buffer. | 194 // NALUs are stored with Annex B format in the bitstream buffer (start codes), |
195 // but VideoToolbox expects AVCC/MPEG-4 format (length headers), so we must | |
196 // rewrite the data. | |
197 // | |
198 // 1. Locate relevant NALUs and compute the size of the translated data. | |
199 // Also record any parameter sets for VideoToolbox initialization. | |
171 size_t data_size = 0; | 200 size_t data_size = 0; |
172 std::vector<media::H264NALU> nalus; | 201 std::vector<media::H264NALU> nalus; |
173 std::vector<const uint8_t*> config_nalu_data_ptrs; | 202 std::vector<const uint8_t*> config_nalu_data_ptrs; |
174 std::vector<size_t> config_nalu_data_sizes; | 203 std::vector<size_t> config_nalu_data_sizes; |
175 parser_.SetStream(buf, size); | 204 parser_.SetStream(buf, size); |
176 media::H264NALU nalu; | 205 media::H264NALU nalu; |
177 while (true) { | 206 while (true) { |
178 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); | 207 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); |
179 if (result == media::H264Parser::kEOStream) | 208 if (result == media::H264Parser::kEOStream) |
180 break; | 209 break; |
181 CHECK_EQ(result, media::H264Parser::kOk); | 210 CHECK_EQ(result, media::H264Parser::kOk); |
Pawel Osciak
2014/08/02 00:28:14
In general this means that any bad stream provided
sandersd (OOO until July 31)
2014/08/02 01:43:14
Done.
| |
211 // TODO(sandersd): Check that these are only at the start. | |
182 if (nalu.nal_unit_type == media::H264NALU::kSPS || | 212 if (nalu.nal_unit_type == media::H264NALU::kSPS || |
183 nalu.nal_unit_type == media::H264NALU::kPPS || | 213 nalu.nal_unit_type == media::H264NALU::kPPS || |
184 nalu.nal_unit_type == media::H264NALU::kSPSExt) { | 214 nalu.nal_unit_type == media::H264NALU::kSPSExt) { |
215 DVLOG(2) << "Parameter set " << nalu.nal_unit_type; | |
185 config_nalu_data_ptrs.push_back(nalu.data); | 216 config_nalu_data_ptrs.push_back(nalu.data); |
186 config_nalu_data_sizes.push_back(nalu.size); | 217 config_nalu_data_sizes.push_back(nalu.size); |
218 } else { | |
219 nalus.push_back(nalu); | |
220 data_size += kNALUHeaderLength + nalu.size; | |
187 } | 221 } |
188 nalus.push_back(nalu); | |
189 // Each NALU will have a 4-byte length header prepended. | |
190 data_size += kNALUHeaderLength + nalu.size; | |
191 } | 222 } |
192 | 223 |
193 if (!config_nalu_data_ptrs.empty()) | 224 // 2. Initialize VideoToolbox. |
225 // TODO(sandersd): Reinitialize when there are new parameter sets. | |
226 if (!session_) | |
194 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); | 227 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); |
Pawel Osciak
2014/08/02 00:28:14
What if there were no SPS and/or PPS in this bitst
sandersd (OOO until July 31)
2014/08/02 01:43:14
This code is knowingly wrong in that case. The log
| |
195 | 228 |
196 // TODO(sandersd): Rewrite slice NALU headers and send for decoding. | 229 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. |
230 base::ScopedCFTypeRef<CMBlockBufferRef> data; | |
231 CHECK(!CMBlockBufferCreateWithMemoryBlock( | |
232 kCFAllocatorDefault, | |
233 NULL, // &memory_block | |
234 data_size, // block_length | |
235 kCFAllocatorDefault, // block_allocator | |
236 NULL, // &custom_block_source | |
237 0, // offset_to_data | |
238 data_size, // data_length | |
239 0, // flags | |
240 data.InitializeInto())); | |
241 | |
242 // 4. Copy NALU data, inserting length headers. | |
243 size_t offset = 0; | |
244 for (size_t i = 0; i < nalus.size(); i++) { | |
Pawel Osciak
2014/08/02 00:28:14
Do we require at least one NALU to be there? Maybe
sandersd (OOO until July 31)
2014/08/02 01:43:14
If we don't get any NALUs, it means something has
| |
245 media::H264NALU& nalu = nalus[i]; | |
246 uint8_t header[4] = {0xff & nalu.size >> 24, | |
Pawel Osciak
2014/08/02 00:28:14
Please base::HostToNet32(checked_cast<uint32_t>(na
sandersd (OOO until July 31)
2014/08/02 01:43:14
I have a deep personal aversion to byte swapping,
| |
247 0xff & nalu.size >> 16, | |
248 0xff & nalu.size >> 8, | |
249 0xff & nalu.size}; | |
250 CHECK(!CMBlockBufferReplaceDataBytes(header, data, offset, 4)); | |
Pawel Osciak
2014/08/02 00:28:14
s/4/kNALUHeaderLength/
sandersd (OOO until July 31)
2014/08/02 01:43:13
Done.
| |
251 offset += 4; | |
Pawel Osciak
2014/08/02 00:28:14
ditto
sandersd (OOO until July 31)
2014/08/02 01:43:14
Done.
| |
252 CHECK(!CMBlockBufferReplaceDataBytes(nalu.data, data, offset, nalu.size)); | |
253 offset += nalu.size; | |
254 } | |
255 | |
256 // 5. Package the data for VideoToolbox and request decoding. | |
257 base::ScopedCFTypeRef<CMSampleBufferRef> frame; | |
258 CHECK(!CMSampleBufferCreate( | |
259 kCFAllocatorDefault, | |
260 data, // data_buffer | |
261 true, // data_ready | |
262 NULL, // make_data_ready_callback | |
263 NULL, // make_data_ready_refcon | |
264 format_, // format_description | |
265 1, // num_samples | |
266 0, // num_sample_timing_entries | |
267 NULL, // &sample_timing_array | |
268 0, // num_sample_size_entries | |
269 NULL, // &sample_size_array | |
270 frame.InitializeInto())); | |
271 | |
272 VTDecodeFrameFlags decode_flags = | |
Pawel Osciak
2014/08/02 00:28:14
Please comment what this does.
sandersd (OOO until July 31)
2014/08/02 01:43:13
Done.
| |
273 kVTDecodeFrame_EnableAsynchronousDecompression | | |
274 kVTDecodeFrame_EnableTemporalProcessing; | |
275 | |
276 intptr_t bitstream_id = bitstream.id(); | |
277 CHECK(!VTDecompressionSessionDecodeFrame( | |
278 session_, | |
279 frame, // sample_buffer | |
280 decode_flags, // decode_flags | |
281 reinterpret_cast<void*>(bitstream_id), // source_frame_refcon | |
282 NULL)); // &info_flags_out | |
197 } | 283 } |
198 | 284 |
199 // This method may be called on any VideoToolbox thread. | 285 // This method may be called on any VideoToolbox thread. |
200 void VTVideoDecodeAccelerator::Output( | 286 void VTVideoDecodeAccelerator::Output( |
201 int32_t bitstream_id, | 287 int32_t bitstream_id, |
202 OSStatus status, | 288 OSStatus status, |
203 VTDecodeInfoFlags info_flags, | |
204 CVImageBufferRef image_buffer) { | 289 CVImageBufferRef image_buffer) { |
205 // TODO(sandersd): Store the frame in a queue. | 290 CHECK(!status); |
206 CFRelease(image_buffer); | 291 CHECK_EQ(CFGetTypeID(image_buffer), CVPixelBufferGetTypeID()); |
292 CFRetain(image_buffer); | |
Pawel Osciak
2014/08/02 00:28:14
TODO for later, I guess if we get killed before th
sandersd (OOO until July 31)
2014/08/02 01:43:14
This is the standard mode for CoreFramework APIs;
| |
293 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
294 &VTVideoDecodeAccelerator::OutputTask, | |
295 weak_this_factory_.GetWeakPtr(), | |
296 DecodedFrame(bitstream_id, image_buffer))); | |
297 } | |
298 | |
299 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { | |
300 DCHECK(CalledOnValidThread()); | |
301 decoded_frames_.push(frame); | |
302 SendPictures(); | |
303 } | |
304 | |
305 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { | |
Pawel Osciak
2014/08/02 00:28:14
DCHECK(CalledOnValidThread()); ?
sandersd (OOO until July 31)
2014/08/02 01:43:14
Done.
| |
306 texture_size_ = coded_size; | |
307 // TODO(sandersd): Dismiss existing picture buffers. | |
Pawel Osciak
2014/08/02 00:28:14
And todo hold the decode thread while we replace p
sandersd (OOO until July 31)
2014/08/02 01:43:14
This one is going to be a rather tricky dance, but
| |
308 client_->ProvidePictureBuffers( | |
309 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); | |
207 } | 310 } |
208 | 311 |
209 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 312 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
210 const std::vector<media::PictureBuffer>& pictures) { | 313 const std::vector<media::PictureBuffer>& pictures) { |
211 DCHECK(CalledOnValidThread()); | 314 DCHECK(CalledOnValidThread()); |
315 | |
316 for (size_t i = 0; i < pictures.size(); i++) { | |
317 available_picture_ids_.push(pictures[i].id()); | |
Pawel Osciak
2014/08/02 00:28:14
check these are empty first just in case maybe...
sandersd (OOO until July 31)
2014/08/02 01:43:13
Done.
| |
318 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); | |
319 } | |
320 | |
321 // Pictures are not marked as uncleared until this method returns. They will | |
Pawel Osciak
2014/08/02 00:28:14
I don't understand this. How are they marked by th
sandersd (OOO until July 31)
2014/08/02 01:43:13
https://code.google.com/p/chromium/codesearch#chro
| |
322 // become broken if they are used before that happens. | |
323 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
324 &VTVideoDecodeAccelerator::SendPictures, | |
325 weak_this_factory_.GetWeakPtr())); | |
212 } | 326 } |
213 | 327 |
214 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 328 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
215 DCHECK(CalledOnValidThread()); | 329 DCHECK(CalledOnValidThread()); |
330 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); | |
331 picture_bindings_.erase(picture_id); | |
332 available_picture_ids_.push(picture_id); | |
333 SendPictures(); | |
334 } | |
335 | |
336 void VTVideoDecodeAccelerator::SendPictures() { | |
337 DCHECK(CalledOnValidThread()); | |
338 if (available_picture_ids_.empty() || decoded_frames_.empty()) | |
339 return; | |
340 | |
341 CGLContextObj prev_context = CGLGetCurrentContext(); | |
Pawel Osciak
2014/08/02 00:28:14
Looks like we have a gfx::ScopedCGLSetCurrentConte
sandersd (OOO until July 31)
2014/08/02 01:43:14
Done.
| |
342 CHECK(!CGLSetCurrentContext(cgl_context_)); | |
343 glEnable(GL_TEXTURE_RECTANGLE_ARB); | |
344 | |
345 while (!available_picture_ids_.empty() && !decoded_frames_.empty()) { | |
346 int32_t picture_id = available_picture_ids_.front(); | |
347 available_picture_ids_.pop(); | |
348 DecodedFrame frame = decoded_frames_.front(); | |
349 decoded_frames_.pop(); | |
350 IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer); | |
351 | |
352 gfx::ScopedTextureBinder | |
353 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); | |
354 CHECK(!CGLTexImageIOSurface2D( | |
355 cgl_context_, // ctx | |
356 GL_TEXTURE_RECTANGLE_ARB, // target | |
357 GL_RGB, // internal_format | |
358 texture_size_.width(), // width | |
359 texture_size_.height(), // height | |
360 GL_YCBCR_422_APPLE, // format | |
361 GL_UNSIGNED_SHORT_8_8_APPLE, // type | |
362 surface, // io_surface | |
363 0)); // plane | |
364 | |
365 picture_bindings_[picture_id] = frame.image_buffer; | |
366 client_->PictureReady(media::Picture(picture_id, frame.bitstream_id)); | |
367 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | |
368 } | |
369 | |
370 glDisable(GL_TEXTURE_RECTANGLE_ARB); | |
371 CHECK(!CGLSetCurrentContext(prev_context)); | |
216 } | 372 } |
217 | 373 |
218 void VTVideoDecodeAccelerator::Flush() { | 374 void VTVideoDecodeAccelerator::Flush() { |
219 DCHECK(CalledOnValidThread()); | 375 DCHECK(CalledOnValidThread()); |
220 // TODO(sandersd): Trigger flush, sending frames. | 376 // TODO(sandersd): Trigger flush, sending frames. |
221 } | 377 } |
222 | 378 |
223 void VTVideoDecodeAccelerator::Reset() { | 379 void VTVideoDecodeAccelerator::Reset() { |
224 DCHECK(CalledOnValidThread()); | 380 DCHECK(CalledOnValidThread()); |
225 // TODO(sandersd): Trigger flush, discarding frames. | 381 // TODO(sandersd): Trigger flush, discarding frames. |
226 } | 382 } |
227 | 383 |
228 void VTVideoDecodeAccelerator::Destroy() { | 384 void VTVideoDecodeAccelerator::Destroy() { |
229 DCHECK(CalledOnValidThread()); | 385 DCHECK(CalledOnValidThread()); |
230 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. | 386 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. |
231 delete this; | 387 delete this; |
232 } | 388 } |
233 | 389 |
234 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 390 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
235 return false; | 391 return false; |
236 } | 392 } |
237 | 393 |
238 } // namespace content | 394 } // namespace content |
OLD | NEW |