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

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

Powered by Google App Engine
This is Rietveld 408576698