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 |