Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: content/common/gpu/media/vt_video_decode_accelerator.cc

Issue 397883002: Implement actually decoding frames in VTVideoDecodeAccelerator. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Nits. Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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(), // &parameter_set_pointers 113 &nalu_data_ptrs.front(), // &parameter_set_pointers
96 &nalu_data_sizes.front(), // &parameter_set_sizes 114 &nalu_data_sizes.front(), // &parameter_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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698