Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(727)

Unified Diff: content/renderer/media/rtc_video_decoder.cc

Issue 13890012: Integrate VDA with WebRTC. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add comments and address some review comments Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/renderer/media/rtc_video_decoder.h ('k') | content/renderer/media/rtc_video_decoder_factory.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/renderer/media/rtc_video_decoder.cc
diff --git a/content/renderer/media/rtc_video_decoder.cc b/content/renderer/media/rtc_video_decoder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5a347d95ed218b4bfaef2a37d20169a658c3ad7a
--- /dev/null
+++ b/content/renderer/media/rtc_video_decoder.cc
@@ -0,0 +1,335 @@
+// 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.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
+#include "content/renderer/media/native_handle_impl.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/bind_to_loop.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/decoder_buffer_queue.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/video_decoder_config.h"
+#include "third_party/webrtc/system_wrappers/interface/ref_count.h"
+
+
+namespace content {
+
+class RTCDemuxerStream
+ : public media::DemuxerStream,
+ public base::SupportsWeakPtr<RTCDemuxerStream> {
+ public:
+ RTCDemuxerStream(const scoped_refptr<base::MessageLoopProxy>& message_loop);
+ // 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(gfx::Size size);
+ void QueueBuffer(scoped_refptr<media::DecoderBuffer> buffer);
+ void ClearBuffer();
+
+ private:
+ media::DecoderBufferQueue buffer_queue_;
+ ReadCB read_cb_;
+ // Make sure all the methods are called by the same thread.
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ media::VideoDecoderConfig video_decoder_config_;
+};
+
+RTCDemuxerStream::RTCDemuxerStream(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop)
+ : message_loop_(message_loop) {
+}
+
+void RTCDemuxerStream::Read(const ReadCB& read_cb) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ CHECK(read_cb_.is_null());
+ read_cb_ = read_cb;
+
+ if(!buffer_queue_.IsEmpty()) {
+ base::ResetAndReturn(&read_cb_).Run(
+ DemuxerStream::kOk, buffer_queue_.Pop());
+ }
+}
+
+const media::AudioDecoderConfig& RTCDemuxerStream::audio_decoder_config() {
+ LOG(FATAL) << "Audio is not supported.";
+ return *(new media::AudioDecoderConfig);
+}
+
+const media::VideoDecoderConfig& RTCDemuxerStream::video_decoder_config() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ return video_decoder_config_;
+}
+
+media::DemuxerStream::Type RTCDemuxerStream::type() {
+ return media::DemuxerStream::VIDEO;
+}
+
+void RTCDemuxerStream::EnableBitstreamConverter() {
+ NOTREACHED();
+}
+
+void RTCDemuxerStream::UpdateSize(gfx::Size size) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ gfx::Rect rect(size);
+ video_decoder_config_.Initialize(
+ media::kCodecVP8,
+ media::VP8PROFILE_MAIN,
+ media::VideoFrame::NATIVE_TEXTURE,
+ size, rect, size, NULL, 0, false, false);
+}
+
+void RTCDemuxerStream::QueueBuffer(
+ scoped_refptr<media::DecoderBuffer> buffer) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DVLOG(3) << "QueueBuffer";
+ buffer_queue_.Push(buffer);
+
+ if (!read_cb_.is_null()) {
+ base::ResetAndReturn(&read_cb_).Run(
+ DemuxerStream::kOk, buffer_queue_.Pop());
+ }
+ DVLOG(3) << "QueueBuffer end";
+}
+
+void RTCDemuxerStream::ClearBuffer() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ buffer_queue_.Clear();
+}
+
+RTCVideoDecoder::RTCVideoDecoder(
+ media::VideoDecoder* video_decoder,
+ const scoped_refptr<base::MessageLoopProxy>& message_loop)
+ : video_decoder_(video_decoder),
+ decoder_message_loop_(message_loop),
+ decode_complete_callback_(NULL),
+ pipeline_status_(media::PIPELINE_OK),
+ decoder_waiter_(false, false),
+ stream_(new RTCDemuxerStream(message_loop)),
+ state_(kUninitialized),
+ weak_factory_(this),
+ decoding_error_occurred_(false) {
+ decoder_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoDecoder::InitWeakPtr, base::Unretained(this)));
+ decoder_waiter_.Wait();
+}
+
+void RTCVideoDecoder::InitWeakPtr() {
+ DCHECK(decoder_message_loop_->BelongsToCurrentThread());
+ weak_this_ = weak_factory_.GetWeakPtr();
+ weak_stream_ = stream_->AsWeakPtr();
+ decoder_waiter_.Signal();
+}
+
+RTCVideoDecoder::~RTCVideoDecoder() {
+}
+
+// Called by WebRTC DecodingThread
+int32_t RTCVideoDecoder::InitDecode(
+ const webrtc::VideoCodec* codecSettings,
+ int32_t /*numberOfCores*/) {
+ DVLOG(3) << "InitDecode";
+ if (state_ != kUninitialized) {
+ LOG(ERROR) << "state_ != kUninitialized!";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ if (codecSettings->codecType != webrtc::kVideoCodecVP8) {
+ LOG(ERROR) << "codec != VP8!";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ if (codecSettings->codecSpecific.VP8.feedbackModeOn) {
+ LOG(ERROR) << "Feedback mode not supported";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+
+ decoder_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCDemuxerStream::UpdateSize, weak_stream_,
+ gfx::Size(codecSettings->width, codecSettings->height)));
+ decoder_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&media::VideoDecoder::Initialize,
+ base::Unretained(video_decoder_.get()),
+ base::Unretained(stream_.get()),
+ base::Bind(&RTCVideoDecoder::OnUpdatePipelineStatus,
+ weak_this_),
+ base::Bind(&RTCVideoDecoder::OnUpdateStatistics, weak_this_)));
+ decoder_waiter_.Wait();
+ if (pipeline_status_ != media::PIPELINE_OK) {
+ LOG(ERROR) << "Initialize failed. pipeline_status_=" << pipeline_status_;
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ state_ = kInitialized;
+ DVLOG(3) << "InitDecode end";
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+// Called by WebRTC DecodingThread
+int32_t RTCVideoDecoder::Decode(
+ const webrtc::EncodedImage& inputImage,
+ bool missingFrames,
+ const webrtc::RTPFragmentationHeader* /*fragmentation*/,
+ const webrtc::CodecSpecificInfo* /*codecSpecificInfo*/,
+ int64_t /*renderTimeMs*/) {
+ DVLOG(3) << "Decode";
+
+ if (decoding_error_occurred_) {
+ LOG(ERROR) << "Decoding error occurred.";
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
+ if (state_ == kUninitialized || decode_complete_callback_ == NULL) {
+ LOG(ERROR) << "WebRTC video codec unintialized.";
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ if (missingFrames || !inputImage._completeFrame) {
+ LOG(ERROR) << "Missing frames or not completed frames.";
+ // Unlike the SW decoder in libvpx, hw decoder cannot handle broken frames.
+ // Return an error 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;
+ decoder_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCDemuxerStream::UpdateSize, weak_stream_, new_size));
+ }
+ }
+ scoped_refptr<media::DecoderBuffer> buffer = media::DecoderBuffer::CopyFrom(
wuchengli 2013/05/10 15:57:15 Copy is necessary because the buffer will be relea
+ inputImage._buffer, inputImage._length);
+ buffer->SetTimestamp(base::TimeDelta::FromInternalValue(
+ inputImage._timeStamp));
+
+ decoder_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCDemuxerStream::QueueBuffer, weak_stream_, buffer));
+
+ if (state_ == kInitialized) {
+ state_ = kDecoding;
+ decoder_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&RTCVideoDecoder::RequestFrame, weak_this_));
+ }
+
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t RTCVideoDecoder::RegisterDecodeCompleteCallback(
+ webrtc::DecodedImageCallback* callback) {
+ decode_complete_callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t RTCVideoDecoder::Release() {
+ DVLOG(3) << "Release";
+ if (state_ == kUninitialized) {
+ LOG(ERROR) << "Decoder not initialized.";
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ decoder_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&media::VideoDecoder::Stop,
+ base::Unretained(video_decoder_.get()),
+ base::Bind(&RTCVideoDecoder::ReleaseComplete, weak_this_)));
+ decoder_waiter_.Wait();
+ state_ = kUninitialized;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t RTCVideoDecoder::Reset() {
+ DVLOG(3) << "Reset";
+ if (state_ == kUninitialized) {
+ LOG(ERROR) << "Decoder not initialized.";
+ return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ }
+ decoder_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&media::VideoDecoder::Reset,
+ base::Unretained(video_decoder_.get()),
+ base::Bind(&RTCVideoDecoder::ResetComplete, weak_this_)));
+ decoder_waiter_.Wait();
+ state_ = kInitialized;
+ DVLOG(3) << "Reset end";
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+void RTCVideoDecoder::OnUpdateStatistics(
+ const media::PipelineStatistics& /*stats*/) {
+ // We don't use statistics for now.
+}
+
+void RTCVideoDecoder::OnUpdatePipelineStatus(
+ const media::PipelineStatus status) {
+ DVLOG(3) << "OnUpdatePipelineStatus. status=" << status;
+ DCHECK(decoder_message_loop_->BelongsToCurrentThread());
+ pipeline_status_ = status;
+ decoder_waiter_.Signal();
+}
+
+void RTCVideoDecoder::ReleaseComplete() {
+ DVLOG(3) << "ReleaseComplete";
+ DCHECK(decoder_message_loop_->BelongsToCurrentThread());
+ stream_->ClearBuffer();
+ decoder_waiter_.Signal();
+}
+
+void RTCVideoDecoder::ResetComplete() {
+ DVLOG(3) << "ResetComplete";
+ DCHECK(decoder_message_loop_->BelongsToCurrentThread());
+ stream_->ClearBuffer();
+ decoder_waiter_.Signal();
+}
+
+void RTCVideoDecoder::FrameReady(
+ media::VideoDecoder::Status status,
+ const scoped_refptr<media::VideoFrame>& frame) {
+ DCHECK(decoder_message_loop_->BelongsToCurrentThread());
+ DVLOG(3) << "FrameReady. status=" << status;
+
+ if (status != media::VideoDecoder::kOk) {
+ LOG(ERROR) << "FrameReady error. status=" << status;
+ decoding_error_occurred_ = true;
+ return;
+ }
+ gfx::Rect rect = frame->visible_rect();
+
+ webrtc::I420VideoFrame decoded_image;
+ decoded_image.CreateEmptyFrame(
+ rect.width(), rect.height(),
+ rect.width(), rect.width() / 2, rect.width() / 2);
+ webrtc::RefCountImpl<NativeHandleImpl>* handle =
+ new webrtc::RefCountImpl<NativeHandleImpl>();
+ handle->SetHandle(frame.get());
+ decoded_image.set_native_handle(handle);
+ decoded_image.set_timestamp(
+ static_cast<uint32_t>(frame->GetTimestamp().InMicroseconds()));
+ decode_complete_callback_->Decoded(decoded_image);
+
+ if (state_ == kDecoding) {
+ video_decoder_->Read(base::Bind(&RTCVideoDecoder::FrameReady, weak_this_));
+ }
+}
+
+void RTCVideoDecoder::RequestFrame() {
+ DCHECK(decoder_message_loop_->BelongsToCurrentThread());
+ video_decoder_->Read(
+ base::Bind(&RTCVideoDecoder::FrameReady, weak_this_));
+}
+
+} // namespace content
+
« no previous file with comments | « content/renderer/media/rtc_video_decoder.h ('k') | content/renderer/media/rtc_video_decoder_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698