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..b37b0f29e42cd338861593d55bb88df207388e89 |
--- /dev/null |
+++ b/content/renderer/media/rtc_video_decoder_bridge_tv.cc |
@@ -0,0 +1,351 @@ |
+// Copyright (c) 2012 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 "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/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( |
+ const media::DemuxerStream::ReadCB& read_cb, |
+ const 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); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+class RTCDemuxerStream : public media::DemuxerStream { |
+ public: |
+ explicit RTCDemuxerStream(const gfx::Size& size); |
+ // 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); |
+ |
+ protected: |
+ friend class base::RefCountedThreadSafe<RTCDemuxerStream>; |
+ |
+ virtual ~RTCDemuxerStream(); |
+ |
+ private: |
+ media::DecoderBufferQueue buffer_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() { |
+ DCHECK(false) << "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) { |
+ buffer_queue_.Push(buffer); |
+ frame_rate_tracker_.Update(1); |
+ DLOG(INFO) << "frame rate received : " << frame_rate_tracker_.units_second(); |
+ |
+ if (!read_cb_.is_null()) { |
+ base::ResetAndReturn(&read_cb_).Run( |
+ DemuxerStream::kOk, buffer_queue_.Pop()); |
+ } |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+RTCDemuxerProxy::RTCDemuxerProxy( |
+ const scoped_refptr<base::MessageLoopProxy>& message_loop) |
+ : loop_proxy_(message_loop), |
+ stream_(NULL), |
+ host_(NULL) { |
+} |
+ |
+RTCDemuxerProxy::~RTCDemuxerProxy() {} |
+ |
+void RTCDemuxerProxy::Initialize(media::DemuxerHost* host, |
+ const media::PipelineStatusCB& cb) { |
+ host_ = host; |
+ init_cb_ = cb; |
+} |
+ |
+scoped_refptr<media::DemuxerStream> RTCDemuxerProxy::GetStream( |
+ media::DemuxerStream::Type type) { |
+ if (type == media::DemuxerStream::VIDEO) |
+ return stream_; |
+ return NULL; |
+} |
+ |
+base::TimeDelta RTCDemuxerProxy::GetStartTime() const { |
+ return base::TimeDelta(); |
+} |
+ |
+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_ = new RTCDemuxerStream(size); |
+ if (!init_cb_.is_null()) |
+ base::ResetAndReturn(&init_cb_).Run(media::PIPELINE_OK); |
+ } |
+} |
+ |
+void RTCDemuxerProxy::QueueBuffer( |
+ scoped_refptr<media::DecoderBuffer> buffer) { |
+ if (!loop_proxy_->BelongsToCurrentThread()) { |
+ loop_proxy_->PostTask(FROM_HERE, base::Bind( |
+ &RTCDemuxerProxy::QueueBuffer, this, buffer)); |
+ return; |
+ } |
+ stream_->QueueBuffer(buffer); |
+} |
+ |
+void RTCDemuxerStream::Read(const ReadCB& read_cb) { |
+ CHECK(read_cb_.is_null()); |
+ read_cb_ = base::Bind(&RunOnMessageLoop, read_cb, |
+ base::MessageLoopProxy::current()); |
+ |
+ if(!buffer_queue_.IsEmpty()) { |
+ base::ResetAndReturn(&read_cb_).Run( |
+ DemuxerStream::kOk, buffer_queue_.Pop()); |
+ } |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+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; |
+ |
+ bool RegisterDemuxer(scoped_refptr<RTCDemuxerProxy> demuxer) OVERRIDE; |
+ bool AcquireOwnership() OVERRIDE; |
+ void ReleaseOwnership() OVERRIDE; |
+ |
+ private: |
+ friend struct DefaultSingletonTraits<RTCVideoDecoderBridgeTv>; |
+ |
+ static const base::TimeDelta kDecoderTimeOut; |
+ enum Status { |
+ kNoInit, |
+ kInitialized, |
+ }; |
+ |
+ base::TimeDelta first_render_wall_time_; |
+ |
+ webrtc::DecodedImageCallback* decode_complete_callback_; |
+ scoped_refptr<RTCDemuxerProxy> demuxer_; |
+ gfx::Size size_; |
+ Status status_; |
+ bool being_used_; |
+ bool first_frame_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(RTCVideoDecoderBridgeTvImpl); |
+}; |
+ |
+RTCVideoDecoderBridgeTvImpl::RTCVideoDecoderBridgeTvImpl() |
+ : decode_complete_callback_(NULL), |
+ status_(kNoInit), |
+ being_used_(false), |
+ first_frame_(true) { |
+} |
+ |
+RTCVideoDecoderBridgeTvImpl::~RTCVideoDecoderBridgeTvImpl() { |
+} |
+ |
+bool RTCVideoDecoderBridgeTvImpl::AcquireOwnership() { |
+ if (being_used_) { |
+ return false; |
+ } |
+ being_used_ = true; |
+ return true; |
+} |
+ |
+void RTCVideoDecoderBridgeTvImpl::ReleaseOwnership() { |
+ DCHECK(being_used_); |
+ being_used_ = false; |
+} |
+ |
+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; |
+ } |
+ if (codecSettings->codecSpecific.VP8.feedbackModeOn) { |
+ // We don't support feedback mode. |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ } |
+ size_ = gfx::Size(codecSettings->width, codecSettings->height); |
+ status_ = kInitialized; |
+ if (demuxer_) |
+ 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; |
+ } |
+ if (missingFrames || !inputImage._completeFrame) { |
+ // 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. |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ } |
+ if (!demuxer_) { |
+ // Drop frames until demuxer is up and running. We'll request key frame |
+ // once demuxer is ready. |
+ return WEBRTC_VIDEO_CODEC_NO_OUTPUT; |
+ } |
+ |
+ if (first_frame_ && inputImage._frameType != webrtc::kKeyFrame) { |
+ // If the first frame is not the key frame, return an error to request a key |
+ // frame. |
+ return WEBRTC_VIDEO_CODEC_ERROR; |
+ } |
+ 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); |
+ |
+ demuxer_->QueueBuffer(buffer); |
+ |
+ return WEBRTC_VIDEO_CODEC_NO_OUTPUT; |
+} |
+ |
+WebRtc_Word32 RTCVideoDecoderBridgeTvImpl::RegisterDecodeCompleteCallback( |
+ webrtc::DecodedImageCallback* callback) { |
+ decode_complete_callback_ = callback; |
+ return WEBRTC_VIDEO_CODEC_OK; |
+} |
+ |
+WebRtc_Word32 RTCVideoDecoderBridgeTvImpl::Release() { |
+ demuxer_ = NULL; |
+ status_ = kNoInit; |
+ return WEBRTC_VIDEO_CODEC_OK; |
+} |
+ |
+WebRtc_Word32 RTCVideoDecoderBridgeTvImpl::Reset() { |
+ return WEBRTC_VIDEO_CODEC_OK; |
+} |
+ |
+bool RTCVideoDecoderBridgeTvImpl::RegisterDemuxer( |
+ scoped_refptr<RTCDemuxerProxy> demuxer) { |
+ if (demuxer_) { |
+ return false; |
+ } |
+ demuxer_ = demuxer; |
+ // If this is initialized before demuxer is set, update the demuxer with size |
+ // information. |
+ if (status_ == kInitialized) |
+ demuxer_->UpdateSize(size_); |
+ first_frame_ = true; |
+ return true; |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+// static |
+RTCVideoDecoderBridgeTv* RTCVideoDecoderBridgeTv::Get() { |
+ return RTCVideoDecoderBridgeTvImpl::GetInstance(); |
+} |
+ |
+} // namespace content |