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

Unified Diff: media/filters/gpu_video_decoder.cc

Issue 8922010: <video> decode in hardware! (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 9 years 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/gpu_video_decoder.h ('k') | media/filters/video_renderer_base.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/gpu_video_decoder.cc
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..11580a346c9feda70b0ca607786c89889fde176d
--- /dev/null
+++ b/media/filters/gpu_video_decoder.cc
@@ -0,0 +1,443 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/filters/gpu_video_decoder.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/stl_util.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/filter_host.h"
+#include "media/base/video_decoder_config.h"
+#include "media/ffmpeg/ffmpeg_common.h"
+
+namespace media {
+
+GpuVideoDecoder::Factories::~Factories() {}
+
+// Size of shared-memory segments we allocate. Since we reuse them we let them
+// be on the beefy side.
+static const size_t kSharedMemorySegmentBytes = 100 << 10;
+
+GpuVideoDecoder::SHMBuffer::SHMBuffer(base::SharedMemory* m, size_t s)
+ : shm(m), size(s) {
+}
+
+GpuVideoDecoder::SHMBuffer::~SHMBuffer() {}
+
+GpuVideoDecoder::BufferPair::BufferPair(
+ SHMBuffer* s, const scoped_refptr<Buffer>& b) : shm_buffer(s), buffer(b) {
+}
+
+GpuVideoDecoder::BufferPair::~BufferPair() {}
+
+GpuVideoDecoder::GpuVideoDecoder(
+ MessageLoop* message_loop,
+ Factories* factories)
+ : message_loop_(message_loop),
+ factories_(factories),
+ flush_in_progress_(false),
+ demuxer_read_in_progress_(false),
+ next_picture_buffer_id_(0),
+ next_bitstream_buffer_id_(0) {
+ DCHECK(message_loop_ && factories_.get());
+}
+
+GpuVideoDecoder::~GpuVideoDecoder() {
+ DCHECK(!vda_); // Stop should have been already called.
+ STLDeleteElements(&available_shm_segments_);
+ 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();
+ }
+ bitstream_buffers_in_decoder_.clear();
+}
+
+void GpuVideoDecoder::Stop(const base::Closure& callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::Stop, this, callback));
+ return;
+ }
+ if (!vda_) {
+ callback.Run();
+ return;
+ }
+ vda_->Destroy();
+ vda_ = NULL;
+ callback.Run();
+}
+
+void GpuVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::Seek, this, time, cb));
+ return;
+ }
+ pts_stream_.Seek(time);
+ cb.Run(PIPELINE_OK);
+}
+
+void GpuVideoDecoder::Pause(const base::Closure& callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::Pause, this, callback));
+ return;
+ }
+ callback.Run();
+}
+
+void GpuVideoDecoder::Flush(const base::Closure& callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::Flush, this, callback));
+ return;
+ }
+ // Pipeline should have quiesced (via Pause() to all filters) before calling
+ // us, so there should be nothing pending.
+ DCHECK(pending_read_cb_.is_null());
+
+ // Throw away any already-decoded frames.
+ ready_video_frames_.clear();
+
+ if (!vda_) {
+ callback.Run();
+ return;
+ }
+ DCHECK(pending_flush_cb_.is_null());
+ pending_flush_cb_ = callback;
+ pts_stream_.Flush();
+ vda_->Reset();
+}
+
+void GpuVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
+ const PipelineStatusCB& callback,
+ const StatisticsCallback& stats_callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::Initialize, this,
+ make_scoped_refptr(demuxer_stream), callback, stats_callback));
+ return;
+ }
+
+ DCHECK(!demuxer_stream_);
+ if (!demuxer_stream) {
+ callback.Run(PIPELINE_ERROR_DECODE);
+ return;
+ }
+
+ const VideoDecoderConfig& config = demuxer_stream->video_decoder_config();
+ // TODO(scherkus): this check should go in PipelineImpl prior to creating
+ // decoder objects.
+ if (!config.IsValidConfig()) {
+ DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString();
+ callback.Run(PIPELINE_ERROR_DECODE);
+ return;
+ }
+
+ vda_ = factories_->CreateVideoDecodeAccelerator(config.profile(), this);
+ if (!vda_) {
+ callback.Run(DECODER_ERROR_NOT_SUPPORTED);
+ return;
+ }
+
+ demuxer_stream_ = demuxer_stream;
+ statistics_callback_ = stats_callback;
+
+ demuxer_stream_->EnableBitstreamConverter();
+
+ pts_stream_.Initialize(GetFrameDuration(config));
+ natural_size_ = config.natural_size();
+
+ callback.Run(PIPELINE_OK);
+}
+
+void GpuVideoDecoder::Read(const ReadCB& callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::Read, this, callback));
+ return;
+ }
+
+ if (!vda_) {
+ callback.Run(VideoFrame::CreateEmptyFrame());
+ return;
+ }
+
+ DCHECK(pending_read_cb_.is_null());
+ pending_read_cb_ = callback;
+
+ if (!ready_video_frames_.empty()) {
+ DeliverFrame(ready_video_frames_.front());
+ ready_video_frames_.pop_front();
+ return;
+ }
+ EnsureDemuxOrDecode();
+}
+
+void GpuVideoDecoder::RequestBufferDecode(const scoped_refptr<Buffer>& buffer) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::RequestBufferDecode, this, buffer));
+ return;
+ }
+ demuxer_read_in_progress_ = false;
+
+ if (!vda_) {
+ DeliverFrame(VideoFrame::CreateEmptyFrame());
+ return;
+ }
+
+ if (buffer->IsEndOfStream()) {
+ if (!flush_in_progress_) {
+ flush_in_progress_ = true;
+ vda_->Flush();
+ }
+ return;
+ }
+
+ size_t size = buffer->GetDataSize();
+ SHMBuffer* shm_buffer = GetSHM(size);
+ memcpy(shm_buffer->shm->memory(), buffer->GetData(), size);
+ BitstreamBuffer bitstream_buffer(
+ next_bitstream_buffer_id_++, shm_buffer->shm->handle(), size);
+ bool inserted = bitstream_buffers_in_decoder_.insert(std::make_pair(
+ bitstream_buffer.id(), BufferPair(shm_buffer, buffer))).second;
+ DCHECK(inserted);
+ pts_stream_.EnqueuePts(buffer.get());
+
+ vda_->Decode(bitstream_buffer);
+}
+
+const gfx::Size& GpuVideoDecoder::natural_size() {
+ return natural_size_;
+}
+
+void GpuVideoDecoder::NotifyInitializeDone() {
+ NOTREACHED() << "GpuVideoDecodeAcceleratorHost::Initialize is synchronous!";
+}
+
+void GpuVideoDecoder::ProvidePictureBuffers(uint32 count,
+ const gfx::Size& size) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::ProvidePictureBuffers, this, count, size));
+ return;
+ }
+
+ std::vector<uint32> texture_ids;
+ if (!factories_->CreateTextures(count, size, &texture_ids)) {
+ NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
+ return;
+ }
+
+ if (!vda_)
+ return;
+
+ std::vector<PictureBuffer> picture_buffers;
+ for (size_t i = 0; i < texture_ids.size(); ++i) {
+ picture_buffers.push_back(PictureBuffer(
+ next_picture_buffer_id_++, size, texture_ids[i]));
+ bool inserted = picture_buffers_in_decoder_.insert(std::make_pair(
+ picture_buffers.back().id(), picture_buffers.back())).second;
+ DCHECK(inserted);
+ }
+ vda_->AssignPictureBuffers(picture_buffers);
+}
+
+void GpuVideoDecoder::DismissPictureBuffer(int32 id) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::DismissPictureBuffer, this, id));
+ return;
+ }
+ std::map<int32, PictureBuffer>::iterator it =
+ picture_buffers_in_decoder_.find(id);
+ if (it == picture_buffers_in_decoder_.end()) {
+ NOTREACHED() << "Missing picture buffer: " << id;
+ return;
+ }
+ if (!factories_->DeleteTexture(it->second.texture_id())) {
+ NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
+ return;
+ }
+ picture_buffers_in_decoder_.erase(it);
+}
+
+static void ResetAndRunCB(VideoDecoder::ReadCB* cb,
+ scoped_refptr<VideoFrame> frame) {
+ DCHECK(!cb->is_null());
+ VideoDecoder::ReadCB tmp_cb(*cb);
+ cb->Reset();
+ tmp_cb.Run(frame);
+}
+
+void GpuVideoDecoder::PictureReady(const media::Picture& picture) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::PictureReady, this, picture));
+ return;
+ }
+ std::map<int32, PictureBuffer>::iterator it =
+ picture_buffers_in_decoder_.find(picture.picture_buffer_id());
+ if (it == picture_buffers_in_decoder_.end()) {
+ NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id();
+ NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
+ return;
+ }
+ const PictureBuffer& pb = it->second;
+
+ // Update frame's timestamp.
+ base::TimeDelta timestamp;
+ base::TimeDelta duration;
+ std::map<int32, BufferPair>::const_iterator buf_it =
+ bitstream_buffers_in_decoder_.find(picture.bitstream_buffer_id());
+ if (buf_it != bitstream_buffers_in_decoder_.end()) {
+ // Sufficiently out-of-order decoding could have already called
+ // NotifyEndOfBitstreamBuffer on this buffer, but that's ok since we only
+ // need the buffer's time info for best-effort PTS updating.
+ timestamp = buf_it->second.buffer->GetTimestamp();
+ duration = buf_it->second.buffer->GetDuration();
+ }
+
+ scoped_refptr<VideoFrame> frame(VideoFrame::WrapNativeTexture(
+ pb.texture_id(), pb.size().width(),
+ pb.size().height(), timestamp, duration,
+ base::Bind(&GpuVideoDecoder::ReusePictureBuffer, this,
+ picture.picture_buffer_id())));
+ pts_stream_.UpdatePtsAndDuration(frame.get());
+ frame->SetTimestamp(pts_stream_.current_pts());
+ frame->SetDuration(pts_stream_.current_duration());
+
+ // Deliver the frame.
+ DeliverFrame(frame);
+}
+
+void GpuVideoDecoder::DeliverFrame(const scoped_refptr<VideoFrame>& frame) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::DeliverFrameOutOfLine, this, frame));
+}
+
+void GpuVideoDecoder::DeliverFrameOutOfLine(
+ const scoped_refptr<VideoFrame>& frame) {
+ if (pending_read_cb_.is_null()) {
+ ready_video_frames_.push_back(frame);
+ return;
+ }
+ ResetAndRunCB(&pending_read_cb_, frame);
+}
+
+void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::ReusePictureBuffer, this, picture_buffer_id));
+ return;
+ }
+ if (!vda_)
+ return;
+ vda_->ReusePictureBuffer(picture_buffer_id);
+}
+
+GpuVideoDecoder::SHMBuffer* GpuVideoDecoder::GetSHM(size_t min_size) {
+ DCHECK(MessageLoop::current() == message_loop_);
+ if (available_shm_segments_.empty() ||
+ available_shm_segments_.back()->size < min_size) {
+ size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes);
+ base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate);
+ DCHECK(shm);
+ return new SHMBuffer(shm, size_to_allocate);
+ }
+ SHMBuffer* ret = available_shm_segments_.back();
+ available_shm_segments_.pop_back();
+ return ret;
+}
+
+void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) {
+ DCHECK(MessageLoop::current() == message_loop_);
+ available_shm_segments_.push_back(shm_buffer);
+}
+
+void GpuVideoDecoder::NotifyEndOfStream() {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::NotifyEndOfStream, this));
+ return;
+ }
+ DeliverFrame(VideoFrame::CreateEmptyFrame());
+}
+
+void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::NotifyEndOfBitstreamBuffer, this, id));
+ return;
+ }
+
+ std::map<int32, BufferPair>::iterator it =
+ bitstream_buffers_in_decoder_.find(id);
+ if (it == bitstream_buffers_in_decoder_.end()) {
+ NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
+ NOTREACHED() << "Missing bitstream buffer: " << id;
+ return;
+ }
+ PutSHM(it->second.shm_buffer);
+ const scoped_refptr<Buffer>& buffer = it->second.buffer;
+ if (buffer->GetDataSize()) {
+ PipelineStatistics statistics;
+ statistics.video_bytes_decoded = buffer->GetDataSize();
+ statistics_callback_.Run(statistics);
+ }
+ bitstream_buffers_in_decoder_.erase(it);
+
+ if (!pending_read_cb_.is_null()) {
+ DCHECK(ready_video_frames_.empty());
+ EnsureDemuxOrDecode();
+ }
+}
+
+void GpuVideoDecoder::EnsureDemuxOrDecode() {
+ DCHECK(MessageLoop::current() == message_loop_);
+ if (demuxer_read_in_progress_ || !bitstream_buffers_in_decoder_.empty())
+ return;
+ demuxer_read_in_progress_ = true;
+ demuxer_stream_->Read(base::Bind(
+ &GpuVideoDecoder::RequestBufferDecode, this));
+}
+
+void GpuVideoDecoder::NotifyFlushDone() {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::NotifyFlushDone, this));
+ return;
+ }
+ DCHECK(flush_in_progress_);
+ flush_in_progress_ = false;
+ DeliverFrame(VideoFrame::CreateEmptyFrame());
+}
+
+void GpuVideoDecoder::NotifyResetDone() {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::NotifyResetDone, this));
+ return;
+ }
+ // Throw away any already-decoded frames that have come in during the reset.
+ ready_video_frames_.clear();
+ ResetAndRunCB(&pending_flush_cb_);
+}
+
+void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &GpuVideoDecoder::NotifyError, this, error));
+ return;
+ }
+ vda_ = NULL;
+ DLOG(ERROR) << "VDA Error: " << error;
+ if (host())
+ host()->SetError(PIPELINE_ERROR_DECODE);
+}
+
+} // namespace media
« no previous file with comments | « media/filters/gpu_video_decoder.h ('k') | media/filters/video_renderer_base.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698