summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorwjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-07 16:45:55 +0000
committerwjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-07 16:45:55 +0000
commit4990cf2cb2302f7313564348e36fd0ffebdfaf07 (patch)
treeff2156fe4369150df4e5c86ae038daf3ab964ad2 /content
parentb1bf6fb646bbca873ba3586ce8a7bacce4d1dfbc (diff)
downloadchromium_src-4990cf2cb2302f7313564348e36fd0ffebdfaf07.zip
chromium_src-4990cf2cb2302f7313564348e36fd0ffebdfaf07.tar.gz
chromium_src-4990cf2cb2302f7313564348e36fd0ffebdfaf07.tar.bz2
The RTCVideoDecoder will eventially need to depend on third_party lib (webrtc, libjinle etc), which is not allowed in media. So move rtc_video_decoder* from media/filter/ to content/renderer/media/.
patch by ronghuawu@google.com BUG=none TEST=unit_tests, media_unittests Review URL: http://codereview.chromium.org/7193001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@91721 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/content_renderer.gypi3
-rw-r--r--content/renderer/media/external_renderer.h21
-rw-r--r--content/renderer/media/rtc_video_decoder.cc276
-rw-r--r--content/renderer/media/rtc_video_decoder.h75
-rw-r--r--content/renderer/media/rtc_video_decoder_unittest.cc178
5 files changed, 553 insertions, 0 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index 82be1b8..7a2a7be 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -65,9 +65,12 @@
'renderer/media/audio_message_filter.h',
'renderer/media/audio_renderer_impl.cc',
'renderer/media/audio_renderer_impl.h',
+ 'renderer/media/external_renderer.h',
'renderer/media/media_stream_dispatcher.cc',
'renderer/media/media_stream_dispatcher.h',
'renderer/media/media_stream_dispatcher_eventhandler.h',
+ 'renderer/media/rtc_video_decoder.cc',
+ 'renderer/media/rtc_video_decoder.h',
'renderer/media/video_capture_impl.cc',
'renderer/media/video_capture_impl.h',
'renderer/media/video_capture_impl_manager.cc',
diff --git a/content/renderer/media/external_renderer.h b/content/renderer/media/external_renderer.h
new file mode 100644
index 0000000..fccecd6
--- /dev/null
+++ b/content/renderer/media/external_renderer.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_RENDERER_MEDIA_EXTERNAL_RENDERER_H_
+#define CONTENT_RENDERER_MEDIA_EXTERNAL_RENDERER_H_
+
+// TODO(ronghuawu) ExternalRenderer should be replaced by
+// cricket::VideoRenderer from libjingle
+class ExternalRenderer {
+ public:
+ virtual int FrameSizeChange(unsigned int width,
+ unsigned int height,
+ unsigned int number_of_streams) = 0;
+ virtual int DeliverFrame(unsigned char* buffer, int buffer_size) = 0;
+
+ protected:
+ virtual ~ExternalRenderer() {}
+};
+
+#endif // CONTENT_RENDERER_MEDIA_EXTERNAL_RENDERER_H_
diff --git a/content/renderer/media/rtc_video_decoder.cc b/content/renderer/media/rtc_video_decoder.cc
new file mode 100644
index 0000000..a1f05fb
--- /dev/null
+++ b/content/renderer/media/rtc_video_decoder.cc
@@ -0,0 +1,276 @@
+// Copyright (c) 2011 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 "content/renderer/media/rtc_video_decoder.h"
+
+#include <deque>
+
+#include "base/task.h"
+#include "media/base/callback.h"
+#include "media/base/filter_host.h"
+#include "media/base/filters.h"
+#include "media/base/limits.h"
+#include "media/base/media_format.h"
+#include "media/base/video_frame.h"
+
+using media::DemuxerStream;
+using media::FilterCallback;
+using media::FilterStatusCB;
+using media::kNoTimestamp;
+using media::Limits;
+using media::MediaFormat;
+using media::PIPELINE_OK;
+using media::StatisticsCallback;
+using media::VideoDecoder;
+using media::VideoFrame;
+
+RTCVideoDecoder::RTCVideoDecoder(MessageLoop* message_loop,
+ const std::string& url)
+ : message_loop_(message_loop),
+ width_(176),
+ height_(144),
+ url_(url),
+ state_(kUnInitialized) {
+}
+
+RTCVideoDecoder::~RTCVideoDecoder() {
+}
+
+void RTCVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
+ FilterCallback* filter_callback,
+ StatisticsCallback* stat_callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &RTCVideoDecoder::Initialize,
+ make_scoped_refptr(demuxer_stream),
+ filter_callback, stat_callback));
+ return;
+ }
+
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+
+ lock_.Acquire();
+ frame_queue_available_.clear();
+ lock_.Release();
+ media_format_.SetAsInteger(MediaFormat::kWidth, width_);
+ media_format_.SetAsInteger(MediaFormat::kHeight, height_);
+ media_format_.SetAsInteger(MediaFormat::kSurfaceType,
+ static_cast<int>(VideoFrame::YV12));
+ media_format_.SetAsInteger(MediaFormat::kSurfaceFormat,
+ static_cast<int>(VideoFrame::TYPE_SYSTEM_MEMORY));
+
+ state_ = kNormal;
+
+ filter_callback->Run();
+ delete filter_callback;
+
+ // TODO(acolwell): Implement stats.
+ delete stat_callback;
+}
+
+void RTCVideoDecoder::Play(FilterCallback* callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &RTCVideoDecoder::Play,
+ callback));
+ return;
+ }
+
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+
+ VideoDecoder::Play(callback);
+}
+
+void RTCVideoDecoder::Pause(FilterCallback* callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &RTCVideoDecoder::Pause,
+ callback));
+ return;
+ }
+
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+
+ state_ = kPaused;
+
+ VideoDecoder::Pause(callback);
+}
+
+void RTCVideoDecoder::Stop(FilterCallback* callback) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &RTCVideoDecoder::Stop,
+ callback));
+ return;
+ }
+
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+
+ state_ = kStopped;
+
+ VideoDecoder::Stop(callback);
+
+ // TODO(ronghuawu): Stop rtc
+}
+
+void RTCVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &RTCVideoDecoder::Seek,
+ time, cb));
+ return;
+ }
+
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+
+ state_ = kSeeking;
+ // Create output buffer pool and pass the frames to renderer
+ // so that the renderer can complete the seeking
+ for (size_t i = 0; i < Limits::kMaxVideoFrames; ++i) {
+ scoped_refptr<VideoFrame> video_frame;
+ VideoFrame::CreateFrame(VideoFrame::YV12,
+ width_,
+ height_,
+ kNoTimestamp,
+ kNoTimestamp,
+ &video_frame);
+ if (!video_frame.get()) {
+ break;
+ }
+
+ // Create black frame
+ const uint8 kBlackY = 0x00;
+ const uint8 kBlackUV = 0x80;
+ // Fill the Y plane.
+ uint8* y_plane = video_frame->data(VideoFrame::kYPlane);
+ for (size_t i = 0; i < height_; ++i) {
+ memset(y_plane, kBlackY, width_);
+ y_plane += video_frame->stride(VideoFrame::kYPlane);
+ }
+ // Fill the U and V planes.
+ uint8* u_plane = video_frame->data(VideoFrame::kUPlane);
+ uint8* v_plane = video_frame->data(VideoFrame::kVPlane);
+ for (size_t i = 0; i < (height_ / 2); ++i) {
+ memset(u_plane, kBlackUV, width_ / 2);
+ memset(v_plane, kBlackUV, width_ / 2);
+ u_plane += video_frame->stride(VideoFrame::kUPlane);
+ v_plane += video_frame->stride(VideoFrame::kVPlane);
+ }
+
+ VideoFrameReady(video_frame);
+ }
+
+ state_ = kNormal;
+
+ cb.Run(PIPELINE_OK);
+
+ // TODO(ronghuawu): Start rtc
+}
+
+const MediaFormat& RTCVideoDecoder::media_format() {
+ return media_format_;
+}
+
+void RTCVideoDecoder::ProduceVideoFrame(
+ scoped_refptr<VideoFrame> video_frame) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &RTCVideoDecoder::ProduceVideoFrame, video_frame));
+ return;
+ }
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ lock_.Acquire();
+ frame_queue_available_.push_back(video_frame);
+ lock_.Release();
+}
+
+bool RTCVideoDecoder::ProvidesBuffer() {
+ return true;
+}
+
+int RTCVideoDecoder::FrameSizeChange(unsigned int width,
+ unsigned int height,
+ unsigned int number_of_streams) {
+ width_ = width;
+ height_ = height;
+
+ media_format_.SetAsInteger(MediaFormat::kWidth, width_);
+ media_format_.SetAsInteger(MediaFormat::kHeight, height_);
+ host()->SetVideoSize(width_, height_);
+ return 0;
+}
+
+int RTCVideoDecoder::DeliverFrame(unsigned char* buffer,
+ int buffer_size) {
+ DCHECK(buffer);
+
+ if (frame_queue_available_.size() == 0)
+ return 0;
+
+ if (state_ != kNormal)
+ return 0;
+
+ // This is called from another thread
+ lock_.Acquire();
+ scoped_refptr<VideoFrame> video_frame = frame_queue_available_.front();
+ frame_queue_available_.pop_front();
+ lock_.Release();
+
+ // Check if there's a size change
+ if (video_frame->width() != width_ || video_frame->height() != height_) {
+ video_frame.release();
+ // Allocate new buffer based on the new size
+ VideoFrame::CreateFrame(VideoFrame::YV12,
+ width_,
+ height_,
+ kNoTimestamp,
+ kNoTimestamp,
+ &video_frame);
+ if (!video_frame.get()) {
+ return -1;
+ }
+ }
+
+ video_frame->SetTimestamp(host()->GetTime());
+ video_frame->SetDuration(base::TimeDelta::FromMilliseconds(30));
+
+ uint8* y_plane = video_frame->data(VideoFrame::kYPlane);
+ for (size_t row = 0; row < video_frame->height(); ++row) {
+ memcpy(y_plane, buffer, width_);
+ y_plane += video_frame->stride(VideoFrame::kYPlane);
+ buffer += width_;
+ }
+ size_t uv_width = width_/2;
+ uint8* u_plane = video_frame->data(VideoFrame::kUPlane);
+ for (size_t row = 0; row < video_frame->height(); row += 2) {
+ memcpy(u_plane, buffer, uv_width);
+ u_plane += video_frame->stride(VideoFrame::kUPlane);
+ buffer += uv_width;
+ }
+ uint8* v_plane = video_frame->data(VideoFrame::kVPlane);
+ for (size_t row = 0; row < video_frame->height(); row += 2) {
+ memcpy(v_plane, buffer, uv_width);
+ v_plane += video_frame->stride(VideoFrame::kVPlane);
+ buffer += uv_width;
+ }
+
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &RTCVideoDecoder::VideoFrameReady,
+ video_frame));
+ } else {
+ VideoFrameReady(video_frame);
+ }
+
+ return 0;
+}
diff --git a/content/renderer/media/rtc_video_decoder.h b/content/renderer/media/rtc_video_decoder.h
new file mode 100644
index 0000000..0f58ab8
--- /dev/null
+++ b/content/renderer/media/rtc_video_decoder.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_RENDERER_MEDIA_RTC_VIDEO_DECODER_H_
+#define CONTENT_RENDERER_MEDIA_RTC_VIDEO_DECODER_H_
+
+#include <deque>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/time.h"
+#include "content/renderer/media/external_renderer.h"
+#include "media/base/filters.h"
+#include "media/base/video_frame.h"
+#include "media/filters/decoder_base.h"
+
+class RTCVideoDecoder
+ : public media::VideoDecoder,
+ public ExternalRenderer {
+ public:
+ RTCVideoDecoder(MessageLoop* message_loop, const std::string& url);
+ virtual ~RTCVideoDecoder();
+
+ // Filter implementation.
+ virtual void Play(media::FilterCallback* callback);
+ virtual void Seek(base::TimeDelta time, const media::FilterStatusCB& cb);
+ virtual void Pause(media::FilterCallback* callback);
+ virtual void Stop(media::FilterCallback* callback);
+
+ // Decoder implementation.
+ virtual void Initialize(media::DemuxerStream* demuxer_stream,
+ media::FilterCallback* filter_callback,
+ media::StatisticsCallback* stat_callback);
+ virtual const media::MediaFormat& media_format();
+ virtual void ProduceVideoFrame(scoped_refptr<media::VideoFrame> video_frame);
+ virtual bool ProvidesBuffer();
+
+ // ExternalRenderer implementation
+ virtual int FrameSizeChange(unsigned int width,
+ unsigned int height,
+ unsigned int number_of_streams);
+
+ virtual int DeliverFrame(unsigned char* buffer,
+ int buffer_size);
+
+ private:
+ friend class RTCVideoDecoderTest;
+ FRIEND_TEST_ALL_PREFIXES(RTCVideoDecoderTest, Initialize_Successful);
+ FRIEND_TEST_ALL_PREFIXES(RTCVideoDecoderTest, DoSeek);
+ FRIEND_TEST_ALL_PREFIXES(RTCVideoDecoderTest, DoDeliverFrame);
+ FRIEND_TEST_ALL_PREFIXES(RTCVideoDecoderTest, DoFrameSizeChange);
+
+ enum DecoderState {
+ kUnInitialized,
+ kNormal,
+ kSeeking,
+ kPaused,
+ kStopped
+ };
+
+ MessageLoop* message_loop_;
+ size_t width_;
+ size_t height_;
+ std::string url_;
+ DecoderState state_;
+ media::MediaFormat media_format_;
+ std::deque<scoped_refptr<media::VideoFrame> > frame_queue_available_;
+ // Used for accessing frame queue from another thread.
+ base::Lock lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(RTCVideoDecoder);
+};
+
+#endif // CONTENT_RENDERER_MEDIA_RTC_VIDEO_DECODER_H_
diff --git a/content/renderer/media/rtc_video_decoder_unittest.cc b/content/renderer/media/rtc_video_decoder_unittest.cc
new file mode 100644
index 0000000..0891609
--- /dev/null
+++ b/content/renderer/media/rtc_video_decoder_unittest.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2011 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 <deque>
+
+#include "base/bind.h"
+#include "base/memory/singleton.h"
+#include "base/string_util.h"
+#include "content/renderer/media/rtc_video_decoder.h"
+#include "media/base/data_buffer.h"
+#include "media/base/filters.h"
+#include "media/base/limits.h"
+#include "media/base/mock_callback.h"
+#include "media/base/mock_filter_host.h"
+#include "media/base/mock_filters.h"
+#include "media/base/mock_task.h"
+#include "media/base/video_frame.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::DoAll;
+using ::testing::Message;
+using ::testing::Return;
+using ::testing::ReturnNull;
+using ::testing::SetArgumentPointee;
+using ::testing::StrictMock;
+using ::testing::WithArg;
+using ::testing::Invoke;
+using media::Limits;
+using media::MediaFormat;
+using media::MockStatisticsCallback;
+using media::MockVideoRenderer;
+using media::MockFilterHost;
+using media::NewExpectedCallback;
+using media::PipelineStatistics;
+using media::PIPELINE_OK;
+using media::StatisticsCallback;
+
+class RTCVideoDecoderTest : public testing::Test {
+ protected:
+ static const int kWidth;
+ static const int kHeight;
+ static const char* kUrl;
+ static const PipelineStatistics kStatistics;
+
+ RTCVideoDecoderTest() {
+ MediaFormat media_format;
+ decoder_ = new RTCVideoDecoder(&message_loop_, kUrl);
+ renderer_ = new MockVideoRenderer();
+
+ DCHECK(decoder_);
+
+ // Inject mocks and prepare a demuxer stream.
+ decoder_->set_host(&host_);
+
+ EXPECT_CALL(stats_callback_object_, OnStatistics(_))
+ .Times(AnyNumber());
+ }
+
+ virtual ~RTCVideoDecoderTest() {
+ // Finish up any remaining tasks.
+ message_loop_.RunAllPending();
+ }
+
+ void InitializeDecoderSuccessfully() {
+ // Test successful initialization.
+ decoder_->Initialize(NULL,
+ NewExpectedCallback(), NewStatisticsCallback());
+ message_loop_.RunAllPending();
+ }
+
+ StatisticsCallback* NewStatisticsCallback() {
+ return NewCallback(&stats_callback_object_,
+ &MockStatisticsCallback::OnStatistics);
+ }
+
+ // Fixture members.
+ scoped_refptr<RTCVideoDecoder> decoder_;
+ scoped_refptr<MockVideoRenderer> renderer_;
+ MockStatisticsCallback stats_callback_object_;
+ StrictMock<MockFilterHost> host_;
+ MessageLoop message_loop_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RTCVideoDecoderTest);
+};
+
+const int RTCVideoDecoderTest::kWidth = 176;
+const int RTCVideoDecoderTest::kHeight = 144;
+const char* RTCVideoDecoderTest::kUrl = "media://remote/0";
+const PipelineStatistics RTCVideoDecoderTest::kStatistics;
+
+TEST_F(RTCVideoDecoderTest, Initialize_Successful) {
+ InitializeDecoderSuccessfully();
+
+ // Test that the output media format is an uncompressed video surface that
+ // matches the dimensions specified by rtc.
+ const MediaFormat& media_format = decoder_->media_format();
+ int width = 0;
+ int height = 0;
+ EXPECT_TRUE(media_format.GetAsInteger(MediaFormat::kWidth, &width));
+ EXPECT_EQ(kWidth, width);
+ EXPECT_TRUE(media_format.GetAsInteger(MediaFormat::kHeight, &height));
+ EXPECT_EQ(kHeight, height);
+}
+
+TEST_F(RTCVideoDecoderTest, DoSeek) {
+ const base::TimeDelta kZero;
+
+ InitializeDecoderSuccessfully();
+
+ decoder_->set_consume_video_frame_callback(
+ base::Bind(&MockVideoRenderer::ConsumeVideoFrame,
+ base::Unretained(renderer_.get())));
+
+ // Expect Seek and verify the results.
+ EXPECT_CALL(*renderer_.get(), ConsumeVideoFrame(_))
+ .Times(Limits::kMaxVideoFrames);
+ decoder_->Seek(kZero, NewExpectedStatusCB(PIPELINE_OK));
+
+ message_loop_.RunAllPending();
+ EXPECT_EQ(RTCVideoDecoder::kNormal, decoder_->state_);
+}
+
+TEST_F(RTCVideoDecoderTest, DoDeliverFrame) {
+ const base::TimeDelta kZero;
+ EXPECT_CALL(host_, GetTime()).WillRepeatedly(Return(base::TimeDelta()));
+
+ InitializeDecoderSuccessfully();
+
+ // Pass the frame back to decoder
+ decoder_->set_consume_video_frame_callback(
+ base::Bind(&RTCVideoDecoder::ProduceVideoFrame,
+ base::Unretained(decoder_.get())));
+ decoder_->Seek(kZero, NewExpectedStatusCB(PIPELINE_OK));
+
+ decoder_->set_consume_video_frame_callback(
+ base::Bind(&MockVideoRenderer::ConsumeVideoFrame,
+ base::Unretained(renderer_.get())));
+ EXPECT_CALL(*renderer_.get(), ConsumeVideoFrame(_))
+ .Times(Limits::kMaxVideoFrames);
+
+ unsigned int video_frame_size = decoder_->width_ * decoder_->height_ * 3 / 2;
+ uint8* video_frame = new uint8[video_frame_size];
+
+ for (size_t i = 0; i < Limits::kMaxVideoFrames; ++i) {
+ decoder_->DeliverFrame(video_frame, video_frame_size);
+ }
+ delete [] video_frame;
+
+ message_loop_.RunAllPending();
+ EXPECT_EQ(RTCVideoDecoder::kNormal, decoder_->state_);
+}
+
+TEST_F(RTCVideoDecoderTest, DoFrameSizeChange) {
+ InitializeDecoderSuccessfully();
+
+ int new_width = kWidth * 2;
+ int new_height = kHeight * 2;
+ int new_number_of_streams = 0;
+
+ EXPECT_CALL(host_,
+ SetVideoSize(new_width, new_height)).WillRepeatedly(Return());
+
+ decoder_->FrameSizeChange(new_width, new_height, new_number_of_streams);
+
+ const MediaFormat& media_format = decoder_->media_format();
+ int width = 0;
+ int height = 0;
+ EXPECT_TRUE(media_format.GetAsInteger(MediaFormat::kWidth, &width));
+ EXPECT_EQ(new_width, width);
+ EXPECT_TRUE(media_format.GetAsInteger(MediaFormat::kHeight, &height));
+ EXPECT_EQ(new_height, height);
+
+ message_loop_.RunAllPending();
+}