diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-02 23:45:48 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-02 23:45:48 +0000 |
commit | 175e676451c214e4082d7304c37113aedf69a1f8 (patch) | |
tree | 85ce8cc71e728b6183949cd144ccbff220e3618b /media/video | |
parent | ef0a1d9f2e665e5e33a2566caec6e588f2e3fc4a (diff) | |
download | chromium_src-175e676451c214e4082d7304c37113aedf69a1f8.zip chromium_src-175e676451c214e4082d7304c37113aedf69a1f8.tar.gz chromium_src-175e676451c214e4082d7304c37113aedf69a1f8.tar.bz2 |
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
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58428 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/video')
-rw-r--r-- | media/video/ffmpeg_video_allocator.cc | 179 | ||||
-rw-r--r-- | media/video/ffmpeg_video_allocator.h | 94 | ||||
-rw-r--r-- | media/video/ffmpeg_video_decode_engine.cc | 359 | ||||
-rw-r--r-- | media/video/ffmpeg_video_decode_engine.h | 85 | ||||
-rw-r--r-- | media/video/ffmpeg_video_decode_engine_unittest.cc | 292 | ||||
-rw-r--r-- | media/video/omx_video_decode_engine.cc | 1339 | ||||
-rw-r--r-- | media/video/omx_video_decode_engine.h | 246 | ||||
-rw-r--r-- | media/video/video_decode_engine.h | 128 |
8 files changed, 0 insertions, 2722 deletions
diff --git a/media/video/ffmpeg_video_allocator.cc b/media/video/ffmpeg_video_allocator.cc deleted file mode 100644 index e9530b35..0000000 --- a/media/video/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/video/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/video/ffmpeg_video_allocator.h b/media/video/ffmpeg_video_allocator.h deleted file mode 100644 index 92b84d6..0000000 --- a/media/video/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_VIDEO_FFMPEG_VIDEO_ALLOCATOR_H_ -#define MEDIA_VIDEO_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_VIDEO_FFMPEG_VIDEO_ALLOCATOR_H_ diff --git a/media/video/ffmpeg_video_decode_engine.cc b/media/video/ffmpeg_video_decode_engine.cc deleted file mode 100644 index e71cb6f..0000000 --- a/media/video/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/video/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/video/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/video/ffmpeg_video_decode_engine.h b/media/video/ffmpeg_video_decode_engine.h deleted file mode 100644 index 3e030db..0000000 --- a/media/video/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_VIDEO_FFMPEG_VIDEO_DECODE_ENGINE_H_ -#define MEDIA_VIDEO_FFMPEG_VIDEO_DECODE_ENGINE_H_ - -#include <deque> - -#include "base/scoped_ptr.h" -#include "media/ffmpeg/ffmpeg_common.h" -#include "media/video/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_VIDEO_FFMPEG_VIDEO_DECODE_ENGINE_H_ diff --git a/media/video/ffmpeg_video_decode_engine_unittest.cc b/media/video/ffmpeg_video_decode_engine_unittest.cc deleted file mode 100644 index 73ac917..0000000 --- a/media/video/ffmpeg_video_decode_engine_unittest.cc +++ /dev/null @@ -1,292 +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/video/ffmpeg_video_decode_engine.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/video/omx_video_decode_engine.cc b/media/video/omx_video_decode_engine.cc deleted file mode 100644 index b6d7fea..0000000 --- a/media/video/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/video/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/video/omx_video_decode_engine.h b/media/video/omx_video_decode_engine.h deleted file mode 100644 index 9e8bcf3..0000000 --- a/media/video/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_VIDEO_OMX_VIDEO_DECODE_ENGINE_H_ -#define MEDIA_VIDEO_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/omx/omx_configurator.h" -#include "media/video/video_decode_engine.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_VIDEO_OMX_VIDEO_DECODE_ENGINE_H_ diff --git a/media/video/video_decode_engine.h b/media/video/video_decode_engine.h deleted file mode 100644 index d2cf7b2..0000000 --- a/media/video/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_VIDEO_VIDEO_DECODE_ENGINE_H_ -#define MEDIA_VIDEO_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_VIDEO_VIDEO_DECODE_ENGINE_H_ |