1 // Copyright 2014 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 "media/cast/sender/external_video_encoder.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_vector.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "media/base/video_frame.h"
14 #include "media/base/video_util.h"
15 #include "media/cast/cast_defines.h"
16 #include "media/cast/logging/logging_defines.h"
17 #include "media/cast/net/cast_transport_config.h"
18 #include "media/video/video_encode_accelerator.h"
19
20 namespace media {
21 namespace cast {
22 class LocalVideoEncodeAcceleratorClient;
23 } // namespace cast
24 } // namespace media
25
26 namespace {
27 static const size_t kOutputBufferCount = 3;
28
LogFrameEncodedEvent(const scoped_refptr<media::cast::CastEnvironment> & cast_environment,base::TimeTicks event_time,media::cast::RtpTimestamp rtp_timestamp,uint32 frame_id)29 void LogFrameEncodedEvent(
30 const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
31 base::TimeTicks event_time,
32 media::cast::RtpTimestamp rtp_timestamp,
33 uint32 frame_id) {
34 cast_environment->Logging()->InsertFrameEvent(
35 event_time, media::cast::FRAME_ENCODED, media::cast::VIDEO_EVENT,
36 rtp_timestamp, frame_id);
37 }
38 } // namespace
39
40 namespace media {
41 namespace cast {
42
43 // Container for the associated data of a video frame being processed.
44 struct EncodedFrameReturnData {
EncodedFrameReturnDatamedia::cast::EncodedFrameReturnData45 EncodedFrameReturnData(base::TimeTicks c_time,
46 VideoEncoder::FrameEncodedCallback callback) {
47 capture_time = c_time;
48 frame_encoded_callback = callback;
49 }
50 base::TimeTicks capture_time;
51 VideoEncoder::FrameEncodedCallback frame_encoded_callback;
52 };
53
54 // The ExternalVideoEncoder class can be deleted directly by cast, while
55 // LocalVideoEncodeAcceleratorClient stays around long enough to properly shut
56 // down the VideoEncodeAccelerator.
57 class LocalVideoEncodeAcceleratorClient
58 : public VideoEncodeAccelerator::Client,
59 public base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient> {
60 public:
61 // Create an instance of this class and post a task to create
62 // video_encode_accelerator_. A ref to |this| will be kept, awaiting reply
63 // via ProxyCreateVideoEncodeAccelerator, which will provide us with the
64 // encoder task runner and vea instance. We cannot be destroyed until we
65 // receive the reply, otherwise the VEA object created may leak.
Create(scoped_refptr<CastEnvironment> cast_environment,const CreateVideoEncodeAcceleratorCallback & create_vea_cb,const CreateVideoEncodeMemoryCallback & create_video_encode_mem_cb,const base::WeakPtr<ExternalVideoEncoder> & weak_owner)66 static scoped_refptr<LocalVideoEncodeAcceleratorClient> Create(
67 scoped_refptr<CastEnvironment> cast_environment,
68 const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
69 const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
70 const base::WeakPtr<ExternalVideoEncoder>& weak_owner) {
71 scoped_refptr<LocalVideoEncodeAcceleratorClient> client(
72 new LocalVideoEncodeAcceleratorClient(
73 cast_environment, create_video_encode_mem_cb, weak_owner));
74
75 // This will keep a ref to |client|, if weak_owner is destroyed before
76 // ProxyCreateVideoEncodeAccelerator is called, we will stay alive until
77 // we can properly destroy the VEA.
78 create_vea_cb.Run(base::Bind(
79 &LocalVideoEncodeAcceleratorClient::OnCreateVideoEncodeAcceleratorProxy,
80 client));
81
82 return client;
83 }
84
85 // Initialize the real HW encoder.
Initialize(const VideoSenderConfig & video_config)86 void Initialize(const VideoSenderConfig& video_config) {
87 DCHECK(encoder_task_runner_.get());
88 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
89
90 VideoCodecProfile output_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
91 switch (video_config.codec) {
92 case CODEC_VIDEO_VP8:
93 output_profile = media::VP8PROFILE_ANY;
94 break;
95 case CODEC_VIDEO_H264:
96 output_profile = media::H264PROFILE_MAIN;
97 break;
98 case CODEC_VIDEO_FAKE:
99 NOTREACHED() << "Fake software video encoder cannot be external";
100 break;
101 default:
102 NOTREACHED() << "Video codec not specified or not supported";
103 break;
104 }
105 max_frame_rate_ = video_config.max_frame_rate;
106
107 bool result = video_encode_accelerator_->Initialize(
108 media::VideoFrame::I420,
109 gfx::Size(video_config.width, video_config.height),
110 output_profile,
111 video_config.start_bitrate,
112 this);
113
114 UMA_HISTOGRAM_BOOLEAN("Cast.Sender.VideoEncodeAcceleratorInitializeSuccess",
115 result);
116 if (!result) {
117 cast_environment_->PostTask(
118 CastEnvironment::MAIN,
119 FROM_HERE,
120 base::Bind(&ExternalVideoEncoder::EncoderInitialized, weak_owner_,
121 false));
122 return;
123 }
124
125 // Wait until shared memory is allocated to indicate that encoder is
126 // initialized.
127 }
128
129 // Destroy the VEA on the correct thread.
Destroy()130 void Destroy() {
131 DCHECK(encoder_task_runner_.get());
132 if (!video_encode_accelerator_)
133 return;
134
135 if (encoder_task_runner_->RunsTasksOnCurrentThread()) {
136 video_encode_accelerator_.reset();
137 } else {
138 // We do this instead of just reposting to encoder_task_runner_, because
139 // we are called from the destructor.
140 encoder_task_runner_->PostTask(
141 FROM_HERE,
142 base::Bind(&DestroyVideoEncodeAcceleratorOnEncoderThread,
143 base::Passed(&video_encode_accelerator_)));
144 }
145 }
146
SetBitRate(uint32 bit_rate)147 void SetBitRate(uint32 bit_rate) {
148 DCHECK(encoder_task_runner_.get());
149 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
150
151 video_encode_accelerator_->RequestEncodingParametersChange(bit_rate,
152 max_frame_rate_);
153 }
154
EncodeVideoFrame(const scoped_refptr<media::VideoFrame> & video_frame,const base::TimeTicks & capture_time,bool key_frame_requested,const VideoEncoder::FrameEncodedCallback & frame_encoded_callback)155 void EncodeVideoFrame(
156 const scoped_refptr<media::VideoFrame>& video_frame,
157 const base::TimeTicks& capture_time,
158 bool key_frame_requested,
159 const VideoEncoder::FrameEncodedCallback& frame_encoded_callback) {
160 DCHECK(encoder_task_runner_.get());
161 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
162
163 encoded_frame_data_storage_.push_back(
164 EncodedFrameReturnData(capture_time, frame_encoded_callback));
165
166 // BitstreamBufferReady will be called once the encoder is done.
167 video_encode_accelerator_->Encode(video_frame, key_frame_requested);
168 }
169
170 protected:
NotifyError(VideoEncodeAccelerator::Error error)171 virtual void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE {
172 DCHECK(encoder_task_runner_.get());
173 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
174 VLOG(1) << "ExternalVideoEncoder NotifyError: " << error;
175
176 cast_environment_->PostTask(
177 CastEnvironment::MAIN,
178 FROM_HERE,
179 base::Bind(&ExternalVideoEncoder::EncoderError, weak_owner_));
180 }
181
182 // Called to allocate the input and output buffers.
RequireBitstreamBuffers(unsigned int input_count,const gfx::Size & input_coded_size,size_t output_buffer_size)183 virtual void RequireBitstreamBuffers(unsigned int input_count,
184 const gfx::Size& input_coded_size,
185 size_t output_buffer_size) OVERRIDE {
186 DCHECK(encoder_task_runner_.get());
187 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
188 DCHECK(video_encode_accelerator_);
189
190 for (size_t j = 0; j < kOutputBufferCount; ++j) {
191 create_video_encode_memory_cb_.Run(
192 output_buffer_size,
193 base::Bind(&LocalVideoEncodeAcceleratorClient::OnCreateSharedMemory,
194 this));
195 }
196 }
197
198 // Encoder has encoded a frame and it's available in one of out output
199 // buffers.
BitstreamBufferReady(int32 bitstream_buffer_id,size_t payload_size,bool key_frame)200 virtual void BitstreamBufferReady(int32 bitstream_buffer_id,
201 size_t payload_size,
202 bool key_frame) OVERRIDE {
203 DCHECK(encoder_task_runner_.get());
204 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
205 if (bitstream_buffer_id < 0 ||
206 bitstream_buffer_id >= static_cast<int32>(output_buffers_.size())) {
207 NOTREACHED();
208 VLOG(1) << "BitstreamBufferReady(): invalid bitstream_buffer_id="
209 << bitstream_buffer_id;
210 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
211 return;
212 }
213 base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id];
214 if (payload_size > output_buffer->mapped_size()) {
215 NOTREACHED();
216 VLOG(1) << "BitstreamBufferReady(): invalid payload_size = "
217 << payload_size;
218 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
219 return;
220 }
221 if (key_frame)
222 key_frame_encountered_ = true;
223 if (!key_frame_encountered_) {
224 // Do not send video until we have encountered the first key frame.
225 // Save the bitstream buffer in |stream_header_| to be sent later along
226 // with the first key frame.
227 stream_header_.append(static_cast<const char*>(output_buffer->memory()),
228 payload_size);
229 } else if (!encoded_frame_data_storage_.empty()) {
230 scoped_ptr<EncodedFrame> encoded_frame(
231 new EncodedFrame());
232 encoded_frame->dependency = key_frame ? EncodedFrame::KEY :
233 EncodedFrame::DEPENDENT;
234 encoded_frame->frame_id = ++last_encoded_frame_id_;
235 if (key_frame)
236 encoded_frame->referenced_frame_id = encoded_frame->frame_id;
237 else
238 encoded_frame->referenced_frame_id = encoded_frame->frame_id - 1;
239 encoded_frame->reference_time =
240 encoded_frame_data_storage_.front().capture_time;
241 encoded_frame->rtp_timestamp =
242 GetVideoRtpTimestamp(encoded_frame->reference_time);
243 if (!stream_header_.empty()) {
244 encoded_frame->data = stream_header_;
245 stream_header_.clear();
246 }
247 encoded_frame->data.append(
248 static_cast<const char*>(output_buffer->memory()), payload_size);
249
250 cast_environment_->PostTask(
251 CastEnvironment::MAIN,
252 FROM_HERE,
253 base::Bind(&LogFrameEncodedEvent,
254 cast_environment_,
255 cast_environment_->Clock()->NowTicks(),
256 encoded_frame->rtp_timestamp,
257 encoded_frame->frame_id));
258
259 cast_environment_->PostTask(
260 CastEnvironment::MAIN,
261 FROM_HERE,
262 base::Bind(encoded_frame_data_storage_.front().frame_encoded_callback,
263 base::Passed(&encoded_frame)));
264
265 encoded_frame_data_storage_.pop_front();
266 } else {
267 VLOG(1) << "BitstreamBufferReady(): no encoded frame data available";
268 }
269
270 // We need to re-add the output buffer to the encoder after we are done
271 // with it.
272 video_encode_accelerator_->UseOutputBitstreamBuffer(media::BitstreamBuffer(
273 bitstream_buffer_id,
274 output_buffers_[bitstream_buffer_id]->handle(),
275 output_buffers_[bitstream_buffer_id]->mapped_size()));
276 }
277
278 private:
LocalVideoEncodeAcceleratorClient(scoped_refptr<CastEnvironment> cast_environment,const CreateVideoEncodeMemoryCallback & create_video_encode_mem_cb,const base::WeakPtr<ExternalVideoEncoder> & weak_owner)279 LocalVideoEncodeAcceleratorClient(
280 scoped_refptr<CastEnvironment> cast_environment,
281 const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
282 const base::WeakPtr<ExternalVideoEncoder>& weak_owner)
283 : cast_environment_(cast_environment),
284 create_video_encode_memory_cb_(create_video_encode_mem_cb),
285 weak_owner_(weak_owner),
286 last_encoded_frame_id_(kStartFrameId),
287 key_frame_encountered_(false) {}
288
289 // Trampoline VEA creation callback to OnCreateVideoEncodeAccelerator()
290 // on encoder_task_runner. Normally we would just repost the same method to
291 // it, and would not need a separate proxy method, but we can't
292 // ThreadTaskRunnerHandle::Get() in unittests just yet.
OnCreateVideoEncodeAcceleratorProxy(scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,scoped_ptr<media::VideoEncodeAccelerator> vea)293 void OnCreateVideoEncodeAcceleratorProxy(
294 scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
295 scoped_ptr<media::VideoEncodeAccelerator> vea) {
296 encoder_task_runner->PostTask(
297 FROM_HERE,
298 base::Bind(&media::cast::LocalVideoEncodeAcceleratorClient::
299 OnCreateVideoEncodeAccelerator,
300 this,
301 encoder_task_runner,
302 base::Passed(&vea)));
303 }
304
OnCreateVideoEncodeAccelerator(scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,scoped_ptr<media::VideoEncodeAccelerator> vea)305 void OnCreateVideoEncodeAccelerator(
306 scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
307 scoped_ptr<media::VideoEncodeAccelerator> vea) {
308 encoder_task_runner_ = encoder_task_runner;
309 video_encode_accelerator_.reset(vea.release());
310
311 cast_environment_->PostTask(
312 CastEnvironment::MAIN,
313 FROM_HERE,
314 base::Bind(&ExternalVideoEncoder::OnCreateVideoEncodeAccelerator,
315 weak_owner_,
316 encoder_task_runner_));
317 }
318
319 // Note: This method can be called on any thread.
OnCreateSharedMemory(scoped_ptr<base::SharedMemory> memory)320 void OnCreateSharedMemory(scoped_ptr<base::SharedMemory> memory) {
321 encoder_task_runner_->PostTask(
322 FROM_HERE,
323 base::Bind(&LocalVideoEncodeAcceleratorClient::ReceivedSharedMemory,
324 this,
325 base::Passed(&memory)));
326 }
327
ReceivedSharedMemory(scoped_ptr<base::SharedMemory> memory)328 void ReceivedSharedMemory(scoped_ptr<base::SharedMemory> memory) {
329 DCHECK(encoder_task_runner_.get());
330 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
331
332 output_buffers_.push_back(memory.release());
333
334 // Wait until all requested buffers are received.
335 if (output_buffers_.size() < kOutputBufferCount)
336 return;
337
338 // Immediately provide all output buffers to the VEA.
339 for (size_t i = 0; i < output_buffers_.size(); ++i) {
340 video_encode_accelerator_->UseOutputBitstreamBuffer(
341 media::BitstreamBuffer(static_cast<int32>(i),
342 output_buffers_[i]->handle(),
343 output_buffers_[i]->mapped_size()));
344 }
345
346 cast_environment_->PostTask(
347 CastEnvironment::MAIN,
348 FROM_HERE,
349 base::Bind(&ExternalVideoEncoder::EncoderInitialized, weak_owner_,
350 true));
351 }
352
DestroyVideoEncodeAcceleratorOnEncoderThread(scoped_ptr<media::VideoEncodeAccelerator> vea)353 static void DestroyVideoEncodeAcceleratorOnEncoderThread(
354 scoped_ptr<media::VideoEncodeAccelerator> vea) {
355 // VEA::~VEA specialization takes care of calling Destroy() on the VEA impl.
356 }
357
358 friend class base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient>;
359
~LocalVideoEncodeAcceleratorClient()360 virtual ~LocalVideoEncodeAcceleratorClient() {
361 Destroy();
362 DCHECK(!video_encode_accelerator_);
363 }
364
365 const scoped_refptr<CastEnvironment> cast_environment_;
366 scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
367 scoped_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_;
368 const CreateVideoEncodeMemoryCallback create_video_encode_memory_cb_;
369 const base::WeakPtr<ExternalVideoEncoder> weak_owner_;
370 int max_frame_rate_;
371 uint32 last_encoded_frame_id_;
372 bool key_frame_encountered_;
373 std::string stream_header_;
374
375 // Shared memory buffers for output with the VideoAccelerator.
376 ScopedVector<base::SharedMemory> output_buffers_;
377
378 // FIFO list.
379 std::list<EncodedFrameReturnData> encoded_frame_data_storage_;
380
381 DISALLOW_COPY_AND_ASSIGN(LocalVideoEncodeAcceleratorClient);
382 };
383
ExternalVideoEncoder(scoped_refptr<CastEnvironment> cast_environment,const VideoSenderConfig & video_config,const CastInitializationCallback & initialization_cb,const CreateVideoEncodeAcceleratorCallback & create_vea_cb,const CreateVideoEncodeMemoryCallback & create_video_encode_mem_cb)384 ExternalVideoEncoder::ExternalVideoEncoder(
385 scoped_refptr<CastEnvironment> cast_environment,
386 const VideoSenderConfig& video_config,
387 const CastInitializationCallback& initialization_cb,
388 const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
389 const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb)
390 : video_config_(video_config),
391 cast_environment_(cast_environment),
392 encoder_active_(false),
393 key_frame_requested_(false),
394 initialization_cb_(initialization_cb),
395 weak_factory_(this) {
396 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
397
398 video_accelerator_client_ =
399 LocalVideoEncodeAcceleratorClient::Create(cast_environment_,
400 create_vea_cb,
401 create_video_encode_mem_cb,
402 weak_factory_.GetWeakPtr());
403 DCHECK(video_accelerator_client_.get());
404 }
405
~ExternalVideoEncoder()406 ExternalVideoEncoder::~ExternalVideoEncoder() {
407 }
408
EncoderInitialized(bool success)409 void ExternalVideoEncoder::EncoderInitialized(bool success) {
410 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
411 encoder_active_ = success;
412 DCHECK(!initialization_cb_.is_null());
413 initialization_cb_.Run(
414 success ?
415 STATUS_VIDEO_INITIALIZED : STATUS_HW_VIDEO_ENCODER_NOT_SUPPORTED);
416 initialization_cb_.Reset();
417 }
418
EncoderError()419 void ExternalVideoEncoder::EncoderError() {
420 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
421 encoder_active_ = false;
422 }
423
OnCreateVideoEncodeAccelerator(scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner)424 void ExternalVideoEncoder::OnCreateVideoEncodeAccelerator(
425 scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner) {
426 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
427 encoder_task_runner_ = encoder_task_runner;
428
429 encoder_task_runner_->PostTask(
430 FROM_HERE,
431 base::Bind(&LocalVideoEncodeAcceleratorClient::Initialize,
432 video_accelerator_client_,
433 video_config_));
434 }
435
EncodeVideoFrame(const scoped_refptr<media::VideoFrame> & video_frame,const base::TimeTicks & capture_time,const FrameEncodedCallback & frame_encoded_callback)436 bool ExternalVideoEncoder::EncodeVideoFrame(
437 const scoped_refptr<media::VideoFrame>& video_frame,
438 const base::TimeTicks& capture_time,
439 const FrameEncodedCallback& frame_encoded_callback) {
440 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
441
442 if (!encoder_active_)
443 return false;
444
445 encoder_task_runner_->PostTask(
446 FROM_HERE,
447 base::Bind(&LocalVideoEncodeAcceleratorClient::EncodeVideoFrame,
448 video_accelerator_client_,
449 video_frame,
450 capture_time,
451 key_frame_requested_,
452 frame_encoded_callback));
453
454 key_frame_requested_ = false;
455 return true;
456 }
457
458 // Inform the encoder about the new target bit rate.
SetBitRate(int new_bit_rate)459 void ExternalVideoEncoder::SetBitRate(int new_bit_rate) {
460 if (!encoder_active_) {
461 // If we receive SetBitRate() before VEA creation callback is invoked,
462 // cache the new bit rate in the encoder config and use the new settings
463 // to initialize VEA.
464 video_config_.start_bitrate = new_bit_rate;
465 return;
466 }
467
468 encoder_task_runner_->PostTask(
469 FROM_HERE,
470 base::Bind(&LocalVideoEncodeAcceleratorClient::SetBitRate,
471 video_accelerator_client_,
472 new_bit_rate));
473 }
474
475 // Inform the encoder to encode the next frame as a key frame.
GenerateKeyFrame()476 void ExternalVideoEncoder::GenerateKeyFrame() {
477 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
478 key_frame_requested_ = true;
479 }
480
481 // Inform the encoder to only reference frames older or equal to frame_id;
LatestFrameIdToReference(uint32)482 void ExternalVideoEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) {
483 // Do nothing not supported.
484 }
485 } // namespace cast
486 } // namespace media
487