Chromium Code Reviews| 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 |