Fix crash on app list start page contents not existing.
[chromium-blink-merge.git] / content / renderer / media / rtc_video_encoder.cc
blob85da0c01fc52d0a1152f48f21631f9096f57b14e
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.
5 #include "content/renderer/media/rtc_video_encoder.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/metrics/histogram.h"
13 #include "base/rand_util.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "media/base/bitstream_buffer.h"
16 #include "media/base/video_frame.h"
17 #include "media/base/video_util.h"
18 #include "media/filters/gpu_video_accelerator_factories.h"
19 #include "media/filters/h264_parser.h"
20 #include "media/video/video_encode_accelerator.h"
21 #include "third_party/webrtc/system_wrappers/interface/tick_util.h"
23 #define NOTIFY_ERROR(x) \
24 do { \
25 DLOG(ERROR) << "calling NotifyError(): " << x; \
26 NotifyError(x); \
27 } while (0)
29 namespace content {
31 namespace {
33 // Translate from webrtc::VideoCodecType and webrtc::VideoCodec to
34 // media::VideoCodecProfile.
35 media::VideoCodecProfile WebRTCVideoCodecToVideoCodecProfile(
36 webrtc::VideoCodecType type, const webrtc::VideoCodec* codec_settings) {
37 DCHECK_EQ(type, codec_settings->codecType);
38 switch (type) {
39 case webrtc::kVideoCodecVP8:
40 return media::VP8PROFILE_ANY;
41 case webrtc::kVideoCodecH264: {
42 switch (codec_settings->codecSpecific.H264.profile) {
43 case webrtc::kProfileBase:
44 return media::H264PROFILE_BASELINE;
45 case webrtc::kProfileMain:
46 return media::H264PROFILE_MAIN;
49 default:
50 NOTREACHED() << "Unrecognized video codec type";
51 return media::VIDEO_CODEC_PROFILE_UNKNOWN;
55 // Populates struct webrtc::RTPFragmentationHeader for H264 codec.
56 // Each entry specifies the offset and length (excluding start code) of a NALU.
57 // Returns true if successful.
58 bool GetRTPFragmentationHeaderH264(webrtc::RTPFragmentationHeader* header,
59 const uint8_t* data, uint32_t length) {
60 media::H264Parser parser;
61 parser.SetStream(data, length);
63 std::vector<media::H264NALU> nalu_vector;
64 while (true) {
65 media::H264NALU nalu;
66 const media::H264Parser::Result result = parser.AdvanceToNextNALU(&nalu);
67 if (result == media::H264Parser::kOk) {
68 nalu_vector.push_back(nalu);
69 } else if (result == media::H264Parser::kEOStream) {
70 break;
71 } else {
72 DLOG(ERROR) << "Unexpected H264 parser result";
73 return false;
77 header->VerifyAndAllocateFragmentationHeader(nalu_vector.size());
78 for (size_t i = 0; i < nalu_vector.size(); ++i) {
79 header->fragmentationOffset[i] = nalu_vector[i].data - data;
80 header->fragmentationLength[i] = nalu_vector[i].size;
81 header->fragmentationPlType[i] = 0;
82 header->fragmentationTimeDiff[i] = 0;
84 return true;
87 } // namespace
89 // This private class of RTCVideoEncoder does the actual work of communicating
90 // with a media::VideoEncodeAccelerator for handling video encoding. It can
91 // be created on any thread, but should subsequently be posted to (and Destroy()
92 // called on) a single thread. Callbacks to RTCVideoEncoder are posted to the
93 // thread on which the instance was constructed.
95 // This class separates state related to the thread that RTCVideoEncoder
96 // operates on (presently the libjingle worker thread) from the thread that
97 // |gpu_factories_| provides for accelerator operations (presently the media
98 // thread). The RTCVideoEncoder class can be deleted directly by WebRTC, while
99 // RTCVideoEncoder::Impl stays around long enough to properly shut down the VEA.
100 class RTCVideoEncoder::Impl
101 : public media::VideoEncodeAccelerator::Client,
102 public base::RefCountedThreadSafe<RTCVideoEncoder::Impl> {
103 public:
104 Impl(const base::WeakPtr<RTCVideoEncoder>& weak_encoder,
105 const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories);
107 // Create the VEA and call Initialize() on it. Called once per instantiation,
108 // and then the instance is bound forevermore to whichever thread made the
109 // call.
110 // RTCVideoEncoder expects to be able to call this function synchronously from
111 // its own thread, hence the |async_waiter| and |async_retval| arguments.
112 void CreateAndInitializeVEA(const gfx::Size& input_visible_size,
113 uint32 bitrate,
114 media::VideoCodecProfile profile,
115 base::WaitableEvent* async_waiter,
116 int32_t* async_retval);
117 // Enqueue a frame from WebRTC for encoding.
118 // RTCVideoEncoder expects to be able to call this function synchronously from
119 // its own thread, hence the |async_waiter| and |async_retval| arguments.
120 void Enqueue(const webrtc::I420VideoFrame* input_frame,
121 bool force_keyframe,
122 base::WaitableEvent* async_waiter,
123 int32_t* async_retval);
125 // RTCVideoEncoder is given a buffer to be passed to WebRTC through the
126 // RTCVideoEncoder::ReturnEncodedImage() function. When that is complete,
127 // the buffer is returned to Impl by its index using this function.
128 void UseOutputBitstreamBufferId(int32 bitstream_buffer_id);
130 // Request encoding parameter change for the underlying encoder.
131 void RequestEncodingParametersChange(uint32 bitrate, uint32 framerate);
133 // Destroy this Impl's encoder. The destructor is not explicitly called, as
134 // Impl is a base::RefCountedThreadSafe.
135 void Destroy();
137 // media::VideoEncodeAccelerator::Client implementation.
138 void RequireBitstreamBuffers(unsigned int input_count,
139 const gfx::Size& input_coded_size,
140 size_t output_buffer_size) override;
141 void BitstreamBufferReady(int32 bitstream_buffer_id,
142 size_t payload_size,
143 bool key_frame) override;
144 void NotifyError(media::VideoEncodeAccelerator::Error error) override;
146 private:
147 friend class base::RefCountedThreadSafe<Impl>;
149 enum {
150 kInputBufferExtraCount = 1, // The number of input buffers allocated, more
151 // than what is requested by
152 // VEA::RequireBitstreamBuffers().
153 kOutputBufferCount = 3,
156 ~Impl() override;
158 // Perform encoding on an input frame from the input queue.
159 void EncodeOneFrame();
161 // Notify that an input frame is finished for encoding. |index| is the index
162 // of the completed frame in |input_buffers_|.
163 void EncodeFrameFinished(int index);
165 // Set up/signal |async_waiter_| and |async_retval_|; see declarations below.
166 void RegisterAsyncWaiter(base::WaitableEvent* waiter, int32_t* retval);
167 void SignalAsyncWaiter(int32_t retval);
169 base::ThreadChecker thread_checker_;
171 // Weak pointer to the parent RTCVideoEncoder, for posting back VEA::Client
172 // notifications.
173 const base::WeakPtr<RTCVideoEncoder> weak_encoder_;
175 // The message loop on which to post callbacks to |weak_encoder_|.
176 const scoped_refptr<base::MessageLoopProxy> encoder_message_loop_proxy_;
178 // Factory for creating VEAs, shared memory buffers, etc.
179 const scoped_refptr<media::GpuVideoAcceleratorFactories> gpu_factories_;
181 // webrtc::VideoEncoder expects InitEncode() and Encode() to be synchronous.
182 // Do this by waiting on the |async_waiter_| and returning the return value in
183 // |async_retval_| when initialization completes, encoding completes, or
184 // an error occurs.
185 base::WaitableEvent* async_waiter_;
186 int32_t* async_retval_;
188 // The underlying VEA to perform encoding on.
189 scoped_ptr<media::VideoEncodeAccelerator> video_encoder_;
191 // Next input frame. Since there is at most one next frame, a single-element
192 // queue is sufficient.
193 const webrtc::I420VideoFrame* input_next_frame_;
195 // Whether to encode a keyframe next.
196 bool input_next_frame_keyframe_;
198 // Frame sizes.
199 gfx::Size input_frame_coded_size_;
200 gfx::Size input_visible_size_;
202 // Shared memory buffers for input/output with the VEA.
203 ScopedVector<base::SharedMemory> input_buffers_;
204 ScopedVector<base::SharedMemory> output_buffers_;
206 // Input buffers ready to be filled with input from Encode(). As a LIFO since
207 // we don't care about ordering.
208 std::vector<int> input_buffers_free_;
210 // The number of output buffers ready to be filled with output from the
211 // encoder.
212 int output_buffers_free_count_;
214 // 15 bits running index of the VP8 frames. See VP8 RTP spec for details.
215 uint16 picture_id_;
217 DISALLOW_COPY_AND_ASSIGN(Impl);
220 RTCVideoEncoder::Impl::Impl(
221 const base::WeakPtr<RTCVideoEncoder>& weak_encoder,
222 const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories)
223 : weak_encoder_(weak_encoder),
224 encoder_message_loop_proxy_(base::MessageLoopProxy::current()),
225 gpu_factories_(gpu_factories),
226 async_waiter_(NULL),
227 async_retval_(NULL),
228 input_next_frame_(NULL),
229 input_next_frame_keyframe_(false),
230 output_buffers_free_count_(0) {
231 thread_checker_.DetachFromThread();
232 // Picture ID should start on a random number.
233 picture_id_ = static_cast<uint16_t>(base::RandInt(0, 0x7FFF));
236 void RTCVideoEncoder::Impl::CreateAndInitializeVEA(
237 const gfx::Size& input_visible_size,
238 uint32 bitrate,
239 media::VideoCodecProfile profile,
240 base::WaitableEvent* async_waiter,
241 int32_t* async_retval) {
242 DVLOG(3) << "Impl::CreateAndInitializeVEA()";
243 DCHECK(thread_checker_.CalledOnValidThread());
245 RegisterAsyncWaiter(async_waiter, async_retval);
247 // Check for overflow converting bitrate (kilobits/sec) to bits/sec.
248 if (bitrate > kuint32max / 1000) {
249 NOTIFY_ERROR(media::VideoEncodeAccelerator::kInvalidArgumentError);
250 return;
253 video_encoder_ = gpu_factories_->CreateVideoEncodeAccelerator().Pass();
254 if (!video_encoder_) {
255 NOTIFY_ERROR(media::VideoEncodeAccelerator::kPlatformFailureError);
256 return;
258 input_visible_size_ = input_visible_size;
259 if (!video_encoder_->Initialize(media::VideoFrame::I420,
260 input_visible_size_,
261 profile,
262 bitrate * 1000,
263 this)) {
264 NOTIFY_ERROR(media::VideoEncodeAccelerator::kInvalidArgumentError);
265 return;
269 void RTCVideoEncoder::Impl::Enqueue(const webrtc::I420VideoFrame* input_frame,
270 bool force_keyframe,
271 base::WaitableEvent* async_waiter,
272 int32_t* async_retval) {
273 DVLOG(3) << "Impl::Enqueue()";
274 DCHECK(thread_checker_.CalledOnValidThread());
275 DCHECK(!input_next_frame_);
277 RegisterAsyncWaiter(async_waiter, async_retval);
278 // If there are no free input and output buffers, drop the frame to avoid a
279 // deadlock. If there is a free input buffer, EncodeOneFrame will run and
280 // unblock Encode(). If there are no free input buffers but there is a free
281 // output buffer, EncodeFrameFinished will be called later to unblock
282 // Encode().
284 // The caller of Encode() holds a webrtc lock. The deadlock happens when:
285 // (1) Encode() is waiting for the frame to be encoded in EncodeOneFrame().
286 // (2) There are no free input buffers and they cannot be freed because
287 // the encoder has no output buffers.
288 // (3) Output buffers cannot be freed because ReturnEncodedImage is queued
289 // on libjingle worker thread to be run. But the worker thread is waiting
290 // for the same webrtc lock held by the caller of Encode().
292 // Dropping a frame is fine. The encoder has been filled with all input
293 // buffers. Returning an error in Encode() is not fatal and WebRTC will just
294 // continue. If this is a key frame, WebRTC will request a key frame again.
295 // Besides, webrtc will drop a frame if Encode() blocks too long.
296 if (input_buffers_free_.empty() && output_buffers_free_count_ == 0) {
297 DVLOG(2) << "Run out of input and output buffers. Drop the frame.";
298 SignalAsyncWaiter(WEBRTC_VIDEO_CODEC_ERROR);
299 return;
301 input_next_frame_ = input_frame;
302 input_next_frame_keyframe_ = force_keyframe;
304 if (!input_buffers_free_.empty())
305 EncodeOneFrame();
308 void RTCVideoEncoder::Impl::UseOutputBitstreamBufferId(
309 int32 bitstream_buffer_id) {
310 DVLOG(3) << "Impl::UseOutputBitstreamBufferIndex(): "
311 "bitstream_buffer_id=" << bitstream_buffer_id;
312 DCHECK(thread_checker_.CalledOnValidThread());
313 if (video_encoder_) {
314 video_encoder_->UseOutputBitstreamBuffer(media::BitstreamBuffer(
315 bitstream_buffer_id,
316 output_buffers_[bitstream_buffer_id]->handle(),
317 output_buffers_[bitstream_buffer_id]->mapped_size()));
318 output_buffers_free_count_++;
322 void RTCVideoEncoder::Impl::RequestEncodingParametersChange(uint32 bitrate,
323 uint32 framerate) {
324 DVLOG(3) << "Impl::RequestEncodingParametersChange(): bitrate=" << bitrate
325 << ", framerate=" << framerate;
326 DCHECK(thread_checker_.CalledOnValidThread());
328 // Check for overflow converting bitrate (kilobits/sec) to bits/sec.
329 if (bitrate > kuint32max / 1000) {
330 NOTIFY_ERROR(media::VideoEncodeAccelerator::kInvalidArgumentError);
331 return;
334 if (video_encoder_)
335 video_encoder_->RequestEncodingParametersChange(bitrate * 1000, framerate);
338 void RTCVideoEncoder::Impl::Destroy() {
339 DVLOG(3) << "Impl::Destroy()";
340 DCHECK(thread_checker_.CalledOnValidThread());
341 video_encoder_.reset();
344 void RTCVideoEncoder::Impl::RequireBitstreamBuffers(
345 unsigned int input_count,
346 const gfx::Size& input_coded_size,
347 size_t output_buffer_size) {
348 DVLOG(3) << "Impl::RequireBitstreamBuffers(): input_count=" << input_count
349 << ", input_coded_size=" << input_coded_size.ToString()
350 << ", output_buffer_size=" << output_buffer_size;
351 DCHECK(thread_checker_.CalledOnValidThread());
353 if (!video_encoder_)
354 return;
356 input_frame_coded_size_ = input_coded_size;
358 for (unsigned int i = 0; i < input_count + kInputBufferExtraCount; ++i) {
359 scoped_ptr<base::SharedMemory> shm =
360 gpu_factories_->CreateSharedMemory(media::VideoFrame::AllocationSize(
361 media::VideoFrame::I420, input_coded_size));
362 if (!shm) {
363 DLOG(ERROR) << "Impl::RequireBitstreamBuffers(): "
364 "failed to create input buffer " << i;
365 NOTIFY_ERROR(media::VideoEncodeAccelerator::kPlatformFailureError);
366 return;
368 input_buffers_.push_back(shm.release());
369 input_buffers_free_.push_back(i);
372 for (int i = 0; i < kOutputBufferCount; ++i) {
373 scoped_ptr<base::SharedMemory> shm =
374 gpu_factories_->CreateSharedMemory(output_buffer_size);
375 if (!shm) {
376 DLOG(ERROR) << "Impl::RequireBitstreamBuffers(): "
377 "failed to create output buffer " << i;
378 NOTIFY_ERROR(media::VideoEncodeAccelerator::kPlatformFailureError);
379 return;
381 output_buffers_.push_back(shm.release());
384 // Immediately provide all output buffers to the VEA.
385 for (size_t i = 0; i < output_buffers_.size(); ++i) {
386 video_encoder_->UseOutputBitstreamBuffer(media::BitstreamBuffer(
387 i, output_buffers_[i]->handle(), output_buffers_[i]->mapped_size()));
388 output_buffers_free_count_++;
390 SignalAsyncWaiter(WEBRTC_VIDEO_CODEC_OK);
393 void RTCVideoEncoder::Impl::BitstreamBufferReady(int32 bitstream_buffer_id,
394 size_t payload_size,
395 bool key_frame) {
396 DVLOG(3) << "Impl::BitstreamBufferReady(): "
397 "bitstream_buffer_id=" << bitstream_buffer_id
398 << ", payload_size=" << payload_size
399 << ", key_frame=" << key_frame;
400 DCHECK(thread_checker_.CalledOnValidThread());
402 if (bitstream_buffer_id < 0 ||
403 bitstream_buffer_id >= static_cast<int>(output_buffers_.size())) {
404 DLOG(ERROR) << "Impl::BitstreamBufferReady(): invalid bitstream_buffer_id="
405 << bitstream_buffer_id;
406 NOTIFY_ERROR(media::VideoEncodeAccelerator::kPlatformFailureError);
407 return;
409 base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id];
410 if (payload_size > output_buffer->mapped_size()) {
411 DLOG(ERROR) << "Impl::BitstreamBufferReady(): invalid payload_size="
412 << payload_size;
413 NOTIFY_ERROR(media::VideoEncodeAccelerator::kPlatformFailureError);
414 return;
416 output_buffers_free_count_--;
418 // Use webrtc timestamps to ensure correct RTP sender behavior.
419 // TODO(hshi): obtain timestamp from the capturer, see crbug.com/350106.
420 const int64 capture_time_us = webrtc::TickTime::MicrosecondTimestamp();
422 // Derive the capture time (in ms) and RTP timestamp (in 90KHz ticks).
423 int64 capture_time_ms = capture_time_us / 1000;
424 uint32_t rtp_timestamp = static_cast<uint32_t>(capture_time_us * 90 / 1000);
426 scoped_ptr<webrtc::EncodedImage> image(new webrtc::EncodedImage(
427 reinterpret_cast<uint8_t*>(output_buffer->memory()),
428 payload_size,
429 output_buffer->mapped_size()));
430 image->_encodedWidth = input_visible_size_.width();
431 image->_encodedHeight = input_visible_size_.height();
432 image->_timeStamp = rtp_timestamp;
433 image->capture_time_ms_ = capture_time_ms;
434 image->_frameType = (key_frame ? webrtc::kKeyFrame : webrtc::kDeltaFrame);
435 image->_completeFrame = true;
437 encoder_message_loop_proxy_->PostTask(
438 FROM_HERE,
439 base::Bind(&RTCVideoEncoder::ReturnEncodedImage,
440 weak_encoder_,
441 base::Passed(&image),
442 bitstream_buffer_id,
443 picture_id_));
444 // Picture ID must wrap after reaching the maximum.
445 picture_id_ = (picture_id_ + 1) & 0x7FFF;
448 void RTCVideoEncoder::Impl::NotifyError(
449 media::VideoEncodeAccelerator::Error error) {
450 DVLOG(3) << "Impl::NotifyError(): error=" << error;
451 DCHECK(thread_checker_.CalledOnValidThread());
452 int32_t retval;
453 switch (error) {
454 case media::VideoEncodeAccelerator::kInvalidArgumentError:
455 retval = WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
456 break;
457 default:
458 retval = WEBRTC_VIDEO_CODEC_ERROR;
461 video_encoder_.reset();
463 if (async_waiter_) {
464 SignalAsyncWaiter(retval);
465 } else {
466 encoder_message_loop_proxy_->PostTask(
467 FROM_HERE,
468 base::Bind(&RTCVideoEncoder::NotifyError, weak_encoder_, retval));
472 RTCVideoEncoder::Impl::~Impl() { DCHECK(!video_encoder_); }
474 void RTCVideoEncoder::Impl::EncodeOneFrame() {
475 DVLOG(3) << "Impl::EncodeOneFrame()";
476 DCHECK(thread_checker_.CalledOnValidThread());
477 DCHECK(input_next_frame_);
478 DCHECK(!input_buffers_free_.empty());
480 // EncodeOneFrame() may re-enter EncodeFrameFinished() if VEA::Encode() fails,
481 // we receive a VEA::NotifyError(), and the media::VideoFrame we pass to
482 // Encode() gets destroyed early. Handle this by resetting our
483 // input_next_frame_* state before we hand off the VideoFrame to the VEA.
484 const webrtc::I420VideoFrame* next_frame = input_next_frame_;
485 bool next_frame_keyframe = input_next_frame_keyframe_;
486 input_next_frame_ = NULL;
487 input_next_frame_keyframe_ = false;
489 if (!video_encoder_) {
490 SignalAsyncWaiter(WEBRTC_VIDEO_CODEC_ERROR);
491 return;
494 const int index = input_buffers_free_.back();
495 base::SharedMemory* input_buffer = input_buffers_[index];
496 scoped_refptr<media::VideoFrame> frame =
497 media::VideoFrame::WrapExternalPackedMemory(
498 media::VideoFrame::I420,
499 input_frame_coded_size_,
500 gfx::Rect(input_visible_size_),
501 input_visible_size_,
502 reinterpret_cast<uint8*>(input_buffer->memory()),
503 input_buffer->mapped_size(),
504 input_buffer->handle(),
506 base::TimeDelta(),
507 base::Bind(&RTCVideoEncoder::Impl::EncodeFrameFinished, this, index));
508 if (!frame.get()) {
509 DLOG(ERROR) << "Impl::EncodeOneFrame(): failed to create frame";
510 NOTIFY_ERROR(media::VideoEncodeAccelerator::kPlatformFailureError);
511 return;
514 // Do a strided copy of the input frame to match the input requirements for
515 // the encoder.
516 // TODO(sheu): support zero-copy from WebRTC. http://crbug.com/269312
517 media::CopyYPlane(next_frame->buffer(webrtc::kYPlane),
518 next_frame->stride(webrtc::kYPlane),
519 next_frame->height(),
520 frame.get());
521 media::CopyUPlane(next_frame->buffer(webrtc::kUPlane),
522 next_frame->stride(webrtc::kUPlane),
523 next_frame->height(),
524 frame.get());
525 media::CopyVPlane(next_frame->buffer(webrtc::kVPlane),
526 next_frame->stride(webrtc::kVPlane),
527 next_frame->height(),
528 frame.get());
530 video_encoder_->Encode(frame, next_frame_keyframe);
531 input_buffers_free_.pop_back();
532 SignalAsyncWaiter(WEBRTC_VIDEO_CODEC_OK);
535 void RTCVideoEncoder::Impl::EncodeFrameFinished(int index) {
536 DVLOG(3) << "Impl::EncodeFrameFinished(): index=" << index;
537 DCHECK(thread_checker_.CalledOnValidThread());
538 DCHECK_GE(index, 0);
539 DCHECK_LT(index, static_cast<int>(input_buffers_.size()));
540 input_buffers_free_.push_back(index);
541 if (input_next_frame_)
542 EncodeOneFrame();
545 void RTCVideoEncoder::Impl::RegisterAsyncWaiter(base::WaitableEvent* waiter,
546 int32_t* retval) {
547 DCHECK(thread_checker_.CalledOnValidThread());
548 DCHECK(!async_waiter_);
549 DCHECK(!async_retval_);
550 async_waiter_ = waiter;
551 async_retval_ = retval;
554 void RTCVideoEncoder::Impl::SignalAsyncWaiter(int32_t retval) {
555 DCHECK(thread_checker_.CalledOnValidThread());
556 *async_retval_ = retval;
557 async_waiter_->Signal();
558 async_retval_ = NULL;
559 async_waiter_ = NULL;
562 #undef NOTIFY_ERROR
564 ////////////////////////////////////////////////////////////////////////////////
566 // RTCVideoEncoder
568 ////////////////////////////////////////////////////////////////////////////////
570 RTCVideoEncoder::RTCVideoEncoder(
571 webrtc::VideoCodecType type,
572 const scoped_refptr<media::GpuVideoAcceleratorFactories>& gpu_factories)
573 : video_codec_type_(type),
574 gpu_factories_(gpu_factories),
575 encoded_image_callback_(NULL),
576 impl_status_(WEBRTC_VIDEO_CODEC_UNINITIALIZED),
577 weak_factory_(this) {
578 DVLOG(1) << "RTCVideoEncoder(): codec type=" << type;
581 RTCVideoEncoder::~RTCVideoEncoder() {
582 DVLOG(3) << "~RTCVideoEncoder";
583 DCHECK(thread_checker_.CalledOnValidThread());
584 Release();
585 DCHECK(!impl_.get());
588 int32_t RTCVideoEncoder::InitEncode(const webrtc::VideoCodec* codec_settings,
589 int32_t number_of_cores,
590 size_t max_payload_size) {
591 DVLOG(1) << "InitEncode(): codecType=" << codec_settings->codecType
592 << ", width=" << codec_settings->width
593 << ", height=" << codec_settings->height
594 << ", startBitrate=" << codec_settings->startBitrate;
595 DCHECK(thread_checker_.CalledOnValidThread());
596 DCHECK(!impl_.get());
598 media::VideoCodecProfile profile = WebRTCVideoCodecToVideoCodecProfile(
599 video_codec_type_, codec_settings);
601 weak_factory_.InvalidateWeakPtrs();
602 impl_ = new Impl(weak_factory_.GetWeakPtr(), gpu_factories_);
603 base::WaitableEvent initialization_waiter(true, false);
604 int32_t initialization_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
605 gpu_factories_->GetTaskRunner()->PostTask(
606 FROM_HERE,
607 base::Bind(&RTCVideoEncoder::Impl::CreateAndInitializeVEA,
608 impl_,
609 gfx::Size(codec_settings->width, codec_settings->height),
610 codec_settings->startBitrate,
611 profile,
612 &initialization_waiter,
613 &initialization_retval));
615 // webrtc::VideoEncoder expects this call to be synchronous.
616 initialization_waiter.Wait();
617 RecordInitEncodeUMA(initialization_retval, profile);
618 return initialization_retval;
621 int32_t RTCVideoEncoder::Encode(
622 const webrtc::I420VideoFrame& input_image,
623 const webrtc::CodecSpecificInfo* codec_specific_info,
624 const std::vector<webrtc::VideoFrameType>* frame_types) {
625 DVLOG(3) << "Encode()";
626 if (!impl_.get()) {
627 DVLOG(3) << "Encode(): returning impl_status_=" << impl_status_;
628 return impl_status_;
631 bool want_key_frame = frame_types && frame_types->size() &&
632 frame_types->front() == webrtc::kKeyFrame;
633 base::WaitableEvent encode_waiter(true, false);
634 int32_t encode_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
635 gpu_factories_->GetTaskRunner()->PostTask(
636 FROM_HERE,
637 base::Bind(&RTCVideoEncoder::Impl::Enqueue,
638 impl_,
639 &input_image,
640 want_key_frame,
641 &encode_waiter,
642 &encode_retval));
644 // webrtc::VideoEncoder expects this call to be synchronous.
645 encode_waiter.Wait();
646 DVLOG(3) << "Encode(): returning encode_retval=" << encode_retval;
647 return encode_retval;
650 int32_t RTCVideoEncoder::RegisterEncodeCompleteCallback(
651 webrtc::EncodedImageCallback* callback) {
652 DVLOG(3) << "RegisterEncodeCompleteCallback()";
653 DCHECK(thread_checker_.CalledOnValidThread());
654 if (!impl_.get()) {
655 DVLOG(3) << "RegisterEncodeCompleteCallback(): returning " << impl_status_;
656 return impl_status_;
659 encoded_image_callback_ = callback;
660 return WEBRTC_VIDEO_CODEC_OK;
663 int32_t RTCVideoEncoder::Release() {
664 DVLOG(3) << "Release()";
665 DCHECK(thread_checker_.CalledOnValidThread());
667 if (impl_.get()) {
668 gpu_factories_->GetTaskRunner()->PostTask(
669 FROM_HERE, base::Bind(&RTCVideoEncoder::Impl::Destroy, impl_));
670 impl_ = NULL;
671 weak_factory_.InvalidateWeakPtrs();
672 impl_status_ = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
674 return WEBRTC_VIDEO_CODEC_OK;
677 int32_t RTCVideoEncoder::SetChannelParameters(uint32_t packet_loss,
678 int64_t rtt) {
679 DVLOG(3) << "SetChannelParameters(): packet_loss=" << packet_loss
680 << ", rtt=" << rtt;
681 // Ignored.
682 return WEBRTC_VIDEO_CODEC_OK;
685 int32_t RTCVideoEncoder::SetRates(uint32_t new_bit_rate, uint32_t frame_rate) {
686 DVLOG(3) << "SetRates(): new_bit_rate=" << new_bit_rate
687 << ", frame_rate=" << frame_rate;
688 if (!impl_.get()) {
689 DVLOG(3) << "SetRates(): returning " << impl_status_;
690 return impl_status_;
693 gpu_factories_->GetTaskRunner()->PostTask(
694 FROM_HERE,
695 base::Bind(&RTCVideoEncoder::Impl::RequestEncodingParametersChange,
696 impl_,
697 new_bit_rate,
698 frame_rate));
699 return WEBRTC_VIDEO_CODEC_OK;
702 void RTCVideoEncoder::ReturnEncodedImage(scoped_ptr<webrtc::EncodedImage> image,
703 int32 bitstream_buffer_id,
704 uint16 picture_id) {
705 DCHECK(thread_checker_.CalledOnValidThread());
706 DVLOG(3) << "ReturnEncodedImage(): "
707 << "bitstream_buffer_id=" << bitstream_buffer_id
708 << ", picture_id=" << picture_id;
710 if (!encoded_image_callback_)
711 return;
713 webrtc::RTPFragmentationHeader header;
714 memset(&header, 0, sizeof(header));
715 switch (video_codec_type_) {
716 case webrtc::kVideoCodecVP8:
717 // Generate a header describing a single fragment.
718 header.VerifyAndAllocateFragmentationHeader(1);
719 header.fragmentationOffset[0] = 0;
720 header.fragmentationLength[0] = image->_length;
721 header.fragmentationPlType[0] = 0;
722 header.fragmentationTimeDiff[0] = 0;
723 break;
724 case webrtc::kVideoCodecH264:
725 if (!GetRTPFragmentationHeaderH264(
726 &header, image->_buffer, image->_length)) {
727 DLOG(ERROR) << "Failed to get RTP fragmentation header for H264";
728 NotifyError(WEBRTC_VIDEO_CODEC_ERROR);
729 return;
731 break;
732 default:
733 NOTREACHED() << "Invalid video codec type";
734 return;
737 webrtc::CodecSpecificInfo info;
738 memset(&info, 0, sizeof(info));
739 info.codecType = video_codec_type_;
740 if (video_codec_type_ == webrtc::kVideoCodecVP8) {
741 info.codecSpecific.VP8.pictureId = picture_id;
742 info.codecSpecific.VP8.tl0PicIdx = -1;
743 info.codecSpecific.VP8.keyIdx = -1;
746 int32_t retval = encoded_image_callback_->Encoded(*image, &info, &header);
747 if (retval < 0) {
748 DVLOG(2) << "ReturnEncodedImage(): encoded_image_callback_ returned "
749 << retval;
752 // The call through webrtc::EncodedImageCallback is synchronous, so we can
753 // immediately recycle the output buffer back to the Impl.
754 gpu_factories_->GetTaskRunner()->PostTask(
755 FROM_HERE,
756 base::Bind(&RTCVideoEncoder::Impl::UseOutputBitstreamBufferId,
757 impl_,
758 bitstream_buffer_id));
761 void RTCVideoEncoder::NotifyError(int32_t error) {
762 DCHECK(thread_checker_.CalledOnValidThread());
763 DVLOG(1) << "NotifyError(): error=" << error;
765 impl_status_ = error;
766 gpu_factories_->GetTaskRunner()->PostTask(
767 FROM_HERE, base::Bind(&RTCVideoEncoder::Impl::Destroy, impl_));
768 impl_ = NULL;
771 void RTCVideoEncoder::RecordInitEncodeUMA(
772 int32_t init_retval, media::VideoCodecProfile profile) {
773 UMA_HISTOGRAM_BOOLEAN("Media.RTCVideoEncoderInitEncodeSuccess",
774 init_retval == WEBRTC_VIDEO_CODEC_OK);
775 if (init_retval == WEBRTC_VIDEO_CODEC_OK) {
776 UMA_HISTOGRAM_ENUMERATION("Media.RTCVideoEncoderProfile",
777 profile,
778 media::VIDEO_CODEC_PROFILE_MAX + 1);
782 } // namespace content