Chromium Code Reviews| 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 | 12 |
| 13 using content_common_gpu_media::kModuleVt; | 13 using content_common_gpu_media::kModuleVt; |
| 14 using content_common_gpu_media::InitializeStubs; | 14 using content_common_gpu_media::InitializeStubs; |
| 15 using content_common_gpu_media::IsVtInitialized; | 15 using content_common_gpu_media::IsVtInitialized; |
| 16 using content_common_gpu_media::StubPathMap; | 16 using content_common_gpu_media::StubPathMap; |
| 17 | 17 |
| 18 namespace content { | 18 namespace content { |
| 19 | 19 |
| 20 // Size of length headers prepended to NALUs in MPEG-4 framing. (1, 2, or 4.) | 20 // Size of NALU length headers in AVCC/MPEG-4 format (can be 1, 2, or 4). |
| 21 static const int kNALUHeaderLength = 4; | 21 static const int kNALUHeaderLength = 4; |
| 22 | 22 |
| 23 // We only request 5 picture buffers from the client which are used to hold the | |
| 24 // decoded samples. These buffers are then reused when the client tells us that | |
| 25 // it is done with the buffer. | |
| 26 static const int kNumPictureBuffers = 5; | |
| 27 | |
| 23 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | 28 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
| 24 static void OutputThunk( | 29 static void OutputThunk( |
| 25 void* decompression_output_refcon, | 30 void* decompression_output_refcon, |
| 26 void* source_frame_refcon, | 31 void* source_frame_refcon, |
| 27 OSStatus status, | 32 OSStatus status, |
| 28 VTDecodeInfoFlags info_flags, | 33 VTDecodeInfoFlags info_flags, |
| 29 CVImageBufferRef image_buffer, | 34 CVImageBufferRef image_buffer, |
| 30 CMTime presentation_time_stamp, | 35 CMTime presentation_time_stamp, |
| 31 CMTime presentation_duration) { | 36 CMTime presentation_duration) { |
| 37 // Will be guaranteed to be valid once flush before delete is implemented. | |
|
scherkus (not reviewing)
2014/07/16 18:18:51
nit: this reads more like a TODO -- is that the ca
sandersd (OOO until July 31)
2014/07/17 00:22:22
Done.
| |
| 32 VTVideoDecodeAccelerator* vda = | 38 VTVideoDecodeAccelerator* vda = |
| 33 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); | 39 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); |
| 34 int32_t* bitstream_id_ptr = reinterpret_cast<int32_t*>(source_frame_refcon); | 40 intptr_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon); |
| 35 int32_t bitstream_id = *bitstream_id_ptr; | |
| 36 delete bitstream_id_ptr; | |
| 37 CFRetain(image_buffer); | 41 CFRetain(image_buffer); |
|
scherkus (not reviewing)
2014/07/16 18:18:51
does this call need to be balanced with a CFReleas
sandersd (OOO until July 31)
2014/07/17 00:22:22
No, that is handled by the destruction of what is
| |
| 38 vda->Output(bitstream_id, status, info_flags, image_buffer); | 42 vda->Output(bitstream_id, status, info_flags, image_buffer); |
| 39 } | 43 } |
| 40 | 44 |
| 41 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) | 45 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
| 42 : cgl_context_(cgl_context), | 46 : cgl_context_(cgl_context), |
| 43 client_(NULL), | |
| 44 decoder_thread_("VTDecoderThread"), | |
| 45 format_(NULL), | 47 format_(NULL), |
| 46 session_(NULL), | 48 session_(NULL), |
| 49 frames_pending_decode_(0), | |
|
scherkus (not reviewing)
2014/07/16 18:18:51
while you increment/decrement this variable, I don
sandersd (OOO until July 31)
2014/07/17 00:22:22
It will be used to determine when a request to flu
| |
| 50 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
| 51 decoder_thread_("VTDecoderThread"), | |
| 47 weak_this_factory_(this) { | 52 weak_this_factory_(this) { |
| 48 callback_.decompressionOutputCallback = OutputThunk; | 53 callback_.decompressionOutputCallback = OutputThunk; |
| 49 callback_.decompressionOutputRefCon = this; | 54 callback_.decompressionOutputRefCon = this; |
| 50 } | 55 } |
| 51 | 56 |
| 52 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 57 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
| 53 } | 58 } |
| 54 | 59 |
| 55 bool VTVideoDecodeAccelerator::Initialize( | 60 bool VTVideoDecodeAccelerator::Initialize( |
| 56 media::VideoCodecProfile profile, | 61 media::VideoCodecProfile profile, |
| 57 Client* client) { | 62 Client* client) { |
| 58 DCHECK(CalledOnValidThread()); | 63 DCHECK(CalledOnValidThread()); |
| 59 client_ = client; | 64 |
| 65 weak_client_factory_.reset( | |
| 66 new base::WeakPtrFactory<media::VideoDecodeAccelerator::Client>(client)); | |
| 60 | 67 |
| 61 // Only H.264 is supported. | 68 // Only H.264 is supported. |
| 62 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) | 69 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
| 63 return false; | 70 return false; |
| 64 | 71 |
| 65 // TODO(sandersd): Move VideoToolbox library loading to sandbox startup; | 72 // TODO(sandersd): Move VideoToolbox library loading to sandbox startup; |
| 66 // until then, --no-sandbox is required. | 73 // until then, --no-sandbox is required. |
| 67 if (!IsVtInitialized()) { | 74 if (!IsVtInitialized()) { |
| 68 StubPathMap paths; | 75 StubPathMap paths; |
| 69 // CoreVideo is also required, but the loader stops after the first | 76 // CoreVideo is also required, but the loader stops after the first |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 80 if (!decoder_thread_.Start()) | 87 if (!decoder_thread_.Start()) |
| 81 return false; | 88 return false; |
| 82 | 89 |
| 83 // Note that --ignore-gpu-blacklist is still required to get here. | 90 // Note that --ignore-gpu-blacklist is still required to get here. |
| 84 return true; | 91 return true; |
| 85 } | 92 } |
| 86 | 93 |
| 87 // TODO(sandersd): Proper error reporting instead of CHECKs. | 94 // TODO(sandersd): Proper error reporting instead of CHECKs. |
| 88 void VTVideoDecodeAccelerator::ConfigureDecoder( | 95 void VTVideoDecodeAccelerator::ConfigureDecoder( |
| 89 const std::vector<const uint8_t*>& nalu_data_ptrs, | 96 const std::vector<const uint8_t*>& nalu_data_ptrs, |
| 90 const std::vector<size_t>& nalu_data_sizes) { | 97 const std::vector<size_t>& nalu_data_sizes) { |
|
scherkus (not reviewing)
2014/07/16 18:18:51
add DCHECK(decoder_thread_.message_loop_proxy()->B
sandersd (OOO until July 31)
2014/07/17 00:22:22
Done.
| |
| 98 // Construct a new format description from the parameter sets. | |
| 99 // TODO(sandersd): Replace this with custom code to support OS X < 10.9. | |
| 91 format_.reset(); | 100 format_.reset(); |
| 92 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( | 101 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( |
| 93 kCFAllocatorDefault, | 102 kCFAllocatorDefault, |
| 94 nalu_data_ptrs.size(), // parameter_set_count | 103 nalu_data_ptrs.size(), // parameter_set_count |
| 95 &nalu_data_ptrs.front(), // ¶meter_set_pointers | 104 &nalu_data_ptrs.front(), // ¶meter_set_pointers |
| 96 &nalu_data_sizes.front(), // ¶meter_set_sizes | 105 &nalu_data_sizes.front(), // ¶meter_set_sizes |
| 97 kNALUHeaderLength, // nal_unit_header_length | 106 kNALUHeaderLength, // nal_unit_header_length |
| 98 format_.InitializeInto() | 107 format_.InitializeInto())); |
| 99 )); | 108 CMVideoDimensions coded_dimensions = |
| 109 CMVideoFormatDescriptionGetDimensions(format_); | |
| 100 | 110 |
| 101 // TODO(sandersd): Check if the size has changed and handle picture requests. | 111 // 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( | 112 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( |
| 106 CFDictionaryCreateMutable( | 113 CFDictionaryCreateMutable( |
| 107 kCFAllocatorDefault, | 114 kCFAllocatorDefault, |
| 108 1, // capacity | 115 1, // capacity |
| 109 &kCFTypeDictionaryKeyCallBacks, | 116 &kCFTypeDictionaryKeyCallBacks, |
| 110 &kCFTypeDictionaryValueCallBacks)); | 117 &kCFTypeDictionaryValueCallBacks)); |
| 111 | 118 |
| 112 CFDictionarySetValue( | 119 CFDictionarySetValue( |
| 113 decoder_config, | 120 decoder_config, |
| 114 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder | 121 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder |
| 115 CFSTR("EnableHardwareAcceleratedVideoDecoder"), | 122 CFSTR("EnableHardwareAcceleratedVideoDecoder"), |
| 116 kCFBooleanTrue); | 123 kCFBooleanTrue); |
| 117 | 124 |
| 118 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | 125 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
| 119 CFDictionaryCreateMutable( | 126 CFDictionaryCreateMutable( |
| 120 kCFAllocatorDefault, | 127 kCFAllocatorDefault, |
| 121 4, // capacity | 128 4, // capacity |
| 122 &kCFTypeDictionaryKeyCallBacks, | 129 &kCFTypeDictionaryKeyCallBacks, |
| 123 &kCFTypeDictionaryValueCallBacks)); | 130 &kCFTypeDictionaryValueCallBacks)); |
| 124 | 131 |
| 125 // TODO(sandersd): ARGB for video that is not 4:2:0. | 132 // TODO(sandersd): ARGB for video that is not 4:2:0. |
| 126 int32_t pixel_format = '2vuy'; | 133 int32_t pixel_format = '2vuy'; |
|
scherkus (not reviewing)
2014/07/16 18:18:51
this is used in multiple places -- can you make th
sandersd (OOO until July 31)
2014/07/17 00:22:22
Done.
| |
| 127 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | 134 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) |
| 128 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); | 135 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); |
| 129 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_size.width)); | 136 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); |
| 130 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_size.height)); | 137 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); |
| 131 #undef CFINT | 138 #undef CFINT |
| 132 CFDictionarySetValue( | 139 CFDictionarySetValue( |
| 133 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | 140 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
| 134 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | 141 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
| 135 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | 142 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
| 136 CFDictionarySetValue( | 143 CFDictionarySetValue( |
| 137 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | 144 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
| 138 | 145 |
| 139 // TODO(sandersd): Skip if the session is compatible. | 146 // TODO(sandersd): Check if the session is already compatible. |
| 140 // TODO(sandersd): Flush frames when resetting. | 147 // TODO(sandersd): Flush. |
| 141 session_.reset(); | 148 session_.reset(); |
| 142 CHECK(!VTDecompressionSessionCreate( | 149 CHECK(!VTDecompressionSessionCreate( |
| 143 kCFAllocatorDefault, | 150 kCFAllocatorDefault, |
| 144 format_, // video_format_description | 151 format_, // video_format_description |
| 145 decoder_config, // video_decoder_specification | 152 decoder_config, // video_decoder_specification |
| 146 image_config, // destination_image_buffer_attributes | 153 image_config, // destination_image_buffer_attributes |
| 147 &callback_, // output_callback | 154 &callback_, // output_callback |
| 148 session_.InitializeInto() | 155 session_.InitializeInto())); |
| 149 )); | 156 |
| 150 DVLOG(2) << "Created VTDecompressionSession"; | 157 // If the size has changed, request new picture buffers. |
| 158 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); | |
| 159 if (coded_size_ != new_coded_size) { | |
| 160 coded_size_ = new_coded_size; | |
| 161 // TODO(sandersd): Dismiss existing picture buffers. | |
| 162 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 163 &media::VideoDecodeAccelerator::Client::ProvidePictureBuffers, | |
| 164 weak_client_factory_->GetWeakPtr(), | |
| 165 kNumPictureBuffers, | |
| 166 coded_size_, | |
| 167 GL_TEXTURE_RECTANGLE_ARB)); | |
| 168 } | |
| 151 } | 169 } |
| 152 | 170 |
| 153 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 171 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
| 154 DCHECK(CalledOnValidThread()); | 172 DCHECK(CalledOnValidThread()); |
| 155 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | 173 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
| 156 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), | 174 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), |
| 157 bitstream)); | 175 bitstream)); |
| 158 } | 176 } |
| 159 | 177 |
| 160 void VTVideoDecodeAccelerator::DecodeTask( | 178 void VTVideoDecodeAccelerator::DecodeTask( |
| 161 const media::BitstreamBuffer bitstream) { | 179 const media::BitstreamBuffer bitstream) { |
| 162 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 180 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 163 | 181 |
| 164 // Map the bitstream buffer. | 182 // Map the bitstream buffer. |
| 165 base::SharedMemory memory(bitstream.handle(), true); | 183 base::SharedMemory memory(bitstream.handle(), true); |
| 166 size_t size = bitstream.size(); | 184 size_t size = bitstream.size(); |
| 167 CHECK(memory.Map(size)); | 185 CHECK(memory.Map(size)); |
| 168 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); | 186 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); |
| 169 | 187 |
| 170 // Locate relevant NALUs in the buffer. | 188 // NALUs are stored with Annex B format in the bitstream buffer (3-byte start |
| 189 // codes), but VideoToolbox expects AVCC/MPEG-4 format (length headers), so we | |
| 190 // must to rewrite the data. | |
| 191 // | |
| 192 // 1. Locate relevant NALUs and compute the size of the translated data. | |
| 193 // Also record any parameter sets for VideoToolbox initialization. | |
| 171 size_t data_size = 0; | 194 size_t data_size = 0; |
| 172 std::vector<media::H264NALU> nalus; | 195 std::vector<media::H264NALU> nalus; |
| 173 std::vector<const uint8_t*> config_nalu_data_ptrs; | 196 std::vector<const uint8_t*> config_nalu_data_ptrs; |
| 174 std::vector<size_t> config_nalu_data_sizes; | 197 std::vector<size_t> config_nalu_data_sizes; |
| 175 parser_.SetStream(buf, size); | 198 parser_.SetStream(buf, size); |
| 176 media::H264NALU nalu; | 199 media::H264NALU nalu; |
| 177 while (true) { | 200 while (true) { |
| 178 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); | 201 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); |
| 179 if (result == media::H264Parser::kEOStream) | 202 if (result == media::H264Parser::kEOStream) |
| 180 break; | 203 break; |
| 181 CHECK_EQ(result, media::H264Parser::kOk); | 204 CHECK_EQ(result, media::H264Parser::kOk); |
| 205 // TODO(sandersd): Check that these are only at the start. | |
| 182 if (nalu.nal_unit_type == media::H264NALU::kSPS || | 206 if (nalu.nal_unit_type == media::H264NALU::kSPS || |
| 183 nalu.nal_unit_type == media::H264NALU::kPPS || | 207 nalu.nal_unit_type == media::H264NALU::kPPS || |
| 184 nalu.nal_unit_type == media::H264NALU::kSPSExt) { | 208 nalu.nal_unit_type == media::H264NALU::kSPSExt) { |
| 209 DVLOG(2) << "Parameter set " << nalu.nal_unit_type; | |
| 185 config_nalu_data_ptrs.push_back(nalu.data); | 210 config_nalu_data_ptrs.push_back(nalu.data); |
| 186 config_nalu_data_sizes.push_back(nalu.size); | 211 config_nalu_data_sizes.push_back(nalu.size); |
| 212 } else { | |
| 213 nalus.push_back(nalu); | |
| 214 data_size += kNALUHeaderLength + nalu.size; | |
| 187 } | 215 } |
| 188 nalus.push_back(nalu); | |
| 189 // Each NALU will have a 4-byte length header prepended. | |
| 190 data_size += kNALUHeaderLength + nalu.size; | |
| 191 } | 216 } |
| 192 | 217 |
| 193 if (!config_nalu_data_ptrs.empty()) | 218 // 2. Initialize VideoToolbox. |
| 219 // TODO(sandersd): Reinitialize when there are new parameter sets. | |
| 220 if (!session_) | |
| 194 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); | 221 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); |
| 195 | 222 |
| 196 // TODO(sandersd): Rewrite slice NALU headers and send for decoding. | 223 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. |
| 224 base::ScopedCFTypeRef<CMBlockBufferRef> data; | |
| 225 CHECK(!CMBlockBufferCreateWithMemoryBlock( | |
| 226 kCFAllocatorDefault, | |
| 227 NULL, // &memory_block | |
| 228 data_size, // block_length | |
| 229 kCFAllocatorDefault, // block_allocator | |
| 230 NULL, // &custom_block_source | |
| 231 0, // offset_to_data | |
| 232 data_size, // data_length | |
| 233 0, // flags | |
| 234 data.InitializeInto())); | |
| 235 | |
| 236 // 4. Copy NALU data, inserting length headers. | |
| 237 size_t offset = 0; | |
| 238 for (size_t i = 0; i < nalus.size(); i++) { | |
| 239 media::H264NALU& nalu = nalus[i]; | |
| 240 uint8_t header[4] = {0xff & nalu.size >> 24, | |
| 241 0xff & nalu.size >> 16, | |
| 242 0xff & nalu.size >> 8, | |
| 243 0xff & nalu.size}; | |
| 244 CHECK(!CMBlockBufferReplaceDataBytes(header, data, offset, 4)); | |
| 245 offset += 4; | |
| 246 CHECK(!CMBlockBufferReplaceDataBytes(nalu.data, data, offset, nalu.size)); | |
| 247 offset += nalu.size; | |
| 248 } | |
| 249 | |
| 250 // 5. Package the data for VideoToolbox and request decoding. | |
| 251 base::ScopedCFTypeRef<CMSampleBufferRef> frame; | |
| 252 CHECK(!CMSampleBufferCreate( | |
| 253 kCFAllocatorDefault, | |
| 254 data, // data_buffer | |
| 255 true, // data_ready | |
| 256 NULL, // make_data_ready_callback | |
| 257 NULL, // make_data_ready_refcon | |
| 258 format_, // format_description | |
| 259 1, // num_samples | |
| 260 0, // num_sample_timing_entries | |
| 261 NULL, // &sample_timing_array | |
| 262 0, // num_sample_size_entries | |
| 263 NULL, // &sample_size_array | |
| 264 frame.InitializeInto())); | |
| 265 | |
| 266 // kVTDecodeFrame_EnableAsynchronousDecompression | | |
|
scherkus (not reviewing)
2014/07/16 18:18:51
does it make send to define these somewhere?
perh
sandersd (OOO until July 31)
2014/07/17 00:22:22
Done.
| |
| 267 // kVTDecodeFrame_EnableTemporalProcessing | |
| 268 VTDecodeFrameFlags decode_flags = (1<<0) | (1<<3); | |
|
scherkus (not reviewing)
2014/07/16 18:18:51
spaces around binary operators
(1 << 0) | (1 << 3
sandersd (OOO until July 31)
2014/07/17 00:22:22
Done.
| |
| 269 | |
| 270 intptr_t bitstream_id = bitstream.id(); | |
| 271 CHECK(!VTDecompressionSessionDecodeFrame( | |
| 272 session_, | |
| 273 frame, // sample_buffer | |
| 274 decode_flags, // decode_flags | |
| 275 reinterpret_cast<void*>(bitstream_id), // source_frame_refcon | |
| 276 NULL)); // &info_flags_out | |
| 277 | |
| 278 base::AutoLock lock(lock_); | |
| 279 frames_pending_decode_++; | |
| 197 } | 280 } |
| 198 | 281 |
| 199 // This method may be called on any VideoToolbox thread. | 282 // This method may be called on any VideoToolbox thread. |
| 200 void VTVideoDecodeAccelerator::Output( | 283 void VTVideoDecodeAccelerator::Output( |
| 201 int32_t bitstream_id, | 284 int32_t bitstream_id, |
| 202 OSStatus status, | 285 OSStatus status, |
| 203 VTDecodeInfoFlags info_flags, | 286 VTDecodeInfoFlags info_flags, |
| 204 CVImageBufferRef image_buffer) { | 287 CVImageBufferRef image_buffer) { |
| 205 // TODO(sandersd): Store the frame in a queue. | 288 CHECK(!status); |
| 206 CFRelease(image_buffer); | 289 CHECK(CFGetTypeID(image_buffer) == CVPixelBufferGetTypeID()); |
| 290 CHECK(CVPixelBufferGetPixelFormatType(image_buffer) == '2vuy'); | |
| 291 { | |
| 292 base::AutoLock lock(lock_); | |
| 293 decoded_frames_.push(DecodedFrame(bitstream_id, image_buffer)); | |
|
scherkus (not reviewing)
2014/07/16 18:18:51
do we need locking around decoded_frames_?
altern
sandersd (OOO until July 31)
2014/07/17 00:22:22
I've implemented a task-posting version of this. I
| |
| 294 frames_pending_decode_--; | |
| 295 } | |
| 296 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 297 &VTVideoDecodeAccelerator::SendPictures, | |
| 298 weak_this_factory_.GetWeakPtr())); | |
| 207 } | 299 } |
| 208 | 300 |
| 209 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 301 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
| 210 const std::vector<media::PictureBuffer>& pictures) { | 302 const std::vector<media::PictureBuffer>& pictures) { |
| 211 DCHECK(CalledOnValidThread()); | 303 DCHECK(CalledOnValidThread()); |
| 304 | |
| 305 for (size_t i = 0; i < pictures.size(); i++) { | |
| 306 picture_ids_.push(pictures[i].id()); | |
| 307 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); | |
| 308 } | |
| 309 | |
| 310 // Pictures are not marked as uncleared until this method returns. They will | |
| 311 // become broken if they are used before that happens. | |
| 312 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 313 &VTVideoDecodeAccelerator::SendPictures, | |
| 314 weak_this_factory_.GetWeakPtr())); | |
| 212 } | 315 } |
| 213 | 316 |
| 214 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 317 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
| 215 DCHECK(CalledOnValidThread()); | 318 DCHECK(CalledOnValidThread()); |
| 319 | |
| 320 retained_images_.erase(picture_id); | |
| 321 picture_ids_.push(picture_id); | |
| 322 | |
| 323 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 324 &VTVideoDecodeAccelerator::SendPictures, | |
| 325 weak_this_factory_.GetWeakPtr())); | |
| 326 } | |
| 327 | |
| 328 void VTVideoDecodeAccelerator::SendPictures() { | |
| 329 DCHECK(CalledOnValidThread()); | |
| 330 base::AutoLock lock(lock_); | |
| 331 if (!picture_ids_.empty() && !decoded_frames_.empty()) { | |
| 332 CGLContextObj prev_context = CGLGetCurrentContext(); | |
| 333 CHECK(!CGLSetCurrentContext(cgl_context_)); | |
| 334 glEnable(GL_TEXTURE_RECTANGLE_ARB); | |
| 335 | |
| 336 while (!picture_ids_.empty() && !decoded_frames_.empty()) { | |
| 337 DecodedFrame frame = decoded_frames_.front(); decoded_frames_.pop(); | |
|
scherkus (not reviewing)
2014/07/16 18:18:51
put the pop() calls on separate lines
sandersd (OOO until July 31)
2014/07/17 00:22:22
Done.
| |
| 338 IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer); | |
| 339 int32_t picture_id = picture_ids_.front(); picture_ids_.pop(); | |
|
scherkus (not reviewing)
2014/07/16 18:18:51
ditto
sandersd (OOO until July 31)
2014/07/17 00:22:22
Done.
| |
| 340 int32_t texture_id = texture_ids_[picture_id]; | |
| 341 | |
| 342 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id); | |
| 343 CHECK(!CGLTexImageIOSurface2D( | |
| 344 cgl_context_, // ctx | |
| 345 GL_TEXTURE_RECTANGLE_ARB, // target | |
| 346 GL_RGB, // internal_format | |
| 347 coded_size_.width(), // width | |
| 348 coded_size_.height(), // height | |
| 349 GL_YCBCR_422_APPLE, // format | |
| 350 GL_UNSIGNED_SHORT_8_8_APPLE, // type | |
| 351 surface, // io_surface | |
| 352 0)); // plane | |
| 353 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | |
| 354 | |
| 355 // The image buffer must be retained as it now backs the texture. | |
| 356 retained_images_[picture_id].reset(frame.image_buffer); | |
| 357 | |
| 358 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 359 &media::VideoDecodeAccelerator::Client::PictureReady, | |
| 360 weak_client_factory_->GetWeakPtr(), | |
| 361 media::Picture(picture_id, frame.bitstream_id))); | |
| 362 | |
| 363 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 364 &media::VideoDecodeAccelerator::Client::NotifyEndOfBitstreamBuffer, | |
| 365 weak_client_factory_->GetWeakPtr(), | |
| 366 frame.bitstream_id)); | |
| 367 } | |
| 368 | |
| 369 glDisable(GL_TEXTURE_RECTANGLE_ARB); | |
| 370 CHECK(!CGLSetCurrentContext(prev_context)); | |
| 371 } | |
| 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. |
| 387 // TODO(sandersd): Make sure decoded_frames_ is empty. | |
| 231 delete this; | 388 delete this; |
| 232 } | 389 } |
| 233 | 390 |
| 234 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 391 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
| 235 return false; | 392 return false; |
| 236 } | 393 } |
| 237 | 394 |
| 238 } // namespace content | 395 } // namespace content |
| OLD | NEW |