OLD | NEW |
---|---|
(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 | |
OLD | NEW |