diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-23 02:11:44 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-23 02:11:44 +0000 |
commit | 71a418ccaceeea49dc8049b01a17a501e8070157 (patch) | |
tree | 28a9be5d04adb0d99e5acc28b037eda5933e8542 /media | |
parent | 13347d1506a5a5174bbe39099dc93041f1d0a497 (diff) | |
download | chromium_src-71a418ccaceeea49dc8049b01a17a501e8070157.zip chromium_src-71a418ccaceeea49dc8049b01a17a501e8070157.tar.gz chromium_src-71a418ccaceeea49dc8049b01a17a501e8070157.tar.bz2 |
Use OpenMAX for video decoding in the media pipeline
This patch is to fix some issues when using OpenMAX video decoder
in the media pipeline. It will enable player_x11 to decode using
OpenMAX.
This still requires some more work to enable Chrome to use
OpenMAX for decoding.
Review URL: http://codereview.chromium.org/549124
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36936 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/decoder_base.h | 4 | ||||
-rw-r--r-- | media/filters/omx_video_decode_engine.cc | 117 | ||||
-rw-r--r-- | media/filters/omx_video_decode_engine.h | 21 | ||||
-rw-r--r-- | media/omx/omx_codec.h | 2 | ||||
-rw-r--r-- | media/tools/player_x11/player_x11.cc | 15 |
5 files changed, 111 insertions, 48 deletions
diff --git a/media/filters/decoder_base.h b/media/filters/decoder_base.h index cc403bc..89bab8b 100644 --- a/media/filters/decoder_base.h +++ b/media/filters/decoder_base.h @@ -245,7 +245,9 @@ class DecoderBase : public Decoder { // Note that it's possible for us to decode but not produce a frame, in // which case |pending_reads_| will remain less than |read_queue_| so we // need to schedule an additional read. - DCHECK_LE(pending_reads_, read_queue_.size()); + // TODO(hclam): Enable this line again to make sure we don't break the + // flow control. (BUG=32947) + // DCHECK_LE(pending_reads_, read_queue_.size()); while (pending_reads_ < read_queue_.size()) { demuxer_stream_->Read(NewCallback(this, &DecoderBase::OnReadComplete)); ++pending_reads_; diff --git a/media/filters/omx_video_decode_engine.cc b/media/filters/omx_video_decode_engine.cc index 89e687f..95209d7 100644 --- a/media/filters/omx_video_decode_engine.cc +++ b/media/filters/omx_video_decode_engine.cc @@ -2,8 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This class is in interface to OmxCodec from the media playback +// pipeline. It interacts with OmxCodec and the VideoDecoderImpl +// in the media pipeline. +// +// THREADING SEMANTICS +// +// This class is created by VideoDecoderImpl and lives on the thread +// that VideoDecoderImpl lives. This class is given the message loop +// for the above thread. The same message loop is used to host +// OmxCodec which is the interface to the actual OpenMAX hardware. +// OmxCodec gurantees that all the callbacks are executed on the +// hosting message loop. This essentially means that all methods in +// this class are executed on the same thread as VideoDecoderImpl. +// Because of that there's no need for locking anywhere. + #include "media/filters/omx_video_decode_engine.h" +#include "base/message_loop.h" #include "media/base/callback.h" #include "media/filters/ffmpeg_common.h" @@ -20,6 +36,8 @@ OmxVideoDecodeEngine::~OmxVideoDecodeEngine() { } void OmxVideoDecodeEngine::Initialize(AVStream* stream, Task* done_cb) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + AutoTaskRunner done_runner(done_cb); omx_codec_ = new media::OmxCodec(message_loop_); @@ -29,13 +47,15 @@ void OmxVideoDecodeEngine::Initialize(AVStream* stream, Task* done_cb) { // TODO(ajwong): Extract magic formula to something based on output // pixel format. frame_bytes_ = (width_ * height_ * 3) / 2; + y_buffer_.reset(new uint8[width_ * height_]); + u_buffer_.reset(new uint8[width_ * height_ / 4]); + v_buffer_.reset(new uint8[width_ * height_ / 4]); // TODO(ajwong): Find the right way to determine the Omx component name. - OmxCodec::OmxMediaFormat input_format; + OmxCodec::OmxMediaFormat input_format, output_format; memset(&input_format, 0, sizeof(input_format)); - input_format.codec = OmxCodec::kCodecH264; - OmxCodec::OmxMediaFormat output_format; memset(&output_format, 0, sizeof(output_format)); + input_format.codec = OmxCodec::kCodecH264; output_format.codec = OmxCodec::kCodecRaw; omx_codec_->Setup(input_format, output_format); omx_codec_->SetErrorCallback( @@ -49,22 +69,29 @@ void OmxVideoDecodeEngine::Initialize(AVStream* stream, Task* done_cb) { void OmxVideoDecodeEngine::OnFormatChange( OmxCodec::OmxMediaFormat* input_format, OmxCodec::OmxMediaFormat* output_format) { + DCHECK_EQ(message_loop_, MessageLoop::current()); // TODO(jiesun): We should not need this for here, because width and height // are already known from upper layer of the stack. } void OmxVideoDecodeEngine::OnHardwareError() { - // TODO(ajwong): Threading? + DCHECK_EQ(message_loop_, MessageLoop::current()); state_ = kError; } +// This method assumes the input buffer contains exactly one frame or is an +// end-of-stream buffer. The logic of this method and in OnReadComplete() is +// based on this assumation. +// +// For every input buffer received here, we submit one read request to the +// decoder. So when a read complete callback is received, a corresponding +// decode request must exist. void OmxVideoDecodeEngine::DecodeFrame(const Buffer& buffer, AVFrame* yuv_frame, bool* got_result, Task* done_cb) { - AutoTaskRunner done_runner(done_cb); - + DCHECK_EQ(message_loop_, MessageLoop::current()); if (state_ != kNormal) { return; } @@ -90,35 +117,21 @@ void OmxVideoDecodeEngine::DecodeFrame(const Buffer& buffer, } } - omx_codec_->Read(NewCallback(this, &OmxVideoDecodeEngine::OnReadComplete)); - - if (DecodedFrameAvailable()) { - scoped_ptr<YuvFrame> frame(GetFrame()); + // Enqueue the decode request and the associated buffer. + decode_request_queue_.push_back( + DecodeRequest(yuv_frame, got_result, done_cb)); - // TODO(ajwong): This is a memcpy(). Avoid this. - // TODO(ajwong): This leaks memory. Fix by not using AVFrame. - const size_t frame_pixels = width_ * height_; - yuv_frame->data[0] = new uint8_t[frame_pixels]; - yuv_frame->data[1] = new uint8_t[frame_pixels / 4]; - yuv_frame->data[2] = new uint8_t[frame_pixels / 4]; - yuv_frame->linesize[0] = width_; - yuv_frame->linesize[1] = width_ / 2; - yuv_frame->linesize[2] = width_ / 2; - - memcpy(yuv_frame->data[0], frame->data, frame_pixels); - memcpy(yuv_frame->data[1], frame->data + frame_pixels, frame_pixels / 4); - memcpy(yuv_frame->data[2], - frame->data + frame_pixels + frame_pixels/4, - frame_pixels / 4); - } + // Submit a read request to the decoder. + omx_codec_->Read(NewCallback(this, &OmxVideoDecodeEngine::OnReadComplete)); } void OmxVideoDecodeEngine::OnFeedDone(InputBuffer* buffer) { + DCHECK_EQ(message_loop_, MessageLoop::current()); // TODO(ajwong): Add a DoNothingCallback or similar. } void OmxVideoDecodeEngine::Flush(Task* done_cb) { - AutoLock lock(lock_); + DCHECK_EQ(message_loop_, MessageLoop::current()); omx_codec_->Flush(TaskToCallbackAdapter::NewCallback(done_cb)); } @@ -127,16 +140,52 @@ VideoSurface::Format OmxVideoDecodeEngine::GetSurfaceFormat() const { } void OmxVideoDecodeEngine::Stop(Callback0::Type* done_cb) { - AutoLock lock(lock_); + DCHECK_EQ(message_loop_, MessageLoop::current()); omx_codec_->Stop(done_cb); state_ = kStopped; } void OmxVideoDecodeEngine::OnReadComplete(uint8* buffer, int size) { + DCHECK_EQ(message_loop_, MessageLoop::current()); if ((size_t)size != frame_bytes_) { LOG(ERROR) << "Read completed with weird size: " << size; } + + // Merge the buffer into previous buffers. MergeBytesFrameQueue(buffer, size); + + // We assume that when we receive a read complete callback from + // OmxCodec there was a read request made. + CHECK(!decode_request_queue_.empty()); + const DecodeRequest request = decode_request_queue_.front(); + decode_request_queue_.pop_front(); + *request.got_result = false; + + // Detect if we have received a full decoded frame. + if (DecodedFrameAvailable()) { + // |frame| carries the decoded frame. + scoped_ptr<YuvFrame> frame(yuv_frame_queue_.front()); + yuv_frame_queue_.pop_front(); + *request.got_result = true; + + // TODO(ajwong): This is a memcpy(). Avoid this. + // TODO(ajwong): This leaks memory. Fix by not using AVFrame. + const int pixels = width_ * height_; + request.frame->data[0] = y_buffer_.get(); + request.frame->data[1] = u_buffer_.get(); + request.frame->data[2] = v_buffer_.get(); + request.frame->linesize[0] = width_; + request.frame->linesize[1] = width_ / 2; + request.frame->linesize[2] = width_ / 2; + + memcpy(request.frame->data[0], frame->data, pixels); + memcpy(request.frame->data[1], frame->data + pixels, + pixels / 4); + memcpy(request.frame->data[2], + frame->data + pixels + pixels /4, + pixels / 4); + } + request.done_cb->Run(); } bool OmxVideoDecodeEngine::IsFrameComplete(const YuvFrame* frame) { @@ -144,13 +193,11 @@ bool OmxVideoDecodeEngine::IsFrameComplete(const YuvFrame* frame) { } bool OmxVideoDecodeEngine::DecodedFrameAvailable() { - AutoLock lock(lock_); return (!yuv_frame_queue_.empty() && IsFrameComplete(yuv_frame_queue_.front())); } void OmxVideoDecodeEngine::MergeBytesFrameQueue(uint8* buffer, int size) { - AutoLock lock(lock_); int amount_left = size; // TODO(ajwong): Do the swizzle here instead of in DecodeFrame. This @@ -167,14 +214,4 @@ void OmxVideoDecodeEngine::MergeBytesFrameQueue(uint8* buffer, int size) { } } -OmxVideoDecodeEngine::YuvFrame* OmxVideoDecodeEngine::GetFrame() { - AutoLock lock(lock_); - if (yuv_frame_queue_.empty()) { - return NULL; - } - YuvFrame* frame = yuv_frame_queue_.front(); - yuv_frame_queue_.pop_front(); - return frame; -} - } // namespace media diff --git a/media/filters/omx_video_decode_engine.h b/media/filters/omx_video_decode_engine.h index fc3e4a1..29acf38 100644 --- a/media/filters/omx_video_decode_engine.h +++ b/media/filters/omx_video_decode_engine.h @@ -63,6 +63,20 @@ class OmxVideoDecodeEngine : public VideoDecodeEngine { unsigned char* data; }; + // A struct to hold parameters of a decode request. Objects pointed by + // these parameters are owned by the caller. + struct DecodeRequest { + DecodeRequest(AVFrame* f, bool* b, Task* cb) + : frame(f), + got_result(b), + done_cb(cb) { + } + + AVFrame* frame; + bool* got_result; + Task* done_cb; + }; + virtual void OnFeedDone(InputBuffer* buffer); virtual void OnHardwareError(); virtual void OnReadComplete(uint8* buffer, int size); @@ -72,18 +86,21 @@ class OmxVideoDecodeEngine : public VideoDecodeEngine { virtual bool DecodedFrameAvailable(); virtual void MergeBytesFrameQueue(uint8* buffer, int size); virtual bool IsFrameComplete(const YuvFrame* frame); - virtual YuvFrame* GetFrame(); - Lock lock_; // Locks the |state_| variable and the |yuv_frame_queue_|. State state_; size_t frame_bytes_; size_t width_; size_t height_; + scoped_array<uint8> y_buffer_; + scoped_array<uint8> u_buffer_; + scoped_array<uint8> v_buffer_; + // TODO(hclam): We should let OmxCodec handle this case. bool has_fed_on_eos_; // Used to avoid sending an end of stream to // OpenMax twice since OpenMax does not always // handle this nicely. std::list<YuvFrame*> yuv_frame_queue_; + std::list<DecodeRequest> decode_request_queue_; scoped_refptr<media::OmxCodec> omx_codec_; MessageLoop* message_loop_; diff --git a/media/omx/omx_codec.h b/media/omx/omx_codec.h index bb98a3d..e49fb4f 100644 --- a/media/omx/omx_codec.h +++ b/media/omx/omx_codec.h @@ -169,6 +169,8 @@ class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { }; }; + // Initialize an OmxCodec object that runs on |message_loop|. It is + // guaranteed that callbacks are executed on this message loop. explicit OmxCodec(MessageLoop* message_loop); virtual ~OmxCodec(); diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc index 7cba68f..aa8194b 100644 --- a/media/tools/player_x11/player_x11.cc +++ b/media/tools/player_x11/player_x11.cc @@ -1,6 +1,6 @@ -// 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. +// Copyright (c) 2009-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 <iostream> #include <signal.h> @@ -106,8 +106,13 @@ void TerminateHandler(int signal) { int main(int argc, char** argv) { // Read arguments. if (argc == 1) { - std::cout << "Usage: " << argv[0] << " --file=FILE [--audio]" - " [--alsa-device=DEVICE]" << std::endl; + std::cout << "Usage: " << argv[0] << " --file=FILE" << std::endl + << std::endl + << "Optional arguments:" << std::endl + << " [--enable-openmax]" + << " [--enable-h264-annexb-filter]" + << " [--audio]" + << " [--alsa-device=DEVICE]" << std::endl; return 1; } |