OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 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_encoder.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/location.h" | |
11 #include "base/logging.h" | |
12 #include "base/message_loop/message_loop_proxy.h" | |
13 #include "base/synchronization/waitable_event.h" | |
14 #include "media/base/bitstream_buffer.h" | |
15 #include "media/filters/gpu_video_accelerator_factories.h" | |
16 #include "media/video/video_encode_accelerator.h" | |
17 | |
18 namespace content { | |
19 | |
20 // This private class of RTCVideoEncoder does the actual work of communicating | |
21 // with a media::VideoEncodeAccelerator for handling video encoding. It can | |
22 // be created on any thread, but should subsequently be posted to (and | |
23 // destroyed) on the thread that calls CreateAndInitializeVEA(). Notifications | |
24 // returned by the encoder are posted to the thread on which the instance was | |
25 // constructed. | |
26 // | |
27 // This class is separated from the RTCVideoEncoder class to allow | |
28 // RTCVideoEncoder to be deleted directly by WebRTC, while RTCVideoEncoder::Impl | |
29 // stays long enough to properly shut down the VEA. | |
30 class RTCVideoEncoder::Impl : public media::VideoEncodeAccelerator::Client { | |
31 public: | |
32 Impl(const base::WeakPtr<RTCVideoEncoder>& weak_encoder); | |
Ami GONE FROM CHROMIUM
2013/07/31 23:01:12
single-arg ctors need to be marked explicit.
sheu
2013/08/02 01:27:49
Done.
| |
33 virtual ~Impl(); | |
34 | |
35 // Create the VEA and call Initialize() on it. This instance of Impl is bound | |
36 // to whichever thread makes this call. | |
37 void CreateAndInitializeVEA( | |
38 const webrtc::VideoCodec& codecSettings, | |
39 media::VideoCodecProfile profile, | |
40 base::WaitableEvent* initialization_waiter, | |
41 int32_t* initialization_retval, | |
42 const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories); | |
43 void Encode(const media::BitstreamBuffer& buffer, bool force_keyframe); | |
44 void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer); | |
45 void RequestEncodingParameterChange(int32 bitrate); | |
46 static void Destroy(scoped_ptr<Impl> self); | |
47 | |
48 // media::VideoEncodeAccelerator::Client implementation. | |
49 virtual void NotifyInitializeDone() OVERRIDE; | |
50 virtual void RequireBitstreamBuffers(int input_count, | |
51 const gfx::Size& input_dimensions, | |
52 size_t output_size) OVERRIDE; | |
53 virtual void NotifyInputDone(int32 bitstream_buffer_id) OVERRIDE; | |
54 virtual void BitstreamBufferReady(int32 bitstream_buffer_id, | |
55 size_t payload_size, | |
56 bool key_frame) OVERRIDE; | |
57 virtual void NotifyError(media::VideoEncodeAccelerator::Error error) OVERRIDE; | |
58 | |
59 private: | |
60 base::ThreadChecker thread_checker_; | |
61 | |
62 // Weak pointer to the parent RTCVideoEncoder, for posting back VEA::Client | |
63 // notifications. | |
64 const base::WeakPtr<RTCVideoEncoder> weak_encoder_; | |
65 | |
66 // The message loop on which to post notifications. | |
67 const scoped_refptr<base::MessageLoopProxy> encoder_message_loop_proxy_; | |
68 | |
69 // webrtc::VideoEncoder expects initialization to be synchronous. Do this by | |
70 // waiting on the |initialization_waiter_| in InitEncode(). The event is | |
71 // signalled when initialization completes, or an error ooccurs, and the | |
72 // return value is retuned in |initialization_retval_|. | |
73 base::WaitableEvent* initialization_waiter_; | |
74 int32_t* initialization_retval_; | |
75 | |
76 // The underling VEA to perform encoding on. | |
77 scoped_ptr<media::VideoEncodeAccelerator> video_encoder_; | |
78 | |
79 DISALLOW_COPY_AND_ASSIGN(Impl); | |
80 }; | |
81 | |
82 RTCVideoEncoder::Impl::Impl( | |
83 const base::WeakPtr<RTCVideoEncoder>& weak_encoder) | |
84 : weak_encoder_(weak_encoder), | |
85 encoder_message_loop_proxy_(base::MessageLoopProxy::current()), | |
86 initialization_waiter_(NULL), | |
87 initialization_retval_(NULL) { | |
88 thread_checker_.DetachFromThread(); | |
89 } | |
90 | |
91 RTCVideoEncoder::Impl::~Impl() { | |
92 DCHECK(thread_checker_.CalledOnValidThread()); | |
93 DCHECK(!video_encoder_); | |
94 } | |
95 | |
96 void RTCVideoEncoder::Impl::CreateAndInitializeVEA( | |
97 const webrtc::VideoCodec& codecSettings, | |
98 media::VideoCodecProfile profile, | |
99 base::WaitableEvent* initialization_waiter, | |
100 int32_t* initialization_retval, | |
101 const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories) { | |
102 DCHECK(thread_checker_.CalledOnValidThread()); | |
103 | |
104 initialization_waiter_ = initialization_waiter; | |
105 initialization_retval_ = initialization_retval; | |
106 video_encoder_ = gpu_factories->CreateVideoEncodeAccelerator(this).Pass(); | |
107 if (!video_encoder_) { | |
108 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError); | |
109 return; | |
110 } | |
111 gfx::Size dimensions(codecSettings.width, codecSettings.height); | |
112 video_encoder_->Initialize( | |
113 media::VideoFrame::I420, | |
114 dimensions, | |
115 profile, | |
116 codecSettings.startBitrate * 1000); // startBitrate is in kbits/sec. | |
117 } | |
118 | |
119 void RTCVideoEncoder::Impl::Encode(const media::BitstreamBuffer& buffer, | |
120 bool force_keyframe) { | |
121 DCHECK(thread_checker_.CalledOnValidThread()); | |
122 if (video_encoder_) | |
123 video_encoder_->Encode(buffer, force_keyframe); | |
124 } | |
125 | |
126 void RTCVideoEncoder::Impl::UseOutputBitstreamBuffer( | |
127 const media::BitstreamBuffer& buffer) { | |
128 DCHECK(thread_checker_.CalledOnValidThread()); | |
129 if (video_encoder_) | |
130 video_encoder_->UseOutputBitstreamBuffer(buffer); | |
131 } | |
132 | |
133 void RTCVideoEncoder::Impl::RequestEncodingParameterChange(int32 bitrate) { | |
134 DCHECK(thread_checker_.CalledOnValidThread()); | |
135 if (video_encoder_) | |
136 video_encoder_->RequestEncodingParameterChange(bitrate); | |
137 } | |
138 | |
139 // static | |
140 void RTCVideoEncoder::Impl::Destroy(scoped_ptr<Impl> self) { | |
141 DCHECK(self->thread_checker_.CalledOnValidThread()); | |
142 if (self->video_encoder_) | |
143 self->video_encoder_.release()->Destroy(); | |
144 } | |
145 | |
146 void RTCVideoEncoder::Impl::NotifyInitializeDone() { | |
147 DCHECK(thread_checker_.CalledOnValidThread()); | |
148 *initialization_retval_ = WEBRTC_VIDEO_CODEC_OK; | |
149 initialization_waiter_->Signal(); | |
150 initialization_retval_ = NULL; | |
151 initialization_waiter_ = NULL; | |
152 } | |
153 | |
154 void RTCVideoEncoder::Impl::RequireBitstreamBuffers( | |
155 int input_count, | |
156 const gfx::Size& input_dimensions, | |
157 size_t output_size) { | |
158 DCHECK(thread_checker_.CalledOnValidThread()); | |
159 encoder_message_loop_proxy_->PostTask( | |
160 FROM_HERE, | |
161 base::Bind(&RTCVideoEncoder::RequireBitstreamBuffers, | |
162 weak_encoder_, | |
163 input_count, | |
164 input_dimensions, | |
165 output_size)); | |
166 } | |
167 | |
168 void RTCVideoEncoder::Impl::NotifyInputDone(int32 bitstream_buffer_id) { | |
169 DCHECK(thread_checker_.CalledOnValidThread()); | |
170 encoder_message_loop_proxy_->PostTask( | |
171 FROM_HERE, | |
172 base::Bind(&RTCVideoEncoder::NotifyInputDone, | |
173 weak_encoder_, | |
174 bitstream_buffer_id)); | |
175 } | |
176 | |
177 void RTCVideoEncoder::Impl::BitstreamBufferReady(int32 bitstream_buffer_id, | |
178 size_t payload_size, | |
179 bool key_frame) { | |
180 DCHECK(thread_checker_.CalledOnValidThread()); | |
181 encoder_message_loop_proxy_->PostTask( | |
182 FROM_HERE, | |
183 base::Bind(&RTCVideoEncoder::BitstreamBufferReady, | |
184 weak_encoder_, | |
185 bitstream_buffer_id, | |
186 payload_size, | |
187 key_frame)); | |
188 } | |
189 | |
190 void RTCVideoEncoder::Impl::NotifyError( | |
191 media::VideoEncodeAccelerator::Error error) { | |
192 DCHECK(thread_checker_.CalledOnValidThread()); | |
193 int32_t retval; | |
194 switch (error) { | |
195 default: | |
196 retval = WEBRTC_VIDEO_CODEC_ERROR; | |
197 } | |
198 | |
199 if (video_encoder_) | |
200 video_encoder_.release()->Destroy(); | |
201 | |
202 if (initialization_waiter_) { | |
203 *initialization_retval_ = retval; | |
204 initialization_waiter_->Signal(); | |
205 initialization_retval_ = NULL; | |
206 initialization_waiter_ = NULL; | |
207 } else { | |
208 encoder_message_loop_proxy_->PostTask( | |
209 FROM_HERE, | |
210 base::Bind(&RTCVideoEncoder::NotifyError, weak_encoder_, retval)); | |
211 } | |
212 } | |
213 | |
214 RTCVideoEncoder::RTCVideoEncoder( | |
215 media::VideoCodecProfile profile, | |
216 const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories) | |
217 : video_codec_profile_(profile), | |
218 gpu_factories_(gpu_factories), | |
219 impl_message_loop_proxy_(gpu_factories_->GetMessageLoop()), | |
220 weak_this_factory_(this), | |
221 weak_this_(weak_this_factory_.GetWeakPtr()), | |
222 encoded_image_callback_(NULL), | |
223 impl_status_(WEBRTC_VIDEO_CODEC_OK) { | |
224 DVLOG(1) << "RTCVideoEncoder::RTCVideoEncoder(): profile=" << profile; | |
225 } | |
226 | |
227 RTCVideoEncoder::~RTCVideoEncoder() { | |
228 DCHECK(thread_checker_.CalledOnValidThread()); | |
229 Release(); | |
230 DCHECK(!impl_); | |
231 } | |
232 | |
233 int32_t RTCVideoEncoder::InitEncode(const webrtc::VideoCodec* codec_settings, | |
234 int32_t number_of_cores, | |
235 uint32_t max_payload_size) { | |
236 DCHECK(thread_checker_.CalledOnValidThread()); | |
237 DCHECK(!impl_); | |
238 DVLOG(1) << "RTCVideoEncoder::InitEncode(): " | |
239 "codecType=" << codec_settings->codecType | |
240 << ", width=" << codec_settings->width | |
241 << ", height=" << codec_settings->height | |
242 << ", startBitrate=" << codec_settings->startBitrate; | |
243 | |
244 impl_.reset(new Impl(weak_this_)); | |
245 base::WaitableEvent initialization_waiter(true, false); | |
246 int32_t initialization_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
247 impl_message_loop_proxy_->PostTask( | |
248 FROM_HERE, | |
249 base::Bind(&RTCVideoEncoder::Impl::CreateAndInitializeVEA, | |
250 base::Unretained(impl_.get()), | |
251 *codec_settings, | |
252 video_codec_profile_, | |
253 &initialization_waiter, | |
254 &initialization_retval, | |
255 gpu_factories_)); | |
256 | |
257 // webrtc::VideoEncoder expects this call to be synchronous. | |
258 initialization_waiter.Wait(); | |
259 return initialization_retval; | |
260 } | |
261 | |
262 int32_t RTCVideoEncoder::Encode( | |
263 const webrtc::I420VideoFrame& input_image, | |
264 const webrtc::CodecSpecificInfo* codec_specific_info, | |
265 const std::vector<webrtc::VideoFrameType>* frame_types) { | |
266 DCHECK(thread_checker_.CalledOnValidThread()); | |
267 if (!impl_) | |
268 return impl_status_; | |
269 | |
270 if (input_buffers_free_.empty()) | |
271 return WEBRTC_VIDEO_CODEC_OK; | |
272 int index = input_buffers_free_.back(); | |
273 input_buffers_free_.pop_back(); | |
274 base::SharedMemory* input_buffer = input_buffers_[index]; | |
275 | |
276 // Do a strided copy of the input frame to match the input requirements for | |
277 // the encoder. | |
278 const uint8_t* src = input_image.buffer(webrtc::kYPlane); | |
279 uint8* dst = reinterpret_cast<uint8*>(input_buffer->memory()); | |
280 int width = input_frame_dimensions_.width(); | |
281 int stride = input_image.stride(webrtc::kYPlane); | |
282 for (int i = 0; i < input_image.height(); ++i) { | |
283 memcpy(dst, src, width); | |
284 dst += stride; | |
285 src += width; | |
286 } | |
287 src = input_image.buffer(webrtc::kUPlane); | |
288 width = input_frame_dimensions_.width() / 2; | |
289 stride = input_image.stride(webrtc::kUPlane); | |
290 for (int i = 0; i < input_image.height() / 2; ++i) { | |
291 memcpy(dst, src, width); | |
292 dst += stride; | |
293 src += width; | |
294 } | |
295 src = input_image.buffer(webrtc::kVPlane); | |
296 width = input_frame_dimensions_.width() / 2; | |
297 stride = input_image.stride(webrtc::kVPlane); | |
298 for (int i = 0; i < input_image.height() / 2; ++i) { | |
299 memcpy(dst, src, width); | |
300 dst += stride; | |
301 src += width; | |
302 } | |
303 | |
304 const webrtc::VideoFrameType type = frame_types->front(); | |
305 DVLOG(3) << "RTCVideoEncoder::Encode() buffer.id()=" << index; | |
306 impl_message_loop_proxy_->PostTask( | |
307 FROM_HERE, | |
308 base::Bind( | |
309 &RTCVideoEncoder::Impl::Encode, | |
310 base::Unretained(impl_.get()), | |
311 media::BitstreamBuffer(index, | |
312 input_buffer->handle(), | |
313 input_frame_dimensions_.GetArea() * 3 / 2), | |
314 (type == webrtc::kKeyFrame))); | |
315 return WEBRTC_VIDEO_CODEC_OK; | |
316 } | |
317 | |
318 int32_t RTCVideoEncoder::RegisterEncodeCompleteCallback( | |
319 webrtc::EncodedImageCallback* callback) { | |
320 DCHECK(thread_checker_.CalledOnValidThread()); | |
321 if (!impl_) | |
322 return impl_status_; | |
323 | |
324 encoded_image_callback_ = callback; | |
325 return WEBRTC_VIDEO_CODEC_OK; | |
326 } | |
327 | |
328 int32_t RTCVideoEncoder::Release() { | |
329 DCHECK(thread_checker_.CalledOnValidThread()); | |
330 if (!impl_) | |
331 return impl_status_; | |
332 | |
333 impl_message_loop_proxy_->PostTask( | |
334 FROM_HERE, | |
335 base::Bind(&RTCVideoEncoder::Impl::Destroy, base::Passed(&impl_))); | |
336 return WEBRTC_VIDEO_CODEC_OK; | |
337 } | |
338 | |
339 int32_t RTCVideoEncoder::SetChannelParameters(uint32_t packet_loss, int rtt) { | |
340 DCHECK(thread_checker_.CalledOnValidThread()); | |
341 // Ignored. | |
342 return WEBRTC_VIDEO_CODEC_OK; | |
343 } | |
344 | |
345 int32_t RTCVideoEncoder::SetRates(uint32_t new_bit_rate, uint32_t frame_rate) { | |
346 DCHECK(thread_checker_.CalledOnValidThread()); | |
347 if (!impl_) | |
348 return impl_status_; | |
349 | |
350 impl_message_loop_proxy_->PostTask( | |
351 FROM_HERE, | |
352 base::Bind(&RTCVideoEncoder::Impl::RequestEncodingParameterChange, | |
353 base::Unretained(impl_.get()), | |
354 new_bit_rate)); | |
355 return WEBRTC_VIDEO_CODEC_OK; | |
356 } | |
357 | |
358 void RTCVideoEncoder::RequireBitstreamBuffers(int input_count, | |
359 const gfx::Size& input_dimensions, | |
360 size_t output_size) { | |
361 DCHECK(thread_checker_.CalledOnValidThread()); | |
362 DCHECK_EQ(input_buffers_.size(), 0U); | |
363 DCHECK_EQ(output_buffers_.size(), 0U); | |
364 input_frame_dimensions_ = input_dimensions; | |
365 output_buffer_size_ = output_size; | |
366 | |
367 for (int i = 0; i < input_count + kInputBufferExtraCount; ++i) { | |
368 scoped_ptr<base::SharedMemory> shm(gpu_factories_->CreateSharedMemory( | |
369 input_frame_dimensions_.GetArea() * 3 / 2)); | |
370 if (shm) | |
371 input_buffers_.push_back(shm.release()); | |
372 } | |
373 for (size_t i = 0; i < input_buffers_.size(); ++i) | |
374 input_buffers_free_.push_back(i); | |
375 | |
376 for (int i = 0; i < kOutputBufferCount; ++i) { | |
377 scoped_ptr<base::SharedMemory> shm( | |
378 gpu_factories_->CreateSharedMemory(output_buffer_size_)); | |
379 if (shm) | |
380 output_buffers_.push_back(shm.release()); | |
381 } | |
382 | |
383 // Immediately provide all output buffers to the VEA. | |
384 for (size_t i = 0; i < output_buffers_.size(); ++i) { | |
385 impl_message_loop_proxy_->PostTask( | |
386 FROM_HERE, | |
387 base::Bind(&RTCVideoEncoder::Impl::UseOutputBitstreamBuffer, | |
388 base::Unretained(impl_.get()), | |
389 media::BitstreamBuffer(i, | |
390 output_buffers_[i]->handle(), | |
391 output_buffers_[i]->mapped_size()))); | |
392 } | |
393 } | |
394 | |
395 void RTCVideoEncoder::NotifyInputDone(int32 bitstream_buffer_id) { | |
396 DCHECK(thread_checker_.CalledOnValidThread()); | |
397 | |
398 if (!impl_) | |
399 return; | |
400 if (bitstream_buffer_id < 0 || | |
401 bitstream_buffer_id >= (int32)input_buffers_.size()) | |
402 return; | |
403 input_buffers_free_.push_back(bitstream_buffer_id); | |
404 } | |
405 | |
406 void RTCVideoEncoder::BitstreamBufferReady(int32 bitstream_buffer_id, | |
407 size_t payload_size, | |
408 bool key_frame) { | |
409 DCHECK(thread_checker_.CalledOnValidThread()); | |
410 | |
411 if (!impl_) | |
412 return; | |
413 if (!encoded_image_callback_) | |
414 return; | |
415 if (bitstream_buffer_id < 0 || | |
416 bitstream_buffer_id >= (int32)output_buffers_.size()) | |
417 return; | |
418 base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id]; | |
419 if (payload_size > output_buffer->mapped_size()) | |
420 return; | |
421 | |
422 webrtc::EncodedImage image( | |
423 reinterpret_cast<uint8_t*>(output_buffer->memory()), | |
424 payload_size, | |
425 output_buffer->mapped_size()); | |
426 image._encodedWidth = output_frame_dimensions_.width(); | |
427 image._encodedHeight = output_frame_dimensions_.height(); | |
428 image._frameType = (key_frame ? webrtc::kKeyFrame : webrtc::kDeltaFrame); | |
429 image._completeFrame = true; | |
430 DVLOG(3) << "RTCVideoEncoder::BitstreamBufferReady() buffer.id()=" | |
431 << bitstream_buffer_id; | |
432 int32_t retval = encoded_image_callback_->Encoded(image, NULL, NULL); | |
433 if (retval < 0) { | |
434 NotifyError(retval); | |
435 return; | |
436 } | |
437 | |
438 // The call through webrtc::EncodedImageCallback is synchronous, so we can | |
439 // immediately recycle the output buffer back to the VEA. | |
440 impl_message_loop_proxy_->PostTask( | |
441 FROM_HERE, | |
442 base::Bind(&RTCVideoEncoder::Impl::UseOutputBitstreamBuffer, | |
443 base::Unretained(impl_.get()), | |
444 media::BitstreamBuffer(bitstream_buffer_id, | |
445 output_buffer->handle(), | |
446 output_buffer->mapped_size()))); | |
447 } | |
448 | |
449 void RTCVideoEncoder::NotifyError(int32_t error) { | |
450 DCHECK(thread_checker_.CalledOnValidThread()); | |
451 impl_status_ = error; | |
452 impl_message_loop_proxy_->PostTask( | |
453 FROM_HERE, | |
454 base::Bind(&RTCVideoEncoder::Impl::Destroy, base::Passed(&impl_))); | |
455 } | |
456 | |
457 } // namespace content | |
OLD | NEW |