Index: media/filters/gpu_video_decoder.cc |
=================================================================== |
--- media/filters/gpu_video_decoder.cc (revision 277175) |
+++ media/filters/gpu_video_decoder.cc (working copy) |
@@ -43,14 +43,12 @@ |
GpuVideoDecoder::SHMBuffer::~SHMBuffer() {} |
-GpuVideoDecoder::PendingDecoderBuffer::PendingDecoderBuffer( |
- SHMBuffer* s, |
- const scoped_refptr<DecoderBuffer>& b, |
- const DecodeCB& done_cb) |
- : shm_buffer(s), buffer(b), done_cb(done_cb) { |
+GpuVideoDecoder::BufferPair::BufferPair( |
+ SHMBuffer* s, const scoped_refptr<DecoderBuffer>& b) |
+ : shm_buffer(s), buffer(b) { |
} |
-GpuVideoDecoder::PendingDecoderBuffer::~PendingDecoderBuffer() {} |
+GpuVideoDecoder::BufferPair::~BufferPair() {} |
GpuVideoDecoder::BufferData::BufferData( |
int32 bbid, base::TimeDelta ts, const gfx::Rect& vr, const gfx::Size& ns) |
@@ -84,14 +82,24 @@ |
FROM_HERE, |
base::Bind( |
&GpuVideoDecoder::Reset, weak_factory_.GetWeakPtr(), closure)); |
+ // NOTE: if we're deferring Reset() until a Flush() completes, return |
+ // queued pictures to the VDA so they can be used to finish that Flush(). |
+ if (pending_decode_cb_.is_null()) |
+ ready_video_frames_.clear(); |
return; |
} |
+ // Throw away any already-decoded, not-yet-delivered frames. |
+ ready_video_frames_.clear(); |
+ |
if (!vda_) { |
base::MessageLoop::current()->PostTask(FROM_HERE, closure); |
return; |
} |
+ if (!pending_decode_cb_.is_null()) |
+ EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEOSFrame()); |
+ |
DCHECK(pending_reset_cb_.is_null()); |
pending_reset_cb_ = BindToCurrentLoop(closure); |
@@ -102,7 +110,8 @@ |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
if (vda_) |
DestroyVDA(); |
- DCHECK(bitstream_buffers_in_decoder_.empty()); |
+ if (!pending_decode_cb_.is_null()) |
+ EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEOSFrame()); |
if (!pending_reset_cb_.is_null()) |
base::ResetAndReturn(&pending_reset_cb_).Run(); |
} |
@@ -149,8 +158,7 @@ |
void GpuVideoDecoder::Initialize(const VideoDecoderConfig& config, |
bool live_mode, |
- const PipelineStatusCB& orig_status_cb, |
- const OutputCB& output_cb) { |
+ const PipelineStatusCB& orig_status_cb) { |
DVLOG(3) << "Initialize()"; |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
DCHECK(config.IsValidConfig()); |
@@ -179,7 +187,6 @@ |
config_ = config; |
needs_bitstream_conversion_ = (config.codec() == kCodecH264); |
- output_cb_ = BindToCurrentLoop(output_cb); |
if (previously_initialized) { |
// Reinitialization with a different config (but same codec and profile). |
@@ -230,39 +237,52 @@ |
const DecodeCB& decode_cb) { |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
DCHECK(pending_reset_cb_.is_null()); |
+ DCHECK(pending_decode_cb_.is_null()); |
- DecodeCB bound_decode_cb = BindToCurrentLoop(decode_cb); |
+ pending_decode_cb_ = BindToCurrentLoop(decode_cb); |
if (state_ == kError || !vda_) { |
- bound_decode_cb.Run(kDecodeError); |
+ base::ResetAndReturn(&pending_decode_cb_).Run(kDecodeError, NULL); |
return; |
} |
switch (state_) { |
case kDecoderDrained: |
+ if (!ready_video_frames_.empty()) { |
+ EnqueueFrameAndTriggerFrameDelivery(NULL); |
+ return; |
+ } |
state_ = kNormal; |
// Fall-through. |
case kNormal: |
break; |
case kDrainingDecoder: |
+ DCHECK(buffer->end_of_stream()); |
+ // Do nothing. Will be satisfied either by a PictureReady or |
+ // NotifyFlushDone below. |
+ return; |
case kError: |
NOTREACHED(); |
return; |
} |
- DCHECK_EQ(state_, kNormal); |
- |
if (buffer->end_of_stream()) { |
- state_ = kDrainingDecoder; |
- eos_decode_cb_ = bound_decode_cb; |
- vda_->Flush(); |
+ if (state_ == kNormal) { |
+ state_ = kDrainingDecoder; |
+ vda_->Flush(); |
+ // If we have ready frames, go ahead and process them to ensure that the |
+ // Flush operation does not block in the VDA due to lack of picture |
+ // buffers. |
+ if (!ready_video_frames_.empty()) |
+ EnqueueFrameAndTriggerFrameDelivery(NULL); |
+ } |
return; |
} |
size_t size = buffer->data_size(); |
SHMBuffer* shm_buffer = GetSHM(size); |
if (!shm_buffer) { |
- bound_decode_cb.Run(kDecodeError); |
+ base::ResetAndReturn(&pending_decode_cb_).Run(kDecodeError, NULL); |
return; |
} |
@@ -271,17 +291,26 @@ |
next_bitstream_buffer_id_, shm_buffer->shm->handle(), size); |
// Mask against 30 bits, to avoid (undefined) wraparound on signed integer. |
next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF; |
- DCHECK(!ContainsKey(bitstream_buffers_in_decoder_, bitstream_buffer.id())); |
- bitstream_buffers_in_decoder_.insert( |
- std::make_pair(bitstream_buffer.id(), |
- PendingDecoderBuffer(shm_buffer, buffer, decode_cb))); |
- DCHECK_LE(static_cast<int>(bitstream_buffers_in_decoder_.size()), |
- kMaxInFlightDecodes); |
+ bool inserted = bitstream_buffers_in_decoder_.insert(std::make_pair( |
+ bitstream_buffer.id(), BufferPair(shm_buffer, buffer))).second; |
+ DCHECK(inserted); |
RecordBufferData(bitstream_buffer, *buffer.get()); |
vda_->Decode(bitstream_buffer); |
+ |
+ if (!ready_video_frames_.empty()) { |
+ EnqueueFrameAndTriggerFrameDelivery(NULL); |
+ return; |
+ } |
+ |
+ if (CanMoreDecodeWorkBeDone()) |
+ base::ResetAndReturn(&pending_decode_cb_).Run(kNotEnoughData, NULL); |
} |
+bool GpuVideoDecoder::CanMoreDecodeWorkBeDone() { |
+ return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes; |
+} |
+ |
void GpuVideoDecoder::RecordBufferData(const BitstreamBuffer& bitstream_buffer, |
const DecoderBuffer& buffer) { |
input_buffer_data_.push_front(BufferData(bitstream_buffer.id(), |
@@ -323,13 +352,9 @@ |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
return |
next_picture_buffer_id_ == 0 || // Decode() will ProvidePictureBuffers(). |
- available_pictures_ > 0; |
+ available_pictures_ > 0 || !ready_video_frames_.empty(); |
} |
-int GpuVideoDecoder::GetMaxDecodeRequests() const { |
- return kMaxInFlightDecodes; |
-} |
- |
void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, |
const gfx::Size& size, |
uint32 texture_target) { |
@@ -460,10 +485,10 @@ |
pb.texture_id())).second; |
DCHECK(inserted); |
- DeliverFrame(frame); |
+ EnqueueFrameAndTriggerFrameDelivery(frame); |
} |
-void GpuVideoDecoder::DeliverFrame( |
+void GpuVideoDecoder::EnqueueFrameAndTriggerFrameDelivery( |
const scoped_refptr<VideoFrame>& frame) { |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
@@ -472,7 +497,17 @@ |
if (!pending_reset_cb_.is_null()) |
return; |
- output_cb_.Run(frame); |
+ if (frame.get()) |
+ ready_video_frames_.push_back(frame); |
+ else |
+ DCHECK(!ready_video_frames_.empty()); |
+ |
+ if (pending_decode_cb_.is_null()) |
+ return; |
+ |
+ base::ResetAndReturn(&pending_decode_cb_) |
+ .Run(kOk, ready_video_frames_.front()); |
+ ready_video_frames_.pop_front(); |
} |
// static |
@@ -545,7 +580,7 @@ |
DVLOG(3) << "NotifyEndOfBitstreamBuffer(" << id << ")"; |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
- std::map<int32, PendingDecoderBuffer>::iterator it = |
+ std::map<int32, BufferPair>::iterator it = |
bitstream_buffers_in_decoder_.find(id); |
if (it == bitstream_buffers_in_decoder_.end()) { |
NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); |
@@ -554,21 +589,25 @@ |
} |
PutSHM(it->second.shm_buffer); |
- it->second.done_cb.Run(state_ == kError ? kDecodeError : kOk); |
bitstream_buffers_in_decoder_.erase(it); |
+ |
+ if (pending_reset_cb_.is_null() && state_ != kDrainingDecoder && |
+ CanMoreDecodeWorkBeDone() && !pending_decode_cb_.is_null()) { |
+ base::ResetAndReturn(&pending_decode_cb_).Run(kNotEnoughData, NULL); |
+ } |
} |
GpuVideoDecoder::~GpuVideoDecoder() { |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
// Stop should have been already called. |
DCHECK(!vda_.get() && assigned_picture_buffers_.empty()); |
- DCHECK(bitstream_buffers_in_decoder_.empty()); |
+ DCHECK(pending_decode_cb_.is_null()); |
for (size_t i = 0; i < available_shm_segments_.size(); ++i) { |
available_shm_segments_[i]->shm->Close(); |
delete available_shm_segments_[i]; |
} |
available_shm_segments_.clear(); |
- for (std::map<int32, PendingDecoderBuffer>::iterator it = |
+ for (std::map<int32, BufferPair>::iterator it = |
bitstream_buffers_in_decoder_.begin(); |
it != bitstream_buffers_in_decoder_.end(); ++it) { |
it->second.shm_buffer->shm->Close(); |
@@ -581,14 +620,13 @@ |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
DCHECK_EQ(state_, kDrainingDecoder); |
state_ = kDecoderDrained; |
- DeliverFrame(VideoFrame::CreateEOSFrame()); |
- base::ResetAndReturn(&eos_decode_cb_).Run(kOk); |
+ EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEOSFrame()); |
} |
void GpuVideoDecoder::NotifyResetDone() { |
DVLOG(3) << "NotifyResetDone()"; |
DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
- DCHECK(bitstream_buffers_in_decoder_.empty()); |
+ DCHECK(ready_video_frames_.empty()); |
// This needs to happen after the Reset() on vda_ is done to ensure pictures |
// delivered during the reset can find their time data. |
@@ -596,6 +634,9 @@ |
if (!pending_reset_cb_.is_null()) |
base::ResetAndReturn(&pending_reset_cb_).Run(); |
+ |
+ if (!pending_decode_cb_.is_null()) |
+ EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEOSFrame()); |
} |
void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { |
@@ -603,10 +644,15 @@ |
if (!vda_) |
return; |
+ DLOG(ERROR) << "VDA Error: " << error; |
+ DestroyVDA(); |
+ |
state_ = kError; |
- DLOG(ERROR) << "VDA Error: " << error; |
- DestroyVDA(); |
+ if (!pending_decode_cb_.is_null()) { |
+ base::ResetAndReturn(&pending_decode_cb_).Run(kDecodeError, NULL); |
+ return; |
+ } |
} |
void GpuVideoDecoder::DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent() |