| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/renderer/media/rtc_video_decoder.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/callback.h" | |
| 11 #include "base/message_loop.h" | |
| 12 #include "media/base/demuxer.h" | |
| 13 #include "media/base/limits.h" | |
| 14 #include "media/base/video_frame.h" | |
| 15 #include "media/base/video_util.h" | |
| 16 #include "third_party/libjingle/source/talk/base/timeutils.h" | |
| 17 #include "third_party/libjingle/source/talk/media/base/videoframe.h" | |
| 18 | |
| 19 using media::CopyUPlane; | |
| 20 using media::CopyVPlane; | |
| 21 using media::CopyYPlane; | |
| 22 using media::DemuxerStream; | |
| 23 using media::kNoTimestamp; | |
| 24 using media::PIPELINE_OK; | |
| 25 using media::PipelineStatusCB; | |
| 26 using media::StatisticsCB; | |
| 27 using media::VideoDecoder; | |
| 28 | |
| 29 namespace content { | |
| 30 | |
| 31 RTCVideoDecoder::RTCVideoDecoder(base::TaskRunner* video_decoder_thread, | |
| 32 base::TaskRunner* main_thread, | |
| 33 webrtc::VideoTrackInterface* video_track) | |
| 34 : video_decoder_thread_(video_decoder_thread), | |
| 35 main_thread_(main_thread), | |
| 36 visible_size_(640, 480), | |
| 37 state_(kUnInitialized), | |
| 38 got_first_frame_(false), | |
| 39 video_track_(video_track) { | |
| 40 } | |
| 41 | |
| 42 void RTCVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, | |
| 43 const PipelineStatusCB& status_cb, | |
| 44 const StatisticsCB& statistics_cb) { | |
| 45 main_thread_->PostTask( | |
| 46 FROM_HERE, | |
| 47 base::Bind(&RTCVideoDecoder::RegisterToVideoTrack, this)); | |
| 48 if (!video_decoder_thread_->RunsTasksOnCurrentThread()) { | |
| 49 video_decoder_thread_->PostTask( | |
| 50 FROM_HERE, | |
| 51 base::Bind(&RTCVideoDecoder::Initialize, this, | |
| 52 stream, status_cb, statistics_cb)); | |
| 53 return; | |
| 54 } | |
| 55 | |
| 56 state_ = kNormal; | |
| 57 status_cb.Run(PIPELINE_OK); | |
| 58 | |
| 59 // TODO(acolwell): Implement stats. | |
| 60 } | |
| 61 | |
| 62 void RTCVideoDecoder::Read(const ReadCB& read_cb) { | |
| 63 if (!video_decoder_thread_->RunsTasksOnCurrentThread()) { | |
| 64 video_decoder_thread_->PostTask( | |
| 65 FROM_HERE, | |
| 66 base::Bind(&RTCVideoDecoder::Read, this, read_cb)); | |
| 67 return; | |
| 68 } | |
| 69 | |
| 70 base::AutoLock auto_lock(lock_); | |
| 71 CHECK(read_cb_.is_null()); | |
| 72 | |
| 73 if (state_ == kStopped) { | |
| 74 read_cb.Run(kOk, NULL); | |
| 75 return; | |
| 76 } | |
| 77 | |
| 78 read_cb_ = read_cb; | |
| 79 } | |
| 80 | |
| 81 void RTCVideoDecoder::Reset(const base::Closure& closure) { | |
| 82 if (!video_decoder_thread_->RunsTasksOnCurrentThread()) { | |
| 83 video_decoder_thread_->PostTask( | |
| 84 FROM_HERE, | |
| 85 base::Bind(&RTCVideoDecoder::Reset, this, closure)); | |
| 86 return; | |
| 87 } | |
| 88 | |
| 89 CancelPendingRead(); | |
| 90 closure.Run(); | |
| 91 } | |
| 92 | |
| 93 void RTCVideoDecoder::Stop(const base::Closure& closure) { | |
| 94 main_thread_->PostTask( | |
| 95 FROM_HERE, | |
| 96 base::Bind(&RTCVideoDecoder::DeregisterFromVideoTrack, this)); | |
| 97 if (!video_decoder_thread_->RunsTasksOnCurrentThread()) { | |
| 98 video_decoder_thread_->PostTask( | |
| 99 FROM_HERE, base::Bind(&RTCVideoDecoder::Stop, this, closure)); | |
| 100 return; | |
| 101 } | |
| 102 | |
| 103 state_ = kStopped; | |
| 104 CancelPendingRead(); | |
| 105 closure.Run(); | |
| 106 } | |
| 107 | |
| 108 void RTCVideoDecoder::SetSize(int width, int height) { | |
| 109 visible_size_.SetSize(width, height); | |
| 110 } | |
| 111 | |
| 112 // TODO(wjia): this function can be split into two parts so that the |lock_| | |
| 113 // can be removed. | |
| 114 // First creates media::VideoFrame, then post a task onto |message_loop_| | |
| 115 // to deliver that frame. | |
| 116 void RTCVideoDecoder::RenderFrame(const cricket::VideoFrame* frame) { | |
| 117 // Called from libjingle thread. | |
| 118 DCHECK(frame); | |
| 119 | |
| 120 base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds( | |
| 121 frame->GetTimeStamp() / talk_base::kNumNanosecsPerMillisec); | |
| 122 | |
| 123 ReadCB read_cb; | |
| 124 { | |
| 125 base::AutoLock auto_lock(lock_); | |
| 126 if (read_cb_.is_null() || state_ != kNormal) { | |
| 127 // TODO(ronghuawu): revisit TS adjustment when crbug.com/111672 is | |
| 128 // resolved. | |
| 129 if (got_first_frame_) { | |
| 130 start_time_ += timestamp - last_frame_timestamp_; | |
| 131 } | |
| 132 last_frame_timestamp_ = timestamp; | |
| 133 return; | |
| 134 } | |
| 135 std::swap(read_cb, read_cb_); | |
| 136 } | |
| 137 | |
| 138 // Rebase timestamp with zero as starting point. | |
| 139 if (!got_first_frame_) { | |
| 140 start_time_ = timestamp; | |
| 141 got_first_frame_ = true; | |
| 142 } | |
| 143 | |
| 144 // Always allocate a new frame. | |
| 145 // | |
| 146 // TODO(scherkus): migrate this to proper buffer recycling. | |
| 147 scoped_refptr<media::VideoFrame> video_frame = | |
| 148 media::VideoFrame::CreateFrame(media::VideoFrame::YV12, | |
| 149 visible_size_, | |
| 150 gfx::Rect(visible_size_), | |
| 151 visible_size_, | |
| 152 timestamp - start_time_); | |
| 153 last_frame_timestamp_ = timestamp; | |
| 154 | |
| 155 // Aspect ratio unsupported; DCHECK when there are non-square pixels. | |
| 156 DCHECK_EQ(frame->GetPixelWidth(), 1u); | |
| 157 DCHECK_EQ(frame->GetPixelHeight(), 1u); | |
| 158 | |
| 159 int y_rows = frame->GetHeight(); | |
| 160 int uv_rows = frame->GetHeight() / 2; // YV12 format. | |
| 161 CopyYPlane(frame->GetYPlane(), frame->GetYPitch(), y_rows, video_frame); | |
| 162 CopyUPlane(frame->GetUPlane(), frame->GetUPitch(), uv_rows, video_frame); | |
| 163 CopyVPlane(frame->GetVPlane(), frame->GetVPitch(), uv_rows, video_frame); | |
| 164 | |
| 165 read_cb.Run(kOk, video_frame); | |
| 166 } | |
| 167 | |
| 168 void RTCVideoDecoder::CancelPendingRead() { | |
| 169 DCHECK(video_decoder_thread_->RunsTasksOnCurrentThread()); | |
| 170 ReadCB read_cb; | |
| 171 { | |
| 172 base::AutoLock auto_lock(lock_); | |
| 173 std::swap(read_cb, read_cb_); | |
| 174 } | |
| 175 | |
| 176 if (!read_cb.is_null()) { | |
| 177 read_cb.Run(kOk, NULL); | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 void RTCVideoDecoder::RegisterToVideoTrack() { | |
| 182 DCHECK(main_thread_->RunsTasksOnCurrentThread()); | |
| 183 if (video_track_) | |
| 184 video_track_->AddRenderer(this); | |
| 185 } | |
| 186 | |
| 187 void RTCVideoDecoder::DeregisterFromVideoTrack() { | |
| 188 DCHECK(main_thread_->RunsTasksOnCurrentThread()); | |
| 189 if (video_track_) { | |
| 190 video_track_->RemoveRenderer(this); | |
| 191 video_track_ = NULL; | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 RTCVideoDecoder::~RTCVideoDecoder() { | |
| 196 DCHECK_NE(kNormal, state_); | |
| 197 } | |
| 198 | |
| 199 } // namespace content | |
| OLD | NEW |