OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/media/capture_video_decoder.h" | 5 #include "content/renderer/media/capture_video_decoder.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "content/renderer/media/video_capture_impl_manager.h" | 8 #include "content/renderer/media/video_capture_impl_manager.h" |
9 #include "media/base/filter_host.h" | 9 #include "media/base/filter_host.h" |
10 #include "media/base/limits.h" | 10 #include "media/base/limits.h" |
(...skipping 23 matching lines...) Expand all Loading... |
34 media::DemuxerStream* demuxer_stream, | 34 media::DemuxerStream* demuxer_stream, |
35 const base::Closure& filter_callback, | 35 const base::Closure& filter_callback, |
36 const media::StatisticsCallback& stat_callback) { | 36 const media::StatisticsCallback& stat_callback) { |
37 message_loop_proxy_->PostTask( | 37 message_loop_proxy_->PostTask( |
38 FROM_HERE, | 38 FROM_HERE, |
39 base::Bind(&CaptureVideoDecoder::InitializeOnDecoderThread, | 39 base::Bind(&CaptureVideoDecoder::InitializeOnDecoderThread, |
40 this, make_scoped_refptr(demuxer_stream), | 40 this, make_scoped_refptr(demuxer_stream), |
41 filter_callback, stat_callback)); | 41 filter_callback, stat_callback)); |
42 } | 42 } |
43 | 43 |
44 void CaptureVideoDecoder::ProduceVideoFrame( | 44 void CaptureVideoDecoder::Read(const ReadCB& callback) { |
45 scoped_refptr<media::VideoFrame> video_frame) { | |
46 message_loop_proxy_->PostTask( | 45 message_loop_proxy_->PostTask( |
47 FROM_HERE, | 46 FROM_HERE, |
48 base::Bind(&CaptureVideoDecoder::ProduceVideoFrameOnDecoderThread, | 47 base::Bind(&CaptureVideoDecoder::ReadOnDecoderThread, |
49 this, video_frame)); | 48 this, callback)); |
50 } | 49 } |
51 | 50 |
52 gfx::Size CaptureVideoDecoder::natural_size() { | 51 gfx::Size CaptureVideoDecoder::natural_size() { |
53 return gfx::Size(capability_.width, capability_.height); | 52 return gfx::Size(capability_.width, capability_.height); |
54 } | 53 } |
55 | 54 |
56 void CaptureVideoDecoder::Play(const base::Closure& callback) { | 55 void CaptureVideoDecoder::Play(const base::Closure& callback) { |
57 message_loop_proxy_->PostTask( | 56 message_loop_proxy_->PostTask( |
58 FROM_HERE, | 57 FROM_HERE, |
59 base::Bind(&CaptureVideoDecoder::PlayOnDecoderThread, | 58 base::Bind(&CaptureVideoDecoder::PlayOnDecoderThread, |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 | 123 |
125 void CaptureVideoDecoder::InitializeOnDecoderThread( | 124 void CaptureVideoDecoder::InitializeOnDecoderThread( |
126 media::DemuxerStream* demuxer_stream, | 125 media::DemuxerStream* demuxer_stream, |
127 const base::Closure& filter_callback, | 126 const base::Closure& filter_callback, |
128 const media::StatisticsCallback& stat_callback) { | 127 const media::StatisticsCallback& stat_callback) { |
129 VLOG(1) << "InitializeOnDecoderThread."; | 128 VLOG(1) << "InitializeOnDecoderThread."; |
130 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); | 129 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); |
131 | 130 |
132 capture_engine_ = vc_manager_->AddDevice(video_stream_id_, this); | 131 capture_engine_ = vc_manager_->AddDevice(video_stream_id_, this); |
133 | 132 |
134 available_frames_.clear(); | |
135 | |
136 statistics_callback_ = stat_callback; | 133 statistics_callback_ = stat_callback; |
137 filter_callback.Run(); | 134 filter_callback.Run(); |
138 state_ = kNormal; | 135 state_ = kNormal; |
139 } | 136 } |
140 | 137 |
141 void CaptureVideoDecoder::ProduceVideoFrameOnDecoderThread( | 138 void CaptureVideoDecoder::ReadOnDecoderThread(const ReadCB& callback) { |
142 scoped_refptr<media::VideoFrame> video_frame) { | |
143 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); | 139 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); |
144 available_frames_.push_back(video_frame); | 140 CHECK(read_cb_.is_null()); |
| 141 read_cb_ = callback; |
145 } | 142 } |
146 | 143 |
147 void CaptureVideoDecoder::PlayOnDecoderThread(const base::Closure& callback) { | 144 void CaptureVideoDecoder::PlayOnDecoderThread(const base::Closure& callback) { |
148 VLOG(1) << "PlayOnDecoderThread."; | 145 VLOG(1) << "PlayOnDecoderThread."; |
149 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); | 146 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); |
150 callback.Run(); | 147 callback.Run(); |
151 } | 148 } |
152 | 149 |
153 void CaptureVideoDecoder::PauseOnDecoderThread(const base::Closure& callback) { | 150 void CaptureVideoDecoder::PauseOnDecoderThread(const base::Closure& callback) { |
154 VLOG(1) << "PauseOnDecoderThread."; | 151 VLOG(1) << "PauseOnDecoderThread."; |
155 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); | 152 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); |
156 state_ = kPaused; | 153 state_ = kPaused; |
157 media::VideoDecoder::Pause(callback); | 154 media::VideoDecoder::Pause(callback); |
158 } | 155 } |
159 | 156 |
160 void CaptureVideoDecoder::StopOnDecoderThread(const base::Closure& callback) { | 157 void CaptureVideoDecoder::StopOnDecoderThread(const base::Closure& callback) { |
161 VLOG(1) << "StopOnDecoderThread."; | 158 VLOG(1) << "StopOnDecoderThread."; |
162 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); | 159 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); |
163 pending_stop_cb_ = callback; | 160 pending_stop_cb_ = callback; |
164 state_ = kStopped; | 161 state_ = kStopped; |
165 capture_engine_->StopCapture(this); | 162 capture_engine_->StopCapture(this); |
166 } | 163 } |
167 | 164 |
168 void CaptureVideoDecoder::SeekOnDecoderThread(base::TimeDelta time, | 165 void CaptureVideoDecoder::SeekOnDecoderThread(base::TimeDelta time, |
169 const media::FilterStatusCB& cb) { | 166 const media::FilterStatusCB& cb) { |
170 VLOG(1) << "SeekOnDecoderThread."; | 167 VLOG(1) << "SeekOnDecoderThread."; |
171 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); | 168 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); |
172 | 169 |
173 state_ = kSeeking; | |
174 // Create output buffer pool and pass the frames to renderer | |
175 // so that the renderer can complete the seeking | |
176 for (size_t i = 0; i < media::Limits::kMaxVideoFrames; ++i) { | |
177 VideoFrameReady(media::VideoFrame::CreateBlackFrame(capability_.width, | |
178 capability_.height)); | |
179 } | |
180 | |
181 cb.Run(media::PIPELINE_OK); | 170 cb.Run(media::PIPELINE_OK); |
182 state_ = kNormal; | 171 state_ = kNormal; |
183 capture_engine_->StartCapture(this, capability_); | 172 capture_engine_->StartCapture(this, capability_); |
184 } | 173 } |
185 | 174 |
186 void CaptureVideoDecoder::OnStoppedOnDecoderThread( | 175 void CaptureVideoDecoder::OnStoppedOnDecoderThread( |
187 media::VideoCapture* capture) { | 176 media::VideoCapture* capture) { |
188 VLOG(1) << "OnStoppedOnDecoderThread."; | 177 VLOG(1) << "OnStoppedOnDecoderThread."; |
189 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); | 178 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); |
190 if (!pending_stop_cb_.is_null()) | 179 if (!pending_stop_cb_.is_null()) |
191 media::ResetAndRunCB(&pending_stop_cb_); | 180 media::ResetAndRunCB(&pending_stop_cb_); |
192 vc_manager_->RemoveDevice(video_stream_id_, this); | 181 vc_manager_->RemoveDevice(video_stream_id_, this); |
193 } | 182 } |
194 | 183 |
195 void CaptureVideoDecoder::OnBufferReadyOnDecoderThread( | 184 void CaptureVideoDecoder::OnBufferReadyOnDecoderThread( |
196 media::VideoCapture* capture, | 185 media::VideoCapture* capture, |
197 scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf) { | 186 scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf) { |
198 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); | 187 DCHECK(message_loop_proxy_->BelongsToCurrentThread()); |
199 | 188 |
200 if (available_frames_.size() == 0 || kNormal != state_) { | 189 if (read_cb_.is_null() || kNormal != state_) { |
201 capture->FeedBuffer(buf); | 190 capture->FeedBuffer(buf); |
202 return; | 191 return; |
203 } | 192 } |
204 | 193 |
205 scoped_refptr<media::VideoFrame> video_frame = available_frames_.front(); | |
206 available_frames_.pop_front(); | |
207 | |
208 if (buf->width != capability_.width || buf->height != capability_.height) { | 194 if (buf->width != capability_.width || buf->height != capability_.height) { |
209 capability_.width = buf->width; | 195 capability_.width = buf->width; |
210 capability_.height = buf->height; | 196 capability_.height = buf->height; |
211 host()->SetNaturalVideoSize( | 197 host()->SetNaturalVideoSize( |
212 gfx::Size(capability_.width, capability_.height)); | 198 gfx::Size(capability_.width, capability_.height)); |
213 } | 199 } |
214 | 200 |
215 // Check if there's a size change. | 201 // Always allocate a new frame. |
216 if (static_cast<int>(video_frame->width()) != capability_.width || | 202 // |
217 static_cast<int>(video_frame->height()) != capability_.height) { | 203 // TODO(scherkus): migrate this to proper buffer recycling. |
218 // Allocate new buffer based on the new size. | 204 scoped_refptr<media::VideoFrame> video_frame = |
219 video_frame = media::VideoFrame::CreateFrame(media::VideoFrame::YV12, | 205 media::VideoFrame::CreateFrame(media::VideoFrame::YV12, |
220 capability_.width, | 206 capability_.width, |
221 capability_.height, | 207 capability_.height, |
222 media::kNoTimestamp, | 208 buf->timestamp - start_time_, |
223 media::kNoTimestamp); | 209 base::TimeDelta::FromMilliseconds(33)); |
224 } | |
225 | |
226 video_frame->SetTimestamp(buf->timestamp - start_time_); | |
227 video_frame->SetDuration(base::TimeDelta::FromMilliseconds(33)); | |
228 | 210 |
229 uint8* buffer = buf->memory_pointer; | 211 uint8* buffer = buf->memory_pointer; |
230 | 212 |
231 // Assume YV12 format. | 213 // Assume YV12 format. |
232 // TODO(vrk): This DCHECK fails in content_unittests ... it should not! | 214 // TODO(vrk): This DCHECK fails in content_unittests ... it should not! |
233 // DCHECK(capability_.raw_type == media::VideoFrame::YV12); | 215 // DCHECK(capability_.raw_type == media::VideoFrame::YV12); |
234 int y_width = capability_.width; | 216 int y_width = capability_.width; |
235 int y_height = capability_.height; | 217 int y_height = capability_.height; |
236 int uv_width = capability_.width / 2; | 218 int uv_width = capability_.width / 2; |
237 int uv_height = capability_.height / 2; // YV12 format. | 219 int uv_height = capability_.height / 2; // YV12 format. |
238 CopyYPlane(buffer, y_width, y_height, video_frame); | 220 CopyYPlane(buffer, y_width, y_height, video_frame); |
239 buffer += y_width * y_height; | 221 buffer += y_width * y_height; |
240 CopyUPlane(buffer, uv_width, uv_height, video_frame); | 222 CopyUPlane(buffer, uv_width, uv_height, video_frame); |
241 buffer += uv_width * uv_height; | 223 buffer += uv_width * uv_height; |
242 CopyVPlane(buffer, uv_width, uv_height, video_frame); | 224 CopyVPlane(buffer, uv_width, uv_height, video_frame); |
243 | 225 |
244 VideoFrameReady(video_frame); | 226 DeliverFrame(video_frame); |
245 capture->FeedBuffer(buf); | 227 capture->FeedBuffer(buf); |
246 } | 228 } |
| 229 |
| 230 void CaptureVideoDecoder::DeliverFrame( |
| 231 const scoped_refptr<media::VideoFrame>& video_frame) { |
| 232 // Reset the callback before running to protect against reentrancy. |
| 233 ReadCB read_cb = read_cb_; |
| 234 read_cb_.Reset(); |
| 235 read_cb.Run(video_frame); |
| 236 } |
OLD | NEW |