Index: chrome/renderer/media/ipc_video_decoder.cc |
diff --git a/chrome/renderer/media/ipc_video_decoder.cc b/chrome/renderer/media/ipc_video_decoder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9d47260c5a2381ffc57390907f66db59c1e6e0d7 |
--- /dev/null |
+++ b/chrome/renderer/media/ipc_video_decoder.cc |
@@ -0,0 +1,344 @@ |
+// Copyright (c) 2010 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 "chrome/renderer/media/ipc_video_decoder.h" |
+ |
+#include "base/task.h" |
+#include "media/base/callback.h" |
+#include "media/base/filters.h" |
+#include "media/base/filter_host.h" |
+#include "media/base/limits.h" |
+#include "media/base/media_format.h" |
+#include "media/base/video_frame.h" |
+#include "media/ffmpeg/ffmpeg_common.h" |
+#include "media/ffmpeg/ffmpeg_util.h" |
+#include "media/filters/ffmpeg_interfaces.h" |
+ |
+namespace media { |
+ |
+IpcVideoDecoder::IpcVideoDecoder(MessageLoop* message_loop) |
+ : width_(0), |
+ height_(0), |
+ state_(kUnInitialized), |
+ pending_reads_(0), |
+ pending_requests_(0), |
+ renderer_thread_message_loop_(message_loop) { |
+} |
+ |
+IpcVideoDecoder::~IpcVideoDecoder() { |
+} |
+ |
+void IpcVideoDecoder::Initialize(DemuxerStream* demuxer_stream, |
+ FilterCallback* callback) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::Initialize, |
+ demuxer_stream, |
+ callback)); |
+ return; |
+ } |
+ |
+ CHECK(!demuxer_stream_); |
+ demuxer_stream_ = demuxer_stream; |
+ initialize_callback_.reset(callback); |
+ |
+ // Get the AVStream by querying for the provider interface. |
+ AVStreamProvider* av_stream_provider; |
+ if (!demuxer_stream->QueryInterface(&av_stream_provider)) { |
+ GpuVideoDecoderInitDoneParam param; |
+ OnInitializeDone(false, param); |
+ return; |
+ } |
+ |
+ AVStream* av_stream = av_stream_provider->GetAVStream(); |
+ width_ = av_stream->codec->width; |
+ height_ = av_stream->codec->height; |
+ |
+ // Create hardware decoder instance. |
+ GpuVideoServiceHost* gpu_video_service_host = GpuVideoServiceHost::get(); |
+ gpu_video_decoder_host_ = gpu_video_service_host->CreateVideoDecoder(this); |
+ |
+ // Initialize hardware decoder. |
+ GpuVideoDecoderInitParam param; |
+ param.width_ = width_; |
+ param.height_ = height_; |
+ if (!gpu_video_decoder_host_->Initialize(param)) { |
+ GpuVideoDecoderInitDoneParam param; |
+ OnInitializeDone(false, param); |
+ } |
+} |
+ |
+void IpcVideoDecoder::OnInitializeDone( |
+ bool success, const GpuVideoDecoderInitDoneParam& param) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::OnInitializeDone, |
+ success, |
+ param)); |
+ return; |
+ } |
+ |
+ AutoCallbackRunner done_runner(initialize_callback_.release()); |
+ |
+ if (success) { |
+ media_format_.SetAsString(MediaFormat::kMimeType, |
+ mime_type::kUncompressedVideo); |
+ media_format_.SetAsInteger(MediaFormat::kWidth, width_); |
+ media_format_.SetAsInteger(MediaFormat::kHeight, height_); |
+ media_format_.SetAsInteger(MediaFormat::kSurfaceType, |
+ static_cast<int>(param.surface_type_)); |
+ media_format_.SetAsInteger(MediaFormat::kSurfaceFormat, |
+ static_cast<int>(param.format_)); |
+ state_ = kPlaying; |
+ } else { |
+ LOG(ERROR) << "IpcVideoDecoder initialization failed!"; |
+ host()->SetError(PIPELINE_ERROR_DECODE); |
+ } |
+} |
+ |
+void IpcVideoDecoder::Stop(FilterCallback* callback) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::Stop, |
+ callback)); |
+ return; |
+ } |
+ |
+ stop_callback_.reset(callback); |
+ if (!gpu_video_decoder_host_->Uninitialize()) { |
+ LOG(ERROR) << "gpu video decoder destroy failed"; |
+ IpcVideoDecoder::OnUninitializeDone(); |
+ } |
+} |
+ |
+void IpcVideoDecoder::OnUninitializeDone() { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::OnUninitializeDone)); |
+ return; |
+ } |
+ |
+ AutoCallbackRunner done_runner(stop_callback_.release()); |
+ |
+ state_ = kStopped; |
+} |
+ |
+void IpcVideoDecoder::Pause(FilterCallback* callback) { |
+ Flush(callback); // TODO(jiesun): move this to flush(). |
+} |
+ |
+void IpcVideoDecoder::Flush(FilterCallback* callback) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::Flush, |
+ callback)); |
+ return; |
+ } |
+ |
+ state_ = kFlushing; |
+ |
+ flush_callback_.reset(callback); |
+ |
+ if (!gpu_video_decoder_host_->Flush()) { |
+ LOG(ERROR) << "gpu video decoder flush failed"; |
+ OnFlushDone(); |
+ } |
+} |
+ |
+void IpcVideoDecoder::OnFlushDone() { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::OnFlushDone)); |
+ return; |
+ } |
+ |
+ if (pending_reads_ == 0 && pending_requests_ == 0 && flush_callback_.get()) { |
+ flush_callback_->Run(); |
+ flush_callback_.reset(); |
+ } |
+} |
+ |
+void IpcVideoDecoder::Seek(base::TimeDelta time, FilterCallback* callback) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::Seek, |
+ time, |
+ callback)); |
+ return; |
+ } |
+ |
+ OnSeekComplete(callback); |
+} |
+ |
+void IpcVideoDecoder::OnSeekComplete(FilterCallback* callback) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::OnSeekComplete, |
+ callback)); |
+ return; |
+ } |
+ |
+ AutoCallbackRunner done_runner(callback); |
+ |
+ state_ = kPlaying; |
+ |
+ for (int i = 0; i < 20; ++i) { |
+ demuxer_stream_->Read( |
+ NewCallback(this, |
+ &IpcVideoDecoder::OnReadComplete)); |
+ ++pending_reads_; |
+ } |
+} |
+ |
+void IpcVideoDecoder::OnReadComplete(Buffer* buffer) { |
+ scoped_refptr<Buffer> buffer_ref = buffer; |
+ ReadCompleteTask(buffer_ref); |
+} |
+ |
+void IpcVideoDecoder::ReadCompleteTask(scoped_refptr<Buffer> buffer) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::ReadCompleteTask, |
+ buffer)); |
+ return; |
+ } |
+ |
+ DCHECK_GT(pending_reads_, 0u); |
+ --pending_reads_; |
+ |
+ if (state_ == kStopped || state_ == kEnded) { |
+ // Just discard the input buffers |
+ return; |
+ } |
+ |
+ if (state_ == kFlushing) { |
+ if (pending_reads_ == 0 && pending_requests_ == 0) { |
+ CHECK(flush_callback_.get()); |
+ flush_callback_->Run(); |
+ flush_callback_.reset(); |
+ state_ = kPlaying; |
+ } |
+ return; |
+ } |
+ // Transition to kFlushCodec on the first end of input stream buffer. |
+ if (state_ == kPlaying && buffer->IsEndOfStream()) { |
+ state_ = kFlushCodec; |
+ } |
+ |
+ gpu_video_decoder_host_->EmptyThisBuffer(buffer); |
+} |
+ |
+void IpcVideoDecoder::FillThisBuffer(scoped_refptr<VideoFrame> video_frame) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::FillThisBuffer, |
+ video_frame)); |
+ return; |
+ } |
+ |
+ // Synchronized flushing before stop should prevent this. |
+ CHECK_NE(state_, kStopped); |
+ |
+ // Notify decode engine the available of new frame. |
+ ++pending_requests_; |
+ gpu_video_decoder_host_->FillThisBuffer(video_frame); |
+} |
+ |
+void IpcVideoDecoder::OnFillBufferDone(scoped_refptr<VideoFrame> video_frame) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::OnFillBufferDone, |
+ video_frame)); |
+ return; |
+ } |
+ |
+ if (video_frame.get()) { |
+ --pending_requests_; |
+ fill_buffer_done_callback()->Run(video_frame); |
+ if (state_ == kFlushing && pending_reads_ == 0 && pending_requests_ == 0) { |
+ CHECK(flush_callback_.get()); |
+ flush_callback_->Run(); |
+ flush_callback_.reset(); |
+ state_ = kPlaying; |
+ } |
+ |
+ } else { |
+ if (state_ == kFlushCodec) { |
+ // When in kFlushCodec, any errored decode, or a 0-lengthed frame, |
+ // is taken as a signal to stop decoding. |
+ state_ = kEnded; |
+ scoped_refptr<VideoFrame> video_frame; |
+ VideoFrame::CreateEmptyFrame(&video_frame); |
+ fill_buffer_done_callback()->Run(video_frame); |
+ } |
+ } |
+} |
+ |
+void IpcVideoDecoder::OnEmptyBufferDone(scoped_refptr<Buffer> buffer) { |
+ if (MessageLoop::current() != renderer_thread_message_loop_) { |
+ renderer_thread_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, |
+ &IpcVideoDecoder::OnEmptyBufferDone, |
+ buffer)); |
+ return; |
+ } |
+ |
+ // TODO(jiesun): We haven't recycle input buffer yet. |
+ demuxer_stream_->Read(NewCallback(this, &IpcVideoDecoder::OnReadComplete)); |
+ ++pending_reads_; |
+} |
+ |
+void IpcVideoDecoder::OnDeviceError() { |
+ host()->SetError(PIPELINE_ERROR_DECODE); |
+} |
+ |
+bool IpcVideoDecoder::ProvidesBuffer() { |
+ return true; |
+} |
+ |
+// static |
+FilterFactory* IpcVideoDecoder::CreateFactory(MessageLoop* message_loop) { |
+ return new FilterFactoryImpl1<IpcVideoDecoder, MessageLoop*>(message_loop); |
+} |
+ |
+// static |
+bool IpcVideoDecoder::IsMediaFormatSupported(const MediaFormat& format) { |
+ std::string mime_type; |
+ if (!format.GetAsString(MediaFormat::kMimeType, &mime_type) && |
+ mime_type::kFFmpegVideo != mime_type) |
+ return false; |
+ |
+ // TODO(jiesun): Although we current only support H264 hardware decoding, |
+ // in the future, we should query GpuVideoService for capabilities. |
+ int codec_id; |
+ return format.GetAsInteger(MediaFormat::kFFmpegCodecID, &codec_id) && |
+ codec_id == CODEC_ID_H264; |
+} |
+ |
+} // namespace media |
+ |