diff options
author | tyoshino@chromium.org <tyoshino@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-11 02:34:54 +0000 |
---|---|---|
committer | tyoshino@chromium.org <tyoshino@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-11 02:34:54 +0000 |
commit | 4a46734e12d09c0fa38a2ce706441a25e5d016c0 (patch) | |
tree | 38534d75bd050fcfd8c14055974e76524b3c39d6 | |
parent | 1d124d66343f85bdb54e3cf3fc4ce44ffb2e7cf7 (diff) | |
download | chromium_src-4a46734e12d09c0fa38a2ce706441a25e5d016c0.zip chromium_src-4a46734e12d09c0fa38a2ce706441a25e5d016c0.tar.gz chromium_src-4a46734e12d09c0fa38a2ce706441a25e5d016c0.tar.bz2 |
Revert 77476 - Remove FFmpegVideoDecodeEngine's dependency on AVStream.
This change seems to have caused Linux Heapcheck bot redness.
http://build.chromium.org/p/chromium.memory/builders/Linux%20Heapcheck/builds/4846
First step of many towards removing DemuxerStream::QueryInterface, AVStreamProvider, and MediaFormat.
BUG=28206
TEST=media_unittests
Review URL: http://codereview.chromium.org/6624062
TBR=scherkus@chromium.org
Review URL: http://codereview.chromium.org/6665020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77770 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 226 insertions, 335 deletions
@@ -10,7 +10,7 @@ vars = { "nacl_revision": "4382", "libjingle_revision": "55", "libvpx_revision": "76510", - "ffmpeg_revision": "77311", + "ffmpeg_revision": "76547", "skia_revision": "900", "v8_revision": "7134", } diff --git a/chrome/gpu/gpu_video_decoder.cc b/chrome/gpu/gpu_video_decoder.cc index 96be0c3..ca72c40 100644 --- a/chrome/gpu/gpu_video_decoder.cc +++ b/chrome/gpu/gpu_video_decoder.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -86,8 +86,7 @@ void GpuVideoDecoder::OnInitializeComplete(const VideoCodecInfo& info) { } // TODO(jiesun): Check the assumption of input size < original size. - param.input_buffer_size = - info.stream_info.surface_width * info.stream_info.surface_height * 3 / 2; + param.input_buffer_size = config_.width * config_.height * 3 / 2; if (!CreateInputTransferBuffer(param.input_buffer_size, ¶m.input_buffer_handle)) { SendInitializeDone(param); @@ -250,6 +249,7 @@ GpuVideoDecoder::GpuVideoDecoder( sender_(sender), renderer_handle_(handle), gles2_decoder_(decoder) { + memset(&config_, 0, sizeof(config_)); memset(&info_, 0, sizeof(info_)); // TODO(jiesun): find a better way to determine which VideoDecodeEngine @@ -272,14 +272,11 @@ GpuVideoDecoder::~GpuVideoDecoder() {} void GpuVideoDecoder::OnInitialize(const GpuVideoDecoderInitParam& param) { // TODO(jiesun): codec id should come from |param|. - media::VideoCodecConfig config(media::kCodecH264, - param.width, - param.height, - param.frame_rate_num, - param.frame_rate_den, - NULL, - 0); - decode_engine_->Initialize(message_loop_, this, this, config); + config_.codec = media::kCodecH264; + config_.width = param.width; + config_.height = param.height; + config_.opaque_context = NULL; + decode_engine_->Initialize(message_loop_, this, this, config_); } void GpuVideoDecoder::OnUninitialize() { diff --git a/chrome/gpu/gpu_video_decoder.h b/chrome/gpu/gpu_video_decoder.h index 92bbb70..bda31c7 100644 --- a/chrome/gpu/gpu_video_decoder.h +++ b/chrome/gpu/gpu_video_decoder.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -201,6 +201,7 @@ class GpuVideoDecoder typedef std::map<int32, scoped_refptr<media::VideoFrame> > VideoFrameMap; VideoFrameMap video_frame_map_; + media::VideoCodecConfig config_; media::VideoCodecInfo info_; DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoder); diff --git a/chrome/gpu/media/fake_gl_video_decode_engine.cc b/chrome/gpu/media/fake_gl_video_decode_engine.cc index 2c68f11..0e6a696 100644 --- a/chrome/gpu/media/fake_gl_video_decode_engine.cc +++ b/chrome/gpu/media/fake_gl_video_decode_engine.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -25,8 +25,8 @@ void FakeGlVideoDecodeEngine::Initialize( const media::VideoCodecConfig& config) { handler_ = event_handler; context_ = context; - width_ = config.width(); - height_ = config.height(); + width_ = config.width; + height_ = config.height; // Create an internal VideoFrame that we can write to. This is going to be // uploaded through VideoDecodeContext. diff --git a/chrome/renderer/gpu_video_decoder_host.cc b/chrome/renderer/gpu_video_decoder_host.cc index 5afa69c..f031ab7 100644 --- a/chrome/renderer/gpu_video_decoder_host.cc +++ b/chrome/renderer/gpu_video_decoder_host.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -19,13 +19,12 @@ GpuVideoDecoderHost::GpuVideoDecoderHost(MessageRouter* router, message_loop_(NULL), event_handler_(NULL), context_(NULL), - width_(0), - height_(0), state_(kStateUninitialized), decoder_host_id_(decoder_host_id), decoder_id_(0), input_buffer_busy_(false), current_frame_id_(0) { + memset(&config_, 0, sizeof(config_)); } GpuVideoDecoderHost::~GpuVideoDecoderHost() {} @@ -66,21 +65,33 @@ bool GpuVideoDecoderHost::OnMessageReceived(const IPC::Message& msg) { void GpuVideoDecoderHost::Initialize( MessageLoop* message_loop, VideoDecodeEngine::EventHandler* event_handler, media::VideoDecodeContext* context, const media::VideoCodecConfig& config) { + if (MessageLoop::current() != message_loop) { + message_loop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &GpuVideoDecoderHost::Initialize, message_loop, + event_handler, context, config)); + return; + } + + // Initialization operations should be performed on the message loop assigned. + // Save the parameters and post a task to complete initialization. DCHECK_EQ(kStateUninitialized, state_); DCHECK(!message_loop_); message_loop_ = message_loop; event_handler_ = event_handler; context_ = context; - width_ = config.width(); - height_ = config.height(); + config_ = config; - if (MessageLoop::current() != message_loop) { - message_loop->PostTask( - FROM_HERE, - NewRunnableMethod(this, &GpuVideoDecoderHost::CreateVideoDecoder)); + // Add the route so we'll receive messages. + router_->AddRoute(decoder_host_id_, this); + + if (!ipc_sender_->Send( + new GpuChannelMsg_CreateVideoDecoder(context_route_id_, + decoder_host_id_))) { + LOG(ERROR) << "GpuChannelMsg_CreateVideoDecoder failed"; + event_handler_->OnError(); return; } - CreateVideoDecoder(); } void GpuVideoDecoderHost::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { @@ -179,29 +190,14 @@ void GpuVideoDecoderHost::Seek() { } } -void GpuVideoDecoderHost::CreateVideoDecoder() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - // Add the route so we'll receive messages. - router_->AddRoute(decoder_host_id_, this); - - if (!ipc_sender_->Send( - new GpuChannelMsg_CreateVideoDecoder(context_route_id_, - decoder_host_id_))) { - LOG(ERROR) << "GpuChannelMsg_CreateVideoDecoder failed"; - event_handler_->OnError(); - return; - } -} - void GpuVideoDecoderHost::OnCreateVideoDecoderDone(int32 decoder_id) { DCHECK_EQ(message_loop_, MessageLoop::current()); decoder_id_ = decoder_id; // TODO(hclam): Initialize |param| with the right values. GpuVideoDecoderInitParam param; - param.width = width_; - param.height = height_; + param.width = config_.width; + param.height = config_.height; if (!ipc_sender_->Send( new GpuVideoDecoderMsg_Initialize(decoder_id, param))) { @@ -229,8 +225,8 @@ void GpuVideoDecoderHost::OnInitializeDone( // TODO(hclam): Need to fill in more information. media::VideoCodecInfo info; info.success = success; - info.stream_info.surface_width = width_; - info.stream_info.surface_height = height_; + info.stream_info.surface_width = config_.width; + info.stream_info.surface_height = config_.height; event_handler_->OnInitializeComplete(info); } diff --git a/chrome/renderer/gpu_video_decoder_host.h b/chrome/renderer/gpu_video_decoder_host.h index 34b5903..b5c7178 100644 --- a/chrome/renderer/gpu_video_decoder_host.h +++ b/chrome/renderer/gpu_video_decoder_host.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -75,9 +75,6 @@ class GpuVideoDecoderHost : public media::VideoDecodeEngine, kStateFlushing, }; - // Takes care of sending IPC message to create a video decoder. - void CreateVideoDecoder(); - // Handlers for messages received from the GPU process. void OnCreateVideoDecoderDone(int32 decoder_id); void OnInitializeDone(const GpuVideoDecoderInitDoneParam& param); @@ -127,9 +124,8 @@ class GpuVideoDecoderHost : public media::VideoDecodeEngine, // A Context for allocating video frame textures. media::VideoDecodeContext* context_; - // Dimensions of the video. - int width_; - int height_; + // Hold information about GpuVideoDecoder configuration. + media::VideoCodecConfig config_; // Current state of video decoder. GpuVideoDecoderHostState state_; diff --git a/chrome/renderer/gpu_video_decoder_host_unittest.cc b/chrome/renderer/gpu_video_decoder_host_unittest.cc index 6186e48..7d23f1d 100644 --- a/chrome/renderer/gpu_video_decoder_host_unittest.cc +++ b/chrome/renderer/gpu_video_decoder_host_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -23,8 +23,6 @@ static const int kDecoderId = 51; static const int kVideoFrames = 3; static const int kWidth = 320; static const int kHeight = 240; -static const int kFrameRateNumerator = 25; -static const int kFrameRateDenominator = 1; static const int kTransportBufferSize = 1024; ACTION_P(SimulateAllocateVideoFrames, frames) { @@ -130,14 +128,10 @@ class GpuVideoDecoderHostTest : public testing::Test, .WillOnce(SendMessage(decoder_host_.get(), msg2)); EXPECT_CALL(*this, OnInitializeComplete(_)); - media::VideoCodecConfig config( - media::kCodecH264, - kWidth, - kHeight, - kFrameRateNumerator, - kFrameRateDenominator, - NULL, - 0); + media::VideoCodecConfig config; + config.codec = media::kCodecH264; + config.width = kWidth; + config.height = kHeight; decoder_host_->Initialize(&message_loop_, this, &context_, config); message_loop_.RunAllPending(); } diff --git a/chrome/renderer/media/ipc_video_decoder.cc b/chrome/renderer/media/ipc_video_decoder.cc index 9593cfb..eeaebf3 100644 --- a/chrome/renderer/media/ipc_video_decoder.cc +++ b/chrome/renderer/media/ipc_video_decoder.cc @@ -52,8 +52,8 @@ void IpcVideoDecoder::Initialize(media::DemuxerStream* demuxer_stream, AVStream* av_stream = av_stream_provider->GetAVStream(); - int width = av_stream->codec->coded_width; - int height = av_stream->codec->coded_height; + int width = av_stream->codec->width; + int height = av_stream->codec->height; if (width > media::Limits::kMaxDimension || height > media::Limits::kMaxDimension || (width * height) > media::Limits::kMaxCanvas) { @@ -71,18 +71,16 @@ void IpcVideoDecoder::Initialize(media::DemuxerStream* demuxer_stream, // Create a hardware video decoder handle. decode_engine_.reset(ggl::CreateVideoDecodeEngine(ggl_context_)); - media::VideoCodecConfig config( - media::CodecIDToVideoCodec(av_stream->codec->codec_id), - width, height, - av_stream->r_frame_rate.num, - av_stream->r_frame_rate.den, - av_stream->codec->extradata, - av_stream->codec->extradata_size); + // Initialize hardware decoder. + media::VideoCodecConfig param; + memset(¶m, 0, sizeof(param)); + param.width = width; + param.height = height; // VideoDecodeEngine will perform initialization on the message loop // given to it so it doesn't matter on which thread we are calling this. decode_engine_->Initialize(ChildProcess::current()->io_message_loop(), this, - decode_context_.get(), config); + decode_context_.get(), param); } const media::MediaFormat& IpcVideoDecoder::media_format() { diff --git a/media/base/mock_ffmpeg.cc b/media/base/mock_ffmpeg.cc index f1e28f9..ec19b1b 100644 --- a/media/base/mock_ffmpeg.cc +++ b/media/base/mock_ffmpeg.cc @@ -120,10 +120,6 @@ void avcodec_flush_buffers(AVCodecContext* avctx) { return MockFFmpeg::get()->AVCodecFlushBuffers(avctx); } -AVCodecContext* avcodec_alloc_context() { - return MockFFmpeg::get()->AVCodecAllocContext(); -} - AVFrame* avcodec_alloc_frame() { return MockFFmpeg::get()->AVCodecAllocFrame(); } diff --git a/media/base/mock_ffmpeg.h b/media/base/mock_ffmpeg.h index b004aa4..89402fe 100644 --- a/media/base/mock_ffmpeg.h +++ b/media/base/mock_ffmpeg.h @@ -28,7 +28,6 @@ class MockFFmpeg { MOCK_METHOD1(AVCodecClose, int(AVCodecContext* avctx)); MOCK_METHOD2(AVCodecThreadInit, int(AVCodecContext* avctx, int threads)); MOCK_METHOD1(AVCodecFlushBuffers, void(AVCodecContext* avctx)); - MOCK_METHOD0(AVCodecAllocContext, AVCodecContext*()); MOCK_METHOD0(AVCodecAllocFrame, AVFrame*()); MOCK_METHOD4(AVCodecDecodeVideo2, int(AVCodecContext* avctx, AVFrame* picture, diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc index dce96a5..244d2729 100644 --- a/media/ffmpeg/ffmpeg_common.cc +++ b/media/ffmpeg/ffmpeg_common.cc @@ -4,50 +4,8 @@ #include "media/ffmpeg/ffmpeg_common.h" -#include "base/logging.h" - namespace media { // TODO(scherkus): combine ffmpeg_common.h with ffmpeg_util.h -VideoCodec CodecIDToVideoCodec(CodecID codec_id) { - switch (codec_id) { - case CODEC_ID_VC1: - return kCodecVC1; - case CODEC_ID_H264: - return kCodecH264; - case CODEC_ID_THEORA: - return kCodecTheora; - case CODEC_ID_MPEG2VIDEO: - return kCodecMPEG2; - case CODEC_ID_MPEG4: - return kCodecMPEG4; - case CODEC_ID_VP8: - return kCodecVP8; - default: - NOTREACHED(); - } - return kUnknown; -} - -CodecID VideoCodecToCodecID(VideoCodec video_codec) { - switch (video_codec) { - case kCodecVC1: - return CODEC_ID_VC1; - case kCodecH264: - return CODEC_ID_H264; - case kCodecTheora: - return CODEC_ID_THEORA; - case kCodecMPEG2: - return CODEC_ID_MPEG2VIDEO; - case kCodecMPEG4: - return CODEC_ID_MPEG4; - case kCodecVP8: - return CODEC_ID_VP8; - default: - NOTREACHED(); - } - return CODEC_ID_NONE; -} - } // namespace media diff --git a/media/ffmpeg/ffmpeg_common.h b/media/ffmpeg/ffmpeg_common.h index 3b0d0ee..714a6a1 100644 --- a/media/ffmpeg/ffmpeg_common.h +++ b/media/ffmpeg/ffmpeg_common.h @@ -10,7 +10,6 @@ #include "base/compiler_specific.h" #include "base/singleton.h" -#include "media/video/video_decode_engine.h" // Include FFmpeg header files. extern "C" { @@ -49,9 +48,6 @@ class ScopedPtrAVFreePacket { } }; -VideoCodec CodecIDToVideoCodec(CodecID codec_id); -CodecID VideoCodecToCodecID(VideoCodec video_codec); - } // namespace media #endif // MEDIA_FFMPEG_FFMPEG_COMMON_H_ diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc index 313d55c..698423b 100644 --- a/media/filters/ffmpeg_video_decoder.cc +++ b/media/filters/ffmpeg_video_decoder.cc @@ -67,8 +67,8 @@ void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream, time_base_->den = av_stream->r_frame_rate.num; time_base_->num = av_stream->r_frame_rate.den; - int width = av_stream->codec->coded_width; - int height = av_stream->codec->coded_height; + int width = av_stream->codec->width; + int height = av_stream->codec->height; if (width > Limits::kMaxDimension || height > Limits::kMaxDimension || (width * height) > Limits::kMaxCanvas) { @@ -77,12 +77,26 @@ void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream, return; } - VideoCodecConfig config(CodecIDToVideoCodec(av_stream->codec->codec_id), - width, height, - av_stream->r_frame_rate.num, - av_stream->r_frame_rate.den, - av_stream->codec->extradata, - av_stream->codec->extradata_size); + VideoCodecConfig config; + switch (av_stream->codec->codec_id) { + case CODEC_ID_VC1: + config.codec = kCodecVC1; break; + case CODEC_ID_H264: + config.codec = kCodecH264; break; + case CODEC_ID_THEORA: + config.codec = kCodecTheora; break; + case CODEC_ID_MPEG2VIDEO: + config.codec = kCodecMPEG2; break; + case CODEC_ID_MPEG4: + config.codec = kCodecMPEG4; break; + case CODEC_ID_VP8: + config.codec = kCodecVP8; break; + default: + NOTREACHED(); + } + config.opaque_context = av_stream; + config.width = width; + config.height = height; state_ = kInitializing; decode_engine_->Initialize(message_loop_, this, NULL, config); } diff --git a/media/filters/omx_video_decoder.cc b/media/filters/omx_video_decoder.cc index 0dc25f9..c8962a3 100644 --- a/media/filters/omx_video_decoder.cc +++ b/media/filters/omx_video_decoder.cc @@ -62,8 +62,8 @@ void OmxVideoDecoder::Initialize(DemuxerStream* demuxer_stream, } AVStream* av_stream = av_stream_provider->GetAVStream(); - int width = av_stream->codec->coded_width; - int height = av_stream->codec->coded_height; + int width = av_stream->codec->width; + int height = av_stream->codec->height; if (width > Limits::kMaxDimension || height > Limits::kMaxDimension || (width * height) > Limits::kMaxCanvas) { @@ -72,12 +72,24 @@ void OmxVideoDecoder::Initialize(DemuxerStream* demuxer_stream, return; } - VideoCodecConfig config(CodecIDToVideoCodec(av_stream->codec->codec_id), - width, height, - av_stream->r_frame_rate.num, - av_stream->r_frame_rate.den, - av_stream->codec->extradata, - av_stream->codec->extradata_size); + VideoCodecConfig config; + switch (av_stream->codec->codec_id) { + case CODEC_ID_VC1: + config.codec = kCodecVC1; break; + case CODEC_ID_H264: + config.codec = kCodecH264; break; + case CODEC_ID_THEORA: + config.codec = kCodecTheora; break; + case CODEC_ID_MPEG2VIDEO: + config.codec = kCodecMPEG2; break; + case CODEC_ID_MPEG4: + config.codec = kCodecMPEG4; break; + default: + NOTREACHED(); + } + config.opaque_context = NULL; + config.width = width; + config.height = height; decode_engine_->Initialize(message_loop_, this, NULL, config); } diff --git a/media/tools/omx_test/omx_test.cc b/media/tools/omx_test/omx_test.cc index 2821f08..24cf4cc 100644 --- a/media/tools/omx_test/omx_test.cc +++ b/media/tools/omx_test/omx_test.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -170,15 +170,26 @@ class TestApp : public base::RefCountedThreadSafe<TestApp>, void Run() { StartProfiler(); - media::VideoCodecConfig config( - media::CodecIDToVideoCodec(av_stream_->codec->codec_id), - av_stream_->codec->coded_width, - av_stream_->codec->coded_height, - av_stream_->r_frame_rate.num, - av_stream_->r_frame_rate.den, - av_stream_->codec->extradata, - av_stream_->codec->extradata_size); - + // Setup the |engine_| with the message loop of the current thread. Also + // setup codec format and callbacks. + media::VideoCodecConfig config; + switch (av_stream_->codec->codec_id) { + case CODEC_ID_VC1: + config.codec = media::kCodecVC1; break; + case CODEC_ID_H264: + config.codec = media::kCodecH264; break; + case CODEC_ID_THEORA: + config.codec = media::kCodecTheora; break; + case CODEC_ID_MPEG2VIDEO: + config.codec = media::kCodecMPEG2; break; + case CODEC_ID_MPEG4: + config.codec = media::kCodecMPEG4; break; + default: + NOTREACHED(); break; + } + config.opaque_context = NULL; + config.width = av_stream_->codec->width; + config.height = av_stream_->codec->height; engine_.reset(new OmxVideoDecodeEngine()); engine_->Initialize(&message_loop_, this, NULL, config); diff --git a/media/video/ffmpeg_video_decode_engine.cc b/media/video/ffmpeg_video_decode_engine.cc index ce6b8f5..786be41 100644 --- a/media/video/ffmpeg_video_decode_engine.cc +++ b/media/video/ffmpeg_video_decode_engine.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -21,9 +21,8 @@ namespace media { FFmpegVideoDecodeEngine::FFmpegVideoDecodeEngine() : codec_context_(NULL), + av_stream_(NULL), event_handler_(NULL), - frame_rate_numerator_(0), - frame_rate_denominator_(0), direct_rendering_(false), pending_input_buffers_(0), pending_output_buffers_(0), @@ -32,10 +31,6 @@ FFmpegVideoDecodeEngine::FFmpegVideoDecodeEngine() } FFmpegVideoDecodeEngine::~FFmpegVideoDecodeEngine() { - if (codec_context_) { - av_free(codec_context_->extradata); - av_free(codec_context_); - } } void FFmpegVideoDecodeEngine::Initialize( @@ -57,27 +52,8 @@ void FFmpegVideoDecodeEngine::Initialize( static const int kDecodeThreads = 2; static const int kMaxDecodeThreads = 16; - // Initialize AVCodecContext structure. - codec_context_ = avcodec_alloc_context(); - - // TODO(scherkus): should video format get passed in via VideoCodecConfig? - codec_context_->pix_fmt = PIX_FMT_YUV420P; - codec_context_->codec_type = AVMEDIA_TYPE_VIDEO; - codec_context_->codec_id = VideoCodecToCodecID(config.codec()); - codec_context_->coded_width = config.width(); - codec_context_->coded_height = config.height(); - - frame_rate_numerator_ = config.frame_rate_numerator(); - frame_rate_denominator_ = config.frame_rate_denominator(); - - if (config.extra_data() != NULL) { - codec_context_->extradata_size = config.extra_data_size(); - codec_context_->extradata = - reinterpret_cast<uint8_t*>(av_malloc(config.extra_data_size())); - memcpy(codec_context_->extradata, config.extra_data(), - config.extra_data_size()); - } - + 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; @@ -117,8 +93,8 @@ void FFmpegVideoDecodeEngine::Initialize( 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(); + 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; @@ -128,8 +104,8 @@ void FFmpegVideoDecodeEngine::Initialize( for (size_t i = 0; i < Limits::kMaxVideoFrames; ++i) { scoped_refptr<VideoFrame> video_frame; VideoFrame::CreateFrame(VideoFrame::YV12, - config.width(), - config.height(), + config.width, + config.height, kNoTimestamp, kNoTimestamp, &video_frame); @@ -282,15 +258,18 @@ void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) { // Determine timestamp and calculate the duration based on the repeat picture // count. According to FFmpeg docs, the total duration can be calculated as // follows: - // fps = 1 / time_base - // // duration = (1 / fps) + (repeat_pict) / (2 * fps) // = (2 + repeat_pict) / (2 * fps) - // = (2 + repeat_pict) / (2 * (1 / time_base)) 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.num = frame_rate_denominator_; - doubled_time_base.den = frame_rate_numerator_ * 2; + 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); @@ -362,6 +341,14 @@ void FFmpegVideoDecodeEngine::Seek() { event_handler_->OnSeekComplete(); } +AVCodecContext* FFmpegVideoDecodeEngine::codec_context() const { + return codec_context_; +} + +void FFmpegVideoDecodeEngine::SetCodecContextForTest(AVCodecContext* context) { + codec_context_ = context; +} + void FFmpegVideoDecodeEngine::ReadInput() { DCHECK_EQ(output_eos_reached_, false); pending_input_buffers_++; diff --git a/media/video/ffmpeg_video_decode_engine.h b/media/video/ffmpeg_video_decode_engine.h index b9e76b7..53baa54 100644 --- a/media/video/ffmpeg_video_decode_engine.h +++ b/media/video/ffmpeg_video_decode_engine.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -14,6 +14,7 @@ // FFmpeg types. struct AVCodecContext; struct AVFrame; +struct AVStream; namespace media { @@ -35,21 +36,21 @@ class FFmpegVideoDecodeEngine : public VideoDecodeEngine { virtual void Flush(); virtual void Seek(); - VideoFrame::Format GetSurfaceFormat() const; + virtual AVCodecContext* codec_context() const; + + virtual void SetCodecContextForTest(AVCodecContext* 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_; - // Frame rate of the video. - int frame_rate_numerator_; - int frame_rate_denominator_; - // Whether direct rendering is used. bool direct_rendering_; diff --git a/media/video/ffmpeg_video_decode_engine_unittest.cc b/media/video/ffmpeg_video_decode_engine_unittest.cc index a4e3123..e3bea41 100644 --- a/media/video/ffmpeg_video_decode_engine_unittest.cc +++ b/media/video/ffmpeg_video_decode_engine_unittest.cc @@ -23,7 +23,7 @@ namespace media { static const int kWidth = 320; static const int kHeight = 240; -static const AVRational kFrameRate = { 100, 1 }; +static const AVRational kTimeBase = { 1, 100 }; static void InitializeFrame(uint8_t* data, int width, AVFrame* frame) { frame->data[0] = data; @@ -35,7 +35,7 @@ static void InitializeFrame(uint8_t* data, int width, AVFrame* frame) { } ACTION_P(DecodeComplete, decoder) { - decoder->set_video_frame(arg0); + decoder->video_frame_ = arg0; } ACTION_P2(DemuxComplete, engine, buffer) { @@ -43,27 +43,33 @@ ACTION_P2(DemuxComplete, engine, buffer) { } ACTION_P(SaveInitializeResult, engine) { - engine->set_video_codec_info(arg0); + engine->info_ = arg0; } class FFmpegVideoDecodeEngineTest : public testing::Test, public VideoDecodeEngine::EventHandler { - public: - FFmpegVideoDecodeEngineTest() - : config_(kCodecH264, kWidth, kHeight, - kFrameRate.num, kFrameRate.den, NULL, 0) { - + protected: + FFmpegVideoDecodeEngineTest() { // Setup FFmpeg structures. frame_buffer_.reset(new uint8[kWidth * kHeight]); memset(&yuv_frame_, 0, sizeof(yuv_frame_)); InitializeFrame(frame_buffer_.get(), kWidth, &yuv_frame_); 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); test_engine_.reset(new FFmpegVideoDecodeEngine()); + test_engine_->SetCodecContextForTest(&codec_context_); VideoFrame::CreateFrame(VideoFrame::YV12, kWidth, @@ -78,9 +84,7 @@ class FFmpegVideoDecodeEngineTest : public testing::Test, } void Initialize() { - EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) - .WillOnce(Return(&codec_context_)); - EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) + EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_NONE)) .WillOnce(Return(&codec_)); EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) .WillOnce(Return(&yuv_frame_)); @@ -90,9 +94,11 @@ class FFmpegVideoDecodeEngineTest : public testing::Test, .WillOnce(Return(0)); EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) .Times(1); - EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) - .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, NULL, config_); @@ -120,7 +126,7 @@ class FFmpegVideoDecodeEngineTest : public testing::Test, codec_context_.height = height; } - // VideoDecodeEngine::EventHandler implementation. + public: MOCK_METHOD2(ConsumeVideoFrame, void(scoped_refptr<VideoFrame> video_frame, const PipelineStatistics& statistics)); @@ -134,30 +140,20 @@ class FFmpegVideoDecodeEngineTest : public testing::Test, MOCK_METHOD0(OnError, void()); MOCK_METHOD1(OnFormatChange, void(VideoStreamInfo stream_info)); - // Used by gmock actions. - void set_video_frame(scoped_refptr<VideoFrame> video_frame) { - video_frame_ = video_frame; - } - - void set_video_codec_info(const VideoCodecInfo& info) { - info_ = info; - } - - protected: + scoped_refptr<VideoFrame> video_frame_; VideoCodecConfig config_; VideoCodecInfo info_; - scoped_refptr<VideoFrame> video_frame_; + protected: scoped_ptr<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_; - private: - DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecodeEngineTest); }; TEST_F(FFmpegVideoDecodeEngineTest, Initialize_Normal) { @@ -166,17 +162,17 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_Normal) { TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) { // Test avcodec_find_decoder() returning NULL. - EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) - .WillOnce(Return(&codec_context_)); - EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) + EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_NONE)) .WillOnce(ReturnNull()); EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) .WillOnce(Return(&yuv_frame_)); EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) .Times(1); - EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) - .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, NULL, config_); @@ -186,9 +182,7 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) { // Note There are 2 threads for FFmpeg-mt. TEST_F(FFmpegVideoDecodeEngineTest, Initialize_InitThreadFails) { // Test avcodec_thread_init() failing. - EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) - .WillOnce(Return(&codec_context_)); - EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) + EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_NONE)) .WillOnce(Return(&codec_)); EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) .WillOnce(Return(&yuv_frame_)); @@ -196,9 +190,11 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_InitThreadFails) { .WillOnce(Return(-1)); EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) .Times(1); - EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) - .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, NULL, config_); @@ -207,9 +203,7 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_InitThreadFails) { TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) { // Test avcodec_open() failing. - EXPECT_CALL(mock_ffmpeg_, AVCodecAllocContext()) - .WillOnce(Return(&codec_context_)); - EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_H264)) + EXPECT_CALL(mock_ffmpeg_, AVCodecFindDecoder(CODEC_ID_NONE)) .WillOnce(Return(&codec_)); EXPECT_CALL(mock_ffmpeg_, AVCodecAllocFrame()) .WillOnce(Return(&yuv_frame_)); @@ -219,9 +213,11 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_OpenDecoderFails) { .WillOnce(Return(-1)); EXPECT_CALL(mock_ffmpeg_, AVFree(&yuv_frame_)) .Times(1); - EXPECT_CALL(mock_ffmpeg_, AVFree(&codec_context_)) - .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, NULL, config_); @@ -314,8 +310,6 @@ TEST_F(FFmpegVideoDecodeEngineTest, DecodeFrame_SmallerHeight) { } TEST_F(FFmpegVideoDecodeEngineTest, GetSurfaceFormat) { - Initialize(); - // YV12 formats. codec_context_.pix_fmt = PIX_FMT_YUV420P; EXPECT_EQ(VideoFrame::YV12, test_engine_->GetSurfaceFormat()); diff --git a/media/video/mft_h264_decode_engine.cc b/media/video/mft_h264_decode_engine.cc index 4acfc7f..bb3143c 100644 --- a/media/video/mft_h264_decode_engine.cc +++ b/media/video/mft_h264_decode_engine.cc @@ -151,12 +151,11 @@ const GUID ConvertVideoFrameFormatToGuid(VideoFrame::Format format) { MftH264DecodeEngine::MftH264DecodeEngine(bool use_dxva) : use_dxva_(use_dxva), state_(kUninitialized), - width_(0), - height_(0), event_handler_(NULL), context_(NULL) { memset(&input_stream_info_, 0, sizeof(input_stream_info_)); memset(&output_stream_info_, 0, sizeof(output_stream_info_)); + memset(&config_, 0, sizeof(config_)); memset(&info_, 0, sizeof(info_)); } @@ -178,6 +177,7 @@ void MftH264DecodeEngine::Initialize( return; } context_ = context; + config_ = config; event_handler_ = event_handler; info_.provides_buffers = true; @@ -473,8 +473,8 @@ bool MftH264DecodeEngine::SetDecodeEngineOutputMediaType(const GUID subtype) { hr = MFGetAttributeSize(out_media_type, MF_MT_FRAME_SIZE, reinterpret_cast<UINT32*>(&info_.stream_info.surface_width), reinterpret_cast<UINT32*>(&info_.stream_info.surface_height)); - width_ = info_.stream_info.surface_width; - height_ = info_.stream_info.surface_height; + config_.width = info_.stream_info.surface_width; + config_.height = info_.stream_info.surface_height; if (FAILED(hr)) { LOG(ERROR) << "Failed to SetOutputType to |subtype| or obtain " << "width/height " << std::hex << hr; diff --git a/media/video/mft_h264_decode_engine.h b/media/video/mft_h264_decode_engine.h index 9ce7935..d543631 100644 --- a/media/video/mft_h264_decode_engine.h +++ b/media/video/mft_h264_decode_engine.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. // @@ -88,10 +88,8 @@ class MftH264DecodeEngine : public media::VideoDecodeEngine { State state_; - int width_; - int height_; - VideoDecodeEngine::EventHandler* event_handler_; + VideoCodecConfig config_; VideoCodecInfo info_; VideoDecodeContext* context_; diff --git a/media/video/omx_video_decode_engine.cc b/media/video/omx_video_decode_engine.cc index a6996d3..df3f9cf 100644 --- a/media/video/omx_video_decode_engine.cc +++ b/media/video/omx_video_decode_engine.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -90,8 +90,8 @@ void OmxVideoDecodeEngine::Initialize( message_loop_ = message_loop; event_handler_ = event_handler; - width_ = config.width(); - height_ = config.height(); + width_ = config.width; + height_ = config.height; // TODO(wjia): Find the right way to determine the codec type. OmxConfigurator::MediaFormat input_format, output_format; @@ -115,8 +115,8 @@ void OmxVideoDecodeEngine::Initialize( uses_egl_image_ ? VideoFrame::TYPE_GL_TEXTURE : VideoFrame::TYPE_SYSTEM_MEMORY; info.stream_info.surface_format = GetSurfaceFormat(); - info.stream_info.surface_width = config.width(); - info.stream_info.surface_height = config.height(); + info.stream_info.surface_width = config.width; + info.stream_info.surface_height = config.height; event_handler_->OnInitializeComplete(info); } diff --git a/media/video/omx_video_decode_engine.h b/media/video/omx_video_decode_engine.h index 1609548..9a6c0a7 100644 --- a/media/video/omx_video_decode_engine.h +++ b/media/video/omx_video_decode_engine.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -175,8 +175,8 @@ class OmxVideoDecodeEngine : public VideoDecodeEngine { scoped_refptr<VideoFrame> CreateOmxBufferVideoFrame( OMX_BUFFERHEADERTYPE* omx_buffer); - int width_; - int height_; + size_t width_; + size_t height_; MessageLoop* message_loop_; diff --git a/media/video/video_decode_engine.cc b/media/video/video_decode_engine.cc index ca0093f..04f3bea 100644 --- a/media/video/video_decode_engine.cc +++ b/media/video/video_decode_engine.cc @@ -1,61 +1,18 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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/video_decode_engine.h" -#include "base/logging.h" - namespace media { -VideoCodecConfig::VideoCodecConfig(VideoCodec codec, - int width, - int height, - int frame_rate_numerator, - int frame_rate_denominator, - uint8* extra_data, - size_t extra_data_size) - : codec_(codec), - width_(width), - height_(height), - frame_rate_numerator_(frame_rate_numerator), - frame_rate_denominator_(frame_rate_denominator), - extra_data_size_(extra_data_size) { - CHECK(extra_data_size_ == 0 || extra_data); - if (extra_data_size_ > 0) { - extra_data_.reset(new uint8[extra_data_size_]); - memcpy(extra_data_.get(), extra_data, extra_data_size_); - } -} - -VideoCodecConfig::~VideoCodecConfig() {} - -VideoCodec VideoCodecConfig::codec() const { - return codec_; -} - -int VideoCodecConfig::width() const { - return width_; -} - -int VideoCodecConfig::height() const { - return height_; -} - -int VideoCodecConfig::frame_rate_numerator() const { - return frame_rate_numerator_; -} - -int VideoCodecConfig::frame_rate_denominator() const { - return frame_rate_denominator_; -} - -uint8* VideoCodecConfig::extra_data() const { - return extra_data_.get(); -} - -size_t VideoCodecConfig::extra_data_size() const { - return extra_data_size_; +VideoCodecConfig::VideoCodecConfig() + : codec(kCodecH264), + profile(kProfileDoNotCare), + level(kLevelDoNotCare), + width(0), + height(0), + opaque_context(NULL) { } } // namespace media diff --git a/media/video/video_decode_engine.h b/media/video/video_decode_engine.h index 5893b3a..57b76a2 100644 --- a/media/video/video_decode_engine.h +++ b/media/video/video_decode_engine.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -6,7 +6,6 @@ #define MEDIA_VIDEO_VIDEO_DECODE_ENGINE_H_ #include "base/callback.h" -#include "base/scoped_ptr.h" #include "media/base/video_frame.h" class MessageLoop; @@ -19,7 +18,6 @@ class VideoDecodeContext; struct PipelineStatistics; enum VideoCodec { - kUnknown, kCodecH264, kCodecVC1, kCodecMPEG2, @@ -28,38 +26,26 @@ enum VideoCodec { kCodecVP8, }; -class VideoCodecConfig { - public: - VideoCodecConfig(VideoCodec codec, int width, int height, - int frame_rate_numerator, int frame_rate_denominator, - uint8* extra_data, size_t extra_data_size); - ~VideoCodecConfig(); - - VideoCodec codec() const; - int width() const; - int height() const; - int frame_rate_numerator() const; - int frame_rate_denominator() const; - uint8* extra_data() const; - size_t extra_data_size() const; - - private: - VideoCodec codec_; +static const uint32 kProfileDoNotCare = static_cast<uint32>(-1); +static const uint32 kLevelDoNotCare = static_cast<uint32>(-1); - // Container's concept of width and height of this video. - int width_; - int height_; +struct VideoCodecConfig { + VideoCodecConfig(); - // Frame rate in seconds expressed as a fraction. - // TODO(scherkus): fairly certain decoders don't require frame rates. - int frame_rate_numerator_; - int frame_rate_denominator_; + VideoCodec codec; - // Optional byte data requied to initialize video decoders. - scoped_array<uint8> extra_data_; - size_t extra_data_size_; + // 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? - DISALLOW_COPY_AND_ASSIGN(VideoCodecConfig); + // FFMPEG's will use this to pass AVStream. Otherwise, we should remove this. + void* opaque_context; }; struct VideoStreamInfo { |