summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-02 23:49:54 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-02 23:49:54 +0000
commite10e3f1bc8cd6e15c6808e467919f7d6aa547180 (patch)
treea2e145f542decdbeaab5ecb1752eb7d58716c54a /media
parent175e676451c214e4082d7304c37113aedf69a1f8 (diff)
downloadchromium_src-e10e3f1bc8cd6e15c6808e467919f7d6aa547180.zip
chromium_src-e10e3f1bc8cd6e15c6808e467919f7d6aa547180.tar.gz
chromium_src-e10e3f1bc8cd6e15c6808e467919f7d6aa547180.tar.bz2
Revert 58428 - Revert 58423 - Refactor video decode engines to move them to a new folder
Moving video decode engines and friends to media/video. TEST=Tree is green. Video plays. Review URL: http://codereview.chromium.org/3127027 TBR=hclam@chromium.org Review URL: http://codereview.chromium.org/3361004 TBR=akalin@chromium.org Review URL: http://codereview.chromium.org/3323007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58429 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/ffmpeg_video_allocator.cc179
-rw-r--r--media/filters/ffmpeg_video_allocator.h94
-rw-r--r--media/filters/ffmpeg_video_decode_engine.cc359
-rw-r--r--media/filters/ffmpeg_video_decode_engine.h85
-rw-r--r--media/filters/ffmpeg_video_decode_engine_unittest.cc293
-rw-r--r--media/filters/ffmpeg_video_decoder.cc4
-rw-r--r--media/filters/ffmpeg_video_decoder.h2
-rw-r--r--media/filters/ffmpeg_video_decoder_unittest.cc2
-rw-r--r--media/filters/omx_video_decode_engine.cc1339
-rw-r--r--media/filters/omx_video_decode_engine.h246
-rw-r--r--media/filters/omx_video_decoder.cc2
-rw-r--r--media/filters/omx_video_decoder.h2
-rw-r--r--media/filters/video_decode_engine.h128
-rw-r--r--media/media.gyp18
14 files changed, 14 insertions, 2739 deletions
diff --git a/media/filters/ffmpeg_video_allocator.cc b/media/filters/ffmpeg_video_allocator.cc
deleted file mode 100644
index 63665f7..0000000
--- a/media/filters/ffmpeg_video_allocator.cc
+++ /dev/null
@@ -1,179 +0,0 @@
-// 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 "media/filters/ffmpeg_video_allocator.h"
-
-#include "base/logging.h"
-#include "media/ffmpeg/ffmpeg_common.h"
-
-// Because Chromium could be build with FFMPEG version other than FFMPEG-MT
-// by using GYP_DEFINES variable "use-system-ffmpeg". The following code will
-// not build with vanilla FFMPEG. We will fall back to "disable direct
-// rendering" when that happens.
-// TODO(jiesun): Actually we could do better than this: we should modify the
-// following code to work with vanilla FFMPEG.
-
-namespace media {
-
-FFmpegVideoAllocator::FFmpegVideoAllocator()
- : get_buffer_(NULL),
- release_buffer_(NULL) {
-}
-
-void FFmpegVideoAllocator::Initialize(AVCodecContext* codec_context,
- VideoFrame::Format surface_format) {
-#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
- surface_format_ = surface_format;
- get_buffer_ = codec_context->get_buffer;
- release_buffer_ = codec_context->release_buffer;
- codec_context->get_buffer = AllocateBuffer;
- codec_context->release_buffer = ReleaseBuffer;
- codec_context->opaque = this;
-#endif
-}
-
-void FFmpegVideoAllocator::Stop(AVCodecContext* codec_context) {
-#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
- // Restore default buffer allocator functions.
- // This does not work actually, because in ffmpeg-mt, there are
- // multiple codec_context copies per threads. each context maintain
- // its own internal buffer pools.
- codec_context->get_buffer = get_buffer_;
- codec_context->release_buffer = release_buffer_;
-
- while (!frame_pool_.empty()) {
- RefCountedAVFrame* ffmpeg_video_frame = frame_pool_.front();
- frame_pool_.pop_front();
- ffmpeg_video_frame->av_frame_.opaque = NULL;
-
- // Reset per-context default buffer release functions.
- ffmpeg_video_frame->av_frame_.owner->release_buffer = release_buffer_;
- ffmpeg_video_frame->av_frame_.owner->get_buffer = get_buffer_;
- delete ffmpeg_video_frame;
- }
- for (int i = 0; i < kMaxFFmpegThreads; ++i)
- available_frames_[i].clear();
- codec_index_map_.clear();
-#endif
-}
-
-void FFmpegVideoAllocator::DisplayDone(
- AVCodecContext* codec_context,
- scoped_refptr<VideoFrame> video_frame) {
-#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
- RefCountedAVFrame* ffmpeg_video_frame =
- reinterpret_cast<RefCountedAVFrame*>(video_frame->private_buffer());
- if (ffmpeg_video_frame->Release() == 0) {
- int index = codec_index_map_[ffmpeg_video_frame->av_frame_.owner];
- available_frames_[index].push_back(ffmpeg_video_frame);
- }
-#endif
-}
-
-scoped_refptr<VideoFrame> FFmpegVideoAllocator::DecodeDone(
- AVCodecContext* codec_context,
- AVFrame* av_frame) {
- scoped_refptr<VideoFrame> frame;
-#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
- RefCountedAVFrame* ffmpeg_video_frame =
- reinterpret_cast<RefCountedAVFrame*>(av_frame->opaque);
- ffmpeg_video_frame->av_frame_ = *av_frame;
- ffmpeg_video_frame->AddRef();
-
- VideoFrame::CreateFrameExternal(
- VideoFrame::TYPE_SYSTEM_MEMORY, surface_format_,
- codec_context->width, codec_context->height, 3,
- av_frame->data,
- av_frame->linesize,
- StreamSample::kInvalidTimestamp,
- StreamSample::kInvalidTimestamp,
- ffmpeg_video_frame, // |private_buffer_|.
- &frame);
-#endif
- return frame;
-}
-
-int FFmpegVideoAllocator::AllocateBuffer(AVCodecContext* codec_context,
- AVFrame* av_frame) {
- FFmpegVideoAllocator* context =
- reinterpret_cast<FFmpegVideoAllocator*>(codec_context->opaque);
- return context->InternalAllocateBuffer(codec_context, av_frame);
-}
-
-void FFmpegVideoAllocator::ReleaseBuffer(AVCodecContext* codec_context,
- AVFrame* av_frame) {
- FFmpegVideoAllocator* context =
- reinterpret_cast<FFmpegVideoAllocator*>(codec_context->opaque);
- context->InternalReleaseBuffer(codec_context, av_frame);
-}
-
-int FFmpegVideoAllocator::InternalAllocateBuffer(
- AVCodecContext* codec_context, AVFrame* av_frame) {
-#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
- // If |codec_context| is not yet known to us, we add it to our map.
- if (codec_index_map_.find(codec_context) == codec_index_map_.end()) {
- int next_index = codec_index_map_.size();
- codec_index_map_[codec_context] = next_index;
- CHECK_LE((int)codec_index_map_.size(), kMaxFFmpegThreads);
- }
-
- int index = codec_index_map_[codec_context];
-
- RefCountedAVFrame* ffmpeg_video_frame;
- if (available_frames_[index].empty()) {
- int ret = get_buffer_(codec_context, av_frame);
- CHECK_EQ(ret, 0);
- ffmpeg_video_frame = new RefCountedAVFrame();
- ffmpeg_video_frame->av_frame_ = *av_frame;
- frame_pool_.push_back(ffmpeg_video_frame);
- } else {
- ffmpeg_video_frame = available_frames_[index].front();
- available_frames_[index].pop_front();
- // We assume |get_buffer| immediately after |release_buffer| will
- // not trigger real buffer allocation. We just use it to fill the
- // correct value inside |pic|.
- release_buffer_(codec_context, &ffmpeg_video_frame->av_frame_);
- get_buffer_(codec_context, av_frame);
- ffmpeg_video_frame->av_frame_ = *av_frame;
- }
-
- av_frame->opaque = ffmpeg_video_frame;
- av_frame->type = FF_BUFFER_TYPE_USER;
- ffmpeg_video_frame->AddRef();
-#endif
- return 0;
-}
-
-void FFmpegVideoAllocator::InternalReleaseBuffer(
- AVCodecContext* codec_context, AVFrame* av_frame) {
-#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
- if (av_frame->opaque == NULL) {
- // This could happened in two scenario:
- // 1. FFMPEG-MT H264 codec seems to allocate one frame during
- // av_find_stream_info. This happens before we could even
- // install the custom allocator functions.
- // 2. When clean up time, we reset pic->opaque, and destruct ourselves.
- // We could not use our own release_buffer function because
- // handle-delayed-release() is called after we get destructed.
- release_buffer_(codec_context, av_frame);
- return;
- }
-
- RefCountedAVFrame* ffmpeg_video_frame =
- reinterpret_cast<RefCountedAVFrame*>(av_frame->opaque);
- release_buffer_(codec_context, av_frame);
-
- // This is required for get_buffer().
- ffmpeg_video_frame->av_frame_.data[0] = NULL;
- get_buffer_(codec_context, &ffmpeg_video_frame->av_frame_);
- int index = codec_index_map_[codec_context];
- if (ffmpeg_video_frame->Release() == 0)
- available_frames_[index].push_back(ffmpeg_video_frame);
-
- for(int k = 0; k < 4; ++k)
- av_frame->data[k]=NULL;
-#endif
-}
-
-} // namespace media
diff --git a/media/filters/ffmpeg_video_allocator.h b/media/filters/ffmpeg_video_allocator.h
deleted file mode 100644
index b72a1f1..0000000
--- a/media/filters/ffmpeg_video_allocator.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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.
-
-#ifndef MEDIA_FILTERS_FFMPEG_VIDEO_ALLOCATOR_H_
-#define MEDIA_FILTERS_FFMPEG_VIDEO_ALLOCATOR_H_
-
-#include "base/logging.h"
-#include "base/scoped_ptr.h"
-#include "media/base/video_frame.h"
-#include "media/ffmpeg/ffmpeg_common.h"
-#include "media/ffmpeg/ffmpeg_util.h"
-
-#include <deque>
-#include <map>
-
-// FFmpeg types.
-struct AVCodecContext;
-struct AVFrame;
-struct AVStream;
-
-namespace media {
-
-class FFmpegVideoAllocator {
- public:
- FFmpegVideoAllocator();
- virtual ~FFmpegVideoAllocator() {}
-
- struct RefCountedAVFrame {
- RefCountedAVFrame() : usage_count_(0) {}
-
- // TODO(jiesun): we had commented out "DCHECK_EQ(usage_count_, 0);" here.
- // Because the way FFMPEG-MT handle release buffer in delayed fashion.
- // Probably we could wait FFMPEG-MT release all buffers before we callback
- // the flush completion.
- ~RefCountedAVFrame() {}
-
- void AddRef() {
- base::AtomicRefCountIncN(&usage_count_, 1);
- }
-
- bool Release() {
- return base::AtomicRefCountDecN(&usage_count_, 1);
- }
-
- AVFrame av_frame_;
- base::AtomicRefCount usage_count_;
- };
-
- static int AllocateBuffer(AVCodecContext* codec_context, AVFrame* av_frame);
- static void ReleaseBuffer(AVCodecContext* codec_context, AVFrame* av_frame);
-
- void Initialize(AVCodecContext* codec_context,
- VideoFrame::Format surface_format);
- void Stop(AVCodecContext* codec_context);
-
- // DisplayDone() is called when renderer has finished using a frame.
- void DisplayDone(AVCodecContext* codec_context,
- scoped_refptr<VideoFrame> video_frame);
-
- // DecodeDone() is called after avcodec_video_decode() finish so that we can
- // acquire a reference to the video frame before we hand it to the renderer.
- scoped_refptr<VideoFrame> DecodeDone(AVCodecContext* codec_context,
- AVFrame* av_frame);
-
- private:
- int InternalAllocateBuffer(AVCodecContext* codec_context, AVFrame* av_frame);
- void InternalReleaseBuffer(AVCodecContext* codec_context, AVFrame* av_frame);
-
- VideoFrame::Format surface_format_;
-
- // This queue keeps reference count for all VideoFrame allocated.
- std::deque<RefCountedAVFrame*> frame_pool_;
-
- // This queue keeps per-AVCodecContext VideoFrame allocation that
- // was available for recycling.
- static const int kMaxFFmpegThreads = 3;
- std::deque<RefCountedAVFrame*> available_frames_[kMaxFFmpegThreads];
-
- // This map is used to map from AVCodecContext* to index to
- // |available_frames_|, because ffmpeg-mt maintain multiple
- // AVCodecContext (per thread).
- std::map<void*, int> codec_index_map_;
-
- // These function pointer store original ffmpeg AVCodecContext's
- // get_buffer()/release_buffer() function pointer. We use these function
- // to delegate the allocation request.
- int (*get_buffer_)(struct AVCodecContext *c, AVFrame *pic);
- void (*release_buffer_)(struct AVCodecContext *c, AVFrame *pic);
-};
-
-} // namespace media
-
-#endif // MEDIA_FILTERS_FFMPEG_VIDEO_ALLOCATOR_H_
diff --git a/media/filters/ffmpeg_video_decode_engine.cc b/media/filters/ffmpeg_video_decode_engine.cc
deleted file mode 100644
index 0a2f384..0000000
--- a/media/filters/ffmpeg_video_decode_engine.cc
+++ /dev/null
@@ -1,359 +0,0 @@
-// 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 "media/filters/ffmpeg_video_decode_engine.h"
-
-#include "base/command_line.h"
-#include "base/string_number_conversions.h"
-#include "base/task.h"
-#include "media/base/buffers.h"
-#include "media/base/callback.h"
-#include "media/base/limits.h"
-#include "media/base/media_switches.h"
-#include "media/ffmpeg/ffmpeg_common.h"
-#include "media/ffmpeg/ffmpeg_util.h"
-#include "media/filters/ffmpeg_demuxer.h"
-#include "media/filters/ffmpeg_video_allocator.h"
-
-namespace media {
-
-FFmpegVideoDecodeEngine::FFmpegVideoDecodeEngine()
- : codec_context_(NULL),
- av_stream_(NULL),
- event_handler_(NULL),
- direct_rendering_(false),
- pending_input_buffers_(0),
- pending_output_buffers_(0),
- output_eos_reached_(false),
- flush_pending_(false) {
-}
-
-FFmpegVideoDecodeEngine::~FFmpegVideoDecodeEngine() {
-}
-
-void FFmpegVideoDecodeEngine::Initialize(
- MessageLoop* message_loop,
- VideoDecodeEngine::EventHandler* event_handler,
- const VideoCodecConfig& config) {
- allocator_.reset(new FFmpegVideoAllocator());
-
- // Always try to use three threads for video decoding. There is little reason
- // not to since current day CPUs tend to be multi-core and we measured
- // performance benefits on older machines such as P4s with hyperthreading.
- //
- // Handling decoding on separate threads also frees up the pipeline thread to
- // continue processing. Although it'd be nice to have the option of a single
- // decoding thread, FFmpeg treats having one thread the same as having zero
- // threads (i.e., avcodec_decode_video() will execute on the calling thread).
- // Yet another reason for having two threads :)
- static const int kDecodeThreads = 2;
- static const int kMaxDecodeThreads = 16;
-
- av_stream_ = static_cast<AVStream*>(config.opaque_context_);
- codec_context_ = av_stream_->codec;
- // Enable motion vector search (potentially slow), strong deblocking filter
- // for damaged macroblocks, and set our error detection sensitivity.
- codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
- codec_context_->error_recognition = FF_ER_CAREFUL;
-
- AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
-
- if (codec) {
-#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
- direct_rendering_ = codec->capabilities & CODEC_CAP_DR1 ? true : false;
-#endif
- if (direct_rendering_) {
- DLOG(INFO) << "direct rendering is used";
- allocator_->Initialize(codec_context_, GetSurfaceFormat());
- }
- }
-
- // TODO(fbarchard): Improve thread logic based on size / codec.
- // TODO(fbarchard): Fix bug affecting video-cookie.html
- int decode_threads = (codec_context_->codec_id == CODEC_ID_THEORA) ?
- 1 : kDecodeThreads;
-
- const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
- std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads));
- if ((!threads.empty() &&
- !base::StringToInt(threads, &decode_threads)) ||
- decode_threads < 0 || decode_threads > kMaxDecodeThreads) {
- decode_threads = kDecodeThreads;
- }
-
- // We don't allocate AVFrame on the stack since different versions of FFmpeg
- // may change the size of AVFrame, causing stack corruption. The solution is
- // to let FFmpeg allocate the structure via avcodec_alloc_frame().
- av_frame_.reset(avcodec_alloc_frame());
- VideoCodecInfo info;
- info.success_ = false;
- info.provides_buffers_ = true;
- info.stream_info_.surface_type_ = VideoFrame::TYPE_SYSTEM_MEMORY;
- info.stream_info_.surface_format_ = GetSurfaceFormat();
- info.stream_info_.surface_width_ = config.width_;
- info.stream_info_.surface_height_ = config.height_;
-
- // If we do not have enough buffers, we will report error too.
- bool buffer_allocated = true;
- frame_queue_available_.clear();
- if (!direct_rendering_) {
- // Create output buffer pool when direct rendering is not used.
- for (size_t i = 0; i < Limits::kMaxVideoFrames; ++i) {
- scoped_refptr<VideoFrame> video_frame;
- VideoFrame::CreateFrame(VideoFrame::YV12,
- config.width_,
- config.height_,
- StreamSample::kInvalidTimestamp,
- StreamSample::kInvalidTimestamp,
- &video_frame);
- if (!video_frame.get()) {
- buffer_allocated = false;
- break;
- }
- frame_queue_available_.push_back(video_frame);
- }
- }
-
- if (codec &&
- avcodec_thread_init(codec_context_, decode_threads) >= 0 &&
- avcodec_open(codec_context_, codec) >= 0 &&
- av_frame_.get() &&
- buffer_allocated) {
- info.success_ = true;
- }
- event_handler_ = event_handler;
- event_handler_->OnInitializeComplete(info);
-}
-
-// TODO(fbarchard): Find way to remove this memcpy of the entire image.
-static void CopyPlane(size_t plane,
- scoped_refptr<VideoFrame> video_frame,
- const AVFrame* frame) {
- DCHECK_EQ(video_frame->width() % 2, 0u);
- const uint8* source = frame->data[plane];
- const size_t source_stride = frame->linesize[plane];
- uint8* dest = video_frame->data(plane);
- const size_t dest_stride = video_frame->stride(plane);
- size_t bytes_per_line = video_frame->width();
- size_t copy_lines = video_frame->height();
- if (plane != VideoFrame::kYPlane) {
- bytes_per_line /= 2;
- if (video_frame->format() == VideoFrame::YV12) {
- copy_lines = (copy_lines + 1) / 2;
- }
- }
- DCHECK(bytes_per_line <= source_stride && bytes_per_line <= dest_stride);
- for (size_t i = 0; i < copy_lines; ++i) {
- memcpy(dest, source, bytes_per_line);
- source += source_stride;
- dest += dest_stride;
- }
-}
-
-void FFmpegVideoDecodeEngine::EmptyThisBuffer(
- scoped_refptr<Buffer> buffer) {
- pending_input_buffers_--;
- if (flush_pending_) {
- TryToFinishPendingFlush();
- } else {
- // Otherwise try to decode this buffer.
- DecodeFrame(buffer);
- }
-}
-
-void FFmpegVideoDecodeEngine::FillThisBuffer(scoped_refptr<VideoFrame> frame) {
- // We should never receive NULL frame or EOS frame.
- DCHECK(frame.get() && !frame->IsEndOfStream());
-
- // Increment pending output buffer count.
- pending_output_buffers_++;
-
- // Return this frame to available pool or allocator after display.
- if (direct_rendering_)
- allocator_->DisplayDone(codec_context_, frame);
- else
- frame_queue_available_.push_back(frame);
-
- if (flush_pending_) {
- TryToFinishPendingFlush();
- } else if (!output_eos_reached_) {
- // If we already deliver EOS to renderer, we stop reading new input.
- ReadInput();
- }
-}
-
-// Try to decode frame when both input and output are ready.
-void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) {
- scoped_refptr<VideoFrame> video_frame;
-
- // Create a packet for input data.
- // Due to FFmpeg API changes we no longer have const read-only pointers.
- AVPacket packet;
- av_init_packet(&packet);
- packet.data = const_cast<uint8*>(buffer->GetData());
- packet.size = buffer->GetDataSize();
-
- // Let FFmpeg handle presentation timestamp reordering.
- codec_context_->reordered_opaque = buffer->GetTimestamp().InMicroseconds();
-
- // This is for codecs not using get_buffer to initialize
- // |av_frame_->reordered_opaque|
- av_frame_->reordered_opaque = codec_context_->reordered_opaque;
-
- int frame_decoded = 0;
- int result = avcodec_decode_video2(codec_context_,
- av_frame_.get(),
- &frame_decoded,
- &packet);
-
- // Log the problem if we can't decode a video frame and exit early.
- if (result < 0) {
- LOG(INFO) << "Error decoding a video frame with timestamp: "
- << buffer->GetTimestamp().InMicroseconds() << " us"
- << " , duration: "
- << buffer->GetDuration().InMicroseconds() << " us"
- << " , packet size: "
- << buffer->GetDataSize() << " bytes";
- // TODO(jiesun): call event_handler_->OnError() instead.
- event_handler_->OnFillBufferCallback(video_frame);
- return;
- }
-
- // If frame_decoded == 0, then no frame was produced.
- // In this case, if we already begin to flush codec with empty
- // input packet at the end of input stream, the first time we
- // encounter frame_decoded == 0 signal output frame had been
- // drained, we mark the flag. Otherwise we read from demuxer again.
- if (frame_decoded == 0) {
- if (buffer->IsEndOfStream()) { // We had started flushing.
- event_handler_->OnFillBufferCallback(video_frame);
- output_eos_reached_ = true;
- } else {
- ReadInput();
- }
- return;
- }
-
- // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675
- // The decoder is in a bad state and not decoding correctly.
- // Checking for NULL avoids a crash in CopyPlane().
- if (!av_frame_->data[VideoFrame::kYPlane] ||
- !av_frame_->data[VideoFrame::kUPlane] ||
- !av_frame_->data[VideoFrame::kVPlane]) {
- // TODO(jiesun): call event_handler_->OnError() instead.
- event_handler_->OnFillBufferCallback(video_frame);
- return;
- }
-
- // Determine timestamp and calculate the duration based on the repeat picture
- // count. According to FFmpeg docs, the total duration can be calculated as
- // follows:
- // duration = (1 / fps) + (repeat_pict) / (2 * fps)
- // = (2 + repeat_pict) / (2 * fps)
- DCHECK_LE(av_frame_->repeat_pict, 2); // Sanity check.
- // Even frame rate is fixed, for some streams and codecs, the value of
- // |codec_context_->time_base| and |av_stream_->time_base| are not the
- // inverse of the |av_stream_->r_frame_rate|. They use 1 milli-second as
- // time-base units and use increment of av_packet->pts which is not one.
- // Use the inverse of |av_stream_->r_frame_rate| instead of time_base.
- AVRational doubled_time_base;
- doubled_time_base.den = av_stream_->r_frame_rate.num;
- doubled_time_base.num = av_stream_->r_frame_rate.den;
- doubled_time_base.den *= 2;
-
- base::TimeDelta timestamp =
- base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque);
- base::TimeDelta duration =
- ConvertTimestamp(doubled_time_base, 2 + av_frame_->repeat_pict);
-
- if (!direct_rendering_) {
- // Available frame is guaranteed, because we issue as much reads as
- // available frame, except the case of |frame_decoded| == 0, which
- // implies decoder order delay, and force us to read more inputs.
- DCHECK(frame_queue_available_.size());
- video_frame = frame_queue_available_.front();
- frame_queue_available_.pop_front();
-
- // Copy the frame data since FFmpeg reuses internal buffers for AVFrame
- // output, meaning the data is only valid until the next
- // avcodec_decode_video() call.
- CopyPlane(VideoFrame::kYPlane, video_frame.get(), av_frame_.get());
- CopyPlane(VideoFrame::kUPlane, video_frame.get(), av_frame_.get());
- CopyPlane(VideoFrame::kVPlane, video_frame.get(), av_frame_.get());
- } else {
- // Get the VideoFrame from allocator which associate with av_frame_.
- video_frame = allocator_->DecodeDone(codec_context_, av_frame_.get());
- }
-
- video_frame->SetTimestamp(timestamp);
- video_frame->SetDuration(duration);
-
- pending_output_buffers_--;
- event_handler_->OnFillBufferCallback(video_frame);
-}
-
-void FFmpegVideoDecodeEngine::Uninitialize() {
- if (direct_rendering_) {
- allocator_->Stop(codec_context_);
- }
-
- event_handler_->OnUninitializeComplete();
-}
-
-void FFmpegVideoDecodeEngine::Flush() {
- avcodec_flush_buffers(codec_context_);
- flush_pending_ = true;
- TryToFinishPendingFlush();
-}
-
-void FFmpegVideoDecodeEngine::TryToFinishPendingFlush() {
- DCHECK(flush_pending_);
-
- // We consider ourself flushed when there is no pending input buffers
- // and output buffers, which implies that all buffers had been returned
- // to its owner.
- if (!pending_input_buffers_ && !pending_output_buffers_) {
- // Try to finish flushing and notify pipeline.
- flush_pending_ = false;
- event_handler_->OnFlushComplete();
- }
-}
-
-void FFmpegVideoDecodeEngine::Seek() {
- // After a seek, output stream no longer considered as EOS.
- output_eos_reached_ = false;
-
- // The buffer provider is assumed to perform pre-roll operation.
- for (unsigned int i = 0; i < Limits::kMaxVideoFrames; ++i)
- ReadInput();
-
- event_handler_->OnSeekComplete();
-}
-
-void FFmpegVideoDecodeEngine::ReadInput() {
- DCHECK_EQ(output_eos_reached_, false);
- pending_input_buffers_++;
- event_handler_->OnEmptyBufferCallback(NULL);
-}
-
-VideoFrame::Format FFmpegVideoDecodeEngine::GetSurfaceFormat() const {
- // J (Motion JPEG) versions of YUV are full range 0..255.
- // Regular (MPEG) YUV is 16..240.
- // For now we will ignore the distinction and treat them the same.
- switch (codec_context_->pix_fmt) {
- case PIX_FMT_YUV420P:
- case PIX_FMT_YUVJ420P:
- return VideoFrame::YV12;
- break;
- case PIX_FMT_YUV422P:
- case PIX_FMT_YUVJ422P:
- return VideoFrame::YV16;
- break;
- default:
- // TODO(scherkus): More formats here?
- return VideoFrame::INVALID;
- }
-}
-
-} // namespace media
diff --git a/media/filters/ffmpeg_video_decode_engine.h b/media/filters/ffmpeg_video_decode_engine.h
deleted file mode 100644
index fbb465a..0000000
--- a/media/filters/ffmpeg_video_decode_engine.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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.
-
-#ifndef MEDIA_FILTERS_FFMPEG_VIDEO_DECODE_ENGINE_H_
-#define MEDIA_FILTERS_FFMPEG_VIDEO_DECODE_ENGINE_H_
-
-#include <deque>
-
-#include "base/scoped_ptr.h"
-#include "media/ffmpeg/ffmpeg_common.h"
-#include "media/filters/video_decode_engine.h"
-
-// FFmpeg types.
-struct AVCodecContext;
-struct AVFrame;
-struct AVStream;
-
-namespace media {
-
-class FFmpegVideoAllocator;
-
-class FFmpegVideoDecodeEngine : public VideoDecodeEngine {
- public:
- FFmpegVideoDecodeEngine();
- virtual ~FFmpegVideoDecodeEngine();
-
- // Implementation of the VideoDecodeEngine Interface.
- virtual void Initialize(MessageLoop* message_loop,
- VideoDecodeEngine::EventHandler* event_handler,
- const VideoCodecConfig& config);
- virtual void EmptyThisBuffer(scoped_refptr<Buffer> buffer);
- virtual void FillThisBuffer(scoped_refptr<VideoFrame> frame);
- virtual void Uninitialize();
- virtual void Flush();
- virtual void Seek();
-
- virtual AVCodecContext* codec_context() const { return codec_context_; }
-
- virtual void SetCodecContextForTest(AVCodecContext* context) {
- codec_context_ = context;
- }
-
- VideoFrame::Format GetSurfaceFormat() const;
- private:
- void DecodeFrame(scoped_refptr<Buffer> buffer);
- void ReadInput();
- void TryToFinishPendingFlush();
-
- AVCodecContext* codec_context_;
- AVStream* av_stream_;
- scoped_ptr_malloc<AVFrame, ScopedPtrAVFree> av_frame_;
- VideoDecodeEngine::EventHandler* event_handler_;
-
- // Whether direct rendering is used.
- bool direct_rendering_;
-
- // Used when direct rendering is used to recycle output buffers.
- scoped_ptr<FFmpegVideoAllocator> allocator_;
-
- // Indicate how many buffers are pending on input port of this filter:
- // Increment when engine receive one input packet from demuxer;
- // Decrement when engine send one input packet to demuxer;
- int pending_input_buffers_;
-
- // Indicate how many buffers are pending on output port of this filter:
- // Increment when engine receive one output frame from renderer;
- // Decrement when engine send one output frame to renderer;
- int pending_output_buffers_;
-
- // Whether end of stream had been reached at output side.
- bool output_eos_reached_;
-
- // Used when direct rendering is disabled to hold available output buffers.
- std::deque<scoped_refptr<VideoFrame> > frame_queue_available_;
-
- // Whether flush operation is pending.
- bool flush_pending_;
-
- DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecodeEngine);
-};
-
-} // namespace media
-
-#endif // MEDIA_FILTERS_FFMPEG_VIDEO_DECODE_ENGINE_H_
diff --git a/media/filters/ffmpeg_video_decode_engine_unittest.cc b/media/filters/ffmpeg_video_decode_engine_unittest.cc
deleted file mode 100644
index c8e8d06..0000000
--- a/media/filters/ffmpeg_video_decode_engine_unittest.cc
+++ /dev/null
@@ -1,293 +0,0 @@
-// 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 "base/scoped_ptr.h"
-#include "media/base/data_buffer.h"
-#include "media/base/mock_ffmpeg.h"
-#include "media/base/mock_task.h"
-#include "media/filters/ffmpeg_video_decode_engine.h"
-#include "media/filters/ffmpeg_video_decoder.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::DoAll;
-using ::testing::Return;
-using ::testing::ReturnNull;
-using ::testing::SetArgumentPointee;
-using ::testing::StrictMock;
-
-namespace media {
-
-static const int kWidth = 320;
-static const int kHeight = 240;
-static const AVRational kTimeBase = { 1, 100 };
-
-ACTION_P(SaveInitializeResult, engine) {
- engine->info_ = arg0;
-}
-
-class FFmpegVideoDecodeEngineTest : public testing::Test,
- public VideoDecodeEngine::EventHandler {
- protected:
- FFmpegVideoDecodeEngineTest() {
- // Setup FFmpeg structures.
- frame_buffer_.reset(new uint8[kWidth * kHeight]);
- memset(&yuv_frame_, 0, sizeof(yuv_frame_));
-
- // DecodeFrame will check these pointers as non-NULL value.
- yuv_frame_.data[0] = yuv_frame_.data[1] = yuv_frame_.data[2]
- = frame_buffer_.get();
- yuv_frame_.linesize[0] = kWidth;
- yuv_frame_.linesize[1] = yuv_frame_.linesize[2] = kWidth >> 1;
-
- memset(&codec_context_, 0, sizeof(codec_context_));
- codec_context_.width = kWidth;
- codec_context_.height = kHeight;
- codec_context_.time_base = kTimeBase;
-
- memset(&codec_, 0, sizeof(codec_));
- memset(&stream_, 0, sizeof(stream_));
- stream_.codec = &codec_context_;
- stream_.r_frame_rate.num = kTimeBase.den;
- stream_.r_frame_rate.den = kTimeBase.num;
-
- buffer_ = new DataBuffer(1);
-
- // Initialize MockFFmpeg.
- MockFFmpeg::set(&mock_ffmpeg_);
-
- test_engine_ = new FFmpegVideoDecodeEngine();
- test_engine_->SetCodecContextForTest(&codec_context_);
-
- VideoFrame::CreateFrame(VideoFrame::YV12,
- kWidth,
- kHeight,
- StreamSample::kInvalidTimestamp,
- StreamSample::kInvalidTimestamp,
- &video_frame_);
- }
-
- ~FFmpegVideoDecodeEngineTest() {
- test_engine_ = NULL;
- MockFFmpeg::set(NULL);
- }
-
- void Initialize() {
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
- .WillOnce(Return(&codec_));
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecAllocFrame())
- .WillOnce(Return(&yuv_frame_));
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecThreadInit(&codec_context_, 2))
- .WillOnce(Return(0));
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecOpen(&codec_context_, &codec_))
- .WillOnce(Return(0));
- EXPECT_CALL(*MockFFmpeg::get(), AVFree(&yuv_frame_))
- .Times(1);
-
- config_.codec_ = kCodecH264;
- config_.opaque_context_ = &stream_;
- config_.width_ = kWidth;
- config_.height_ = kHeight;
- EXPECT_CALL(*this, OnInitializeComplete(_))
- .WillOnce(SaveInitializeResult(this));
- test_engine_->Initialize(MessageLoop::current(), this, config_);
- EXPECT_TRUE(info_.success_);
- }
-
- public:
- MOCK_METHOD1(OnFillBufferCallback,
- void(scoped_refptr<VideoFrame> video_frame));
- MOCK_METHOD1(OnEmptyBufferCallback,
- void(scoped_refptr<Buffer> buffer));
- MOCK_METHOD1(OnInitializeComplete,
- void(const VideoCodecInfo& info));
- MOCK_METHOD0(OnUninitializeComplete, void());
- MOCK_METHOD0(OnFlushComplete, void());
- MOCK_METHOD0(OnSeekComplete, void());
- MOCK_METHOD0(OnError, void());
- MOCK_METHOD1(OnFormatChange, void(VideoStreamInfo stream_info));
-
- scoped_refptr<VideoFrame> video_frame_;
- VideoCodecConfig config_;
- VideoCodecInfo info_;
- protected:
- scoped_refptr<FFmpegVideoDecodeEngine> test_engine_;
- scoped_array<uint8_t> frame_buffer_;
- StrictMock<MockFFmpeg> mock_ffmpeg_;
-
- AVFrame yuv_frame_;
- AVCodecContext codec_context_;
- AVStream stream_;
- AVCodec codec_;
- scoped_refptr<DataBuffer> buffer_;
-
-};
-
-TEST_F(FFmpegVideoDecodeEngineTest, Initialize_Normal) {
- Initialize();
-}
-
-TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) {
- // Test avcodec_find_decoder() returning NULL.
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
- .WillOnce(ReturnNull());
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecAllocFrame())
- .WillOnce(Return(&yuv_frame_));
- EXPECT_CALL(*MockFFmpeg::get(), AVFree(&yuv_frame_))
- .Times(1);
-
- config_.codec_ = kCodecH264;
- config_.opaque_context_ = &stream_;
- config_.width_ = kWidth;
- config_.height_ = kHeight;
- EXPECT_CALL(*this, OnInitializeComplete(_))
- .WillOnce(SaveInitializeResult(this));
- test_engine_->Initialize(MessageLoop::current(), this, config_);
- EXPECT_FALSE(info_.success_);
-}
-
-// Note There are 2 threads for FFmpeg-mt.
-TEST_F(FFmpegVideoDecodeEngineTest, Initialize_InitThreadFails) {
- // Test avcodec_thread_init() failing.
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
- .WillOnce(Return(&codec_));
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecAllocFrame())
- .WillOnce(Return(&yuv_frame_));
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecThreadInit(&codec_context_, 2))
- .WillOnce(Return(-1));
- EXPECT_CALL(*MockFFmpeg::get(), AVFree(&yuv_frame_))
- .Times(1);
-
- config_.codec_ = kCodecH264;
- config_.opaque_context_ = &stream_;
- config_.width_ = kWidth;
- config_.height_ = kHeight;
- EXPECT_CALL(*this, OnInitializeComplete(_))
- .WillOnce(SaveInitializeResult(this));
- test_engine_->Initialize(MessageLoop::current(), this, config_);
- EXPECT_FALSE(info_.success_);
-}
-
-TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) {
- // Test avcodec_open() failing.
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
- .WillOnce(Return(&codec_));
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecAllocFrame())
- .WillOnce(Return(&yuv_frame_));
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecThreadInit(&codec_context_, 2))
- .WillOnce(Return(0));
- EXPECT_CALL(*MockFFmpeg::get(), AVCodecOpen(&codec_context_, &codec_))
- .WillOnce(Return(-1));
- EXPECT_CALL(*MockFFmpeg::get(), AVFree(&yuv_frame_))
- .Times(1);
-
- config_.codec_ = kCodecH264;
- config_.opaque_context_ = &stream_;
- config_.width_ = kWidth;
- config_.height_ = kHeight;
- EXPECT_CALL(*this, OnInitializeComplete(_))
- .WillOnce(SaveInitializeResult(this));
- test_engine_->Initialize(MessageLoop::current(), this, config_);
- EXPECT_FALSE(info_.success_);
-}
-
-ACTION_P2(DemuxComplete, engine, buffer) {
- engine->EmptyThisBuffer(buffer);
-}
-
-ACTION_P(DecodeComplete, decoder) {
- decoder->video_frame_ = arg0;
-}
-
-TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_Normal) {
- Initialize();
-
- // We rely on FFmpeg for timestamp and duration reporting. The one tricky
- // bit is calculating the duration when |repeat_pict| > 0.
- const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(123);
- const base::TimeDelta kDuration = base::TimeDelta::FromMicroseconds(15000);
- yuv_frame_.repeat_pict = 1;
- yuv_frame_.reordered_opaque = kTimestamp.InMicroseconds();
-
- // Expect a bunch of avcodec calls.
- EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_));
- EXPECT_CALL(mock_ffmpeg_,
- AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _))
- .WillOnce(DoAll(SetArgumentPointee<2>(1), // Simulate 1 byte frame.
- Return(0)));
-
- EXPECT_CALL(*this, OnEmptyBufferCallback(_))
- .WillOnce(DemuxComplete(test_engine_.get(), buffer_));
- EXPECT_CALL(*this, OnFillBufferCallback(_))
- .WillOnce(DecodeComplete(this));
- test_engine_->FillThisBuffer(video_frame_);
-
- // |video_frame_| timestamp is 0 because we set the timestamp based off
- // the buffer timestamp.
- EXPECT_EQ(0, video_frame_->GetTimestamp().ToInternalValue());
- EXPECT_EQ(kDuration.ToInternalValue(),
- video_frame_->GetDuration().ToInternalValue());
-}
-
-TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_0ByteFrame) {
- Initialize();
-
- // Expect a bunch of avcodec calls.
- EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_))
- .Times(2);
- EXPECT_CALL(mock_ffmpeg_,
- AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _))
- .WillOnce(DoAll(SetArgumentPointee<2>(0), // Simulate 0 byte frame.
- Return(0)))
- .WillOnce(DoAll(SetArgumentPointee<2>(1), // Simulate 1 byte frame.
- Return(0)));
-
- EXPECT_CALL(*this, OnEmptyBufferCallback(_))
- .WillOnce(DemuxComplete(test_engine_.get(), buffer_))
- .WillOnce(DemuxComplete(test_engine_.get(), buffer_));
- EXPECT_CALL(*this, OnFillBufferCallback(_))
- .WillOnce(DecodeComplete(this));
- test_engine_->FillThisBuffer(video_frame_);
-
- EXPECT_TRUE(video_frame_.get());
-}
-
-TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_DecodeError) {
- Initialize();
-
- // Expect a bunch of avcodec calls.
- EXPECT_CALL(mock_ffmpeg_, AVInitPacket(_));
- EXPECT_CALL(mock_ffmpeg_,
- AVCodecDecodeVideo2(&codec_context_, &yuv_frame_, _, _))
- .WillOnce(Return(-1));
-
- EXPECT_CALL(*this, OnEmptyBufferCallback(_))
- .WillOnce(DemuxComplete(test_engine_.get(), buffer_));
- EXPECT_CALL(*this, OnFillBufferCallback(_))
- .WillOnce(DecodeComplete(this));
- test_engine_->FillThisBuffer(video_frame_);
-
- EXPECT_FALSE(video_frame_.get());
-}
-
-TEST_F(FFmpegVideoDecodeEngineTest, GetSurfaceFormat) {
- // YV12 formats.
- codec_context_.pix_fmt = PIX_FMT_YUV420P;
- EXPECT_EQ(VideoFrame::YV12, test_engine_->GetSurfaceFormat());
- codec_context_.pix_fmt = PIX_FMT_YUVJ420P;
- EXPECT_EQ(VideoFrame::YV12, test_engine_->GetSurfaceFormat());
-
- // YV16 formats.
- codec_context_.pix_fmt = PIX_FMT_YUV422P;
- EXPECT_EQ(VideoFrame::YV16, test_engine_->GetSurfaceFormat());
- codec_context_.pix_fmt = PIX_FMT_YUVJ422P;
- EXPECT_EQ(VideoFrame::YV16, test_engine_->GetSurfaceFormat());
-
- // Invalid value.
- codec_context_.pix_fmt = PIX_FMT_NONE;
- EXPECT_EQ(VideoFrame::INVALID, test_engine_->GetSurfaceFormat());
-}
-
-} // namespace media
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index 46e4036..eb57493 100644
--- a/media/filters/ffmpeg_video_decoder.cc
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -16,8 +16,8 @@
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/ffmpeg/ffmpeg_util.h"
#include "media/filters/ffmpeg_interfaces.h"
-#include "media/filters/ffmpeg_video_decode_engine.h"
-#include "media/filters/video_decode_engine.h"
+#include "media/video/ffmpeg_video_decode_engine.h"
+#include "media/video/video_decode_engine.h"
namespace media {
diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h
index 972010b..e898266 100644
--- a/media/filters/ffmpeg_video_decoder.h
+++ b/media/filters/ffmpeg_video_decoder.h
@@ -14,7 +14,7 @@
#include "media/base/pts_heap.h"
#include "media/base/video_frame.h"
#include "media/filters/decoder_base.h"
-#include "media/filters/video_decode_engine.h"
+#include "media/video/video_decode_engine.h"
// FFmpeg types.
struct AVRational;
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index 90a538f..b1bd481 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -17,7 +17,7 @@
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_interfaces.h"
#include "media/filters/ffmpeg_video_decoder.h"
-#include "media/filters/video_decode_engine.h"
+#include "media/video/video_decode_engine.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
diff --git a/media/filters/omx_video_decode_engine.cc b/media/filters/omx_video_decode_engine.cc
deleted file mode 100644
index 358e005..0000000
--- a/media/filters/omx_video_decode_engine.cc
+++ /dev/null
@@ -1,1339 +0,0 @@
-// 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.
-
-// This class 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 OMX callbacks are guaranteed to be
-// 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/logging.h"
-#include "base/message_loop.h"
-#include "base/string_util.h"
-#include "media/base/buffers.h"
-
-namespace media {
-
-OmxVideoDecodeEngine::OmxVideoDecodeEngine()
- : width_(16),
- height_(16),
- message_loop_(NULL),
- input_buffer_count_(0),
- input_buffer_size_(0),
- input_port_(0),
- input_buffers_at_component_(0),
- input_pending_request_(0),
- input_queue_has_eos_(false),
- input_has_fed_eos_(false),
- input_port_flushed_(false),
- output_buffer_count_(0),
- output_buffer_size_(0),
- output_port_(0),
- output_buffers_at_component_(0),
- output_pending_request_(0),
- output_eos_(false),
- output_port_flushed_(false),
- il_state_(kIlNone),
- expected_il_state_(kIlNone),
- client_state_(kClientNotInitialized),
- component_handle_(NULL),
- need_free_input_buffers_(false),
- need_free_output_buffers_(false),
- flush_pending_(false),
- output_frames_allocated_(false),
- need_setup_output_port_(false) {
- // TODO(wjia): change uses_egl_image_ to runtime setup
-#if ENABLE_EGLIMAGE == 1
- uses_egl_image_ = true;
- DLOG(INFO) << "Uses egl image for output";
-#else
- uses_egl_image_ = false;
- DLOG(INFO) << "Uses system memory for output";
-#endif
-}
-
-OmxVideoDecodeEngine::~OmxVideoDecodeEngine() {
- DCHECK(client_state_ == kClientNotInitialized ||
- client_state_ == kClientStopped);
- DCHECK_EQ(il_state_, kIlNone);
- DCHECK_EQ(0u, input_buffers_.size());
- DCHECK(free_input_buffers_.empty());
- DCHECK(available_input_buffers_.empty());
- DCHECK_EQ(0, input_buffers_at_component_);
- DCHECK_EQ(0, output_buffers_at_component_);
- DCHECK(output_frames_.empty());
-}
-
-template <typename T>
-static void ResetParamHeader(const OmxVideoDecodeEngine& dec, T* param) {
- memset(param, 0, sizeof(T));
- param->nVersion.nVersion = dec.current_omx_spec_version();
- param->nSize = sizeof(T);
-}
-
-void OmxVideoDecodeEngine::Initialize(
- MessageLoop* message_loop,
- VideoDecodeEngine::EventHandler* event_handler,
- const VideoCodecConfig& config) {
- DCHECK_EQ(message_loop, MessageLoop::current());
-
- message_loop_ = message_loop;
- event_handler_ = event_handler;
-
- width_ = config.width_;
- height_ = config.height_;
-
- // TODO(wjia): Find the right way to determine the codec type.
- OmxConfigurator::MediaFormat input_format, output_format;
- memset(&input_format, 0, sizeof(input_format));
- memset(&output_format, 0, sizeof(output_format));
- input_format.codec = OmxConfigurator::kCodecH264;
- output_format.codec = OmxConfigurator::kCodecRaw;
- configurator_.reset(
- new OmxDecoderConfigurator(input_format, output_format));
-
- // TODO(jiesun): We already ensure Initialize() is called in thread context,
- // We should try to merge the following function into this function.
- client_state_ = kClientInitializing;
- InitializeTask();
-
- VideoCodecInfo info;
- // TODO(jiesun): ridiculous, we never fail initialization?
- info.success_ = true;
- info.provides_buffers_ = !uses_egl_image_;
- info.stream_info_.surface_type_ =
- uses_egl_image_ ? VideoFrame::TYPE_EGL_IMAGE
- : VideoFrame::TYPE_SYSTEM_MEMORY;
- info.stream_info_.surface_format_ = GetSurfaceFormat();
- info.stream_info_.surface_width_ = config.width_;
- info.stream_info_.surface_height_ = config.height_;
- event_handler_->OnInitializeComplete(info);
-}
-
-// This method handles only input buffer, without coupling with output
-void OmxVideoDecodeEngine::EmptyThisBuffer(scoped_refptr<Buffer> buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK(!free_input_buffers_.empty());
- DCHECK_GT(input_pending_request_, 0);
-
- --input_pending_request_;
-
- if (!CanAcceptInput()) {
- FinishEmptyBuffer(buffer);
- return;
- }
-
- if (buffer->IsEndOfStream()) {
- DLOG(INFO) << "Input queue has EOS";
- input_queue_has_eos_ = true;
- }
-
- OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front();
- free_input_buffers_.pop();
-
- // setup |omx_buffer|.
- omx_buffer->pBuffer = const_cast<OMX_U8*>(buffer->GetData());
- omx_buffer->nFilledLen = buffer->GetDataSize();
- omx_buffer->nAllocLen = omx_buffer->nFilledLen;
- if (input_queue_has_eos_)
- omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
- else
- omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
- omx_buffer->nTimeStamp = buffer->GetTimestamp().InMicroseconds();
- omx_buffer->pAppPrivate = buffer.get();
- buffer->AddRef();
- available_input_buffers_.push(omx_buffer);
-
- // Try to feed buffers into the decoder.
- EmptyBufferTask();
-
- if (flush_pending_ && input_pending_request_ == 0)
- StartFlush();
-}
-
-void OmxVideoDecodeEngine::Flush() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(il_state_, kIlExecuting);
-
- if (il_state_ != kIlExecuting) {
- event_handler_->OnFlushComplete();
- return;
- }
-
- client_state_ = kClientFlushing;
- expected_il_state_ = kIlPause;
- OnStateSetEventFunc = &OmxVideoDecodeEngine::PauseFromExecuting;
- TransitionToState(OMX_StatePause);
-}
-
-void OmxVideoDecodeEngine::PauseFromExecuting(OMX_STATETYPE state) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- OnStateSetEventFunc = NULL;
- il_state_ = kIlPause;
-
- if (input_pending_request_ == 0)
- StartFlush();
- else
- flush_pending_ = true;
-}
-
-void OmxVideoDecodeEngine::StartFlush() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(input_pending_request_, 0);
- DLOG(INFO) << "StartFlush";
-
- while (!available_input_buffers_.empty())
- available_input_buffers_.pop();
-
- flush_pending_ = false;
-
- // Flush input port first.
- OnFlushEventFunc = &OmxVideoDecodeEngine::PortFlushDone;
- OMX_ERRORTYPE omxresult;
- omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandFlush,
- input_port_, 0);
-}
-
-bool OmxVideoDecodeEngine::InputPortFlushed() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(client_state_, kClientFlushing);
- // Port flushed is defined by OpenMAX component had signal flush done and
- // We had all buffers returned from demuxer and OpenMAX component.
- int free_input_size = static_cast<int>(free_input_buffers_.size());
- return input_port_flushed_ && free_input_size == input_buffer_count_;
-}
-
-bool OmxVideoDecodeEngine::OutputPortFlushed() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(client_state_, kClientFlushing);
- // Port flushed is defined by OpenMAX component had signal flush done and
- // We had all buffers returned from renderer and OpenMAX component.
- return output_port_flushed_ && output_pending_request_ == 0;
-}
-
-void OmxVideoDecodeEngine::ComponentFlushDone() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DLOG(INFO) << "Component had been flushed!";
-
- if (input_port_flushed_ && output_port_flushed_) {
- event_handler_->OnFlushComplete();
- input_port_flushed_ = false;
- output_port_flushed_ = false;
- }
-}
-
-void OmxVideoDecodeEngine::PortFlushDone(int port) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_NE(port, static_cast<int>(OMX_ALL));
-
- if (port == input_port_) {
- DLOG(INFO) << "Input Port had been flushed";
- DCHECK_EQ(input_buffers_at_component_, 0);
- input_port_flushed_ = true;
- // Flush output port next.
- OMX_ERRORTYPE omxresult;
- omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandFlush,
- output_port_, 0);
- return;
- }
-
- if (port == output_port_) {
- DLOG(INFO) << "Output Port had been flushed";
- DCHECK_EQ(output_buffers_at_component_, 0);
-
- output_port_flushed_ = true;
- }
-
- if (kClientFlushing == client_state_ &&
- InputPortFlushed() && OutputPortFlushed())
- ComponentFlushDone();
-}
-
-void OmxVideoDecodeEngine::Seek() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- DCHECK(client_state_ == kClientFlushing || // After a flush
- client_state_ == kClientInitializing); // After an initialize.
-
- if (client_state_ == kClientFlushing) {
- InitialReadBuffer();
- OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateExecuting;
- TransitionToState(OMX_StateExecuting);
- }
-
- event_handler_->OnSeekComplete();
-}
-
-VideoFrame::Format OmxVideoDecodeEngine::GetSurfaceFormat() const {
- // TODO(jiesun): Both OmxHeaderType and EGLImage surface type could have
- // different surface formats.
- return uses_egl_image_ ? VideoFrame::RGBA : VideoFrame::YV12;
-}
-
-void OmxVideoDecodeEngine::Uninitialize() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (client_state_ == kClientError) {
- OnStopDone();
- return;
- }
-
- // TODO(wjia): add more state checking
- if (kClientRunning == client_state_ || kClientFlushing == client_state_) {
- client_state_ = kClientStopping;
- DeinitFromExecuting(OMX_StateExecuting);
- }
-
- // TODO(wjia): When FillThisBuffer() is added, engine state should be
- // kStopping here. engine state should be set to kStopped in OnStopDone();
- // client_state_ = kClientStopping;
-}
-
-void OmxVideoDecodeEngine::FinishEmptyBuffer(scoped_refptr<Buffer> buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!input_queue_has_eos_) {
- event_handler_->OnEmptyBufferCallback(buffer);
- ++input_pending_request_;
- }
-}
-
-void OmxVideoDecodeEngine::FinishFillBuffer(OMX_BUFFERHEADERTYPE* buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK(buffer);
-
- scoped_refptr<VideoFrame> frame;
- frame = static_cast<VideoFrame*>(buffer->pAppPrivate);
-
- // We should not flush buffer to renderer during decoder flushing if decoder
- // provides the buffer allocator.
- if (kClientFlushing == client_state_ && !uses_egl_image_) return;
-
- frame->SetTimestamp(base::TimeDelta::FromMicroseconds(buffer->nTimeStamp));
- frame->SetDuration(frame->GetTimestamp() - last_pts_);
- last_pts_ = frame->GetTimestamp();
- event_handler_->OnFillBufferCallback(frame);
- output_pending_request_--;
-}
-
-void OmxVideoDecodeEngine::OnStopDone() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- event_handler_->OnUninitializeComplete();
-}
-
-// Function sequence for initializing
-void OmxVideoDecodeEngine::InitializeTask() {
- DCHECK_EQ(il_state_, kIlNone);
-
- il_state_ = kIlNone;
- expected_il_state_ = kIlLoaded;
- output_port_state_ = kPortEnabled;
- if (!CreateComponent()) {
- StopOnError();
- return;
- }
- il_state_ = kIlLoaded;
-
- // TODO(wjia): Disabling output port is to work around racing condition
- // due to bug in some vendor's driver. But it hits another bug.
- // So temporarily fall back to enabling output port. Still keep the code
- // disabling output port here.
- // No need to respond to this PortDisable event
- // OnPortDisableEventFunc = NULL;
- // ChangePort(OMX_CommandPortDisable, output_port_);
- // if (kClientError == client_state_) {
- // StopOnError();
- // return;
- // }
- // output_port_state_ = kPortDisabled;
-
- // Transition component to Idle state
- OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateIdle;
- if (!TransitionToState(OMX_StateIdle)) {
- StopOnError();
- return;
- }
- expected_il_state_ = kIlIdle;
-
- if (!AllocateInputBuffers()) {
- LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error";
- client_state_ = kClientError;
- StopOnError();
- return;
- }
- if (!AllocateOutputBuffers()) {
- LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error";
- client_state_ = kClientError;
- return;
- }
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Initialize OMX (To be removed.)
-// 2. Map role name to component name.
-// 3. Get handle of the OMX component
-// 4. Get the port information.
-// 5. Set role for the component.
-// 6. Input/output ports media format configuration.
-// 7. Obtain the information about the input port.
-// 8. Obtain the information about the output port.
-bool OmxVideoDecodeEngine::CreateComponent() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- static OMX_CALLBACKTYPE callback = {
- &OmxVideoDecodeEngine::EventHandler,
- &OmxVideoDecodeEngine::EmptyBufferCallback,
- &OmxVideoDecodeEngine::FillBufferCallback
- };
-
- // 1. Initialize the OpenMAX Core.
- // TODO(hclam): move this out.
- OMX_ERRORTYPE omxresult = OMX_Init();
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to init OpenMAX core";
- client_state_ = kClientError;
- return false;
- }
-
- // 2. Map role name to component name.
- std::string role_name = configurator_->GetRoleName();
- OMX_U32 roles = 0;
- omxresult = OMX_GetComponentsOfRole(
- const_cast<OMX_STRING>(role_name.c_str()),
- &roles, 0);
- if (omxresult != OMX_ErrorNone || roles == 0) {
- LOG(ERROR) << "Unsupported Role: " << role_name.c_str();
- client_state_ = kClientError;
- return false;
- }
- const OMX_U32 kMaxRolePerComponent = 20;
- CHECK(roles < kMaxRolePerComponent);
-
- OMX_U8** component_names = new OMX_U8*[roles];
- const int kMaxComponentNameLength = 256;
- for (size_t i = 0; i < roles; ++i)
- component_names[i] = new OMX_U8[kMaxComponentNameLength];
-
- omxresult = OMX_GetComponentsOfRole(
- const_cast<OMX_STRING>(role_name.c_str()),
- &roles, component_names);
-
- // Use first component only. Copy the name of the first component
- // so that we could free the memory.
- std::string component_name;
- if (omxresult == OMX_ErrorNone)
- component_name = reinterpret_cast<char*>(component_names[0]);
-
- for (size_t i = 0; i < roles; ++i)
- delete [] component_names[i];
- delete [] component_names;
-
- if (omxresult != OMX_ErrorNone || roles == 0) {
- LOG(ERROR) << "Unsupported Role: " << role_name.c_str();
- client_state_ = kClientError;
- return false;
- }
-
- // 3. Get the handle to the component. After OMX_GetHandle(),
- // the component is in loaded state.
- OMX_STRING component = const_cast<OMX_STRING>(component_name.c_str());
- omxresult = OMX_GetHandle(&component_handle_, component, this, &callback);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to Load the component: " << component;
- client_state_ = kClientError;
- return false;
- }
-
- // 4. Get the port information. This will obtain information about the
- // number of ports and index of the first port.
- OMX_PORT_PARAM_TYPE port_param;
- ResetParamHeader(*this, &port_param);
- omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit,
- &port_param);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to get Port Param";
- client_state_ = kClientError;
- return false;
- }
- input_port_ = port_param.nStartPortNumber;
- output_port_ = input_port_ + 1;
-
- // 5. Set role for the component because our component could
- // have multiple roles.
- OMX_PARAM_COMPONENTROLETYPE role_type;
- ResetParamHeader(*this, &role_type);
- base::strlcpy(reinterpret_cast<char*>(role_type.cRole),
- role_name.c_str(),
- OMX_MAX_STRINGNAME_SIZE);
- role_type.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
- omxresult = OMX_SetParameter(component_handle_,
- OMX_IndexParamStandardComponentRole,
- &role_type);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to Set Role";
- client_state_ = kClientError;
- return false;
- }
-
- // 6. Input/output ports media format configuration.
- if (!ConfigureIOPorts()) {
- LOG(ERROR) << "Media format configurations failed";
- client_state_ = kClientError;
- return false;
- }
-
- // 7. Obtain the information about the input port.
- // This will have the new mini buffer count in |port_format.nBufferCountMin|.
- // Save this value to input_buf_count.
- OMX_PARAM_PORTDEFINITIONTYPE port_format;
- ResetParamHeader(*this, &port_format);
- port_format.nPortIndex = input_port_;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &port_format);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed";
- client_state_ = kClientError;
- return false;
- }
- if (OMX_DirInput != port_format.eDir) {
- LOG(ERROR) << "Expected input port";
- client_state_ = kClientError;
- return false;
- }
- input_buffer_count_ = port_format.nBufferCountMin;
- input_buffer_size_ = port_format.nBufferSize;
-
- // 8. Obtain the information about the output port.
- ResetParamHeader(*this, &port_format);
- port_format.nPortIndex = output_port_;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &port_format);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed";
- client_state_ = kClientError;
- return false;
- }
- if (OMX_DirOutput != port_format.eDir) {
- LOG(ERROR) << "Expect Output Port";
- client_state_ = kClientError;
- return false;
- }
-
- // TODO(wjia): use same buffer recycling for EGLImage and system memory.
- // Override buffer count when EGLImage is used.
- if (uses_egl_image_) {
- // TODO(wjia): remove hard-coded value
- port_format.nBufferCountActual = port_format.nBufferCountMin =
- output_buffer_count_ = 4;
-
- omxresult = OMX_SetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &port_format);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) failed";
- client_state_ = kClientError;
- return false;
- }
- } else {
- output_buffer_count_ = port_format.nBufferCountActual;
- }
- output_buffer_size_ = port_format.nBufferSize;
-
- return true;
-}
-
-// Event callback during initialization to handle DoneStateSet to idle
-void OmxVideoDecodeEngine::DoneSetStateIdle(OMX_STATETYPE state) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(client_state_, kClientInitializing);
- DCHECK_EQ(OMX_StateIdle, state);
- DLOG(INFO) << "OMX video decode engine is in Idle";
-
- il_state_ = kIlIdle;
-
- // start reading bit stream
- InitialReadBuffer();
- OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateExecuting;
- if (!TransitionToState(OMX_StateExecuting)) {
- StopOnError();
- return;
- }
- expected_il_state_ = kIlExecuting;
-}
-
-// Event callback during initialization to handle DoneStateSet to executing
-void OmxVideoDecodeEngine::DoneSetStateExecuting(OMX_STATETYPE state) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK(client_state_ == kClientInitializing ||
- client_state_ == kClientFlushing);
- DCHECK_EQ(OMX_StateExecuting, state);
- DLOG(INFO) << "OMX video decode engine is in Executing";
-
- il_state_ = kIlExecuting;
- client_state_ = kClientRunning;
- OnStateSetEventFunc = NULL;
- EmptyBufferTask();
- InitialFillBuffer();
- if (kClientError == client_state_) {
- StopOnError();
- return;
- }
-}
-
-// Function for receiving output buffers. Hookup for buffer recycling
-// and outside allocator.
-void OmxVideoDecodeEngine::FillThisBuffer(
- scoped_refptr<VideoFrame> video_frame) {
- DCHECK(video_frame.get() && !video_frame->IsEndOfStream());
- output_pending_request_++;
-
- if (!CanAcceptOutput()) {
- if (uses_egl_image_) { // return it to owner.
- output_pending_request_--;
- event_handler_->OnFillBufferCallback(video_frame);
- }
- return;
- }
-
- OMX_BUFFERHEADERTYPE* omx_buffer = FindOmxBuffer(video_frame);
- if (omx_buffer) {
- if (kClientRunning == client_state_) {
- SendOutputBufferToComponent(omx_buffer);
- } else if (kClientFlushing == client_state_) {
- if (uses_egl_image_) { // return it to owner.
- output_pending_request_--;
- event_handler_->OnFillBufferCallback(video_frame);
- }
- if (InputPortFlushed() && OutputPortFlushed())
- ComponentFlushDone();
- }
- } else {
- DCHECK(!output_frames_allocated_);
- DCHECK(uses_egl_image_);
- output_frames_.push_back(std::make_pair(video_frame,
- static_cast<OMX_BUFFERHEADERTYPE*>(NULL)));
- }
-
- DCHECK(static_cast<int>(output_frames_.size()) <= output_buffer_count_);
-
- if ((!output_frames_allocated_) &&
- static_cast<int>(output_frames_.size()) == output_buffer_count_) {
- output_frames_allocated_ = true;
-
- if (need_setup_output_port_) {
- SetupOutputPort();
- }
- }
-
- if (kClientError == client_state_) {
- StopOnError();
- return;
- }
-}
-
-// Reconfigure port
-void OmxVideoDecodeEngine::OnPortSettingsChangedRun(int port,
- OMX_INDEXTYPE index) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(client_state_, kClientRunning);
- DCHECK_EQ(port, output_port_);
-
- // TODO(wjia): add buffer negotiation between decoder and renderer.
- if (uses_egl_image_) {
- DLOG(INFO) << "Port settings are changed";
- return;
- }
-
- // TODO(wjia): remove this checking when all vendors observe same spec.
- if (index > OMX_IndexComponentStartUnused) {
- if (index != OMX_IndexParamPortDefinition)
- return;
- }
-
- OMX_PARAM_PORTDEFINITIONTYPE port_format;
- ResetParamHeader(*this, &port_format);
- port_format.nPortIndex = output_port_;
- OMX_ERRORTYPE omxresult;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &port_format);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed";
- client_state_ = kClientError;
- StopOnError();
- return;
- }
- if (OMX_DirOutput != port_format.eDir) {
- LOG(ERROR) << "Expected Output Port";
- client_state_ = kClientError;
- StopOnError();
- return;
- }
-
- // Update the output format.
- OmxConfigurator::MediaFormat output_format;
- output_format.video_header.height = port_format.format.video.nFrameHeight;
- output_format.video_header.width = port_format.format.video.nFrameWidth;
- output_format.video_header.stride = port_format.format.video.nStride;
- output_buffer_count_ = port_format.nBufferCountActual;
- output_buffer_size_ = port_format.nBufferSize;
-
- if (kPortEnabled == output_port_state_) {
- output_port_state_ = kPortDisabling;
- OnPortDisableEventFunc = &OmxVideoDecodeEngine::OnPortDisableEventRun;
- ChangePort(OMX_CommandPortDisable, output_port_);
- if (kClientError == client_state_) {
- StopOnError();
- return;
- }
- FreeOutputBuffers();
- } else {
- OnPortDisableEventRun(output_port_);
- }
-}
-
-// Post output port disabling
-void OmxVideoDecodeEngine::OnPortDisableEventRun(int port) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(client_state_, kClientRunning);
- DCHECK_EQ(port, output_port_);
-
- output_port_state_ = kPortDisabled;
-
- // make sure all eglimages are available before enabling output port
- if (output_frames_allocated_ || !uses_egl_image_) {
- SetupOutputPort();
- if (kClientError == client_state_) {
- StopOnError();
- return;
- }
- } else {
- need_setup_output_port_ = true;
- }
-}
-
-// Enable output port and allocate buffers correspondingly
-void OmxVideoDecodeEngine::SetupOutputPort() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- need_setup_output_port_ = false;
-
- // Enable output port when necessary since the port could be waiting for
- // buffers, instead of port reconfiguration.
- if (kPortEnabled != output_port_state_) {
- output_port_state_ = kPortEnabling;
- OnPortEnableEventFunc = &OmxVideoDecodeEngine::OnPortEnableEventRun;
- ChangePort(OMX_CommandPortEnable, output_port_);
- if (kClientError == client_state_) {
- return;
- }
- }
-
- // TODO(wjia): add state checking
- // Update the ports in buffer if necessary
- if (!AllocateOutputBuffers()) {
- LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error";
- client_state_ = kClientError;
- return;
- }
-}
-
-// Post output port enabling
-void OmxVideoDecodeEngine::OnPortEnableEventRun(int port) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(port, output_port_);
- DCHECK_EQ(client_state_, kClientRunning);
-
- output_port_state_ = kPortEnabled;
- last_pts_ = base::TimeDelta::FromMilliseconds(0);
- OnPortEnableEventFunc = NULL;
- InitialFillBuffer();
- if (kClientError == client_state_) {
- StopOnError();
- return;
- }
-}
-
-void OmxVideoDecodeEngine::DeinitFromExecuting(OMX_STATETYPE state) {
- DCHECK_EQ(state, OMX_StateExecuting);
-
- DLOG(INFO) << "Deinit from Executing";
- OnStateSetEventFunc = &OmxVideoDecodeEngine::DeinitFromIdle;
- TransitionToState(OMX_StateIdle);
- expected_il_state_ = kIlIdle;
-}
-
-void OmxVideoDecodeEngine::DeinitFromIdle(OMX_STATETYPE state) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(state, OMX_StateIdle);
-
- DLOG(INFO) << "Deinit from Idle";
- il_state_ = kIlIdle;
- OnStateSetEventFunc = &OmxVideoDecodeEngine::DeinitFromLoaded;
- TransitionToState(OMX_StateLoaded);
- expected_il_state_ = kIlLoaded;
-
- if (!input_buffers_at_component_)
- FreeInputBuffers();
- else
- need_free_input_buffers_ = true;
-
- if (!output_buffers_at_component_)
- FreeOutputBuffers();
- else
- need_free_output_buffers_ = true;
-}
-
-void OmxVideoDecodeEngine::DeinitFromLoaded(OMX_STATETYPE state) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(state, OMX_StateLoaded);
-
- DLOG(INFO) << "Deinit from Loaded";
- il_state_ = kIlLoaded;
- if (component_handle_) {
- OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_);
- if (result != OMX_ErrorNone)
- LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result;
- component_handle_ = NULL;
- }
- il_state_ = expected_il_state_ = kIlNone;
-
- // kClientStopped is different from kClientNotInitialized. The former can't
- // accept output buffers, while the latter can.
- client_state_ = kClientStopped;
-
- OMX_Deinit();
-
- OnStopDone();
-}
-
-void OmxVideoDecodeEngine::StopOnError() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- client_state_ = kClientStopping;
-
- if (kIlExecuting == expected_il_state_) {
- DeinitFromExecuting(OMX_StateExecuting);
- } else if (kIlIdle == expected_il_state_) {
- DeinitFromIdle(OMX_StateIdle);
- } else if (kIlLoaded == expected_il_state_) {
- DeinitFromLoaded(OMX_StateLoaded);
- } else if (kIlPause == expected_il_state_) {
- // TODO(jiesun): Make sure this works.
- DeinitFromExecuting(OMX_StateExecuting);
- } else {
- NOTREACHED();
- }
-}
-
-// Call OMX_UseBuffer() to avoid buffer copying when
-// OMX_EmptyThisBuffer() is called
-bool OmxVideoDecodeEngine::AllocateInputBuffers() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- uint8* data = new uint8[input_buffer_size_];
- scoped_array<uint8> data_deleter(data);
-
- for (int i = 0; i < input_buffer_count_; ++i) {
- OMX_BUFFERHEADERTYPE* buffer;
- OMX_ERRORTYPE error =
- OMX_UseBuffer(component_handle_, &buffer, input_port_,
- this, input_buffer_size_, data);
- if (error != OMX_ErrorNone)
- return false;
- buffer->nInputPortIndex = input_port_;
- buffer->nOffset = 0;
- buffer->nFlags = 0;
- input_buffers_.push_back(buffer);
- free_input_buffers_.push(buffer);
- }
- return true;
-}
-
-// This method handles EGLImage and internal buffer cases. Any external
-// allocation case is similar to EGLImage
-bool OmxVideoDecodeEngine::AllocateOutputBuffers() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (uses_egl_image_ && !output_frames_allocated_) {
- DLOG(INFO) << "Output frames are not allocated yet";
- need_setup_output_port_ = true;
- return true;
- }
-
- for (int i = 0; i < output_buffer_count_; ++i) {
- OMX_BUFFERHEADERTYPE* buffer;
- scoped_refptr<VideoFrame> video_frame;
- OMX_ERRORTYPE error;
- if (uses_egl_image_) {
- OutputFrame output_frame = output_frames_[i];
- video_frame = output_frame.first;
- DCHECK(!output_frame.second);
- error = OMX_UseEGLImage(component_handle_, &buffer, output_port_,
- video_frame.get(), video_frame->private_buffer());
- if (error != OMX_ErrorNone)
- return false;
- output_frames_[i].second = buffer;
- } else {
- error = OMX_AllocateBuffer(component_handle_, &buffer, output_port_,
- NULL, output_buffer_size_);
- if (error != OMX_ErrorNone)
- return false;
- video_frame = CreateOmxBufferVideoFrame(buffer);
- output_frames_.push_back(std::make_pair(video_frame, buffer));
- buffer->pAppPrivate = video_frame.get();
- }
- }
-
- return true;
-}
-
-scoped_refptr<VideoFrame> OmxVideoDecodeEngine::CreateOmxBufferVideoFrame(
- OMX_BUFFERHEADERTYPE* omx_buffer) {
- scoped_refptr<VideoFrame> video_frame;
- uint8* data[VideoFrame::kMaxPlanes];
- int32 strides[VideoFrame::kMaxPlanes];
-
- memset(data, 0, sizeof(data));
- memset(strides, 0, sizeof(strides));
- // TODO(jiesun): chroma format 4:2:0 only and 3 planes.
- data[0] = omx_buffer->pBuffer;
- data[1] = data[0] + width_ * height_;
- data[2] = data[1] + width_ * height_ / 4;
- strides[0] = width_;
- strides[1] = strides[2] = width_ >> 1;
-
- VideoFrame::CreateFrameExternal(
- VideoFrame::TYPE_OMXBUFFERHEAD,
- VideoFrame::YV12,
- width_, height_, 3,
- data, strides,
- StreamSample::kInvalidTimestamp,
- StreamSample::kInvalidTimestamp,
- omx_buffer,
- &video_frame);
-
- return video_frame;
-}
-
-void OmxVideoDecodeEngine::FreeInputBuffers() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- // Empty available buffer queue.
- while (!free_input_buffers_.empty()) {
- free_input_buffers_.pop();
- }
-
- while (!available_input_buffers_.empty()) {
- OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front();
- available_input_buffers_.pop();
- Buffer* stored_buffer = static_cast<Buffer*>(omx_buffer->pAppPrivate);
- FinishEmptyBuffer(stored_buffer);
- stored_buffer->Release();
- }
-
- // Calls to OMX to free buffers.
- for (size_t i = 0; i < input_buffers_.size(); ++i)
- OMX_FreeBuffer(component_handle_, input_port_, input_buffers_[i]);
- input_buffers_.clear();
-
- need_free_input_buffers_ = false;
-}
-
-void OmxVideoDecodeEngine::FreeOutputBuffers() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- // Calls to OMX to free buffers.
- for (size_t i = 0; i < output_frames_.size(); ++i) {
- OMX_BUFFERHEADERTYPE* omx_buffer = output_frames_[i].second;
- CHECK(omx_buffer);
- OMX_FreeBuffer(component_handle_, output_port_, omx_buffer);
- }
- output_frames_.clear();
- output_frames_allocated_ = false;
-
- need_free_output_buffers_ = false;
-}
-
-bool OmxVideoDecodeEngine::ConfigureIOPorts() {
- OMX_PARAM_PORTDEFINITIONTYPE input_port_def, output_port_def;
- OMX_ERRORTYPE omxresult = OMX_ErrorNone;
- // Get default input port definition.
- ResetParamHeader(*this, &input_port_def);
- input_port_def.nPortIndex = input_port_;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &input_port_def);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) "
- << "for input port failed";
- return false;
- }
- if (OMX_DirInput != input_port_def.eDir) {
- LOG(ERROR) << "Expected Input Port";
- return false;
- }
-
- // Get default output port definition.
- ResetParamHeader(*this, &output_port_def);
- output_port_def.nPortIndex = output_port_;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &output_port_def);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) "
- << "for output port failed";
- return false;
- }
- if (OMX_DirOutput != output_port_def.eDir) {
- LOG(ERROR) << "Expected Output Port";
- return false;
- }
-
- return configurator_->ConfigureIOPorts(
- static_cast<OMX_COMPONENTTYPE*>(component_handle_),
- &input_port_def, &output_port_def);
-}
-
-bool OmxVideoDecodeEngine::CanEmptyBuffer() {
- // We can call empty buffer while we are in executing and EOS has
- // not been sent
- return (il_state_ == kIlExecuting &&
- !input_has_fed_eos_);
-}
-
-bool OmxVideoDecodeEngine::CanFillBuffer() {
- // Make sure component is in the executing state and end-of-stream
- // has not been reached.
- return (il_state_ == kIlExecuting &&
- !output_eos_ &&
- (output_port_state_ == kPortEnabled ||
- output_port_state_ == kPortEnabling));
-}
-
-bool OmxVideoDecodeEngine::CanAcceptInput() {
- // We can't take input buffer when in error state.
- return (kClientError != client_state_ &&
- kClientStopping != client_state_ &&
- kClientStopped != client_state_ &&
- !input_queue_has_eos_);
-}
-
-bool OmxVideoDecodeEngine::CanAcceptOutput() {
- return (kClientError != client_state_ &&
- kClientStopping != client_state_ &&
- kClientStopped != client_state_ &&
- output_port_state_ == kPortEnabled &&
- !output_eos_);
-}
-
-// TODO(wjia): There are several things need to be done here:
-// 1. Merge this method into EmptyThisBuffer();
-// 2. Get rid of the while loop, this is not needed because when we call
-// OMX_EmptyThisBuffer we assume we *always* have an input buffer.
-void OmxVideoDecodeEngine::EmptyBufferTask() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!CanEmptyBuffer())
- return;
-
- // Loop for all available input data and input buffer for the
- // decoder. When input has reached EOS we need to stop.
- while (!available_input_buffers_.empty() &&
- !input_has_fed_eos_) {
- OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front();
- available_input_buffers_.pop();
-
- input_has_fed_eos_ = omx_buffer->nFlags & OMX_BUFFERFLAG_EOS;
- if (input_has_fed_eos_) {
- DLOG(INFO) << "Input has fed EOS";
- }
-
- // Give this buffer to OMX.
- input_buffers_at_component_++;
- OMX_ERRORTYPE ret = OMX_EmptyThisBuffer(component_handle_, omx_buffer);
- if (ret != OMX_ErrorNone) {
- LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << ret;
- client_state_ = kClientError;
- return;
- }
- }
-}
-
-void OmxVideoDecodeEngine::InitialReadBuffer() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- input_queue_has_eos_ = false;
- input_has_fed_eos_ = false;
- output_eos_ = false;
-
- DLOG(INFO) << "OmxVideoDecodeEngine::InitialReadBuffer";
- for (size_t i = 0; i < free_input_buffers_.size(); i++)
- FinishEmptyBuffer(NULL);
-}
-
-void OmxVideoDecodeEngine::InitialFillBuffer() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- // DCHECK(output_frames_allocated_);
-
- if (!CanFillBuffer())
- return;
-
- DLOG(INFO) << "OmxVideoDecodeEngine::InitialFillBuffer";
-
- // Ask the decoder to fill the output buffers.
- for (uint32 i = 0; i < output_frames_.size(); ++i) {
- OMX_BUFFERHEADERTYPE* omx_buffer = output_frames_[i].second;
- SendOutputBufferToComponent(omx_buffer);
- }
-}
-
-// helper functions
-// Send command to disable/enable port.
-void OmxVideoDecodeEngine::ChangePort(OMX_COMMANDTYPE cmd, int port_index) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- cmd, port_index, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed";
- client_state_ = kClientError;
- return;
- }
-}
-
-// Find if omx_buffer exists corresponding to video_frame
-OMX_BUFFERHEADERTYPE* OmxVideoDecodeEngine::FindOmxBuffer(
- scoped_refptr<VideoFrame> video_frame) {
- for (size_t i = 0; i < output_frames_.size(); ++i) {
- scoped_refptr<VideoFrame> frame = output_frames_[i].first;
- if (video_frame->private_buffer() == frame->private_buffer())
- return output_frames_[i].second;
- }
- return NULL;
-}
-
-OMX_STATETYPE OmxVideoDecodeEngine::GetComponentState() {
- OMX_STATETYPE eState;
- OMX_ERRORTYPE eError;
-
- eError = OMX_GetState(component_handle_, &eState);
- if (OMX_ErrorNone != eError) {
- LOG(ERROR) << "OMX_GetState failed";
- StopOnError();
- }
-
- return eState;
-}
-
-// send one output buffer to component
-void OmxVideoDecodeEngine::SendOutputBufferToComponent(
- OMX_BUFFERHEADERTYPE *omx_buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!CanFillBuffer())
- return;
-
- // clear EOS flag.
- omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
- omx_buffer->nOutputPortIndex = output_port_;
- output_buffers_at_component_++;
- OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer);
-
- if (OMX_ErrorNone != ret) {
- LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret;
- client_state_ = kClientError;
- return;
- }
-}
-
-// Send state transition command to component.
-bool OmxVideoDecodeEngine::TransitionToState(OMX_STATETYPE new_state) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandStateSet,
- new_state, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed";
- client_state_ = kClientError;
- return false;
- }
-
- return true;
-}
-
-void OmxVideoDecodeEngine::EmptyBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_GT(input_buffers_at_component_, 0);
-
- Buffer* stored_buffer = static_cast<Buffer*>(buffer->pAppPrivate);
- buffer->pAppPrivate = NULL;
- if (client_state_ != kClientFlushing)
- FinishEmptyBuffer(stored_buffer);
- stored_buffer->Release();
-
- // Enqueue the available buffer because the decoder has consumed it.
- free_input_buffers_.push(buffer);
- input_buffers_at_component_--;
-
- if (need_free_input_buffers_ && !input_buffers_at_component_) {
- FreeInputBuffers();
- return;
- }
-
- // Try to feed more data into the decoder.
- EmptyBufferTask();
-
- if (client_state_ == kClientFlushing &&
- InputPortFlushed() && OutputPortFlushed())
- ComponentFlushDone();
-}
-
-void OmxVideoDecodeEngine::FillBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_GT(output_buffers_at_component_, 0);
-
- output_buffers_at_component_--;
-
- if (need_free_output_buffers_ && !output_buffers_at_component_) {
- FreeOutputBuffers();
- return;
- }
-
- if (!CanAcceptOutput()) {
- if (uses_egl_image_) {
- scoped_refptr<VideoFrame> frame;
- frame = static_cast<VideoFrame*>(buffer->pAppPrivate);
- event_handler_->OnFillBufferCallback(frame);
- output_pending_request_--;
- }
- return;
- }
-
- // This buffer is received with decoded frame. Enqueue it and make it
- // ready to be consumed by reads.
-
- if (buffer->nFlags & OMX_BUFFERFLAG_EOS) {
- output_eos_ = true;
- DLOG(INFO) << "Output has EOS";
- }
-
- FinishFillBuffer(buffer);
-
- if (buffer->nFlags & OMX_BUFFERFLAG_EOS) {
- // Singal end of stream.
- scoped_refptr<VideoFrame> frame;
- VideoFrame::CreateEmptyFrame(&frame);
- event_handler_->OnFillBufferCallback(frame);
- }
-
- if (client_state_ == kClientFlushing &&
- InputPortFlushed() && OutputPortFlushed())
- ComponentFlushDone();
-}
-
-void OmxVideoDecodeEngine::EventHandlerCompleteTask(OMX_EVENTTYPE event,
- OMX_U32 data1,
- OMX_U32 data2) {
- switch (event) {
- case OMX_EventCmdComplete: {
- // If the last command was successful, we have completed
- // a state transition. So notify that we have done it
- // accordingly.
- OMX_COMMANDTYPE cmd = static_cast<OMX_COMMANDTYPE>(data1);
- if (cmd == OMX_CommandPortDisable) {
- if (OnPortDisableEventFunc)
- (this->*OnPortDisableEventFunc)(static_cast<int>(data2));
- } else if (cmd == OMX_CommandPortEnable) {
- if (OnPortEnableEventFunc)
- (this->*OnPortEnableEventFunc)(static_cast<int>(data2));
- } else if (cmd == OMX_CommandStateSet) {
- (this->*OnStateSetEventFunc)(static_cast<OMX_STATETYPE>(data2));
- } else if (cmd == OMX_CommandFlush) {
- (this->*OnFlushEventFunc)(data2);
- } else {
- LOG(ERROR) << "Unknown command completed\n" << data1;
- }
- break;
- }
- case OMX_EventError:
- if (OMX_ErrorInvalidState == (OMX_ERRORTYPE)data1) {
- // TODO(hclam): what to do here?
- }
- StopOnError();
- break;
- case OMX_EventPortSettingsChanged:
- // TODO(wjia): remove this hack when all vendors observe same spec.
- if (data1 < OMX_IndexComponentStartUnused)
- OnPortSettingsChangedRun(static_cast<int>(data1),
- static_cast<OMX_INDEXTYPE>(data2));
- else
- OnPortSettingsChangedRun(static_cast<int>(data2),
- static_cast<OMX_INDEXTYPE>(data1));
- break;
- default:
- LOG(ERROR) << "Warning - Unknown event received\n";
- break;
- }
-}
-
-// static
-OMX_ERRORTYPE OmxVideoDecodeEngine::EventHandler(OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_EVENTTYPE event,
- OMX_U32 data1,
- OMX_U32 data2,
- OMX_PTR event_data) {
- OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data);
- DCHECK_EQ(component, decoder->component_handle_);
- decoder->message_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(decoder,
- &OmxVideoDecodeEngine::EventHandlerCompleteTask,
- event, data1, data2));
- return OMX_ErrorNone;
-}
-
-// static
-OMX_ERRORTYPE OmxVideoDecodeEngine::EmptyBufferCallback(
- OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_BUFFERHEADERTYPE* buffer) {
- OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data);
- DCHECK_EQ(component, decoder->component_handle_);
- decoder->message_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(decoder,
- &OmxVideoDecodeEngine::EmptyBufferDoneTask, buffer));
- return OMX_ErrorNone;
-}
-
-// static
-OMX_ERRORTYPE OmxVideoDecodeEngine::FillBufferCallback(
- OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_BUFFERHEADERTYPE* buffer) {
- OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data);
- DCHECK_EQ(component, decoder->component_handle_);
- decoder->message_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(decoder,
- &OmxVideoDecodeEngine::FillBufferDoneTask, buffer));
- return OMX_ErrorNone;
-}
-
-} // namespace media
diff --git a/media/filters/omx_video_decode_engine.h b/media/filters/omx_video_decode_engine.h
deleted file mode 100644
index da3fe52..0000000
--- a/media/filters/omx_video_decode_engine.h
+++ /dev/null
@@ -1,246 +0,0 @@
-// 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.
-
-#ifndef MEDIA_FILTERS_OMX_VIDEO_DECODE_ENGINE_H_
-#define MEDIA_FILTERS_OMX_VIDEO_DECODE_ENGINE_H_
-
-#include <queue>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/lock.h"
-#include "base/scoped_ptr.h"
-#include "base/task.h"
-#include "media/filters/video_decode_engine.h"
-#include "media/omx/omx_configurator.h"
-#include "third_party/openmax/il/OMX_Component.h"
-#include "third_party/openmax/il/OMX_Core.h"
-#include "third_party/openmax/il/OMX_Video.h"
-
-namespace media {
-
-class OmxVideoDecodeEngine : public VideoDecodeEngine {
- public:
- OmxVideoDecodeEngine();
- virtual ~OmxVideoDecodeEngine();
-
- // Implementation of the VideoDecodeEngine Interface.
- virtual void Initialize(MessageLoop* message_loop,
- VideoDecodeEngine::EventHandler* event_handler,
- const VideoCodecConfig& config);
- virtual void EmptyThisBuffer(scoped_refptr<Buffer> buffer);
- virtual void FillThisBuffer(scoped_refptr<VideoFrame> frame);
- virtual void Uninitialize();
- virtual void Flush();
- virtual void Seek();
-
- // Subclass can provide a different value.
- virtual int current_omx_spec_version() const { return 0x00000101; }
-
- private:
- enum OmxIlState {
- kIlNone,
- kIlLoaded,
- kIlIdle,
- kIlExecuting,
- kIlPause,
- kIlInvalid,
- kIlUnknown,
- };
-
- enum OmxIlClientState {
- kClientNotInitialized,
- kClientInitializing,
- kClientRunning,
- kClientStopping,
- kClientStopped,
- kClientPausing,
- kClientFlushing,
- kClientError,
- };
-
- enum OmxIlPortState {
- kPortDisabled,
- kPortEnabling,
- kPortEnabled,
- kPortDisabling,
- };
-
- typedef Callback0::Type Callback;
-
- // calls into other classes
- void FinishEmptyBuffer(scoped_refptr<Buffer> buffer);
- void FinishFillBuffer(OMX_BUFFERHEADERTYPE* buffer);
- // Helper method to perform tasks when this object is stopped.
- void OnStopDone();
-
- // Transition method sequence for initialization
- bool CreateComponent();
- void DoneSetStateIdle(OMX_STATETYPE state);
- void DoneSetStateExecuting(OMX_STATETYPE state);
- void OnPortSettingsChangedRun(int port, OMX_INDEXTYPE index);
- void OnPortDisableEventRun(int port);
- void SetupOutputPort();
- void OnPortEnableEventRun(int port);
-
- // Transition methods for shutdown
- void DeinitFromExecuting(OMX_STATETYPE state);
- void DeinitFromIdle(OMX_STATETYPE state);
- void DeinitFromLoaded(OMX_STATETYPE state);
- void PauseFromExecuting(OMX_STATETYPE state);
- void StartFlush();
- void PortFlushDone(int port);
- void ComponentFlushDone();
-
- void StopOnError();
-
- void InitializeTask();
-
- // Methods to free input and output buffers.
- bool AllocateInputBuffers();
- bool AllocateOutputBuffers();
- void FreeInputBuffers();
- void FreeOutputBuffers();
- void FreeInputQueue();
-
- // Helper method to configure port format at LOADED state.
- bool ConfigureIOPorts();
-
- // Determine whether we can issue fill buffer or empty buffer
- // to the decoder based on the current state and port state.
- bool CanEmptyBuffer();
- bool CanFillBuffer();
-
- // Determine whether we can use |input_queue_| and |output_queue_|
- // based on the current state.
- bool CanAcceptInput();
- bool CanAcceptOutput();
-
- bool InputPortFlushed();
- bool OutputPortFlushed();
-
- // Method to send input buffers to component
- void EmptyBufferTask();
-
- // Method doing initial reads to get bit stream from demuxer.
- void InitialReadBuffer();
-
- // Method doing initial fills to kick start the decoding process.
- void InitialFillBuffer();
-
- // helper functions
- void ChangePort(OMX_COMMANDTYPE cmd, int port_index);
- OMX_BUFFERHEADERTYPE* FindOmxBuffer(scoped_refptr<VideoFrame> video_frame);
- OMX_STATETYPE GetComponentState();
- void SendOutputBufferToComponent(OMX_BUFFERHEADERTYPE *omx_buffer);
- bool TransitionToState(OMX_STATETYPE new_state);
- virtual VideoFrame::Format GetSurfaceFormat() const;
-
- // Method to handle events
- void EventHandlerCompleteTask(OMX_EVENTTYPE event,
- OMX_U32 data1,
- OMX_U32 data2);
-
- // Method to receive buffers from component's input port
- void EmptyBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer);
-
- // Method to receive buffers from component's output port
- void FillBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer);
-
- // The following three methods are static callback methods
- // for the OMX component. When these callbacks are received, the
- // call is delegated to the three internal methods above.
- static OMX_ERRORTYPE EventHandler(OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_EVENTTYPE event,
- OMX_U32 data1, OMX_U32 data2,
- OMX_PTR event_data);
-
- static OMX_ERRORTYPE EmptyBufferCallback(OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_BUFFERHEADERTYPE* buffer);
-
- static OMX_ERRORTYPE FillBufferCallback(OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_BUFFERHEADERTYPE* buffer);
-
- // Member function pointers to respond to events
- void (OmxVideoDecodeEngine::*OnPortDisableEventFunc)(int port);
- void (OmxVideoDecodeEngine::*OnPortEnableEventFunc)(int port);
- void (OmxVideoDecodeEngine::*OnStateSetEventFunc)(OMX_STATETYPE state);
- void (OmxVideoDecodeEngine::*OnFlushEventFunc)(int port);
-
- // Helper function
- scoped_refptr<VideoFrame> CreateOmxBufferVideoFrame(
- OMX_BUFFERHEADERTYPE* omx_buffer);
-
- size_t width_;
- size_t height_;
-
- MessageLoop* message_loop_;
-
- std::vector<OMX_BUFFERHEADERTYPE*> input_buffers_;
- int input_buffer_count_;
- int input_buffer_size_;
- int input_port_;
- int input_buffers_at_component_;
- int input_pending_request_;
- bool input_queue_has_eos_;
- bool input_has_fed_eos_;
- bool input_port_flushed_;
-
- int output_buffer_count_;
- int output_buffer_size_;
- int output_port_;
- int output_buffers_at_component_;
- int output_pending_request_;
- bool output_eos_;
- bool output_port_flushed_;
- bool uses_egl_image_;
- base::TimeDelta last_pts_;
-
- // |il_state_| records the current component state. During state transition
- // |expected_il_state_| is the next state that the component will transition
- // to. After a state transition is completed, |il_state_| equals
- // |expected_il_state_|. Inequality can be used to detect a state transition.
- // These two members are read and written only on |message_loop_|.
- OmxIlState il_state_;
- OmxIlState expected_il_state_;
- OmxIlClientState client_state_;
-
- OMX_HANDLETYPE component_handle_;
- scoped_ptr<media::OmxConfigurator> configurator_;
-
- // Free input OpenMAX buffers that can be used to take input bitstream from
- // demuxer.
- std::queue<OMX_BUFFERHEADERTYPE*> free_input_buffers_;
-
- // Available input OpenMAX buffers that we can use to issue
- // OMX_EmptyThisBuffer() call.
- std::queue<OMX_BUFFERHEADERTYPE*> available_input_buffers_;
-
- // flag for freeing input/output buffers
- bool need_free_input_buffers_;
- bool need_free_output_buffers_;
-
- // for calling flush callback only once.
- bool flush_pending_;
-
- // For output buffer recycling cases.
- typedef std::pair<scoped_refptr<VideoFrame>,
- OMX_BUFFERHEADERTYPE*> OutputFrame;
- std::vector<OutputFrame> output_frames_;
- bool output_frames_allocated_;
-
- // port related
- bool need_setup_output_port_;
- OmxIlPortState output_port_state_;
- VideoDecodeEngine::EventHandler* event_handler_;
-
- DISALLOW_COPY_AND_ASSIGN(OmxVideoDecodeEngine);
-};
-
-} // namespace media
-
-#endif // MEDIA_FILTERS_OMX_VIDEO_DECODE_ENGINE_H_
diff --git a/media/filters/omx_video_decoder.cc b/media/filters/omx_video_decoder.cc
index 2c96117..0a6d9910 100644
--- a/media/filters/omx_video_decoder.cc
+++ b/media/filters/omx_video_decoder.cc
@@ -11,7 +11,7 @@
#include "media/base/limits.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_interfaces.h"
-#include "media/filters/omx_video_decode_engine.h"
+#include "media/video/omx_video_decode_engine.h"
namespace media {
diff --git a/media/filters/omx_video_decoder.h b/media/filters/omx_video_decoder.h
index 16f3b64..26efc38 100644
--- a/media/filters/omx_video_decoder.h
+++ b/media/filters/omx_video_decoder.h
@@ -10,7 +10,7 @@
#include "media/base/factory.h"
#include "media/base/filters.h"
#include "media/base/media_format.h"
-#include "media/filters/video_decode_engine.h"
+#include "media/video/video_decode_engine.h"
class MessageLoop;
diff --git a/media/filters/video_decode_engine.h b/media/filters/video_decode_engine.h
deleted file mode 100644
index f1faf7b..0000000
--- a/media/filters/video_decode_engine.h
+++ /dev/null
@@ -1,128 +0,0 @@
-// 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.
-
-#ifndef MEDIA_FILTERS_VIDEO_DECODE_ENGINE_H_
-#define MEDIA_FILTERS_VIDEO_DECODE_ENGINE_H_
-
-#include "base/callback.h"
-#include "base/message_loop.h"
-#include "media/base/video_frame.h"
-
-namespace media {
-
-class Buffer;
-
-enum VideoCodec {
- kCodecH264,
- kCodecVC1,
- kCodecMPEG2,
- kCodecMPEG4,
- kCodecTheora,
- kCodecVP8,
-};
-
-static const uint32 kProfileDoNotCare = static_cast<uint32>(-1);
-static const uint32 kLevelDoNotCare = static_cast<uint32>(-1);
-
-struct VideoCodecConfig {
- VideoCodecConfig() : codec_(kCodecH264),
- profile_(kProfileDoNotCare),
- level_(kLevelDoNotCare),
- width_(0),
- height_(0),
- opaque_context_(NULL) {}
-
- VideoCodec codec_;
-
- // TODO(jiesun): video profile and level are specific to individual codec.
- // Define enum to.
- uint32 profile_;
- uint32 level_;
-
- // Container's concept of width and height of this video.
- int32 width_;
- int32 height_; // TODO(jiesun): Do we allow height to be negative to
- // indicate output is upside-down?
-
- // FFMPEG's will use this to pass AVStream. Otherwise, we should remove this.
- void* opaque_context_;
-};
-
-struct VideoStreamInfo {
- VideoFrame::Format surface_format_;
- VideoFrame::SurfaceType surface_type_;
- uint32 surface_width_; // Can be different with container's value.
- uint32 surface_height_; // Can be different with container's value.
-};
-
-struct VideoCodecInfo {
- // Other parameter is only meaningful when this is true.
- bool success_;
-
- // Whether decoder provides output buffer pool.
- bool provides_buffers_;
-
- // Initial Stream Info. Only part of them could be valid.
- // If they are not valid, Engine should update with OnFormatChange.
- VideoStreamInfo stream_info_;
-};
-
-class VideoDecodeEngine : public base::RefCountedThreadSafe<VideoDecodeEngine> {
- public:
- struct EventHandler {
- public:
- virtual ~EventHandler() {}
- virtual void OnInitializeComplete(const VideoCodecInfo& info) = 0;
- virtual void OnUninitializeComplete() = 0;
- virtual void OnFlushComplete() = 0;
- virtual void OnSeekComplete() = 0;
- virtual void OnError() = 0;
- virtual void OnFormatChange(VideoStreamInfo stream_info) = 0;
- virtual void OnEmptyBufferCallback(scoped_refptr<Buffer> buffer) = 0;
- virtual void OnFillBufferCallback(scoped_refptr<VideoFrame> frame) = 0;
- };
-
- VideoDecodeEngine() {}
- virtual ~VideoDecodeEngine() {}
-
- // Initialized the engine with specified configuration. |message_loop| could
- // be NULL if every operation is synchronous. Engine should call the
- // EventHandler::OnInitializeDone() no matter finished successfully or not.
- // TODO(jiesun): remove message_loop and create thread inside openmax engine?
- // or create thread in GpuVideoDecoder and pass message loop here?
- virtual void Initialize(MessageLoop* message_loop,
- EventHandler* event_handler,
- const VideoCodecConfig& config) = 0;
-
- // Uninitialize the engine. Engine should destroy all resources and call
- // EventHandler::OnUninitializeComplete().
- virtual void Uninitialize() = 0;
-
- // Flush the engine. Engine should return all the buffers to owner ( which
- // could be itself. ) then call EventHandler::OnFlushDone().
- virtual void Flush() = 0;
-
- // Used in openmax to InitialReadBuffers().
- virtual void Seek() = 0; // TODO(jiesun): Do we need this?
-
- // Buffer exchange method for input and output stream.
- // These functions and callbacks could be used in two scenarios for both
- // input and output streams:
- // 1. Engine provide buffers.
- // 2. Outside party provide buffers.
- // The currently planned engine implementation:
- // 1. provides the input buffer request inside engine through
- // |EmptyThisBufferCallback|. The engine implementation has better knowledge
- // of the decoder reordering delay and jittery removal requirements. Input
- // buffers are returned into engine through |EmptyThisBuffer|.
- // 2. Output buffers are provided from outside the engine, and feed into
- // engine through |FillThisBuffer|. Output buffers are returned to outside
- // by |FillThisBufferCallback|.
- virtual void EmptyThisBuffer(scoped_refptr<Buffer> buffer) = 0;
- virtual void FillThisBuffer(scoped_refptr<VideoFrame> frame) = 0;
-};
-
-} // namespace media
-
-#endif // MEDIA_FILTERS_VIDEO_DECODE_ENGINE_H_
diff --git a/media/media.gyp b/media/media.gyp
index 15b4266..21f0dc1 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -126,19 +126,19 @@
'filters/ffmpeg_glue.h',
'filters/ffmpeg_interfaces.cc',
'filters/ffmpeg_interfaces.h',
- 'filters/ffmpeg_video_allocator.cc',
- 'filters/ffmpeg_video_allocator.h',
- 'filters/ffmpeg_video_decode_engine.cc',
- 'filters/ffmpeg_video_decode_engine.h',
'filters/ffmpeg_video_decoder.cc',
'filters/ffmpeg_video_decoder.h',
'filters/file_data_source.cc',
'filters/file_data_source.h',
'filters/null_audio_renderer.cc',
'filters/null_audio_renderer.h',
- 'filters/video_decode_engine.h',
'filters/video_renderer_base.cc',
'filters/video_renderer_base.h',
+ 'video/ffmpeg_video_allocator.cc',
+ 'video/ffmpeg_video_allocator.h',
+ 'video/ffmpeg_video_decode_engine.cc',
+ 'video/ffmpeg_video_decode_engine.h',
+ 'video/video_decode_engine.h',
],
'direct_dependent_settings': {
'include_dirs': [
@@ -170,8 +170,6 @@
}],
['OS=="linux" or OS=="freebsd" or OS=="openbsd"', {
'sources': [
- 'filters/omx_video_decode_engine.cc',
- 'filters/omx_video_decode_engine.h',
'filters/omx_video_decoder.cc',
'filters/omx_video_decoder.h',
],
@@ -272,12 +270,12 @@
'filters/decoder_base_unittest.cc',
'filters/ffmpeg_demuxer_unittest.cc',
'filters/ffmpeg_glue_unittest.cc',
- 'filters/ffmpeg_video_decode_engine_unittest.cc',
'filters/ffmpeg_video_decoder_unittest.cc',
'filters/file_data_source_unittest.cc',
'filters/video_renderer_base_unittest.cc',
'omx/mock_omx.cc',
'omx/mock_omx.h',
+ 'video/ffmpeg_video_decode_engine_unittest.cc',
],
'conditions': [
['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', {
@@ -569,10 +567,10 @@
'../third_party/ffmpeg/ffmpeg.gyp:ffmpeg',
],
'sources': [
- 'filters/omx_video_decode_engine.cc',
- 'filters/omx_video_decode_engine.cc',
'omx/omx_configurator.cc',
'omx/omx_configurator.h',
+ 'video/omx_video_decode_engine.cc',
+ 'video/omx_video_decode_engine.cc',
],
'hard_dependency': 1,
'export_dependent_settings': [