diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-02 23:55:44 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-02 23:55:44 +0000 |
commit | 720ac73783ea732c130ff6d11dffa41919a25809 (patch) | |
tree | 02c4777153a46f0ab78a2b0134d07f4d0d27558d /media | |
parent | c24e5d031e408a81cd6a004353b4d3065d29046d (diff) | |
download | chromium_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.h | 5 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.cc | 138 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.h | 47 |
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); }; |