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