Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(172)

Side by Side Diff: media/cast/video_sender/external_video_encoder.cc

Issue 116623002: Cast: Adding support for GPU accelerated encode (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Removed dependency on content Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "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/video/video_encode_accelerator.h"
16
17 namespace {
18 // The number of input buffers allocated, more than what is requested by
19 // RequireBitstreamBuffers().
mikhal1 2013/12/17 22:39:47 The comment is unclear to me.
pwestin 2013/12/19 16:03:47 Done.
20 static const int kInputBufferExtraCount = 1;
21 static const int kOutputBufferCount = 3;
22
23 void LogFrameEncodedEvent(
24 media::cast::CastEnvironment* const cast_environment,
25 const base::TimeTicks& capture_time) {
26 cast_environment->Logging()->InsertFrameEvent(
27 media::cast::kVideoFrameEncoded,
28 media::cast::GetVideoRtpTimestamp(capture_time),
29 media::cast::kFrameIdUnknown);
30 }
31 } // namespace
32
33 namespace media {
34 namespace cast {
35
36 // Container for the associated data of a video frame being processed.
37 struct EncodedFrameReturnData {
38 EncodedFrameReturnData(base::TimeTicks c_time,
39 VideoEncoder::FrameEncodedCallback callback) {
40 capture_time = c_time;
41 frame_encoded_callback = callback;
42 }
43 base::TimeTicks capture_time;
44 VideoEncoder::FrameEncodedCallback frame_encoded_callback;
45 };
46
47 // The ExternalVideoEncoder class can be deleted directly by cast, while
48 // LocalVideoEncodeAcceleratorClient stays around long enough to properly shut
49 // down the VideoEncodeAccelerator.
50 class LocalVideoEncodeAcceleratorClient
51 : public VideoEncodeAccelerator::Client,
52 public base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient> {
53 public:
54 LocalVideoEncodeAcceleratorClient(
55 scoped_refptr<CastEnvironment> cast_environment,
56 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories,
57 const base::WeakPtr<ExternalVideoEncoder>& weak_owner)
58 : cast_environment_(cast_environment),
59 gpu_factories_(gpu_factories),
60 encoder_message_loop_(gpu_factories->GetMessageLoop()),
61 weak_owner_(weak_owner),
62 last_encoded_frame_id_(kStartFrameId) {
63 DCHECK(encoder_message_loop_);
64 }
65
66 // Initialize the real HW encoder.
67 void Initialize(const VideoSenderConfig& video_config) {
68 DCHECK(gpu_factories_);
69 DCHECK(encoder_message_loop_);
70
71 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
72
73 video_encode_accelerator_ =
74 gpu_factories_->CreateVideoEncodeAccelerator(this).Pass();
75 if (!video_encode_accelerator_) return;
mikhal1 2013/12/17 22:39:47 How would the caller know that this wasn't a valid
pwestin 2013/12/19 16:03:47 Will add that in a future CL
mikhal1 2013/12/19 17:23:10 Add a todo On 2013/12/19 16:03:47, pwestin wrote:
76
77 VideoCodecProfile output_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
78 switch (video_config.codec) {
79 case kVp8:
80 output_profile = media::VP8PROFILE_MAIN;
81 break;
82 case kH264:
83 output_profile = media::H264PROFILE_MAIN;
84 break;
85 }
86 codec_ = video_config.codec;
87 max_frame_rate_ = video_config.max_frame_rate;
88
89 // Asynchronous initialization call; NotifyInitializeDone or NotifyError
90 // will be called once the HW is initialized.
91 video_encode_accelerator_->Initialize(
92 media::VideoFrame::I420,
93 gfx::Size(video_config.width, video_config.height),
94 output_profile,
95 video_config.start_bitrate);
96 }
97
98 // Free the HW.
99 void Destroy() {
100 DCHECK(encoder_message_loop_);
101 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
102
103 if (video_encode_accelerator_) {
104 video_encode_accelerator_.release()->Destroy();
105 }
106 }
107
108 void SetBitRate(uint32 bit_rate) {
109 DCHECK(encoder_message_loop_);
110 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
111
112 video_encode_accelerator_->RequestEncodingParametersChange(
113 bit_rate, max_frame_rate_);
114 }
115
116 void EncodeVideoFrame(
117 const scoped_refptr<media::VideoFrame>& video_frame,
118 const base::TimeTicks& capture_time,
119 bool key_frame_requested,
120 const VideoEncoder::FrameEncodedCallback& frame_encoded_callback) {
121 DCHECK(encoder_message_loop_);
122 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
123
124 if (input_buffers_free_.empty()) {
125 NOTREACHED();
126 VLOG(2) << "EncodeVideoFrame(): drop frame due to no hw buffers";
127 return;
128 }
129 const int index = input_buffers_free_.back();
130 base::SharedMemory* input_buffer = input_buffers_[index];
131
132 // TODO(pwestin): this allocation and copy can be removed once we don't
133 // pass the video frame through liblingle.
134
135 scoped_refptr<media::VideoFrame> frame =
136 media::VideoFrame::WrapExternalPackedMemory(
137 video_frame->format(),
138 video_frame->coded_size(),
139 video_frame->visible_rect(),
140 video_frame->natural_size(),
141 reinterpret_cast<uint8*>(input_buffer->memory()),
142 input_buffer->mapped_size(),
143 input_buffer->handle(),
144 video_frame->GetTimestamp(),
145 base::Bind(&LocalVideoEncodeAcceleratorClient::FinishedWithInBuffer,
146 this, index));
mikhal1 2013/12/17 22:39:47 ident
pwestin 2013/12/19 16:03:47 Done.
mikhal1 2013/12/19 17:23:10 don't think this is right either. On 2013/12/19 16
147
148 if (!frame) {
149 VLOG(1) << "EncodeVideoFrame(): failed to create frame";
150 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
151 return;
152 }
153 // Do a stride copy of the input frame to match the input requirements.
154 media::CopyYPlane(video_frame->data(VideoFrame::kYPlane),
155 video_frame->stride(VideoFrame::kYPlane),
156 video_frame->natural_size().height(),
157 frame.get());
158 media::CopyUPlane(video_frame->data(VideoFrame::kUPlane),
159 video_frame->stride(VideoFrame::kUPlane),
160 video_frame->natural_size().height(),
161 frame.get());
162 media::CopyVPlane(video_frame->data(VideoFrame::kVPlane),
163 video_frame->stride(VideoFrame::kVPlane),
164 video_frame->natural_size().height(),
165 frame.get());
166
167 encoded_frame_data_storage_.push_back(
168 EncodedFrameReturnData(capture_time, frame_encoded_callback));
169
170 // BitstreamBufferReady will be called once the encoder is done.
171 video_encode_accelerator_->Encode(frame, key_frame_requested);
172 }
173
174 protected:
175 virtual void NotifyInitializeDone() OVERRIDE {
176 DCHECK(encoder_message_loop_);
177 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
178
179 cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
180 base::Bind(&ExternalVideoEncoder::EncoderInitialized,
181 weak_owner_));
182 }
183
184 virtual void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE {
185 DCHECK(encoder_message_loop_);
186 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
187 VLOG(1) << "ExternalVideoEncoder NotifyError: " << error;
188
189 if (video_encode_accelerator_) {
190 video_encode_accelerator_.release()->Destroy();
191 }
192 cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
193 base::Bind(&ExternalVideoEncoder::EncoderError,
194 weak_owner_));
195 }
196
197 // Called to allocate the in and output buffers.
mikhal1 2013/12/17 22:39:47 input for consistency
pwestin 2013/12/19 16:03:47 Done.
198 virtual void RequireBitstreamBuffers(unsigned int input_count,
199 const gfx::Size& input_coded_size,
200 size_t output_buffer_size) OVERRIDE {
201 DCHECK(encoder_message_loop_);
202 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
203 DCHECK(video_encode_accelerator_);
204
205 for (unsigned int i = 0; i < input_count + kInputBufferExtraCount; ++i) {
206 base::SharedMemory* shm = gpu_factories_->CreateSharedMemory(
207 media::VideoFrame::AllocationSize(media::VideoFrame::I420,
208 input_coded_size));
209 if (!shm) {
210 VLOG(1) << "RequireBitstreamBuffers(): failed to create input buffer ";
211 return;
212 }
213 input_buffers_.push_back(shm);
214 input_buffers_free_.push_back(i);
215 }
216
217 for (int j = 0; j < kOutputBufferCount; ++j) {
218 base::SharedMemory* shm =
219 gpu_factories_->CreateSharedMemory(output_buffer_size);
220 if (!shm) {
221 VLOG(1) << "RequireBitstreamBuffers(): failed to create input buffer ";
222 return;
223 }
224 output_buffers_.push_back(shm);
225 }
226 // Immediately provide all output buffers to the VEA.
227 for (size_t i = 0; i < output_buffers_.size(); ++i) {
228 video_encode_accelerator_->UseOutputBitstreamBuffer(
229 media::BitstreamBuffer(i, output_buffers_[i]->handle(),
230 output_buffers_[i]->mapped_size()));
231 }
232 }
233
234 // Encoder has encoded a frame and it's available in one of out output
235 // buffers.
236 virtual void BitstreamBufferReady(int32 bitstream_buffer_id,
237 size_t payload_size,
238 bool key_frame) OVERRIDE {
239 DCHECK(encoder_message_loop_);
240 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
241 if (bitstream_buffer_id < 0 ||
242 bitstream_buffer_id >= static_cast<int32>(output_buffers_.size())) {
243 NOTREACHED();
244 VLOG(1) << "BitstreamBufferReady(): invalid bitstream_buffer_id="
245 << bitstream_buffer_id;
246 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
247 return;
248 }
249 base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id];
250 if (payload_size > output_buffer->mapped_size()) {
251 NOTREACHED();
252 VLOG(1) << "BitstreamBufferReady(): invalid payload_size = "
253 << payload_size;
254 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
255 return;
256 }
257 if (encoded_frame_data_storage_.empty()) {
258 NOTREACHED();
259 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
260 return;
261 }
262 scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame());
263
264 encoded_frame->codec = codec_;
265 encoded_frame->key_frame = key_frame;
266 encoded_frame->last_referenced_frame_id = last_encoded_frame_id_;
267 last_encoded_frame_id_++;
268 encoded_frame->frame_id = last_encoded_frame_id_;
269 if (key_frame) {
270 // Self referenced.
271 encoded_frame->last_referenced_frame_id = encoded_frame->frame_id;
272 }
273
274 encoded_frame->data.insert(
275 0, static_cast<const char*>(output_buffer->memory()), payload_size);
276
277 cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
278 base::Bind(encoded_frame_data_storage_.front().frame_encoded_callback,
279 base::Passed(&encoded_frame),
280 encoded_frame_data_storage_.front().capture_time));
281
282 cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
283 base::Bind(LogFrameEncodedEvent, cast_environment_,
284 encoded_frame_data_storage_.front().capture_time));
285
286 encoded_frame_data_storage_.pop_front();
287
288 // We need to re-add the output buffer to the encoder after we are done
289 // with it.
290 video_encode_accelerator_->UseOutputBitstreamBuffer(
291 media::BitstreamBuffer(bitstream_buffer_id,
292 output_buffers_[bitstream_buffer_id]->handle(),
293 output_buffers_[bitstream_buffer_id]->mapped_size()));
294 }
295
296 private:
297 // Encoder is done with the provided input buffer.
298 void FinishedWithInBuffer(int index) {
mikhal1 2013/12/17 22:39:47 input
pwestin 2013/12/19 16:03:47 Done.
299 DCHECK(encoder_message_loop_);
300 DCHECK(encoder_message_loop_->RunsTasksOnCurrentThread());
301 DCHECK_GE(index, 0);
302 DCHECK_LT(index, static_cast<int>(input_buffers_.size()));
303 VLOG(2) << "EncodeFrameFinished(): index=" << index;
304 input_buffers_free_.push_back(index);
305 }
306
307 friend class base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient>;
308
309 virtual ~LocalVideoEncodeAcceleratorClient() {}
310
311 const scoped_refptr<CastEnvironment> cast_environment_;
312 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_;
313 scoped_refptr<base::MessageLoopProxy> encoder_message_loop_;
314 const base::WeakPtr<ExternalVideoEncoder> weak_owner_;
315
316 scoped_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_;
317 int max_frame_rate_;
318 VideoCodec codec_;
319 uint32 last_encoded_frame_id_;
320
321 // Shared memory buffers for input/output with the VideoAccelerator.
322 ScopedVector<base::SharedMemory> input_buffers_;
323 ScopedVector<base::SharedMemory> output_buffers_;
324
325 // Input buffers ready to be filled with input from Encode(). As a LIFO since
326 // we don't care about ordering.
327 std::vector<int> input_buffers_free_;
328
329 // FIFO list.
330 std::list<EncodedFrameReturnData> encoded_frame_data_storage_;
331
332 DISALLOW_COPY_AND_ASSIGN(LocalVideoEncodeAcceleratorClient);
333 };
334
335 ExternalVideoEncoder::ExternalVideoEncoder(
336 scoped_refptr<CastEnvironment> cast_environment,
337 const VideoSenderConfig& video_config,
338 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories)
339 : video_config_(video_config),
340 cast_environment_(cast_environment),
341 encoder_active_(false),
342 key_frame_requested_(false),
343 skip_next_frame_(false),
344 skip_count_(0),
345 encoder_message_loop_(gpu_factories->GetMessageLoop()),
346 weak_factory_(this) {
347 DCHECK(gpu_factories);
348 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
349 video_accelerator_client_ = new LocalVideoEncodeAcceleratorClient(
350 cast_environment, gpu_factories, weak_factory_.GetWeakPtr());
351
352 encoder_message_loop_->PostTask(
353 FROM_HERE,
354 base::Bind(&LocalVideoEncodeAcceleratorClient::Initialize,
355 video_accelerator_client_, video_config));
356 }
357
358 ExternalVideoEncoder::~ExternalVideoEncoder() {
359 encoder_message_loop_->PostTask(
360 FROM_HERE,
361 base::Bind(&LocalVideoEncodeAcceleratorClient::Destroy,
362 video_accelerator_client_));
363 }
364
365 void ExternalVideoEncoder::EncoderInitialized() {
366 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
367 encoder_active_ = true;
368 }
369
370 void ExternalVideoEncoder::EncoderError() {
371 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
372 encoder_active_ = false;
373 }
374
375 bool ExternalVideoEncoder::EncodeVideoFrame(
376 const scoped_refptr<media::VideoFrame>& video_frame,
377 const base::TimeTicks& capture_time,
378 const FrameEncodedCallback& frame_encoded_callback) {
379 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
380
381 if (!encoder_active_) return false;
382
383 if (skip_next_frame_) {
mikhal1 2013/12/17 22:39:47 set this to false after skipping.
pwestin 2013/12/19 16:03:47 Done.
384 ++skip_count_;
385 VLOG(1) << "Skip encoding frame";
386 return false;
387 }
388 cast_environment_->Logging()->InsertFrameEvent(kVideoFrameSentToEncoder,
389 GetVideoRtpTimestamp(capture_time), kFrameIdUnknown);
390
391 encoder_message_loop_->PostTask(
392 FROM_HERE,
393 base::Bind(&LocalVideoEncodeAcceleratorClient::EncodeVideoFrame,
394 video_accelerator_client_, video_frame, capture_time,
395 key_frame_requested_, frame_encoded_callback));
396
397 key_frame_requested_ = false;
398 return true;
399 }
400
401 // Inform the encoder about the new target bit rate.
402 void ExternalVideoEncoder::SetBitRate(int new_bit_rate) {
403 encoder_message_loop_->PostTask(
404 FROM_HERE,
405 base::Bind(&LocalVideoEncodeAcceleratorClient::SetBitRate,
406 video_accelerator_client_, new_bit_rate));
407 }
408
409 // Inform the encoder to not encode the next frame.
410 void ExternalVideoEncoder::SkipNextFrame(bool skip_next_frame) {
411 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
412 skip_next_frame_ = skip_next_frame;
413 }
414
415 // Inform the encoder to encode the next frame as a key frame.
416 void ExternalVideoEncoder::GenerateKeyFrame() {
417 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
418 key_frame_requested_ = true;
419 }
420
421 // Inform the encoder to only reference frames older or equal to frame_id;
422 void ExternalVideoEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) {
423 // Do nothing not supported.
mikhal1 2013/12/17 22:39:47 Add NotSuPPORTED
pwestin 2013/12/19 16:03:47 It will still be called
424 }
425
426 int ExternalVideoEncoder::NumberOfSkippedFrames() const {
427 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
428 return skip_count_;
429 }
430
431 } // namespace cast
432 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698