OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 "content/common/gpu/media/vt_video_encode_accelerator_mac.h" | |
6 | |
7 #include "base/thread_task_runner_handle.h" | |
8 #include "media/base/mac/coremedia_glue.h" | |
9 #include "media/base/mac/corevideo_glue.h" | |
10 #include "media/base/mac/video_frame_mac.h" | |
11 | |
12 namespace content { | |
13 | |
14 namespace { | |
15 | |
16 // TODO(emircan): Check if we can find the actual system capabilities via | |
17 // creating VTCompressionSessions with varying requirements. | |
18 // See crbug.com/584784. | |
19 const size_t kBitsPerByte = 8; | |
20 const size_t kDefaultResolutionWidth = 640; | |
21 const size_t kDefaultResolutionHeight = 480; | |
22 const size_t kMaxFrameRateNumerator = 30; | |
23 const size_t kMaxFrameRateDenominator = 1; | |
24 const size_t kMaxResolutionWidth = 4096; | |
25 const size_t kMaxResolutionHeight = 2160; | |
26 const size_t kNumInputBuffers = 3; | |
27 | |
28 } // namespace | |
29 | |
30 struct VTVideoEncodeAccelerator::InProgressFrameEncode { | |
31 InProgressFrameEncode(base::TimeDelta rtp_timestamp, | |
32 base::TimeTicks ref_time) | |
33 : timestamp(rtp_timestamp), reference_time(ref_time) {} | |
34 const base::TimeDelta timestamp; | |
35 const base::TimeTicks reference_time; | |
36 | |
37 private: | |
38 DISALLOW_IMPLICIT_CONSTRUCTORS(InProgressFrameEncode); | |
39 }; | |
40 | |
41 struct VTVideoEncodeAccelerator::EncodeOutput { | |
42 EncodeOutput(VTEncodeInfoFlags info_flags, CMSampleBufferRef sbuf) | |
43 : info(info_flags), sample_buffer(sbuf, base::scoped_policy::RETAIN) {} | |
44 const VTEncodeInfoFlags info; | |
45 const base::ScopedCFTypeRef<CMSampleBufferRef> sample_buffer; | |
46 | |
47 private: | |
48 DISALLOW_IMPLICIT_CONSTRUCTORS(EncodeOutput); | |
49 }; | |
50 | |
51 struct VTVideoEncodeAccelerator::BitstreamBufferRef { | |
52 BitstreamBufferRef(int32_t id, | |
53 scoped_ptr<base::SharedMemory> shm, | |
54 size_t size) | |
55 : id(id), shm(std::move(shm)), size(size) {} | |
56 const int32_t id; | |
57 const scoped_ptr<base::SharedMemory> shm; | |
58 const size_t size; | |
59 | |
60 private: | |
61 DISALLOW_IMPLICIT_CONSTRUCTORS(BitstreamBufferRef); | |
62 }; | |
63 | |
64 VTVideoEncodeAccelerator::VTVideoEncodeAccelerator() | |
65 : client_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
66 encoder_thread_("VTEncoderThread"), | |
67 encoder_task_weak_factory_(this) { | |
68 encoder_weak_ptr_ = encoder_task_weak_factory_.GetWeakPtr(); | |
69 } | |
70 | |
71 VTVideoEncodeAccelerator::~VTVideoEncodeAccelerator() { | |
72 DVLOG(3) << __FUNCTION__; | |
73 DCHECK(thread_checker_.CalledOnValidThread()); | |
74 | |
75 Destroy(); | |
76 DCHECK(!encoder_thread_.IsRunning()); | |
77 DCHECK(!encoder_task_weak_factory_.HasWeakPtrs()); | |
78 } | |
79 | |
80 media::VideoEncodeAccelerator::SupportedProfiles | |
81 VTVideoEncodeAccelerator::GetSupportedProfiles() { | |
82 DVLOG(3) << __FUNCTION__; | |
83 DCHECK(thread_checker_.CalledOnValidThread()); | |
84 | |
85 SupportedProfiles profiles; | |
86 // Check if HW encoder is supported initially. | |
87 videotoolbox_glue_ = VideoToolboxGlue::Get(); | |
88 if (!videotoolbox_glue_) { | |
89 DLOG(ERROR) << "Failed creating VideoToolbox glue."; | |
90 return profiles; | |
91 } | |
92 const bool rv = CreateCompressionSession( | |
93 media::video_toolbox::DictionaryWithKeysAndValues(nullptr, nullptr, 0), | |
94 gfx::Size(kDefaultResolutionWidth, kDefaultResolutionHeight), true); | |
95 DestroyCompressionSession(); | |
96 if (!rv) { | |
97 VLOG(1) | |
98 << "Hardware encode acceleration is not available on this platform."; | |
99 return profiles; | |
100 } | |
101 | |
102 SupportedProfile profile; | |
103 profile.profile = media::H264PROFILE_BASELINE; | |
104 profile.max_framerate_numerator = kMaxFrameRateNumerator; | |
105 profile.max_framerate_denominator = kMaxFrameRateDenominator; | |
106 profile.max_resolution = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight); | |
107 profiles.push_back(profile); | |
108 return profiles; | |
109 } | |
110 | |
111 bool VTVideoEncodeAccelerator::Initialize( | |
112 media::VideoPixelFormat format, | |
113 const gfx::Size& input_visible_size, | |
114 media::VideoCodecProfile output_profile, | |
115 uint32_t initial_bitrate, | |
116 Client* client) { | |
117 DVLOG(3) << __FUNCTION__ | |
118 << ": input_format=" << media::VideoPixelFormatToString(format) | |
119 << ", input_visible_size=" << input_visible_size.ToString() | |
120 << ", output_profile=" << output_profile | |
121 << ", initial_bitrate=" << initial_bitrate; | |
122 DCHECK(thread_checker_.CalledOnValidThread()); | |
123 DCHECK(client); | |
124 | |
125 if (media::PIXEL_FORMAT_I420 != format) { | |
126 DLOG(ERROR) << "Input format not supported= " | |
127 << media::VideoPixelFormatToString(format); | |
128 return false; | |
129 } | |
130 if (media::H264PROFILE_BASELINE != output_profile) { | |
131 DLOG(ERROR) << "Output profile not supported= " | |
132 << output_profile; | |
133 return false; | |
134 } | |
135 | |
136 videotoolbox_glue_ = VideoToolboxGlue::Get(); | |
137 if (!videotoolbox_glue_) { | |
138 DLOG(ERROR) << "Failed creating VideoToolbox glue."; | |
139 return false; | |
140 } | |
141 | |
142 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client)); | |
143 client_ = client_ptr_factory_->GetWeakPtr(); | |
144 input_visible_size_ = input_visible_size; | |
145 frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator; | |
146 target_bitrate_ = initial_bitrate; | |
147 bitstream_buffer_size_ = input_visible_size.GetArea(); | |
148 | |
149 if (!encoder_thread_.Start()) { | |
150 DLOG(ERROR) << "Failed spawning encoder thread."; | |
151 return false; | |
152 } | |
153 encoder_thread_task_runner_ = encoder_thread_.task_runner(); | |
154 | |
155 if (!ResetCompressionSession()) { | |
156 DLOG(ERROR) << "Failed creating compression session."; | |
157 return false; | |
158 } | |
159 | |
160 client_task_runner_->PostTask( | |
161 FROM_HERE, | |
162 base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers, | |
163 input_visible_size_, bitstream_buffer_size_)); | |
164 return true; | |
165 } | |
166 | |
167 void VTVideoEncodeAccelerator::Encode( | |
168 const scoped_refptr<media::VideoFrame>& frame, | |
169 bool force_keyframe) { | |
170 DVLOG(3) << __FUNCTION__; | |
171 DCHECK(thread_checker_.CalledOnValidThread()); | |
172 | |
173 encoder_thread_task_runner_->PostTask( | |
174 FROM_HERE, base::Bind(&VTVideoEncodeAccelerator::EncodeTask, | |
175 base::Unretained(this), frame, force_keyframe)); | |
176 } | |
177 | |
178 void VTVideoEncodeAccelerator::UseOutputBitstreamBuffer( | |
179 const media::BitstreamBuffer& buffer) { | |
180 DVLOG(3) << __FUNCTION__ << ": buffer size=" << buffer.size(); | |
181 DCHECK(thread_checker_.CalledOnValidThread()); | |
182 | |
183 if (buffer.size() < bitstream_buffer_size_) { | |
184 DLOG(ERROR) << "Output BitstreamBuffer isn't big enough: " << buffer.size() | |
185 << " vs. " << bitstream_buffer_size_; | |
186 client_->NotifyError(kInvalidArgumentError); | |
187 return; | |
188 } | |
189 | |
190 scoped_ptr<base::SharedMemory> shm( | |
191 new base::SharedMemory(buffer.handle(), false)); | |
192 if (!shm->Map(buffer.size())) { | |
193 DLOG(ERROR) << "Failed mapping shared memory."; | |
194 client_->NotifyError(kPlatformFailureError); | |
195 return; | |
196 } | |
197 | |
198 scoped_ptr<BitstreamBufferRef> buffer_ref( | |
199 new BitstreamBufferRef(buffer.id(), std::move(shm), buffer.size())); | |
200 | |
201 encoder_thread_task_runner_->PostTask( | |
202 FROM_HERE, | |
203 base::Bind(&VTVideoEncodeAccelerator::UseOutputBitstreamBufferTask, | |
204 base::Unretained(this), base::Passed(&buffer_ref))); | |
205 } | |
206 | |
207 void VTVideoEncodeAccelerator::RequestEncodingParametersChange( | |
208 uint32_t bitrate, | |
209 uint32_t framerate) { | |
210 DVLOG(3) << __FUNCTION__ << ": bitrate=" << bitrate | |
211 << ": framerate=" << framerate; | |
212 DCHECK(thread_checker_.CalledOnValidThread()); | |
213 | |
214 encoder_thread_task_runner_->PostTask( | |
215 FROM_HERE, | |
216 base::Bind(&VTVideoEncodeAccelerator::RequestEncodingParametersChangeTask, | |
217 base::Unretained(this), bitrate, framerate)); | |
218 } | |
219 | |
220 void VTVideoEncodeAccelerator::Destroy() { | |
221 DVLOG(3) << __FUNCTION__; | |
222 DCHECK(thread_checker_.CalledOnValidThread()); | |
223 | |
224 // Cancel all callbacks. | |
225 client_ptr_factory_.reset(); | |
226 | |
227 if (encoder_thread_.IsRunning()) { | |
228 encoder_thread_task_runner_->PostTask( | |
229 FROM_HERE, | |
230 base::Bind(&VTVideoEncodeAccelerator::DestroyTask, | |
231 base::Unretained(this))); | |
232 encoder_thread_.Stop(); | |
233 } else { | |
234 DestroyTask(); | |
235 } | |
236 } | |
237 | |
238 void VTVideoEncodeAccelerator::EncodeTask( | |
239 const scoped_refptr<media::VideoFrame>& frame, | |
240 bool force_keyframe) { | |
241 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); | |
242 DCHECK(compression_session_); | |
243 DCHECK(frame); | |
244 | |
245 // TODO(emircan): See if we can eliminate a copy here by using | |
246 // CVPixelBufferPool for the allocation of incoming VideoFrames. | |
247 base::ScopedCFTypeRef<CVPixelBufferRef> pixel_buffer = | |
248 media::WrapVideoFrameInCVPixelBuffer(*frame); | |
249 base::ScopedCFTypeRef<CFDictionaryRef> frame_props = | |
250 media::video_toolbox::DictionaryWithKeyValue( | |
251 videotoolbox_glue_->kVTEncodeFrameOptionKey_ForceKeyFrame(), | |
252 force_keyframe ? kCFBooleanTrue : kCFBooleanFalse); | |
253 | |
254 base::TimeTicks ref_time; | |
255 if (!frame->metadata()->GetTimeTicks( | |
256 media::VideoFrameMetadata::REFERENCE_TIME, &ref_time)) { | |
257 ref_time = base::TimeTicks::Now(); | |
258 } | |
259 auto timestamp_cm = CoreMediaGlue::CMTimeMake( | |
260 frame->timestamp().InMicroseconds(), USEC_PER_SEC); | |
261 // Wrap information we'll need after the frame is encoded in a heap object. | |
262 // We'll get the pointer back from the VideoToolbox completion callback. | |
263 scoped_ptr<InProgressFrameEncode> request(new InProgressFrameEncode( | |
264 frame->timestamp(), ref_time)); | |
265 | |
266 // We can pass the ownership of |request| to the encode callback if | |
267 // successful. Otherwise let it fall out of scope. | |
268 OSStatus status = videotoolbox_glue_->VTCompressionSessionEncodeFrame( | |
269 compression_session_, pixel_buffer, timestamp_cm, | |
270 CoreMediaGlue::CMTime{0, 0, 0, 0}, frame_props, | |
271 reinterpret_cast<void*>(request.get()), nullptr); | |
272 if (status != noErr) { | |
273 DLOG(ERROR) << " VTCompressionSessionEncodeFrame failed: " << status; | |
274 NotifyError(kPlatformFailureError); | |
275 } else { | |
276 CHECK(request.release()); | |
277 } | |
278 } | |
279 | |
280 void VTVideoEncodeAccelerator::UseOutputBitstreamBufferTask( | |
281 scoped_ptr<BitstreamBufferRef> buffer_ref) { | |
282 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); | |
283 | |
284 // If there is already EncodeOutput waiting, copy its output first. | |
285 if (!encoder_output_queue_.empty()) { | |
286 scoped_ptr<VTVideoEncodeAccelerator::EncodeOutput> encode_output = | |
287 std::move(encoder_output_queue_.front()); | |
288 encoder_output_queue_.pop_front(); | |
289 ReturnBitstreamBuffer(std::move(encode_output), std::move(buffer_ref)); | |
290 return; | |
291 } | |
292 | |
293 bitstream_buffer_queue_.push_back(std::move(buffer_ref)); | |
294 } | |
295 | |
296 void VTVideoEncodeAccelerator::RequestEncodingParametersChangeTask( | |
297 uint32_t bitrate, | |
298 uint32_t framerate) { | |
299 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); | |
300 | |
301 frame_rate_ = framerate > 1 ? framerate : 1; | |
302 target_bitrate_ = bitrate > 1 ? bitrate : 1; | |
303 | |
304 if (!compression_session_) { | |
305 NotifyError(kPlatformFailureError); | |
306 return; | |
307 } | |
308 | |
309 media::video_toolbox::SessionPropertySetter session_property_setter( | |
310 compression_session_, videotoolbox_glue_); | |
311 // TODO(emircan): See crbug.com/425352. | |
312 bool rv = session_property_setter.Set( | |
313 videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(), | |
314 target_bitrate_); | |
315 rv &= session_property_setter.Set( | |
316 videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(), | |
317 frame_rate_); | |
318 rv &= session_property_setter.Set( | |
319 videotoolbox_glue_->kVTCompressionPropertyKey_DataRateLimits(), | |
320 media::video_toolbox::ArrayWithIntegerAndFloat( | |
321 target_bitrate_ / kBitsPerByte, 1.0f)); | |
322 DLOG_IF(ERROR, !rv) << "Couldn't change session encoding parameters."; | |
323 } | |
324 | |
325 void VTVideoEncodeAccelerator::DestroyTask() { | |
326 DCHECK(thread_checker_.CalledOnValidThread() || | |
327 (encoder_thread_.IsRunning() && | |
328 encoder_thread_task_runner_->BelongsToCurrentThread())); | |
329 | |
330 // Cancel all encoder thread callbacks. | |
331 encoder_task_weak_factory_.InvalidateWeakPtrs(); | |
332 | |
333 // This call blocks until all pending frames are flushed out. | |
334 DestroyCompressionSession(); | |
335 } | |
336 | |
337 void VTVideoEncodeAccelerator::NotifyError( | |
338 media::VideoEncodeAccelerator::Error error) { | |
339 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); | |
340 client_task_runner_->PostTask( | |
341 FROM_HERE, base::Bind(&Client::NotifyError, client_, error)); | |
342 } | |
343 | |
344 // static | |
345 void VTVideoEncodeAccelerator::CompressionCallback(void* encoder_opaque, | |
346 void* request_opaque, | |
347 OSStatus status, | |
348 VTEncodeInfoFlags info, | |
349 CMSampleBufferRef sbuf) { | |
350 // This function may be called asynchronously, on a different thread from the | |
351 // one that calls VTCompressionSessionEncodeFrame. | |
352 DVLOG(3) << __FUNCTION__; | |
353 | |
354 auto encoder = reinterpret_cast<VTVideoEncodeAccelerator*>(encoder_opaque); | |
355 DCHECK(encoder); | |
356 | |
357 // Release InProgressFrameEncode, since we don't have support to return | |
358 // timestamps at this point. | |
359 scoped_ptr<InProgressFrameEncode> request( | |
360 reinterpret_cast<InProgressFrameEncode*>(request_opaque)); | |
361 request.reset(); | |
362 | |
363 // EncodeOutput holds onto CMSampleBufferRef when posting task between | |
364 // threads. | |
365 scoped_ptr<EncodeOutput> encode_output(new EncodeOutput(info, sbuf)); | |
366 | |
367 // This method is NOT called on |encoder_thread_|, so we still need to | |
368 // post a task back to it to do work. | |
369 encoder->encoder_thread_task_runner_->PostTask( | |
370 FROM_HERE, base::Bind(&VTVideoEncodeAccelerator::CompressionCallbackTask, | |
371 encoder->encoder_weak_ptr_, status, | |
372 base::Passed(&encode_output))); | |
373 } | |
374 | |
375 void VTVideoEncodeAccelerator::CompressionCallbackTask( | |
376 OSStatus status, | |
377 scoped_ptr<EncodeOutput> encode_output) { | |
378 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); | |
379 | |
380 if (status != noErr) { | |
381 DLOG(ERROR) << " encode failed: " << status; | |
382 NotifyError(kPlatformFailureError); | |
383 return; | |
384 } | |
385 | |
386 // If there isn't any BitstreamBuffer to copy into, add it to a queue for | |
387 // later use. | |
388 if (bitstream_buffer_queue_.empty()) { | |
389 encoder_output_queue_.push_back(std::move(encode_output)); | |
390 return; | |
391 } | |
392 | |
393 scoped_ptr<VTVideoEncodeAccelerator::BitstreamBufferRef> buffer_ref = | |
394 std::move(bitstream_buffer_queue_.front()); | |
395 bitstream_buffer_queue_.pop_front(); | |
396 ReturnBitstreamBuffer(std::move(encode_output), std::move(buffer_ref)); | |
397 } | |
398 | |
399 void VTVideoEncodeAccelerator::ReturnBitstreamBuffer( | |
400 scoped_ptr<EncodeOutput> encode_output, | |
401 scoped_ptr<VTVideoEncodeAccelerator::BitstreamBufferRef> buffer_ref) { | |
402 DVLOG(3) << __FUNCTION__; | |
403 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); | |
404 | |
405 if (encode_output->info & VideoToolboxGlue::kVTEncodeInfo_FrameDropped) { | |
406 DVLOG(2) << " frame dropped"; | |
407 client_task_runner_->PostTask( | |
408 FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_, | |
409 buffer_ref->id, 0, false)); | |
410 return; | |
411 } | |
412 | |
413 auto sample_attachments = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex( | |
414 CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray( | |
415 encode_output->sample_buffer.get(), true), | |
416 0)); | |
417 const bool keyframe = | |
418 !CFDictionaryContainsKey(sample_attachments, | |
419 CoreMediaGlue::kCMSampleAttachmentKey_NotSync()); | |
420 | |
421 size_t used_buffer_size = 0; | |
422 const bool copy_rv = media::video_toolbox::CopySampleBufferToAnnexBBuffer( | |
423 encode_output->sample_buffer.get(), keyframe, buffer_ref->size, | |
424 reinterpret_cast<char*>(buffer_ref->shm->memory()), &used_buffer_size); | |
425 if (!copy_rv) { | |
426 DLOG(ERROR) << "Cannot copy output from SampleBuffer to AnnexBBuffer."; | |
427 used_buffer_size = 0; | |
428 } | |
429 | |
430 client_task_runner_->PostTask( | |
431 FROM_HERE, base::Bind(&Client::BitstreamBufferReady, client_, | |
432 buffer_ref->id, used_buffer_size, keyframe)); | |
433 } | |
434 | |
435 bool VTVideoEncodeAccelerator::ResetCompressionSession() { | |
436 DCHECK(thread_checker_.CalledOnValidThread()); | |
437 | |
438 DestroyCompressionSession(); | |
439 | |
440 CFTypeRef attributes_keys[] = { | |
441 kCVPixelBufferOpenGLCompatibilityKey, | |
442 kCVPixelBufferIOSurfacePropertiesKey, | |
443 kCVPixelBufferPixelFormatTypeKey | |
444 }; | |
445 const int format[] = { | |
446 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; | |
447 CFTypeRef attributes_values[] = { | |
448 kCFBooleanTrue, | |
449 media::video_toolbox::DictionaryWithKeysAndValues(nullptr, nullptr, 0) | |
450 .release(), | |
451 media::video_toolbox::ArrayWithIntegers(format, arraysize(format)) | |
452 .release()}; | |
453 const base::ScopedCFTypeRef<CFDictionaryRef> attributes = | |
454 media::video_toolbox::DictionaryWithKeysAndValues( | |
455 attributes_keys, attributes_values, arraysize(attributes_keys)); | |
456 for (auto& v : attributes_values) | |
457 CFRelease(v); | |
458 | |
459 bool session_rv = | |
460 CreateCompressionSession(attributes, input_visible_size_, false); | |
461 if (!session_rv) { | |
462 DestroyCompressionSession(); | |
463 return false; | |
464 } | |
465 | |
466 const bool configure_rv = ConfigureCompressionSession(); | |
467 if (configure_rv) | |
468 RequestEncodingParametersChange(target_bitrate_, frame_rate_); | |
469 return configure_rv; | |
470 } | |
471 | |
472 bool VTVideoEncodeAccelerator::CreateCompressionSession( | |
473 base::ScopedCFTypeRef<CFDictionaryRef> attributes, | |
474 const gfx::Size& input_size, | |
475 bool require_hw_encoding) { | |
476 DCHECK(thread_checker_.CalledOnValidThread()); | |
477 | |
478 std::vector<CFTypeRef> encoder_keys; | |
479 std::vector<CFTypeRef> encoder_values; | |
480 if (require_hw_encoding) { | |
481 encoder_keys.push_back(videotoolbox_glue_ | |
482 ->kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder()); | |
483 encoder_values.push_back(kCFBooleanTrue); | |
484 } else { | |
485 encoder_keys.push_back(videotoolbox_glue_ | |
486 ->kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder()); | |
487 encoder_values.push_back(kCFBooleanTrue); | |
488 } | |
489 base::ScopedCFTypeRef<CFDictionaryRef> encoder_spec = | |
490 media::video_toolbox::DictionaryWithKeysAndValues( | |
491 encoder_keys.data(), encoder_values.data(), encoder_keys.size()); | |
492 | |
493 // Create the compression session. | |
494 // Note that the encoder object is given to the compression session as the | |
495 // callback context using a raw pointer. The C API does not allow us to use a | |
496 // smart pointer, nor is this encoder ref counted. However, this is still | |
497 // safe, because we 1) we own the compression session and 2) we tear it down | |
498 // safely. When destructing the encoder, the compression session is flushed | |
499 // and invalidated. Internally, VideoToolbox will join all of its threads | |
500 // before returning to the client. Therefore, when control returns to us, we | |
501 // are guaranteed that the output callback will not execute again. | |
502 OSStatus status = videotoolbox_glue_->VTCompressionSessionCreate( | |
503 kCFAllocatorDefault, | |
504 input_size.width(), | |
505 input_size.height(), | |
506 CoreMediaGlue::kCMVideoCodecType_H264, | |
507 encoder_spec, | |
508 attributes, | |
509 nullptr /* compressedDataAllocator */, | |
510 &VTVideoEncodeAccelerator::CompressionCallback, | |
511 reinterpret_cast<void*>(this), | |
512 compression_session_.InitializeInto()); | |
513 if (status != noErr) { | |
514 DLOG(ERROR) << " VTCompressionSessionCreate failed: " << status; | |
515 return false; | |
516 } | |
517 DVLOG(3) << " VTCompressionSession created with HW encode: " | |
518 << require_hw_encoding << ", input size=" << input_size.ToString(); | |
519 return true; | |
520 } | |
521 | |
522 bool VTVideoEncodeAccelerator::ConfigureCompressionSession() { | |
523 DCHECK(thread_checker_.CalledOnValidThread()); | |
524 DCHECK(compression_session_); | |
525 | |
526 media::video_toolbox::SessionPropertySetter session_property_setter( | |
527 compression_session_, videotoolbox_glue_); | |
528 bool rv = true; | |
529 rv &= session_property_setter.Set( | |
530 videotoolbox_glue_->kVTCompressionPropertyKey_ProfileLevel(), | |
531 videotoolbox_glue_->kVTProfileLevel_H264_Baseline_AutoLevel()); | |
532 rv &= session_property_setter.Set( | |
533 videotoolbox_glue_->kVTCompressionPropertyKey_RealTime(), true); | |
534 rv &= session_property_setter.Set( | |
535 videotoolbox_glue_->kVTCompressionPropertyKey_AllowFrameReordering(), | |
536 false); | |
537 DLOG_IF(ERROR, !rv) << " Setting session property failed."; | |
538 return rv; | |
539 } | |
540 | |
541 void VTVideoEncodeAccelerator::DestroyCompressionSession() { | |
542 DCHECK(thread_checker_.CalledOnValidThread() || | |
543 (encoder_thread_.IsRunning() && | |
544 encoder_thread_task_runner_->BelongsToCurrentThread())); | |
545 | |
546 if (compression_session_) { | |
547 videotoolbox_glue_->VTCompressionSessionInvalidate(compression_session_); | |
548 compression_session_.reset(); | |
549 } | |
550 } | |
551 | |
552 } // namespace content | |
OLD | NEW |