summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-28 01:40:13 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-28 01:40:13 +0000
commit13f1e6af5ceb92cec9642f1deae8de7ae7904805 (patch)
treed7f7590168b790b024c3cc028e846d5bf5f66ec3
parent343a748c7361bba67d2ede646b93afdeb7ae6b8e (diff)
downloadchromium_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
-rw-r--r--chrome/test/base/chrome_test_suite.cc7
-rw-r--r--content/test/content_test_suite_base.cc2
-rw-r--r--media/base/video_frame.cc70
-rw-r--r--media/base/video_frame.h1
-rw-r--r--media/ffmpeg/ffmpeg_common.h1
-rw-r--r--media/filters/ffmpeg_video_decoder.cc91
-rw-r--r--media/filters/ffmpeg_video_decoder.h9
7 files changed, 127 insertions, 54 deletions
diff --git a/chrome/test/base/chrome_test_suite.cc b/chrome/test/base/chrome_test_suite.cc
index d66eec5..0f75785 100644
--- a/chrome/test/base/chrome_test_suite.cc
+++ b/chrome/test/base/chrome_test_suite.cc
@@ -165,15 +165,16 @@ void ChromeTestSuite::Initialize() {
chrome_browser_application_mac::RegisterBrowserCrApp();
#endif
- content::ContentTestSuiteBase::Initialize();
-
chrome::RegisterPathProvider();
-
if (!browser_dir_.empty()) {
PathService::Override(base::DIR_EXE, browser_dir_);
PathService::Override(base::DIR_MODULE, browser_dir_);
}
+ // Initialize after overriding paths as some content paths depend on correct
+ // values for DIR_EXE and DIR_MODULE.
+ content::ContentTestSuiteBase::Initialize();
+
#if defined(OS_MACOSX)
// Look in the framework bundle for resources.
FilePath path;
diff --git a/content/test/content_test_suite_base.cc b/content/test/content_test_suite_base.cc
index adf2079..3274f1b 100644
--- a/content/test/content_test_suite_base.cc
+++ b/content/test/content_test_suite_base.cc
@@ -11,6 +11,7 @@
#include "content/common/url_schemes.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_paths.h"
+#include "media/base/media.h"
#include "ui/base/ui_base_paths.h"
#include "ui/compositor/compositor_setup.h"
@@ -22,6 +23,7 @@ ContentTestSuiteBase::ContentTestSuiteBase(int argc, char** argv)
void ContentTestSuiteBase::Initialize() {
base::TestSuite::Initialize();
+ media::InitializeMediaLibraryForTesting();
scoped_ptr<ContentClient> client_for_init(CreateClientForInitialization());
SetContentClient(client_for_init.get());
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_;