diff options
author | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-28 01:40:13 +0000 |
---|---|---|
committer | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-28 01:40:13 +0000 |
commit | 13f1e6af5ceb92cec9642f1deae8de7ae7904805 (patch) | |
tree | d7f7590168b790b024c3cc028e846d5bf5f66ec3 /media | |
parent | 343a748c7361bba67d2ede646b93afdeb7ae6b8e (diff) | |
download | chromium_src-13f1e6af5ceb92cec9642f1deae8de7ae7904805.zip chromium_src-13f1e6af5ceb92cec9642f1deae8de7ae7904805.tar.gz chromium_src-13f1e6af5ceb92cec9642f1deae8de7ae7904805.tar.bz2 |
Reland r143115: Provide a Chrome-owned buffer to FFmpeg for video decoding (round 3).
TEST=none
BUG=none
TBR=jam
Review URL: https://chromiumcodereview.appspot.com/10703018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144640 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/video_frame.cc | 70 | ||||
-rw-r--r-- | media/base/video_frame.h | 1 | ||||
-rw-r--r-- | media/ffmpeg/ffmpeg_common.h | 1 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.cc | 91 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.h | 9 |
5 files changed, 121 insertions, 51 deletions
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc index f8cdcf9..565290b 100644 --- a/media/base/video_frame.cc +++ b/media/base/video_frame.cc @@ -8,6 +8,11 @@ #include "base/string_piece.h" #include "media/base/limits.h" #include "media/base/video_util.h" +#if !defined(OS_ANDROID) +#include "media/ffmpeg/ffmpeg_common.h" +#endif + +#include <algorithm> namespace media { @@ -93,37 +98,54 @@ static inline size_t RoundUp(size_t value, size_t alignment) { return ((value + (alignment - 1)) & ~(alignment-1)); } +static const int kFrameSizeAlignment = 16; + void VideoFrame::AllocateRGB(size_t bytes_per_pixel) { - // Round up to align at a 64-bit (8 byte) boundary for each row. This - // is sufficient for MMX reads (movq). - size_t bytes_per_row = RoundUp(width_ * bytes_per_pixel, 8); + // Round up to align at least at a 16-byte boundary for each row. + // This is sufficient for MMX and SSE2 reads (movq/movdqa). + size_t bytes_per_row = RoundUp(width_, kFrameSizeAlignment) * bytes_per_pixel; + size_t aligned_height = RoundUp(height_, kFrameSizeAlignment); strides_[VideoFrame::kRGBPlane] = bytes_per_row; - data_[VideoFrame::kRGBPlane] = new uint8[bytes_per_row * height_]; +#if !defined(OS_ANDROID) + // TODO(dalecurtis): use DataAligned or so, so this #ifdef hackery + // doesn't need to be repeated in every single user of aligned data. + data_[VideoFrame::kRGBPlane] = reinterpret_cast<uint8*>( + av_malloc(bytes_per_row * aligned_height)); +#else + data_[VideoFrame::kRGBPlane] = new uint8_t[bytes_per_row * aligned_height]; +#endif DCHECK(!(reinterpret_cast<intptr_t>(data_[VideoFrame::kRGBPlane]) & 7)); COMPILE_ASSERT(0 == VideoFrame::kRGBPlane, RGB_data_must_be_index_0); } -static const int kFramePadBytes = 15; // Allows faster SIMD YUV convert. - void VideoFrame::AllocateYUV() { DCHECK(format_ == VideoFrame::YV12 || format_ == VideoFrame::YV16); - // Align Y rows at 32-bit (4 byte) boundaries. The stride for both YV12 and - // YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for U and V - // applies to two rows of Y (one byte of UV for 4 bytes of Y), so in the - // case of YV12 the strides are identical for the same width surface, but the - // number of bytes allocated for YV12 is 1/2 the amount for U & V as YV16. - // We also round the height of the surface allocated to be an even number - // to avoid any potential of faulting by code that attempts to access the Y - // values of the final row, but assumes that the last row of U & V applies to - // a full two rows of Y. - size_t y_height = rows(VideoFrame::kYPlane); - size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane), 4); - size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane), 4); - size_t uv_height = rows(VideoFrame::kUPlane); + // Align Y rows at least at 16 byte boundaries. The stride for both + // YV12 and YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for + // U and V applies to two rows of Y (one byte of UV for 4 bytes of Y), so in + // the case of YV12 the strides are identical for the same width surface, but + // the number of bytes allocated for YV12 is 1/2 the amount for U & V as + // YV16. We also round the height of the surface allocated to be an even + // number to avoid any potential of faulting by code that attempts to access + // the Y values of the final row, but assumes that the last row of U & V + // applies to a full two rows of Y. + size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane), + kFrameSizeAlignment); + size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane), + kFrameSizeAlignment); + size_t y_height = RoundUp(height_, kFrameSizeAlignment); + size_t uv_height = format_ == VideoFrame::YV12 ? y_height / 2 : y_height; size_t y_bytes = y_height * y_stride; size_t uv_bytes = uv_height * uv_stride; - uint8* data = new uint8[y_bytes + (uv_bytes * 2) + kFramePadBytes]; +#if !defined(OS_ANDROID) + // TODO(dalecurtis): use DataAligned or so, so this #ifdef hackery + // doesn't need to be repeated in every single user of aligned data. + uint8* data = reinterpret_cast<uint8*>( + av_malloc(y_bytes + (uv_bytes * 2))); +#else + uint8* data = new uint8_t[y_bytes + (uv_bytes * 2)]; +#endif COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0); data_[VideoFrame::kYPlane] = data; data_[VideoFrame::kUPlane] = data + y_bytes; @@ -158,7 +180,13 @@ VideoFrame::~VideoFrame() { // In multi-plane allocations, only a single block of memory is allocated // on the heap, and other |data| pointers point inside the same, single block // so just delete index 0. - delete[] data_[0]; + if (data_[0]) { +#if !defined(OS_ANDROID) + av_free(data_[0]); +#else + delete[] data_[0]; +#endif + } } bool VideoFrame::IsValidPlane(size_t plane) const { diff --git a/media/base/video_frame.h b/media/base/video_frame.h index 42b627a..55d4cd5 100644 --- a/media/base/video_frame.h +++ b/media/base/video_frame.h @@ -48,6 +48,7 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { // Call prior to CreateFrame to ensure validity of frame configuration. Called // automatically by VideoDecoderConfig::IsValidConfig(). + // TODO(scherkus): VideoDecoderConfig shouldn't call this method static bool IsValidConfig( Format format, size_t width, diff --git a/media/ffmpeg/ffmpeg_common.h b/media/ffmpeg/ffmpeg_common.h index 442299f..40a559d 100644 --- a/media/ffmpeg/ffmpeg_common.h +++ b/media/ffmpeg/ffmpeg_common.h @@ -28,6 +28,7 @@ MSVC_PUSH_DISABLE_WARNING(4244); #include <libavutil/avutil.h> #include <libavutil/mathematics.h> #include <libavutil/log.h> +#include <libavutil/imgutils.h> MSVC_POP_WARNING(); } // extern "C" diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc index 1902fd8..e09f957 100644 --- a/media/filters/ffmpeg_video_decoder.cc +++ b/media/filters/ffmpeg_video_decoder.cc @@ -63,6 +63,64 @@ FFmpegVideoDecoder::FFmpegVideoDecoder( decryptor_(NULL) { } +int FFmpegVideoDecoder::GetVideoBuffer(AVCodecContext* codec_context, + AVFrame* frame) { + // Don't use |codec_context_| here! With threaded decoding, + // it will contain unsynchronized width/height/pix_fmt values, + // whereas |codec_context| contains the current threads's + // updated width/height/pix_fmt, which can change for adaptive + // content. + VideoFrame::Format format = PixelFormatToVideoFormat(codec_context->pix_fmt); + if (format == VideoFrame::INVALID) + return AVERROR(EINVAL); + DCHECK(format == VideoFrame::YV12 || format == VideoFrame::YV16); + + int width = codec_context->width; + int height = codec_context->height; + int ret; + if ((ret = av_image_check_size(width, height, 0, NULL)) < 0) + return ret; + + scoped_refptr<VideoFrame> video_frame = + VideoFrame::CreateFrame(format, width, height, + kNoTimestamp(), kNoTimestamp()); + + for (int i = 0; i < 3; i++) { + frame->base[i] = video_frame->data(i); + frame->data[i] = video_frame->data(i); + frame->linesize[i] = video_frame->stride(i); + } + + frame->opaque = video_frame.release(); + frame->type = FF_BUFFER_TYPE_USER; + frame->pkt_pts = codec_context->pkt ? codec_context->pkt->pts : + AV_NOPTS_VALUE; + frame->width = codec_context->width; + frame->height = codec_context->height; + frame->format = codec_context->pix_fmt; + + return 0; +} + +static int GetVideoBufferImpl(AVCodecContext* s, AVFrame* frame) { + FFmpegVideoDecoder* vd = static_cast<FFmpegVideoDecoder*>(s->opaque); + return vd->GetVideoBuffer(s, frame); +} + +static void ReleaseVideoBufferImpl(AVCodecContext* s, AVFrame* frame) { + // We're releasing the reference to the buffer allocated in + // GetVideoBuffer() here, so the explicit Release() here is + // intentional. + scoped_refptr<VideoFrame> video_frame = + static_cast<VideoFrame*>(frame->opaque); + video_frame->Release(); + + // The FFmpeg API expects us to zero the data pointers in + // this callback + memset(frame->data, 0, sizeof(frame->data)); + frame->opaque = NULL; +} + void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, const PipelineStatusCB& status_cb, const StatisticsCB& statistics_cb) { @@ -109,6 +167,10 @@ void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; codec_context_->err_recognition = AV_EF_CAREFUL; codec_context_->thread_count = GetThreadCount(codec_context_->codec_id); + codec_context_->opaque = this; + codec_context_->flags |= CODEC_FLAG_EMU_EDGE; + codec_context_->get_buffer = GetVideoBufferImpl; + codec_context_->release_buffer = ReleaseVideoBufferImpl; AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); if (!codec) { @@ -370,12 +432,11 @@ bool FFmpegVideoDecoder::Decode( return false; } - // We've got a frame! Make sure we have a place to store it. - *video_frame = AllocateVideoFrame(); - if (!(*video_frame)) { - LOG(ERROR) << "Failed to allocate video frame"; + if (!av_frame_->opaque) { + LOG(ERROR) << "VideoFrame object associated with frame data not set."; return false; } + *video_frame = static_cast<VideoFrame*>(av_frame_->opaque); // Determine timestamp and calculate the duration based on the repeat picture // count. According to FFmpeg docs, the total duration can be calculated as @@ -395,19 +456,6 @@ bool FFmpegVideoDecoder::Decode( (*video_frame)->SetDuration( ConvertFromTimeBase(doubled_time_base, 2 + av_frame_->repeat_pict)); - // 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. - int y_rows = codec_context_->height; - int uv_rows = codec_context_->height; - if (codec_context_->pix_fmt == PIX_FMT_YUV420P) { - uv_rows /= 2; - } - - CopyYPlane(av_frame_->data[0], av_frame_->linesize[0], y_rows, *video_frame); - CopyUPlane(av_frame_->data[1], av_frame_->linesize[1], uv_rows, *video_frame); - CopyVPlane(av_frame_->data[2], av_frame_->linesize[2], uv_rows, *video_frame); - return true; } @@ -430,13 +478,4 @@ void FFmpegVideoDecoder::ReleaseFFmpegResources() { } } -scoped_refptr<VideoFrame> FFmpegVideoDecoder::AllocateVideoFrame() { - VideoFrame::Format format = PixelFormatToVideoFormat(codec_context_->pix_fmt); - size_t width = codec_context_->width; - size_t height = codec_context_->height; - - return VideoFrame::CreateFrame(format, width, height, - kNoTimestamp(), kNoTimestamp()); -} - } // namespace media diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h index f493792..3bfcf3f 100644 --- a/media/filters/ffmpeg_video_decoder.h +++ b/media/filters/ffmpeg_video_decoder.h @@ -38,6 +38,11 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { // encountered. void set_decryptor(Decryptor* decryptor); + // Callback called from within FFmpeg to allocate a buffer based on + // the dimensions of |codec_context|. See AVCodecContext.get_buffer + // documentation inside FFmpeg. + int GetVideoBuffer(AVCodecContext *codec_context, AVFrame* frame); + protected: virtual ~FFmpegVideoDecoder(); @@ -71,10 +76,6 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { // Reset decoder and call |reset_cb_|. void DoReset(); - // Allocates a video frame based on the current format and dimensions based on - // the current state of |codec_context_|. - scoped_refptr<VideoFrame> AllocateVideoFrame(); - // This is !is_null() iff Initialize() hasn't been called. base::Callback<MessageLoop*()> message_loop_factory_cb_; |