| 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
|
| +
|
|
|