Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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. | |
| 4 | |
| 5 #include "media/filters/gpu_video_decoder.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/message_loop.h" | |
| 9 #include "media/base/demuxer_stream.h" | |
| 10 #include "media/base/filter_host.h" | |
| 11 #include "media/base/video_decoder_config.h" | |
| 12 #include "media/ffmpeg/ffmpeg_common.h" | |
| 13 | |
| 14 namespace media { | |
| 15 | |
| 16 GpuVideoDecoder::Factories::~Factories() {} | |
| 17 | |
| 18 // Size of shared-memory segments we allocate. Since we reuse them we let them | |
| 19 // be on the beefy side. | |
| 20 static const size_t kSharedMemorySegmentBytes = 100 << 10; | |
| 21 | |
| 22 GpuVideoDecoder::SHMBuffer::SHMBuffer(base::SharedMemory* m, size_t s) | |
| 23 : shm(m), size(s) { | |
| 24 } | |
| 25 | |
| 26 GpuVideoDecoder::SHMBuffer::~SHMBuffer() { | |
|
scherkus (not reviewing)
2011/12/09 00:26:20
collapse one-line-able fns to {}
Ami GONE FROM CHROMIUM
2011/12/09 22:55:50
Done.
| |
| 27 } | |
| 28 | |
| 29 GpuVideoDecoder::BufferPair::BufferPair( | |
| 30 SHMBuffer* s, const scoped_refptr<Buffer>& b) : shm_buffer(s), buffer(b) { | |
| 31 } | |
| 32 | |
| 33 GpuVideoDecoder::BufferPair::~BufferPair() { | |
|
scherkus (not reviewing)
2011/12/09 00:26:20
ditto
Ami GONE FROM CHROMIUM
2011/12/09 22:55:50
Done.
| |
| 34 } | |
| 35 | |
| 36 GpuVideoDecoder::GpuVideoDecoder( | |
| 37 MessageLoop* message_loop, | |
| 38 Factories* factories) | |
| 39 : message_loop_(message_loop), | |
| 40 factories_(factories), | |
| 41 flush_in_progress_(false), | |
| 42 demuxer_read_in_progress_(false), | |
| 43 next_picture_buffer_id_(0), | |
| 44 next_bitstream_buffer_id_(0) { | |
| 45 DCHECK(message_loop_ && factories_.get()); | |
| 46 } | |
| 47 | |
| 48 GpuVideoDecoder::~GpuVideoDecoder() { | |
| 49 DCHECK(!vda_); // Stop should have been already called. | |
| 50 for (size_t i = 0; i < available_shm_segments_.size(); ++i) | |
|
scherkus (not reviewing)
2011/12/09 00:26:20
check out base/stl_util.h for stuff like this
Ami GONE FROM CHROMIUM
2011/12/09 22:55:50
Done.
| |
| 51 delete available_shm_segments_[i]; | |
| 52 available_shm_segments_.clear(); | |
| 53 for (std::map<int32, BufferPair>::iterator it = | |
| 54 bitstream_buffers_in_decoder_.begin(); | |
| 55 it != bitstream_buffers_in_decoder_.end(); ++it) { | |
| 56 it->second.shm_buffer->shm->Close(); | |
| 57 } | |
| 58 bitstream_buffers_in_decoder_.clear(); | |
| 59 } | |
| 60 | |
| 61 void GpuVideoDecoder::Stop(const base::Closure& callback) { | |
| 62 if (MessageLoop::current() != message_loop_) { | |
| 63 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 64 &GpuVideoDecoder::Stop, this, callback)); | |
| 65 return; | |
| 66 } | |
| 67 if (!vda_) { | |
| 68 callback.Run(); | |
| 69 return; | |
| 70 } | |
| 71 vda_->Destroy(); | |
| 72 vda_ = NULL; | |
| 73 callback.Run(); | |
| 74 } | |
| 75 | |
| 76 void GpuVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { | |
| 77 if (MessageLoop::current() != message_loop_) { | |
| 78 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 79 &GpuVideoDecoder::Seek, this, time, cb)); | |
| 80 return; | |
| 81 } | |
| 82 pts_stream_.Seek(time); | |
| 83 cb.Run(PIPELINE_OK); | |
| 84 } | |
| 85 | |
| 86 void GpuVideoDecoder::Pause(const base::Closure& callback) { | |
| 87 if (MessageLoop::current() != message_loop_) { | |
| 88 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 89 &GpuVideoDecoder::Pause, this, callback)); | |
| 90 return; | |
| 91 } | |
| 92 callback.Run(); | |
| 93 } | |
| 94 | |
| 95 void GpuVideoDecoder::Flush(const base::Closure& callback) { | |
| 96 if (MessageLoop::current() != message_loop_) { | |
| 97 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 98 &GpuVideoDecoder::Flush, this, callback)); | |
| 99 return; | |
| 100 } | |
| 101 if (!vda_) { | |
| 102 callback.Run(); | |
| 103 return; | |
| 104 } | |
| 105 DCHECK(pending_flush_cb_.is_null()); | |
| 106 pending_flush_cb_ = callback; | |
| 107 pts_stream_.Flush(); | |
| 108 vda_->Reset(); | |
| 109 } | |
| 110 | |
| 111 void GpuVideoDecoder::Initialize(DemuxerStream* demuxer_stream, | |
| 112 const PipelineStatusCB& callback, | |
| 113 const StatisticsCallback& stats_callback) { | |
| 114 if (MessageLoop::current() != message_loop_) { | |
| 115 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 116 &GpuVideoDecoder::Initialize, this, | |
| 117 make_scoped_refptr(demuxer_stream), callback, stats_callback)); | |
| 118 return; | |
| 119 } | |
| 120 | |
| 121 DCHECK(!demuxer_stream_); | |
| 122 if (!demuxer_stream) { | |
| 123 callback.Run(PIPELINE_ERROR_DECODE); | |
| 124 return; | |
| 125 } | |
| 126 | |
| 127 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); | |
| 128 // TODO(scherkus): this check should go in PipelineImpl prior to creating | |
| 129 // decoder objects. | |
| 130 if (!config.IsValidConfig()) { | |
| 131 DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); | |
| 132 callback.Run(PIPELINE_ERROR_DECODE); | |
| 133 return; | |
| 134 } | |
| 135 | |
| 136 vda_ = factories_->CreateVideoDecodeAccelerator(config.profile(), this); | |
| 137 if (!vda_) { | |
| 138 callback.Run(DECODER_ERROR_NOT_SUPPORTED); | |
| 139 return; | |
| 140 } | |
| 141 | |
| 142 demuxer_stream_ = demuxer_stream; | |
| 143 statistics_callback_ = stats_callback; | |
| 144 | |
| 145 demuxer_stream_->EnableBitstreamConverter(); | |
| 146 | |
| 147 pts_stream_.Initialize(GetFrameDuration(config)); | |
| 148 natural_size_ = config.natural_size(); | |
| 149 | |
| 150 callback.Run(PIPELINE_OK); | |
| 151 } | |
| 152 | |
| 153 void GpuVideoDecoder::Read(const ReadCB& callback) { | |
| 154 if (MessageLoop::current() != message_loop_) { | |
| 155 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 156 &GpuVideoDecoder::Read, this, callback)); | |
| 157 return; | |
| 158 } | |
| 159 | |
| 160 if (!vda_) { | |
| 161 callback.Run(VideoFrame::CreateEmptyFrame()); | |
| 162 return; | |
| 163 } | |
| 164 | |
| 165 DCHECK(pending_read_cb_.is_null()); | |
| 166 pending_read_cb_ = callback; | |
| 167 | |
| 168 if (!ready_video_frames_.empty()) { | |
| 169 DeliverFrame(ready_video_frames_.front()); | |
| 170 ready_video_frames_.pop_front(); | |
| 171 return; | |
| 172 } | |
| 173 EnsureDemuxOrDecode(); | |
| 174 } | |
| 175 | |
| 176 void GpuVideoDecoder::RequestBufferDecode(const scoped_refptr<Buffer>& buffer) { | |
| 177 if (MessageLoop::current() != message_loop_) { | |
| 178 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 179 &GpuVideoDecoder::RequestBufferDecode, this, buffer)); | |
| 180 return; | |
| 181 } | |
| 182 demuxer_read_in_progress_ = false; | |
| 183 | |
| 184 if (!vda_) { | |
| 185 DeliverFrame(VideoFrame::CreateEmptyFrame()); | |
| 186 return; | |
| 187 } | |
| 188 | |
| 189 if (buffer->IsEndOfStream()) { | |
| 190 if (!flush_in_progress_) { | |
| 191 flush_in_progress_ = true; | |
| 192 vda_->Flush(); | |
| 193 } | |
| 194 return; | |
| 195 } | |
| 196 | |
| 197 size_t size = buffer->GetDataSize(); | |
| 198 SHMBuffer* shm_buffer = GetSHM(size); | |
| 199 memcpy(shm_buffer->shm->memory(), buffer->GetData(), size); | |
| 200 BitstreamBuffer bitstream_buffer( | |
| 201 next_bitstream_buffer_id_++, shm_buffer->shm->handle(), size); | |
| 202 bool inserted = bitstream_buffers_in_decoder_.insert(std::make_pair( | |
| 203 bitstream_buffer.id(), BufferPair(shm_buffer, buffer))).second; | |
| 204 DCHECK(inserted); | |
| 205 pts_stream_.EnqueuePts(buffer.get()); | |
| 206 | |
| 207 vda_->Decode(bitstream_buffer); | |
| 208 } | |
| 209 | |
| 210 const gfx::Size& GpuVideoDecoder::natural_size() { | |
| 211 return natural_size_; | |
| 212 } | |
| 213 | |
| 214 void GpuVideoDecoder::NotifyInitializeDone() { | |
| 215 NOTREACHED() << "GpuVideoDecodeAcceleratorHost::Initialize is synchronous!"; | |
| 216 } | |
| 217 | |
| 218 void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, | |
| 219 const gfx::Size& size) { | |
| 220 if (MessageLoop::current() != message_loop_) { | |
| 221 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 222 &GpuVideoDecoder::ProvidePictureBuffers, this, count, size)); | |
| 223 return; | |
| 224 } | |
| 225 | |
| 226 std::vector<uint32> texture_ids; | |
| 227 if (!factories_->CreateTextures(count, size, &texture_ids)) { | |
| 228 NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 if (!vda_) | |
| 233 return; | |
| 234 | |
| 235 std::vector<PictureBuffer> picture_buffers; | |
| 236 for (size_t i = 0; i < texture_ids.size(); ++i) { | |
| 237 picture_buffers.push_back(PictureBuffer( | |
| 238 next_picture_buffer_id_++, size, texture_ids[i])); | |
| 239 bool inserted = picture_buffers_in_decoder_.insert(std::make_pair( | |
| 240 picture_buffers.back().id(), picture_buffers.back())).second; | |
| 241 DCHECK(inserted); | |
| 242 } | |
| 243 vda_->AssignPictureBuffers(picture_buffers); | |
| 244 } | |
| 245 | |
| 246 void GpuVideoDecoder::DismissPictureBuffer(int32 id) { | |
| 247 if (MessageLoop::current() != message_loop_) { | |
| 248 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 249 &GpuVideoDecoder::DismissPictureBuffer, this, id)); | |
| 250 return; | |
| 251 } | |
| 252 std::map<int32, PictureBuffer>::iterator it = | |
| 253 picture_buffers_in_decoder_.find(id); | |
| 254 if (it == picture_buffers_in_decoder_.end()) { | |
| 255 NOTREACHED() << "Missing picture buffer: " << id; | |
| 256 return; | |
| 257 } | |
| 258 if (!factories_->DeleteTexture(it->second.texture_id())) { | |
| 259 NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); | |
| 260 return; | |
| 261 } | |
| 262 picture_buffers_in_decoder_.erase(it); | |
| 263 } | |
| 264 | |
| 265 static void ResetAndRunCB(VideoDecoder::ReadCB* cb, | |
| 266 scoped_refptr<VideoFrame> frame) { | |
| 267 DCHECK(!cb->is_null()); | |
| 268 VideoDecoder::ReadCB tmp_cb(*cb); | |
| 269 cb->Reset(); | |
| 270 tmp_cb.Run(frame); | |
| 271 } | |
| 272 | |
| 273 void GpuVideoDecoder::PictureReady(const media::Picture& picture) { | |
| 274 if (MessageLoop::current() != message_loop_) { | |
| 275 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 276 &GpuVideoDecoder::PictureReady, this, picture)); | |
| 277 return; | |
| 278 } | |
| 279 std::map<int32, PictureBuffer>::iterator it = | |
| 280 picture_buffers_in_decoder_.find(picture.picture_buffer_id()); | |
| 281 if (it == picture_buffers_in_decoder_.end()) { | |
| 282 NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id(); | |
| 283 NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); | |
| 284 return; | |
| 285 } | |
| 286 const PictureBuffer& pb = it->second; | |
| 287 | |
| 288 // Update frame's timestamp. | |
| 289 base::TimeDelta timestamp; | |
| 290 base::TimeDelta duration; | |
| 291 std::map<int32, BufferPair>::const_iterator buf_it = | |
| 292 bitstream_buffers_in_decoder_.find(picture.bitstream_buffer_id()); | |
| 293 if (buf_it != bitstream_buffers_in_decoder_.end()) { | |
| 294 // Sufficiently out-of-order decoding could have already called | |
| 295 // NotifyEndOfBitstreamBuffer on this buffer, but that's ok since we only | |
| 296 // need the buffer's time info for best-effort PTS updating. | |
| 297 timestamp = buf_it->second.buffer->GetTimestamp(); | |
| 298 duration = buf_it->second.buffer->GetDuration(); | |
| 299 } | |
| 300 | |
| 301 scoped_refptr<VideoFrame> frame(VideoFrame::WrapNativeTexture( | |
| 302 pb.texture_id(), pb.size().width(), | |
| 303 pb.size().height(), timestamp, duration, | |
| 304 base::Bind(&GpuVideoDecoder::ReusePictureBuffer, this, | |
| 305 picture.picture_buffer_id()))); | |
| 306 pts_stream_.UpdatePtsAndDuration(frame.get()); | |
| 307 frame->SetTimestamp(pts_stream_.current_pts()); | |
| 308 frame->SetDuration(pts_stream_.current_duration()); | |
| 309 | |
| 310 // Deliver the frame. | |
| 311 DeliverFrame(frame); | |
| 312 } | |
| 313 | |
| 314 void GpuVideoDecoder::DeliverFrame(const scoped_refptr<VideoFrame>& frame) { | |
| 315 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 316 &GpuVideoDecoder::DeliverFrameOutOfLine, this, frame)); | |
| 317 } | |
| 318 | |
| 319 void GpuVideoDecoder::DeliverFrameOutOfLine( | |
| 320 const scoped_refptr<VideoFrame>& frame) { | |
| 321 if (pending_read_cb_.is_null()) { | |
| 322 ready_video_frames_.push_back(frame); | |
| 323 return; | |
| 324 } | |
| 325 ResetAndRunCB(&pending_read_cb_, frame); | |
| 326 } | |
| 327 | |
| 328 void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) { | |
| 329 if (MessageLoop::current() != message_loop_) { | |
| 330 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 331 &GpuVideoDecoder::ReusePictureBuffer, this, picture_buffer_id)); | |
| 332 return; | |
| 333 } | |
| 334 if (!vda_) | |
| 335 return; | |
| 336 vda_->ReusePictureBuffer(picture_buffer_id); | |
| 337 } | |
| 338 | |
| 339 GpuVideoDecoder::SHMBuffer* GpuVideoDecoder::GetSHM(size_t min_size) { | |
| 340 DCHECK(MessageLoop::current() == message_loop_); | |
| 341 if (available_shm_segments_.empty() || | |
| 342 available_shm_segments_.back()->size < min_size) { | |
| 343 size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); | |
| 344 base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate); | |
| 345 DCHECK(shm); | |
| 346 return new SHMBuffer(shm, size_to_allocate); | |
| 347 } | |
| 348 SHMBuffer* ret = available_shm_segments_.back(); | |
| 349 available_shm_segments_.pop_back(); | |
| 350 return ret; | |
| 351 } | |
| 352 | |
| 353 void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) { | |
| 354 DCHECK(MessageLoop::current() == message_loop_); | |
| 355 available_shm_segments_.push_back(shm_buffer); | |
| 356 } | |
| 357 | |
| 358 void GpuVideoDecoder::NotifyEndOfStream() { | |
| 359 if (MessageLoop::current() != message_loop_) { | |
| 360 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 361 &GpuVideoDecoder::NotifyEndOfStream, this)); | |
| 362 return; | |
| 363 } | |
| 364 DeliverFrame(VideoFrame::CreateEmptyFrame()); | |
| 365 } | |
| 366 | |
| 367 void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { | |
| 368 if (MessageLoop::current() != message_loop_) { | |
| 369 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 370 &GpuVideoDecoder::NotifyEndOfBitstreamBuffer, this, id)); | |
| 371 return; | |
| 372 } | |
| 373 | |
| 374 std::map<int32, BufferPair>::iterator it = | |
| 375 bitstream_buffers_in_decoder_.find(id); | |
| 376 if (it == bitstream_buffers_in_decoder_.end()) { | |
| 377 NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); | |
| 378 NOTREACHED() << "Missing bitstream buffer: " << id; | |
| 379 return; | |
| 380 } | |
| 381 PutSHM(it->second.shm_buffer); | |
| 382 const scoped_refptr<Buffer>& buffer = it->second.buffer; | |
| 383 if (buffer->GetDataSize()) { | |
| 384 PipelineStatistics statistics; | |
| 385 statistics.video_bytes_decoded = buffer->GetDataSize(); | |
| 386 statistics_callback_.Run(statistics); | |
| 387 } | |
| 388 bitstream_buffers_in_decoder_.erase(it); | |
| 389 | |
| 390 if (!pending_read_cb_.is_null()) { | |
| 391 DCHECK(ready_video_frames_.empty()); | |
| 392 EnsureDemuxOrDecode(); | |
| 393 } | |
| 394 } | |
| 395 | |
| 396 void GpuVideoDecoder::EnsureDemuxOrDecode() { | |
| 397 DCHECK(MessageLoop::current() == message_loop_); | |
| 398 if (demuxer_read_in_progress_ || !bitstream_buffers_in_decoder_.empty()) | |
| 399 return; | |
| 400 demuxer_read_in_progress_ = true; | |
| 401 demuxer_stream_->Read(base::Bind( | |
| 402 &GpuVideoDecoder::RequestBufferDecode, this)); | |
| 403 } | |
| 404 | |
| 405 void GpuVideoDecoder::NotifyFlushDone() { | |
| 406 if (MessageLoop::current() != message_loop_) { | |
| 407 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 408 &GpuVideoDecoder::NotifyFlushDone, this)); | |
| 409 return; | |
| 410 } | |
| 411 DCHECK(flush_in_progress_); | |
| 412 flush_in_progress_ = false; | |
| 413 DeliverFrame(VideoFrame::CreateEmptyFrame()); | |
| 414 } | |
| 415 | |
| 416 void GpuVideoDecoder::NotifyResetDone() { | |
| 417 if (MessageLoop::current() != message_loop_) { | |
| 418 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 419 &GpuVideoDecoder::NotifyResetDone, this)); | |
| 420 return; | |
| 421 } | |
| 422 ResetAndRunCB(&pending_flush_cb_); | |
| 423 } | |
| 424 | |
| 425 void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { | |
| 426 if (MessageLoop::current() != message_loop_) { | |
| 427 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 428 &GpuVideoDecoder::NotifyError, this, error)); | |
| 429 return; | |
| 430 } | |
| 431 vda_ = NULL; | |
| 432 DLOG(ERROR) << "VDA Error: " << error; | |
| 433 if (host()) | |
| 434 host()->SetError(PIPELINE_ERROR_DECODE); | |
| 435 } | |
| 436 | |
| 437 } // namespace media | |
| OLD | NEW |