| 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
|
|
|