summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-02 23:55:44 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-02 23:55:44 +0000
commit720ac73783ea732c130ff6d11dffa41919a25809 (patch)
tree02c4777153a46f0ab78a2b0134d07f4d0d27558d /media
parentc24e5d031e408a81cd6a004353b4d3065d29046d (diff)
downloadchromium_src-720ac73783ea732c130ff6d11dffa41919a25809.zip
chromium_src-720ac73783ea732c130ff6d11dffa41919a25809.tar.gz
chromium_src-720ac73783ea732c130ff6d11dffa41919a25809.tar.bz2
FFmpeg video decoder glue code
Implementation of FFmpeg to perform video decoding. Also added hooks to insert this FFmpeg video decoder filter. Review URL: http://codereview.chromium.org/60069 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13056 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/buffers.h5
-rw-r--r--media/filters/ffmpeg_video_decoder.cc138
-rw-r--r--media/filters/ffmpeg_video_decoder.h47
3 files changed, 178 insertions, 12 deletions
diff --git a/media/base/buffers.h b/media/base/buffers.h
index d600289..527f9e6 100644
--- a/media/base/buffers.h
+++ b/media/base/buffers.h
@@ -157,8 +157,9 @@ struct VideoSurface {
size_t planes;
// Array of strides for each plane, typically greater or equal to the width
- // of the surface divided by the horizontal sampling period.
- size_t strides[kMaxPlanes];
+ // of the surface divided by the horizontal sampling period. Note that
+ // strides can be negative.
+ int32 strides[kMaxPlanes];
// Array of data pointers to each plane.
uint8* data[kMaxPlanes];
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index f429ea6..69111ff 100644
--- a/media/filters/ffmpeg_video_decoder.cc
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -2,32 +2,152 @@
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.
+#include "media/base/video_frame_impl.h"
+#include "media/filters/ffmpeg_common.h"
+#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"
namespace media {
+
FFmpegVideoDecoder::FFmpegVideoDecoder()
- : DecoderBase<VideoDecoder, VideoFrame>(NULL) {
- NOTIMPLEMENTED();
+ : DecoderBase<VideoDecoder, VideoFrame>(NULL),
+ width_(0),
+ height_(0) {
}
FFmpegVideoDecoder::~FFmpegVideoDecoder() {
- NOTIMPLEMENTED();
}
// static
bool FFmpegVideoDecoder::IsMediaFormatSupported(const MediaFormat& format) {
- NOTIMPLEMENTED();
- return false;
+ std::string mime_type;
+ return format.GetAsString(MediaFormat::kMimeType, &mime_type) &&
+ mime_type::kFFmpegVideo == mime_type;
}
bool FFmpegVideoDecoder::OnInitialize(DemuxerStream* demuxer_stream) {
- NOTIMPLEMENTED();
- return false;
+ scoped_refptr<FFmpegDemuxerStream> ffmpeg_demuxer_stream;
+ if (!demuxer_stream->QueryInterface(&ffmpeg_demuxer_stream)) {
+ return false;
+ }
+
+ AVStream* av_stream = ffmpeg_demuxer_stream->av_stream();
+
+ width_ = av_stream->codec->width;
+ height_ = av_stream->codec->height;
+
+ media_format_.SetAsString(MediaFormat::kMimeType,
+ mime_type::kUncompressedVideo);
+ media_format_.SetAsInteger(MediaFormat::kWidth, width_);
+ media_format_.SetAsInteger(MediaFormat::kHeight, height_);
+
+ codec_context_ = ffmpeg_demuxer_stream->av_stream()->codec;
+
+ return true;
+}
+
+void FFmpegVideoDecoder::OnDecode(Buffer* buffer) {
+ // Check for end of stream.
+ // TODO(scherkus): check for end of stream.
+
+ // Queue the incoming timestamp.
+ TimeTuple times;
+ times.timestamp = buffer->GetTimestamp();
+ times.duration = buffer->GetDuration();
+ time_queue_.push(times);
+
+ // Cast everything to FFmpeg types.
+ const uint8_t* data_in = buffer->GetData();
+ const size_t size_in = buffer->GetDataSize();
+
+ int decoded = 0;
+ AVFrame yuv_frame;
+ avcodec_get_frame_defaults(&yuv_frame);
+ int result = avcodec_decode_video(codec_context_, &yuv_frame, &decoded,
+ data_in, size_in);
+
+ if (result < 0) {
+ host_->Error(PIPELINE_ERROR_DECODE);
+ return;
+ }
+
+ if (result == 0 || decoded == 0) {
+ return;
+ }
+
+ VideoSurface::Format surface_format;
+ switch (codec_context_->pix_fmt) {
+ case PIX_FMT_YUV420P:
+ surface_format = VideoSurface::YV12;
+ break;
+ case PIX_FMT_YUV422P:
+ surface_format = VideoSurface::YV16;
+ break;
+ default:
+ // TODO(scherkus): More formats here?
+ NOTREACHED();
+ host_->Error(PIPELINE_ERROR_DECODE);
+ return;
+ }
+ if (!EnqueueVideoFrame(surface_format, yuv_frame)) {
+ host_->Error(PIPELINE_ERROR_DECODE);
+ }
+}
+
+bool FFmpegVideoDecoder::EnqueueVideoFrame(VideoSurface::Format surface_format,
+ const AVFrame& frame) {
+ // Dequeue the next time tuple and create a VideoFrame object with
+ // that timestamp and duration.
+ TimeTuple time = time_queue_.top();
+ time_queue_.pop();
+
+ scoped_refptr<VideoFrame> video_frame;
+ VideoFrameImpl::CreateFrame(surface_format, width_, height_,
+ time.timestamp, time.duration, &video_frame);
+ if (!video_frame) {
+ return false;
+ }
+ // 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.
+ // TODO(scherkus): figure out pre-allocation/buffer cycling scheme.
+ // TODO(scherkus): is there a cleaner way to figure out the # of planes?
+ VideoSurface surface;
+ if (!video_frame->Lock(&surface)) {
+ return false;
+ }
+ CopyPlane(VideoSurface::kYPlane, surface, frame);
+ CopyPlane(VideoSurface::kUPlane, surface, frame);
+ CopyPlane(VideoSurface::kVPlane, surface, frame);
+ video_frame->Unlock();
+ EnqueueResult(video_frame);
+ return true;
}
-void FFmpegVideoDecoder::OnDecode(Buffer* input) {
- NOTIMPLEMENTED();
+void FFmpegVideoDecoder::CopyPlane(size_t plane,
+ const VideoSurface& surface,
+ const AVFrame& frame) {
+ DCHECK(surface.width % 4 == 0);
+ DCHECK(surface.height % 2 == 0);
+ const uint8* source = frame.data[plane];
+ const size_t source_stride = frame.linesize[plane];
+ uint8* dest = surface.data[plane];
+ const size_t dest_stride = surface.strides[plane];
+ size_t bytes_per_line = surface.width;
+ size_t copy_lines = surface.height;
+ if (plane != VideoSurface::kYPlane) {
+ bytes_per_line /= 2;
+ if (surface.format == VideoSurface::YV12) {
+ copy_lines /= 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;
+ }
}
} // namespace
diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h
index 67941c6..1997cd6 100644
--- a/media/filters/ffmpeg_video_decoder.h
+++ b/media/filters/ffmpeg_video_decoder.h
@@ -6,9 +6,15 @@
#ifndef MEDIA_FILTERS_FFMPEG_VIDEO_DECODER_H_
#define MEDIA_FILTERS_FFMPEG_VIDEO_DECODER_H_
+#include <queue>
+
#include "media/base/factory.h"
#include "media/filters/decoder_base.h"
+// FFmpeg types.
+struct AVCodecContext;
+struct AVFrame;
+
namespace media {
//------------------------------------------------------------------------------
@@ -23,13 +29,52 @@ class FFmpegVideoDecoder : public DecoderBase<VideoDecoder, VideoFrame> {
virtual bool OnInitialize(DemuxerStream* demuxer_stream);
- virtual void OnDecode(Buffer* input);
+ virtual void OnDecode(Buffer* buffer);
private:
friend class FilterFactoryImpl0<FFmpegVideoDecoder>;
FFmpegVideoDecoder();
virtual ~FFmpegVideoDecoder();
+ bool EnqueueVideoFrame(VideoSurface::Format surface_format,
+ const AVFrame& frame);
+
+ void CopyPlane(size_t plane, const VideoSurface& surface,
+ const AVFrame& frame);
+
+ size_t width_;
+ size_t height_;
+
+ // FFmpeg outputs packets in decode timestamp (dts) order, which may not
+ // always be in presentation timestamp (pts) order. Therefore, when Process
+ // is called we cannot assume that the pts of the input |buffer| passed to the
+ // OnDecode() method is necessarily the pts of video frame. For example:
+ //
+ // Process() Timestamp Timestamp
+ // Call # Buffer In Buffer Out
+ // 1 1 1
+ // 2 3 --- <--- frame 3 buffered by FFmpeg
+ // 3 2 2
+ // 4 4 3 <--- copying timestamp 4 and 6 would be
+ // 5 6 4 <-' incorrect, which is why we sort and
+ // 6 5 5 queue incoming timestamps
+
+ // A queue entry that holds a timestamp and a duration.
+ struct TimeTuple {
+ base::TimeDelta timestamp;
+ base::TimeDelta duration;
+
+ bool operator<(const TimeTuple& other) const {
+ return timestamp >= other.timestamp;
+ }
+ };
+
+ // A priority queue of presentation TimeTuples.
+ typedef std::priority_queue<TimeTuple> TimeQueue;
+ TimeQueue time_queue_;
+
+ AVCodecContext* codec_context_;
+
DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoder);
};