| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "content/common/gpu/media/mac_video_decode_accelerator.h" | 5 #include "content/common/gpu/media/mac_video_decode_accelerator.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/file_path.h" | 8 #include "base/file_path.h" |
| 9 #import "base/mac/foundation_util.h" | 9 #import "base/mac/foundation_util.h" |
| 10 #import "base/memory/ref_counted_memory.h" | 10 #import "base/memory/ref_counted_memory.h" |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 0) != kCGLNoError) { | 76 0) != kCGLNoError) { |
| 77 DLOG(ERROR) << "Failed to bind image to texture."; | 77 DLOG(ERROR) << "Failed to bind image to texture."; |
| 78 return false; | 78 return false; |
| 79 } | 79 } |
| 80 return glGetError() == GL_NO_ERROR; | 80 return glGetError() == GL_NO_ERROR; |
| 81 } | 81 } |
| 82 | 82 |
| 83 MacVideoDecodeAccelerator::MacVideoDecodeAccelerator( | 83 MacVideoDecodeAccelerator::MacVideoDecodeAccelerator( |
| 84 media::VideoDecodeAccelerator::Client* client) | 84 media::VideoDecodeAccelerator::Client* client) |
| 85 : client_(client), | 85 : client_(client), |
| 86 cgl_context_(NULL), | 86 cgl_context_(NULL) { |
| 87 nalu_len_field_size_(0), | |
| 88 did_request_pictures_(false) { | |
| 89 } | 87 } |
| 90 | 88 |
| 91 void MacVideoDecodeAccelerator::SetGLContext(void* gl_context) { | 89 void MacVideoDecodeAccelerator::SetGLContext(void* gl_context) { |
| 92 DCHECK(CalledOnValidThread()); | 90 DCHECK(CalledOnValidThread()); |
| 93 cgl_context_ = static_cast<CGLContextObj>(gl_context); | 91 cgl_context_ = static_cast<CGLContextObj>(gl_context); |
| 94 } | 92 } |
| 95 | 93 |
| 96 bool MacVideoDecodeAccelerator::SetConfigInfo( | |
| 97 uint32_t frame_width, | |
| 98 uint32_t frame_height, | |
| 99 const std::vector<uint8_t>& avc_data) { | |
| 100 DCHECK(CalledOnValidThread()); | |
| 101 frame_size_ = gfx::Size(frame_width, frame_height); | |
| 102 nalu_len_field_size_ = (avc_data[4] & 0x03) + 1; | |
| 103 | |
| 104 DCHECK(!vda_support_.get()); | |
| 105 vda_support_ = new gfx::VideoDecodeAccelerationSupport(); | |
| 106 return vda_support_->Create(frame_size_.width(), frame_size_.height(), | |
| 107 kCVPixelFormatType_422YpCbCr8, &avc_data.front(), avc_data.size()) == | |
| 108 gfx::VideoDecodeAccelerationSupport::SUCCESS; | |
| 109 } | |
| 110 | |
| 111 bool MacVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile) { | 94 bool MacVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile) { |
| 112 DCHECK(CalledOnValidThread()); | 95 DCHECK(CalledOnValidThread()); |
| 113 | 96 |
| 114 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); | 97 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); |
| 115 if (!io_surface_support) | 98 if (!io_surface_support) |
| 116 return false; | 99 return false; |
| 117 | 100 |
| 118 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( | 101 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| 119 &MacVideoDecodeAccelerator::NotifyInitializeDone, this)); | 102 &MacVideoDecodeAccelerator::NotifyInitializeDone, this)); |
| 120 return true; | 103 return true; |
| 121 } | 104 } |
| 122 | 105 |
| 123 void MacVideoDecodeAccelerator::Decode( | 106 void MacVideoDecodeAccelerator::Decode( |
| 124 const media::BitstreamBuffer& bitstream_buffer) { | 107 const media::BitstreamBuffer& bitstream_buffer) { |
| 125 DCHECK(CalledOnValidThread()); | 108 DCHECK(CalledOnValidThread()); |
| 126 base::SharedMemory memory(bitstream_buffer.handle(), true); | 109 base::SharedMemory memory(bitstream_buffer.handle(), true); |
| 127 if (!memory.Map(bitstream_buffer.size())) { | 110 if (!memory.Map(bitstream_buffer.size())) { |
| 128 LOG(ERROR) << "Failed to SharedMemory::Map()."; | 111 LOG(ERROR) << "Failed to SharedMemory::Map()."; |
| 129 if (client_) | 112 if (client_) |
| 130 client_->NotifyError(UNREADABLE_INPUT); | 113 client_->NotifyError(UNREADABLE_INPUT); |
| 131 return; | 114 return; |
| 132 } | 115 } |
| 133 | 116 |
| 134 size_t buffer_size = bitstream_buffer.size(); | 117 h264_parser_.SetStream(static_cast<const uint8_t*>(memory.memory()), |
| 135 if (buffer_size < nalu_len_field_size_ + 1) { | 118 bitstream_buffer.size()); |
| 136 LOG(ERROR) << "Bitstream contains invalid data."; | 119 while (true) { |
| 137 if (client_) | 120 content::H264NALU nalu; |
| 138 client_->NotifyError(INVALID_ARGUMENT); | 121 content::H264Parser::Result result = h264_parser_.AdvanceToNextNALU(&nalu); |
| 139 return; | 122 if (result == content::H264Parser::kEOStream) { |
| 140 } | 123 if (client_) |
| 141 | 124 client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id()); |
| 142 // The decoder can only handle slice types 1-5. | 125 return; |
| 143 const uint8_t* buffer = static_cast<const uint8_t*>(memory.memory()); | 126 } |
| 144 uint8_t nalu_type = buffer[nalu_len_field_size_] & 0x1f; | 127 if (result != content::H264Parser::kOk) { |
| 145 if (nalu_type < 1 || nalu_type > 5) { | 128 if (client_) |
| 146 if (client_) | 129 client_->NotifyError(UNREADABLE_INPUT); |
| 147 client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id()); | 130 return; |
| 148 return; | 131 } |
| 149 } | 132 if (!did_build_config_record_) { |
| 150 | 133 bool did_consume_nalu = false; |
| 151 // Keep a ref counted copy of the buffer. | 134 std::vector<uint8_t> config_record; |
| 152 std::vector<uint8_t> vbuffer(buffer, buffer + buffer_size); | 135 if (!config_record_builder_.ProcessNALU( |
| 153 scoped_refptr<base::RefCountedBytes> bytes( | 136 &h264_parser_, nalu, &config_record)) { |
| 154 base::RefCountedBytes::TakeVector(&vbuffer)); | 137 if (client_) |
| 155 | 138 client_->NotifyError(UNREADABLE_INPUT); |
| 156 // Store the buffer size at the beginning of the buffer as the decoder | 139 return; |
| 157 // expects. | 140 } |
| 158 size_t frame_buffer_size = buffer_size - nalu_len_field_size_; | 141 if (!config_record.empty()) { |
| 159 DCHECK(nalu_len_field_size_ <= 4); | 142 did_build_config_record_ = true; |
| 160 uint64_t max_frame_buffer_size = (1llu << (nalu_len_field_size_ * 8)) - 1; | 143 CreateDecoder(config_record); |
| 161 if (frame_buffer_size > max_frame_buffer_size) { | 144 } |
| 162 LOG(ERROR) << "Bitstream is too large."; | 145 if (did_consume_nalu) |
| 163 if (client_) | 146 continue; |
| 164 client_->NotifyError(INVALID_ARGUMENT); | 147 } |
| 165 return; | 148 // If the decoder has been created and this is a slice type then pass it |
| 166 } | 149 // to the decoder. |
| 167 for (size_t i = 0; i < nalu_len_field_size_; ++i) { | 150 if (vda_support_.get() && nalu.nal_unit_type >= 1 && |
| 168 size_t shift = nalu_len_field_size_ * 8 - (i + 1) * 8; | 151 nalu.nal_unit_type <= 5) { |
| 169 bytes->data()[i] = (frame_buffer_size >> shift) & 0xff; | 152 DecodeNALU(nalu, bitstream_buffer.id()); |
| 170 } | 153 } |
| 171 | |
| 172 if (vda_support_) { | |
| 173 vda_support_->Decode(bytes->front(), bytes->size(), | |
| 174 base::Bind(&MacVideoDecodeAccelerator::OnFrameReady, | |
| 175 this, bitstream_buffer.id(), bytes)); | |
| 176 } | |
| 177 | |
| 178 if (!did_request_pictures_) { | |
| 179 did_request_pictures_ = true; | |
| 180 if (client_) | |
| 181 client_->ProvidePictureBuffers(kNumPictureBuffers, frame_size_); | |
| 182 } | 154 } |
| 183 } | 155 } |
| 184 | 156 |
| 185 void MacVideoDecodeAccelerator::AssignPictureBuffers( | 157 void MacVideoDecodeAccelerator::AssignPictureBuffers( |
| 186 const std::vector<media::PictureBuffer>& buffers) { | 158 const std::vector<media::PictureBuffer>& buffers) { |
| 187 DCHECK(CalledOnValidThread()); | 159 DCHECK(CalledOnValidThread()); |
| 188 available_pictures_.insert( | 160 available_pictures_.insert( |
| 189 available_pictures_.end(), buffers.begin(), buffers.end()); | 161 available_pictures_.end(), buffers.begin(), buffers.end()); |
| 190 SendImages(); | 162 SendImages(); |
| 191 } | 163 } |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 if (client_) | 258 if (client_) |
| 287 client_->NotifyFlushDone(); | 259 client_->NotifyFlushDone(); |
| 288 } | 260 } |
| 289 | 261 |
| 290 void MacVideoDecodeAccelerator::NotifyResetDone() { | 262 void MacVideoDecodeAccelerator::NotifyResetDone() { |
| 291 decoded_images_.clear(); | 263 decoded_images_.clear(); |
| 292 if (client_) | 264 if (client_) |
| 293 client_->NotifyResetDone(); | 265 client_->NotifyResetDone(); |
| 294 } | 266 } |
| 295 | 267 |
| 268 void MacVideoDecodeAccelerator::CreateDecoder( |
| 269 const std::vector<uint8_t>& extra_data) { |
| 270 vda_support_ = new gfx::VideoDecodeAccelerationSupport(); |
| 271 gfx::VideoDecodeAccelerationSupport::Status status = vda_support_->Create( |
| 272 config_record_builder_.coded_width(), |
| 273 config_record_builder_.coded_height(), |
| 274 kCVPixelFormatType_422YpCbCr8, |
| 275 &extra_data[0], extra_data.size()); |
| 276 if (status != gfx::VideoDecodeAccelerationSupport::SUCCESS) { |
| 277 if (client_) |
| 278 client_->NotifyError(PLATFORM_FAILURE); |
| 279 return; |
| 280 } |
| 281 |
| 282 if (client_) { |
| 283 client_->ProvidePictureBuffers( |
| 284 kNumPictureBuffers, |
| 285 gfx::Size(config_record_builder_.coded_width(), |
| 286 config_record_builder_.coded_height())); |
| 287 } |
| 288 } |
| 289 |
| 290 void MacVideoDecodeAccelerator::DecodeNALU(const content::H264NALU& nalu, |
| 291 int32 bitstream_buffer_id) { |
| 292 // Assume the NALU length field size is 4 bytes. |
| 293 const int kNALULengthFieldSize = 4; |
| 294 std::vector<uint8_t> data(kNALULengthFieldSize + nalu.size); |
| 295 |
| 296 // Store the buffer size at the beginning of the buffer as the decoder |
| 297 // expects. |
| 298 for (size_t i = 0; i < kNALULengthFieldSize; ++i) { |
| 299 size_t shift = kNALULengthFieldSize * 8 - (i + 1) * 8; |
| 300 data[i] = (nalu.size >> shift) & 0xff; |
| 301 } |
| 302 |
| 303 // Copy the NALU data. |
| 304 memcpy(&data[kNALULengthFieldSize], nalu.data, nalu.size); |
| 305 |
| 306 // Keep a ref counted copy of the buffer. |
| 307 scoped_refptr<base::RefCountedBytes> bytes( |
| 308 base::RefCountedBytes::TakeVector(&data)); |
| 309 vda_support_->Decode(bytes->front(), bytes->size(), |
| 310 base::Bind(&MacVideoDecodeAccelerator::OnFrameReady, |
| 311 this, bitstream_buffer_id, bytes)); |
| 312 } |
| 313 |
| 296 MacVideoDecodeAccelerator::UsedPictureInfo::UsedPictureInfo( | 314 MacVideoDecodeAccelerator::UsedPictureInfo::UsedPictureInfo( |
| 297 const media::PictureBuffer& pic, | 315 const media::PictureBuffer& pic, |
| 298 const base::mac::ScopedCFTypeRef<CVImageBufferRef>& image) | 316 const base::mac::ScopedCFTypeRef<CVImageBufferRef>& image) |
| 299 : picture_buffer(pic), | 317 : picture_buffer(pic), |
| 300 image(image, base::mac::RETAIN) { | 318 image(image, base::mac::RETAIN) { |
| 301 } | 319 } |
| 302 | 320 |
| 303 MacVideoDecodeAccelerator::UsedPictureInfo::~UsedPictureInfo() { | 321 MacVideoDecodeAccelerator::UsedPictureInfo::~UsedPictureInfo() { |
| 304 } | 322 } |
| 305 | 323 |
| 306 MacVideoDecodeAccelerator::DecodedImageInfo::DecodedImageInfo() { | 324 MacVideoDecodeAccelerator::DecodedImageInfo::DecodedImageInfo() { |
| 307 } | 325 } |
| 308 | 326 |
| 309 MacVideoDecodeAccelerator::DecodedImageInfo::~DecodedImageInfo() { | 327 MacVideoDecodeAccelerator::DecodedImageInfo::~DecodedImageInfo() { |
| 310 } | 328 } |
| OLD | NEW |