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