Chromium Code Reviews| Index: content/renderer/media/rtc_video_decoder_bridge.cc |
| diff --git a/content/renderer/media/rtc_video_decoder_bridge.cc b/content/renderer/media/rtc_video_decoder_bridge.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7bc555c5a9f5a4339d25663e36c52573570cb6a2 |
| --- /dev/null |
| +++ b/content/renderer/media/rtc_video_decoder_bridge.cc |
| @@ -0,0 +1,325 @@ |
| +// 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.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/message_loop_proxy.h" |
| +#include "base/time.h" |
| +#include "content/renderer/media/native_handle_impl.h" |
| +#include "media/base/decoder_buffer.h" |
| +#include "media/base/yuv_convert.h" |
| +#include "third_party/libjingle/source/talk/base/ratetracker.h" // XXX remove |
| +#include "third_party/webrtc/system_wrappers/interface/ref_count.h" |
| + |
| + |
| +namespace content { |
| + |
| +#define LOG_LINE() VLOG(0) << __FILE__ << ":" << __FUNCTION__ |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
Planning to remove before landing?
wuchengli
2013/05/08 15:58:56
Removed.
On 2013/04/26 00:42:05, Ami Fischman wrot
|
| + |
| +// static |
| +const base::TimeDelta RTCVideoDecoderBridge::kDecoderTimeOut = |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
Why is this a good idea?
wuchengli
2013/05/08 15:58:56
Removed. Now InitDecode waits for GpuVideoDecoder:
|
| + base::TimeDelta::FromMilliseconds(3000); |
| + |
| +// Helper function that makes sure |read_cb| runs on |message_loop_proxy|. |
| +static void RunOnMessageLoop( |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
Any reason this can't be replaced by BindToLoop?
wuchengli
2013/05/08 15:58:56
Removed.
|
| + 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); |
| +} |
| + |
| +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()); |
| + } |
| +} |
| + |
| +const media::AudioDecoderConfig& RTCDemuxerStream::audio_decoder_config() { |
| + CHECK(false) << "Does not support audio."; |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
FWIW
LOG(FATAL)
reads slightly more clearly to me
wuchengli
2013/05/08 15:58:56
Done.
|
| + return dummy_audio_decoder_config_; |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
this is unreachable, and therefore the member is u
wuchengli
2013/05/08 15:58:56
Removed.
|
| +} |
| + |
| +const media::VideoDecoderConfig& RTCDemuxerStream::video_decoder_config() { |
| + return video_decoder_config_; |
| +} |
| + |
| +media::DemuxerStream::Type RTCDemuxerStream::type() { |
| + return media::DemuxerStream::VIDEO; |
| +} |
| + |
| +void RTCDemuxerStream::EnableBitstreamConverter() { |
| +} |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
LOG(FATAL) << "Unreachable!";
wuchengli
2013/05/08 15:58:56
Changed to NOTREACHED();
|
| + |
| +void RTCDemuxerStream::UpdateSize(gfx::Size size) { |
| + gfx::Rect rect(size); |
| + video_decoder_config_.Initialize( |
| + media::kCodecVP8, |
| + media::VP8PROFILE_MAIN, |
| + media::VideoFrame::NATIVE_TEXTURE, |
| + size, rect, size, NULL, 0, false, false); |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
Coded==visible==natural sizes/rects?
I suspect the
wuchengli
2013/05/08 15:58:56
Can you explain more? I'm not sure what to put in
|
| +} |
| + |
| +void RTCDemuxerStream::QueueBuffer( |
| + scoped_refptr<media::DecoderBuffer> buffer) { |
| + LOG_LINE(); |
| + LOG_LINE() << " end"; |
| + buffer_queue_.Push(buffer); |
| + |
| + if (!read_cb_.is_null()) { |
| + base::ResetAndReturn(&read_cb_).Run( |
| + DemuxerStream::kOk, buffer_queue_.Pop()); |
| + } |
| +} |
| + |
| +RTCVideoDecoderBridge::RTCVideoDecoderBridge( |
| + scoped_refptr<media::VideoDecoder> video_decoder, |
| + const scoped_refptr<base::MessageLoopProxy>& message_loop) |
| + : video_decoder_(video_decoder), |
| + loop_proxy_(message_loop), |
| + decode_complete_callback_(NULL), |
| + pipeline_status_(media::PIPELINE_OK), |
| + decoder_init_waiter_(false, false), |
| + decoder_reset_waiter_(false, false), |
| + stream_(new RTCDemuxerStream()), |
| + status_(kNoInit), |
| + decoding_error_occured_(false) { |
| + LOG_LINE(); |
| +} |
| + |
| +RTCVideoDecoderBridge::~RTCVideoDecoderBridge() { |
| + LOG_LINE(); |
| +} |
| + |
| +WebRtc_Word32 RTCVideoDecoderBridge::InitDecode( |
| + const webrtc::VideoCodec* codecSettings, |
| + WebRtc_Word32 numberOfCores) { |
| + LOG_LINE(); |
| + if (status_ != kNoInit) { |
| + return WEBRTC_VIDEO_CODEC_ERROR; |
| + } |
| + if (codecSettings->codecType != webrtc::kVideoCodecVP8) { |
|
Pawel Osciak
2013/04/26 07:08:01
Can this happen if the check in RTCVideoDecoderFac
wuchengli
2013/05/08 15:58:56
All possible codecType are kVideoCodecVP8, kVideoC
|
| + return WEBRTC_VIDEO_CODEC_ERROR; |
| + } |
| + if (codecSettings->codecSpecific.VP8.feedbackModeOn) { |
| + // We don't support feedback mode. |
| + return WEBRTC_VIDEO_CODEC_ERROR; |
| + } |
| + |
| + loop_proxy_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&RTCDemuxerStream::UpdateSize, stream_, |
| + gfx::Size(codecSettings->width, codecSettings->height))); |
| + loop_proxy_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&media::VideoDecoder::Initialize, |
| + video_decoder_, |
| + stream_, |
| + base::Bind(&RTCVideoDecoderBridge::OnUpdatePipelineStatus, |
| + base::Unretained(this)), |
| + base::Bind(&RTCVideoDecoderBridge::OnUpdateStatistics, |
| + base::Unretained(this)))); |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
why is this Unretained safe?
|
| + decoder_init_waiter_.TimedWait(kDecoderTimeOut); |
| + if (pipeline_status_ != media::PIPELINE_OK) { |
| + return WEBRTC_VIDEO_CODEC_ERROR; |
| + } |
|
Pawel Osciak
2013/04/26 07:08:01
No need for braces.
wuchengli
2013/05/08 15:58:56
I added one more line.
|
| + status_ = kInitialized; |
| + LOG_LINE() << "OK"; |
| + return WEBRTC_VIDEO_CODEC_OK; |
| +} |
| + |
| +void RTCVideoDecoderBridge::OnUpdateStatistics( |
| + const media::PipelineStatistics& stats) { |
| + // We don't use statistics for now. |
| +} |
| + |
| +void RTCVideoDecoderBridge::OnUpdatePipelineStatus( |
| + const media::PipelineStatus status) { |
| + VLOG(0) << "RTCVideoDecoderBridge::OnUpdatePipelineStatus. status=" << status; |
| + pipeline_status_ = status; |
| + decoder_init_waiter_.Signal(); |
| +} |
| + |
| +void RTCVideoDecoderBridge::ResetComplete() { |
| + LOG_LINE(); |
| + decoder_reset_waiter_.Signal(); |
| +} |
| + |
| + |
| +WebRtc_Word32 RTCVideoDecoderBridge::Decode( |
| + const webrtc::EncodedImage& inputImage, |
| + bool missingFrames, |
| + const webrtc::RTPFragmentationHeader* fragmentation, |
| + const webrtc::CodecSpecificInfo* codecSpecificInfo, |
| + WebRtc_Word64 renderTimeMs) { |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
unused parameters can have their name omitted, whi
wuchengli
2013/05/08 15:58:56
Done.
|
| + VLOG(0) << "RTCVideoDecoderBridge::Decode. status=" << status_; |
| + { |
| + base::AutoLock auto_lock(lock_); |
|
Pawel Osciak
2013/04/26 07:08:01
Lock usage is mysterious here, especially since th
wuchengli
2013/05/08 15:58:56
decoding_error_occurred_ is read by WebRTC Decodin
|
| + if (decoding_error_occured_) { |
| + // TODO(dwkang): consider creating a SW decoder and falling back to it. |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
s/dwkang/wuchengli/g ? :)
wuchengli
2013/05/08 15:58:56
Removed the todo. This doesn't look like the right
|
| + return WEBRTC_VIDEO_CODEC_ERROR; |
| + } |
| + } |
| + if (status_ == kNoInit || decode_complete_callback_ == NULL) { |
| + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| + } |
|
Pawel Osciak
2013/04/26 07:08:01
Braces not needed and below too.
wuchengli
2013/05/08 15:58:56
Done.
|
| + if (!(status_ == kInitialized || status_ == kDecoding)) { |
|
Pawel Osciak
2013/04/26 07:08:01
if (status_ != kInitialized && status_ != kDecodin
wuchengli
2013/05/08 15:58:56
Done.
|
| + 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 (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; |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
member never used; drop?
wuchengli
2013/05/08 15:58:56
This is used so that RTCDemuxerStream::UpdateSize
|
| + loop_proxy_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&RTCDemuxerStream::UpdateSize, stream_, new_size)); |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
In this case, you need to signal DemuxerStream::kC
|
| + } |
| + } |
| + scoped_refptr<media::DecoderBuffer> buffer; |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
I only got down to here but I think I see your pro
wuchengli
2013/04/26 05:08:08
GpuVideoDecoder::Read is called in RTCVideoDecoder
Ami GONE FROM CHROMIUM
2013/04/26 15:13:34
I see. IIUC the code will wait for each frame to
wuchengli
2013/05/09 15:43:14
The latest patchset does not wait from the GPU pro
|
| + buffer = media::DecoderBuffer::CopyFrom( |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
assign on prev line?
wuchengli
2013/05/08 15:58:56
Done.
|
| + inputImage._buffer, inputImage._length); |
| + buffer->SetTimestamp(base::TimeDelta::FromInternalValue( |
| + inputImage._timeStamp)); |
|
Ami GONE FROM CHROMIUM
2013/04/26 00:42:05
This looks strange; internalvalue is in microsecon
wuchengli
2013/05/09 15:43:14
inputImage._timeStamp is in 90KHz. We need to divi
|
| + |
| + loop_proxy_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&RTCDemuxerStream::QueueBuffer, stream_, buffer)); |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + for (VideoFrameList::iterator it = ready_video_frames_.begin(); |
| + it != ready_video_frames_.end(); ++it) { |
| + gfx::Rect rect = (*it)->visible_rect(); |
| + decoded_image_.CreateEmptyFrame( |
| + rect.width(), rect.height(), |
| + rect.width(), rect.width() / 2, rect.width() / 2); |
| + webrtc::RefCountImpl<media::NativeHandleImpl>* handle = |
| + new webrtc::RefCountImpl<media::NativeHandleImpl>(); |
| + handle->SetHandle((*it).get()); |
| + decoded_image_.set_native_handle(handle); |
| + decoded_image_.set_timestamp( |
| + static_cast<uint32_t>((*it)->GetTimestamp().InMicroseconds())); |
| + decode_complete_callback_->Decoded(decoded_image_); |
| + decoded_image_.set_native_handle(NULL); |
| + } |
| + ready_video_frames_.clear(); |
| + } |
| + |
| + if (status_ == kInitialized) { |
| + status_ = kDecoding; |
| + RequestFrame(); |
| + } |
| + |
| + return WEBRTC_VIDEO_CODEC_OK; |
| +} |
| + |
| +void RTCVideoDecoderBridge::RequestFrame() { |
| + loop_proxy_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&media::VideoDecoder::Read, |
| + video_decoder_, |
| + base::Bind(&RTCVideoDecoderBridge::FrameReady, |
| + base::AsWeakPtr(this)))); |
| +} |
| + |
| +WebRtc_Word32 RTCVideoDecoderBridge::RegisterDecodeCompleteCallback( |
| + webrtc::DecodedImageCallback* callback) { |
| + decode_complete_callback_ = callback; |
| + return WEBRTC_VIDEO_CODEC_OK; |
| +} |
| + |
| +WebRtc_Word32 RTCVideoDecoderBridge::Release() { |
| + LOG_LINE(); |
| + /*if (status_ == kNoInit) { |
| + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| + } |
| + base::WaitableEvent event(false, false); |
| + loop_proxy_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&media::VideoDecoder::Stop, |
| + video_decoder_, |
| + base::Bind(&base::WaitableEvent::Signal, |
| + base::Unretained(&event)))); |
| + decoder_init_waiter_.Wait(); |
| + status_ = kReleased;*/ |
| + return WEBRTC_VIDEO_CODEC_OK; |
| +} |
| + |
| +WebRtc_Word32 RTCVideoDecoderBridge::Reset() { |
| + LOG_LINE(); |
| + if (status_ == kNoInit) { |
| + LOG(ERROR) << "RTCVideoDecoderBridge::Reset. status==kNoInit"; |
| + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| + } |
| + status_ = kReseting; |
| + base::WaitableEvent event(false, false); |
|
Pawel Osciak
2013/04/26 07:08:01
unused?
wuchengli
2013/05/08 15:58:56
Removed.
|
| + loop_proxy_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&media::VideoDecoder::Reset, |
| + video_decoder_, |
| + base::Bind(&RTCVideoDecoderBridge::ResetComplete, |
| + base::Unretained(this)))); |
| + decoder_reset_waiter_.Wait(); |
| + status_ = kInitialized; |
| + LOG_LINE() << " OK"; |
| + return WEBRTC_VIDEO_CODEC_OK; |
| +} |
| + |
| +void RTCVideoDecoderBridge::FrameReady( |
| + media::VideoDecoder::Status status, |
| + const scoped_refptr<media::VideoFrame>& frame) { |
| + VLOG(0) << "RTCVideoDecoderBridge::FrameReady. status=" << status |
| + << ". status_=" << status_; |
| + /*{ |
| + static talk_base::RateTracker frame_rate_tracker_; |
| + static base::Time start_time = base::Time::Now(); |
| + base::Time diff = base::Time::Now() - start_time; |
| + VLOG(1) << "RTCVideoDecoderBridge::FrameReady DeliverFrame framerate " |
| + << frame_rate_tracker_.units_second() |
| + << " avg framerate " |
| + << frame_rate_tracker_.total_units() / diff.InSecondsF(); |
| + frame_rate_tracker_.Update(1); |
| + }*/ |
| + |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + if (status != media::VideoDecoder::kOk) { |
| + LOG(ERROR) << "RTCVideoDecoderBridge::FrameReady. status!=kOk"; |
| + decoding_error_occured_ = true; |
| + return; |
| + } |
| + |
| + ready_video_frames_.push_back(frame); |
|
wuchengli
2013/04/25 12:38:54
How can I call decode_complete_callback_ using web
|
| + } |
| + if (status_ == kDecoding) { |
| + RequestFrame(); |
| + } |
| +} |
| + |
| +} // namespace content |
| + |