diff options
Diffstat (limited to 'content/renderer/media/ipc_video_decoder.cc')
-rw-r--r-- | content/renderer/media/ipc_video_decoder.cc | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/content/renderer/media/ipc_video_decoder.cc b/content/renderer/media/ipc_video_decoder.cc new file mode 100644 index 0000000..8656e6f --- /dev/null +++ b/content/renderer/media/ipc_video_decoder.cc @@ -0,0 +1,207 @@ +// 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 "content/renderer/media/ipc_video_decoder.h" + +#include "base/task.h" +#include "content/common/child_process.h" +#include "content/renderer/ggl.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/filters/ffmpeg_interfaces.h" +#include "media/video/video_decode_engine.h" + +IpcVideoDecoder::IpcVideoDecoder(MessageLoop* message_loop, + ggl::Context* ggl_context) + : decode_context_message_loop_(message_loop), + ggl_context_(ggl_context) { +} + +IpcVideoDecoder::~IpcVideoDecoder() { +} + +void IpcVideoDecoder::Initialize(media::DemuxerStream* demuxer_stream, + media::FilterCallback* callback, + media::StatisticsCallback* statsCallback) { + // It doesn't matter which thread we perform initialization because + // all this method does is create objects and delegate the initialize + // messsage. + + DCHECK(!demuxer_stream_); + demuxer_stream_ = demuxer_stream; + initialize_callback_.reset(callback); + statistics_callback_.reset(statsCallback); + + // We require bit stream converter for hardware decoder. + demuxer_stream->EnableBitstreamConverter(); + + // Get the AVStream by querying for the provider interface. + media::AVStreamProvider* av_stream_provider; + if (!demuxer_stream->QueryInterface(&av_stream_provider)) { + media::VideoCodecInfo info = {0}; + OnInitializeComplete(info); + return; + } + + AVStream* av_stream = av_stream_provider->GetAVStream(); + + int width = av_stream->codec->coded_width; + int height = av_stream->codec->coded_height; + if (width > media::Limits::kMaxDimension || + height > media::Limits::kMaxDimension || + (width * height) > media::Limits::kMaxCanvas) { + media::VideoCodecInfo info = {0}; + OnInitializeComplete(info); + return; + } + + // Create a video decode context that assocates with the graphics + // context. + decode_context_.reset( + ggl::CreateVideoDecodeContext( + ggl_context_, decode_context_message_loop_, true)); + + // Create a hardware video decoder handle. + decode_engine_.reset(ggl::CreateVideoDecodeEngine(ggl_context_)); + + media::VideoCodecConfig config( + media::CodecIDToVideoCodec(av_stream->codec->codec_id), + width, height, + av_stream->r_frame_rate.num, + av_stream->r_frame_rate.den, + av_stream->codec->extradata, + av_stream->codec->extradata_size); + + // VideoDecodeEngine will perform initialization on the message loop + // given to it so it doesn't matter on which thread we are calling this. + decode_engine_->Initialize(ChildProcess::current()->io_message_loop(), this, + decode_context_.get(), config); +} + +const media::MediaFormat& IpcVideoDecoder::media_format() { + return media_format_; +} + +void IpcVideoDecoder::Stop(media::FilterCallback* callback) { + stop_callback_.reset(callback); + decode_engine_->Uninitialize(); +} + +void IpcVideoDecoder::Pause(media::FilterCallback* callback) { + // TODO(hclam): It looks like that pause is not necessary so implement this + // later. + callback->Run(); + delete callback; +} + +void IpcVideoDecoder::Flush(media::FilterCallback* callback) { + flush_callback_.reset(callback); + decode_engine_->Flush(); +} + +void IpcVideoDecoder::Seek(base::TimeDelta time, + media::FilterCallback* callback) { + seek_callback_.reset(callback); + decode_engine_->Seek(); +} + +void IpcVideoDecoder::OnInitializeComplete(const media::VideoCodecInfo& info) { + DCHECK_EQ(ChildProcess::current()->io_message_loop(), MessageLoop::current()); + + if (info.success) { + media_format_.SetAsInteger(media::MediaFormat::kSurfaceType, + media::VideoFrame::TYPE_GL_TEXTURE); + media_format_.SetAsInteger(media::MediaFormat::kSurfaceFormat, + info.stream_info.surface_format); + media_format_.SetAsInteger(media::MediaFormat::kWidth, + info.stream_info.surface_width); + media_format_.SetAsInteger(media::MediaFormat::kHeight, + info.stream_info.surface_height); + media_format_.SetAsInteger( + media::MediaFormat::kSurfaceType, + static_cast<int>(media::VideoFrame::TYPE_GL_TEXTURE)); + } else { + LOG(ERROR) << "IpcVideoDecoder initialization failed!"; + host()->SetError(media::PIPELINE_ERROR_DECODE); + } + + initialize_callback_->Run(); + initialize_callback_.reset(); +} + +void IpcVideoDecoder::OnUninitializeComplete() { + DCHECK_EQ(ChildProcess::current()->io_message_loop(), MessageLoop::current()); + + // After the decode engine is uninitialized we are safe to destroy the decode + // context. The task will add a refcount to this object so don't need to worry + // about objects lifetime. + decode_context_->Destroy( + NewRunnableMethod(this, &IpcVideoDecoder::OnDestroyComplete)); + + // We don't need to wait for destruction of decode context to complete because + // it can happen asynchronously. This object and decode context will live + // until the destruction task is called. + stop_callback_->Run(); + stop_callback_.reset(); +} + +void IpcVideoDecoder::OnFlushComplete() { + DCHECK_EQ(ChildProcess::current()->io_message_loop(), MessageLoop::current()); + flush_callback_->Run(); + flush_callback_.reset(); +} + +void IpcVideoDecoder::OnSeekComplete() { + DCHECK_EQ(ChildProcess::current()->io_message_loop(), MessageLoop::current()); + seek_callback_->Run(); + seek_callback_.reset(); +} + +void IpcVideoDecoder::OnError() { + DCHECK_EQ(ChildProcess::current()->io_message_loop(), MessageLoop::current()); + host()->SetError(media::PIPELINE_ERROR_DECODE); +} + +// This methid is called by Demuxer after a demuxed packet is produced. +void IpcVideoDecoder::OnReadComplete(media::Buffer* buffer) { + decode_engine_->ConsumeVideoSample(buffer); +} + +void IpcVideoDecoder::OnDestroyComplete() { + // We don't need to do anything in this method. Destruction of objects will + // occur as soon as refcount goes to 0. +} + +// This method is called by VideoRenderer. We delegate the method call to +// VideoDecodeEngine. +void IpcVideoDecoder::ProduceVideoFrame( + scoped_refptr<media::VideoFrame> video_frame) { + decode_engine_->ProduceVideoFrame(video_frame); +} + +bool IpcVideoDecoder::ProvidesBuffer() { + return true; +} + +// This method is called by VideoDecodeEngine that a video frame is produced. +// This is then passed to VideoRenderer. +void IpcVideoDecoder::ConsumeVideoFrame( + scoped_refptr<media::VideoFrame> video_frame, + const media::PipelineStatistics& statistics) { + DCHECK(video_frame); + statistics_callback_->Run(statistics); + + VideoFrameReady(video_frame); +} + +// This method is called by VideoDecodeEngine to request a video frame. The +// request is passed to demuxer. +void IpcVideoDecoder::ProduceVideoSample(scoped_refptr<media::Buffer> buffer) { + demuxer_stream_->Read(NewCallback(this, &IpcVideoDecoder::OnReadComplete)); +} |