Index: content/renderer/media/rtc_video_decoder_bridge_tv.cc |
diff --git a/content/renderer/media/rtc_video_decoder_bridge_tv.cc b/content/renderer/media/rtc_video_decoder_bridge_tv.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6b1aaaa6045af2d44fdbb2d6b8a9c2770736ea81 |
--- /dev/null |
+++ b/content/renderer/media/rtc_video_decoder_bridge_tv.cc |
@@ -0,0 +1,473 @@ |
+// Copyright (c) 2013 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_bridge_tv.h" |
+ |
+#include <queue> |
+ |
+#include "base/bind.h" |
+#include "base/callback_helpers.h" |
+#include "base/location.h" |
+#include "base/logging.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/singleton.h" |
+#include "base/message_loop_proxy.h" |
+#include "base/synchronization/lock.h" |
+#include "base/time.h" |
+#include "media/base/decoder_buffer.h" |
+#include "third_party/libjingle/source/talk/base/ratetracker.h" |
+ |
+namespace content { |
+ |
+// Helper function that makes sure |read_cb| runs on |message_loop_proxy|. |
+static void RunOnMessageLoop( |
+ media::DemuxerStream::ReadCB read_cb, |
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy, |
+ media::DemuxerStream::Status status, |
+ const scoped_refptr<media::DecoderBuffer>& buffer) { |
+ if (!message_loop_proxy->BelongsToCurrentThread()) { |
+ message_loop_proxy->PostTask(FROM_HERE, base::Bind( |
+ &RunOnMessageLoop, read_cb, message_loop_proxy, status, buffer)); |
+ return; |
+ } |
+ read_cb.Run(status, buffer); |
+} |
+ |
+// RTCDemuxerStream ------------------------------------------------------------ |
+ |
+namespace { |
+ |
+class RTCDemuxerStream : public media::DemuxerStream { |
+ public: |
+ explicit RTCDemuxerStream(const gfx::Size& size); |
+ virtual ~RTCDemuxerStream(); |
+ // media::DemuxerStream implementation. |
+ virtual void Read(const ReadCB& read_cb) OVERRIDE; |
+ virtual const media::AudioDecoderConfig& audio_decoder_config() OVERRIDE; |
+ virtual const media::VideoDecoderConfig& video_decoder_config() OVERRIDE; |
+ virtual Type type() OVERRIDE; |
+ virtual void EnableBitstreamConverter() OVERRIDE; |
+ |
+ void UpdateSize(const gfx::Size& size); |
+ void QueueBuffer(scoped_refptr<media::DecoderBuffer> buffer, |
+ const base::Closure& done_cb); |
+ |
+ void RunDoneCallback(); |
ycheo (away)
2013/05/02 02:45:21
private?
wonsik
2013/05/02 15:22:00
Done.
|
+ |
+ private: |
+ void RunReadCallback(); |
+ |
+ media::DecoderBufferQueue buffer_queue_; |
+ std::queue<base::Closure> done_cb_queue_; |
+ ReadCB read_cb_; |
+ |
+ media::AudioDecoderConfig dummy_audio_decoder_config_; |
+ media::VideoDecoderConfig video_decoder_config_; |
+ talk_base::RateTracker frame_rate_tracker_; |
+}; |
+ |
+RTCDemuxerStream::RTCDemuxerStream(const gfx::Size& size) |
+ : video_decoder_config_( |
+ media::kCodecVP8, |
+ media::VP8PROFILE_MAIN, |
+ media::VideoFrame::NATIVE_TEXTURE, |
+ size, gfx::Rect(size), size, NULL, 0, false) { |
+} |
+ |
+RTCDemuxerStream::~RTCDemuxerStream() {} |
+ |
+const media::AudioDecoderConfig& RTCDemuxerStream::audio_decoder_config() { |
+ NOTREACHED() << "Does not support audio."; |
+ return dummy_audio_decoder_config_; |
+} |
+ |
+const media::VideoDecoderConfig& RTCDemuxerStream::video_decoder_config() { |
+ return video_decoder_config_; |
+} |
+ |
+media::DemuxerStream::Type RTCDemuxerStream::type() { |
+ return media::DemuxerStream::VIDEO; |
+} |
+ |
+void RTCDemuxerStream::EnableBitstreamConverter() { |
+} |
+ |
+void RTCDemuxerStream::UpdateSize(const gfx::Size& size) { |
+ video_decoder_config_.Initialize( |
+ media::kCodecVP8, |
+ media::VP8PROFILE_MAIN, |
+ media::VideoFrame::NATIVE_TEXTURE, |
+ size, gfx::Rect(size), size, NULL, 0, false, false); |
+} |
+ |
+void RTCDemuxerStream::QueueBuffer(scoped_refptr<media::DecoderBuffer> buffer, |
+ const base::Closure& done_cb) { |
+ buffer_queue_.Push(buffer); |
+ done_cb_queue_.push(done_cb); |
+ frame_rate_tracker_.Update(1); |
+ DLOG(INFO) << "frame rate received : " << frame_rate_tracker_.units_second(); |
+ RunReadCallback(); |
+} |
+ |
+void RTCDemuxerStream::Read(const ReadCB& read_cb) { |
+ CHECK(read_cb_.is_null()); |
+ // A call to |Read| operation means that |MediaSourceDelegate| is done with |
+ // the previous buffer. |
+ if (!done_cb_queue_.empty()) |
+ RunDoneCallback(); |
+ read_cb_ = base::Bind(&RunOnMessageLoop, read_cb, |
+ base::MessageLoopProxy::current()); |
+ RunReadCallback(); |
+} |
+ |
+void RTCDemuxerStream::RunReadCallback() { |
+ if (!read_cb_.is_null() && !buffer_queue_.IsEmpty()) { |
+ base::ResetAndReturn(&read_cb_).Run( |
+ DemuxerStream::kOk, buffer_queue_.Pop()); |
+ } |
+} |
+ |
+void RTCDemuxerStream::RunDoneCallback() { |
+ DCHECK(!done_cb_queue_.empty()); |
+ done_cb_queue_.front().Run(); |
+ done_cb_queue_.pop(); |
+} |
+ |
+// RTCDemuxerProxy ------------------------------------------------------------- |
+ |
+class RTCDemuxerProxy : public base::RefCountedThreadSafe<RTCDemuxerProxy> { |
+ public: |
+ RTCDemuxerProxy(const scoped_refptr<base::MessageLoopProxy>& message_loop); |
+ |
+ void Initialize(media::DemuxerHost* host, const media::PipelineStatusCB& cb); |
+ media::DemuxerStream* GetStream(media::DemuxerStream::Type type); |
+ void UpdateSize(const gfx::Size& size); |
+ void QueueBuffer(scoped_refptr<media::DecoderBuffer> buffer, |
+ const base::Closure& done_cb); |
+ |
+ protected: |
+ friend class base::RefCountedThreadSafe<RTCDemuxerProxy>; |
+ |
+ virtual ~RTCDemuxerProxy(); |
+ |
+ private: |
+ scoped_refptr<base::MessageLoopProxy> loop_proxy_; |
+ scoped_ptr<RTCDemuxerStream> stream_; |
+ media::DemuxerHost* host_; |
+ media::PipelineStatusCB init_cb_; |
+}; |
+ |
+RTCDemuxerProxy::RTCDemuxerProxy( |
+ const scoped_refptr<base::MessageLoopProxy>& message_loop) |
+ : loop_proxy_(message_loop), |
+ host_(NULL) { |
+} |
+ |
+RTCDemuxerProxy::~RTCDemuxerProxy() {} |
+ |
+void RTCDemuxerProxy::Initialize(media::DemuxerHost* host, |
+ const media::PipelineStatusCB& cb) { |
+ host_ = host; |
+ init_cb_ = cb; |
+} |
+ |
+media::DemuxerStream* RTCDemuxerProxy::GetStream( |
+ media::DemuxerStream::Type type) { |
+ if (type == media::DemuxerStream::VIDEO) |
+ return stream_.get(); |
+ return NULL; |
+} |
+ |
+void RTCDemuxerProxy::UpdateSize(const gfx::Size& size) { |
+ if (!loop_proxy_->BelongsToCurrentThread()) { |
+ loop_proxy_->PostTask(FROM_HERE, base::Bind( |
+ &RTCDemuxerProxy::UpdateSize, this, size)); |
+ return; |
+ } |
+ if (stream_) { |
+ stream_->UpdateSize(size); |
+ } else { |
+ stream_.reset(new RTCDemuxerStream(size)); |
+ if (!init_cb_.is_null()) |
+ base::ResetAndReturn(&init_cb_).Run(media::PIPELINE_OK); |
+ } |
+} |
+ |
+void RTCDemuxerProxy::QueueBuffer(scoped_refptr<media::DecoderBuffer> buffer, |
+ const base::Closure& done_cb) { |
+ if (!loop_proxy_->BelongsToCurrentThread()) { |
+ loop_proxy_->PostTask(FROM_HERE, base::Bind( |
+ &RTCDemuxerProxy::QueueBuffer, this, buffer, done_cb)); |
+ return; |
+ } |
+ if (stream_) |
+ stream_->QueueBuffer(buffer, done_cb); |
+ else |
+ done_cb.Run(); |
+} |
+ |
+// RTCDemuxer ------------------------------------------------------------------ |
+ |
+class RTCDemuxer : public media::Demuxer { |
+ public: |
+ RTCDemuxer(const scoped_refptr<base::MessageLoopProxy>& message_loop); |
+ virtual ~RTCDemuxer(); |
+ |
+ // media::Demuxer implementation. |
+ virtual void Initialize(media::DemuxerHost* host, |
+ const media::PipelineStatusCB& cb) OVERRIDE; |
+ virtual media::DemuxerStream* GetStream( |
+ media::DemuxerStream::Type type) OVERRIDE; |
+ virtual base::TimeDelta GetStartTime() const OVERRIDE; |
+ |
+ void UpdateSize(const gfx::Size& size); |
+ void QueueBuffer(scoped_refptr<media::DecoderBuffer> buffer, |
+ const base::Closure& done_cb); |
+ |
+ private: |
+ friend class RTCVideoDecoderBridgeTv; |
+ |
+ scoped_refptr<RTCDemuxerProxy> proxy_; |
+}; |
+ |
+RTCDemuxer::RTCDemuxer( |
+ const scoped_refptr<base::MessageLoopProxy>& message_loop) |
+ : proxy_(new RTCDemuxerProxy(message_loop)) {} |
+ |
+RTCDemuxer::~RTCDemuxer() {} |
+ |
+void RTCDemuxer::Initialize(media::DemuxerHost* host, |
+ const media::PipelineStatusCB& cb) { |
+ proxy_->Initialize(host, cb); |
+} |
+ |
+media::DemuxerStream* RTCDemuxer::GetStream(media::DemuxerStream::Type type) { |
+ return proxy_->GetStream(type); |
+} |
+ |
+base::TimeDelta RTCDemuxer::GetStartTime() const { |
+ return base::TimeDelta(); |
+} |
+ |
+void RTCDemuxer::UpdateSize(const gfx::Size& size) { |
+ proxy_->UpdateSize(size); |
+} |
+ |
+void RTCDemuxer::QueueBuffer(scoped_refptr<media::DecoderBuffer> buffer, |
+ const base::Closure& done_cb) { |
+ proxy_->QueueBuffer(buffer, done_cb); |
+} |
+ |
+// RTCVideoDecoderBridgeTvImpl ------------------------------------------------- |
+ |
+class RTCVideoDecoderBridgeTvImpl : public RTCVideoDecoderBridgeTv { |
+ public: |
+ static RTCVideoDecoderBridgeTvImpl* GetInstance() { |
+ return Singleton<RTCVideoDecoderBridgeTvImpl>::get(); |
+ } |
+ RTCVideoDecoderBridgeTvImpl(); |
+ virtual ~RTCVideoDecoderBridgeTvImpl(); |
+ |
+ // webrtc::VideoDecoder implementation. |
+ virtual WebRtc_Word32 InitDecode( |
+ const webrtc::VideoCodec* codecSettings, |
+ WebRtc_Word32 numberOfCores) OVERRIDE; |
+ virtual WebRtc_Word32 Decode( |
+ const webrtc::EncodedImage& inputImage, |
+ bool missingFrames, |
+ const webrtc::RTPFragmentationHeader* fragmentation, |
+ const webrtc::CodecSpecificInfo* codecSpecificInfo = NULL, |
+ WebRtc_Word64 renderTimeMs = -1) OVERRIDE; |
+ virtual WebRtc_Word32 RegisterDecodeCompleteCallback( |
+ webrtc::DecodedImageCallback* callback) OVERRIDE; |
+ virtual WebRtc_Word32 Release() OVERRIDE; |
+ virtual WebRtc_Word32 Reset() OVERRIDE; |
+ |
+ virtual media::Demuxer* CreateDemuxer( |
+ const scoped_refptr<base::MessageLoopProxy>& message_loop) OVERRIDE; |
+ bool AcquireOwnership() OVERRIDE; |
+ void ReleaseOwnership() OVERRIDE; |
+ |
+ static void RunDecodeCompleteCallback(webrtc::DecodedImageCallback* callback, |
+ WebRtc_Word64 timestamp); |
+ |
+ private: |
+ friend struct DefaultSingletonTraits<RTCVideoDecoderBridgeTv>; |
+ |
+ static const base::TimeDelta kDecoderTimeOut; |
+ enum Status { |
+ kNoInit, |
+ kInitialized, |
+ }; |
+ |
+ // Lock protected, since these can be accessed in CreateDemuxer, |
+ // AcquireOwnership, and ReleaseOwnership. No guarantee of calling thread for |
+ // these methods. |
+ base::Lock lock_; |
+ scoped_ptr<RTCDemuxer> demuxer_; |
+ int generation_; |
+ int demuxer_generation_; |
+ bool being_used_; |
+ bool first_frame_; |
+ |
+ // Only used by DecoderThread, after AcquireOwnership() is successful. |
+ webrtc::DecodedImageCallback* decode_complete_callback_; |
+ gfx::Size size_; |
+ Status status_; |
+ WebRtc_Word64 timestamp_offset_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(RTCVideoDecoderBridgeTvImpl); |
+}; |
+ |
+RTCVideoDecoderBridgeTvImpl::RTCVideoDecoderBridgeTvImpl() |
+ : demuxer_(NULL), |
+ generation_(0), |
+ demuxer_generation_(-1), |
+ being_used_(false), |
+ first_frame_(true), |
+ decode_complete_callback_(NULL), |
+ status_(kNoInit) {} |
+ |
+RTCVideoDecoderBridgeTvImpl::~RTCVideoDecoderBridgeTvImpl() {} |
+ |
+media::Demuxer* RTCVideoDecoderBridgeTvImpl::CreateDemuxer( |
+ const scoped_refptr<base::MessageLoopProxy>& message_loop) { |
+ base::AutoLock lock(lock_); |
+ if (generation_ == demuxer_generation_) |
+ return NULL; |
+ demuxer_generation_ = generation_; |
+ demuxer_.reset(new RTCDemuxer(message_loop)); |
+ // If this is initialized before demuxer is set, update the demuxer with size |
+ // information. |
+ if (status_ == kInitialized) |
+ demuxer_->UpdateSize(size_); |
+ first_frame_ = true; |
+ return demuxer_.get(); |
+} |
+ |
+bool RTCVideoDecoderBridgeTvImpl::AcquireOwnership() { |
+ base::AutoLock lock(lock_); |
+ if (being_used_) |
+ return false; |
+ |
+ being_used_ = true; |
+ return true; |
+} |
+ |
+void RTCVideoDecoderBridgeTvImpl::ReleaseOwnership() { |
+ base::AutoLock lock(lock_); |
+ DCHECK(being_used_); |
+ being_used_ = false; |
+ ++generation_; |
+} |
+ |
+WebRtc_Word32 RTCVideoDecoderBridgeTvImpl::InitDecode( |
+ const webrtc::VideoCodec* codecSettings, |
+ WebRtc_Word32 numberOfCores) { |
+ if (status_ != kNoInit) |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ if (codecSettings->codecType != webrtc::kVideoCodecVP8) |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ // We don't support feedback mode. |
+ if (codecSettings->codecSpecific.VP8.feedbackModeOn) |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ size_ = gfx::Size(codecSettings->width, codecSettings->height); |
+ status_ = kInitialized; |
+ base::AutoLock lock(lock_); |
+ if (demuxer_ && demuxer_generation_ == generation_) |
ycheo (away)
2013/05/02 02:45:21
You may want to add some warning message if the ge
wonsik
2013/05/03 18:31:19
N/A
|
+ demuxer_->UpdateSize(size_); |
+ |
+ return WEBRTC_VIDEO_CODEC_OK; |
+} |
+ |
+WebRtc_Word32 RTCVideoDecoderBridgeTvImpl::Decode( |
+ const webrtc::EncodedImage& inputImage, |
+ bool missingFrames, |
+ const webrtc::RTPFragmentationHeader* fragmentation, |
+ const webrtc::CodecSpecificInfo* codecSpecificInfo, |
+ WebRtc_Word64 renderTimeMs) { |
+ if (status_ == kNoInit || decode_complete_callback_ == NULL) |
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
+ if (!status_ == kInitialized) |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ // Unlike the SW decoder in libvpx, hw decoder can not handle broken frames. |
+ // Here, we return an error in order to request a key frame. |
+ if (missingFrames || !inputImage._completeFrame) |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ base::AutoLock lock(lock_); |
+ // Drop frames until demuxer is up and running. We'll request key frame |
+ // once demuxer is ready. |
+ if (!demuxer_ || demuxer_generation_ != generation_) |
+ return WEBRTC_VIDEO_CODEC_NO_OUTPUT; |
+ |
+ if (first_frame_) { |
+ // If the first frame is not the key frame, return an error to request a key |
+ // frame. |
+ if (inputImage._frameType != webrtc::kKeyFrame) |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ |
+ // Google TV expects timestamp from 0. |
+ timestamp_offset_ = renderTimeMs; |
+ } |
+ first_frame_ = false; |
+ if (inputImage._frameType == webrtc::kKeyFrame && |
+ inputImage._encodedWidth != 0 && inputImage._encodedHeight != 0) { |
+ // Only key frame has the size. |
+ gfx::Size new_size(inputImage._encodedWidth, inputImage._encodedHeight); |
+ if (size_ != new_size) { |
+ size_ = new_size; |
+ demuxer_->UpdateSize(new_size); |
+ } |
+ } |
+ scoped_refptr<media::DecoderBuffer> buffer; |
+ buffer = media::DecoderBuffer::CopyFrom( |
+ inputImage._buffer, inputImage._length); |
+ if (renderTimeMs != -1) { |
+ buffer->SetTimestamp( |
+ base::TimeDelta::FromMilliseconds(renderTimeMs - timestamp_offset_)); |
+ } |
+ |
+ demuxer_->QueueBuffer( |
+ buffer, |
+ base::Bind(&RTCVideoDecoderBridgeTvImpl::RunDecodeCompleteCallback, |
+ decode_complete_callback_, |
+ renderTimeMs)); |
+ |
+ return WEBRTC_VIDEO_CODEC_OK; |
+} |
+ |
+WebRtc_Word32 RTCVideoDecoderBridgeTvImpl::RegisterDecodeCompleteCallback( |
+ webrtc::DecodedImageCallback* callback) { |
+ decode_complete_callback_ = callback; |
+ return WEBRTC_VIDEO_CODEC_OK; |
+} |
+ |
+WebRtc_Word32 RTCVideoDecoderBridgeTvImpl::Release() { |
+ status_ = kNoInit; |
+ return WEBRTC_VIDEO_CODEC_OK; |
+} |
+ |
+WebRtc_Word32 RTCVideoDecoderBridgeTvImpl::Reset() { |
+ return WEBRTC_VIDEO_CODEC_OK; |
+} |
+ |
+// static |
+void RTCVideoDecoderBridgeTvImpl::RunDecodeCompleteCallback( |
+ webrtc::DecodedImageCallback* callback, WebRtc_Word64 timestamp) { |
+ webrtc::I420VideoFrame dummy_video_frame; |
+ dummy_video_frame.CreateEmptyFrame(2, 1, 2, 1, 1); |
+ dummy_video_frame.set_timestamp(timestamp); |
+ callback->Decoded(dummy_video_frame); |
+} |
+ |
+} // anonymous namespace |
+ |
+// RTCVideoDecoderBridgeTv ----------------------------------------------------- |
+ |
+// static |
+RTCVideoDecoderBridgeTv* RTCVideoDecoderBridgeTv::Get() { |
+ return RTCVideoDecoderBridgeTvImpl::GetInstance(); |
+} |
+ |
+} // namespace content |