OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/cast/sender/h264_vt_encoder.h" | 5 #include "media/cast/sender/h264_vt_encoder.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <string> | 9 #include <string> |
10 #include <vector> | 10 #include <vector> |
(...skipping 25 matching lines...) Expand all Loading... |
36 const VideoEncoder::FrameEncodedCallback frame_encoded_callback; | 36 const VideoEncoder::FrameEncodedCallback frame_encoded_callback; |
37 | 37 |
38 InProgressFrameEncode(RtpTimeTicks rtp, | 38 InProgressFrameEncode(RtpTimeTicks rtp, |
39 base::TimeTicks r_time, | 39 base::TimeTicks r_time, |
40 VideoEncoder::FrameEncodedCallback callback) | 40 VideoEncoder::FrameEncodedCallback callback) |
41 : rtp_timestamp(rtp), | 41 : rtp_timestamp(rtp), |
42 reference_time(r_time), | 42 reference_time(r_time), |
43 frame_encoded_callback(callback) {} | 43 frame_encoded_callback(callback) {} |
44 }; | 44 }; |
45 | 45 |
46 base::ScopedCFTypeRef<CFDictionaryRef> | |
47 DictionaryWithKeysAndValues(CFTypeRef* keys, CFTypeRef* values, size_t size) { | |
48 return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate( | |
49 kCFAllocatorDefault, keys, values, size, &kCFTypeDictionaryKeyCallBacks, | |
50 &kCFTypeDictionaryValueCallBacks)); | |
51 } | |
52 | |
53 base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeyValue(CFTypeRef key, | |
54 CFTypeRef value) { | |
55 CFTypeRef keys[1] = {key}; | |
56 CFTypeRef values[1] = {value}; | |
57 return DictionaryWithKeysAndValues(keys, values, 1); | |
58 } | |
59 | |
60 base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const int* v, size_t size) { | |
61 std::vector<CFNumberRef> numbers; | |
62 numbers.reserve(size); | |
63 for (const int* end = v + size; v < end; ++v) | |
64 numbers.push_back(CFNumberCreate(nullptr, kCFNumberSInt32Type, v)); | |
65 base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate( | |
66 kCFAllocatorDefault, reinterpret_cast<const void**>(&numbers[0]), | |
67 numbers.size(), &kCFTypeArrayCallBacks)); | |
68 for (auto& number : numbers) { | |
69 CFRelease(number); | |
70 } | |
71 return array; | |
72 } | |
73 | |
74 template <typename NalSizeType> | |
75 void CopyNalsToAnnexB(char* avcc_buffer, | |
76 const size_t avcc_size, | |
77 std::string* annexb_buffer) { | |
78 static_assert(sizeof(NalSizeType) == 1 || sizeof(NalSizeType) == 2 || | |
79 sizeof(NalSizeType) == 4, | |
80 "NAL size type has unsupported size"); | |
81 static const char startcode_3[3] = {0, 0, 1}; | |
82 DCHECK(avcc_buffer); | |
83 DCHECK(annexb_buffer); | |
84 size_t bytes_left = avcc_size; | |
85 while (bytes_left > 0) { | |
86 DCHECK_GT(bytes_left, sizeof(NalSizeType)); | |
87 NalSizeType nal_size; | |
88 base::ReadBigEndian(avcc_buffer, &nal_size); | |
89 bytes_left -= sizeof(NalSizeType); | |
90 avcc_buffer += sizeof(NalSizeType); | |
91 | |
92 DCHECK_GE(bytes_left, nal_size); | |
93 annexb_buffer->append(startcode_3, sizeof(startcode_3)); | |
94 annexb_buffer->append(avcc_buffer, nal_size); | |
95 bytes_left -= nal_size; | |
96 avcc_buffer += nal_size; | |
97 } | |
98 } | |
99 | |
100 // Copy a H.264 frame stored in a CM sample buffer to an Annex B buffer. Copies | |
101 // parameter sets for keyframes before the frame data as well. | |
102 void CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf, | |
103 std::string* annexb_buffer, | |
104 bool keyframe) { | |
105 // Perform two pass, one to figure out the total output size, and another to | |
106 // copy the data after having performed a single output allocation. Note that | |
107 // we'll allocate a bit more because we'll count 4 bytes instead of 3 for | |
108 // video NALs. | |
109 | |
110 OSStatus status; | |
111 | |
112 // Get the sample buffer's block buffer and format description. | |
113 auto bb = CoreMediaGlue::CMSampleBufferGetDataBuffer(sbuf); | |
114 DCHECK(bb); | |
115 auto fdesc = CoreMediaGlue::CMSampleBufferGetFormatDescription(sbuf); | |
116 DCHECK(fdesc); | |
117 | |
118 size_t bb_size = CoreMediaGlue::CMBlockBufferGetDataLength(bb); | |
119 size_t total_bytes = bb_size; | |
120 | |
121 size_t pset_count; | |
122 int nal_size_field_bytes; | |
123 status = CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
124 fdesc, 0, nullptr, nullptr, &pset_count, &nal_size_field_bytes); | |
125 if (status == | |
126 CoreMediaGlue::kCMFormatDescriptionBridgeError_InvalidParameter) { | |
127 DLOG(WARNING) << " assuming 2 parameter sets and 4 bytes NAL length header"; | |
128 pset_count = 2; | |
129 nal_size_field_bytes = 4; | |
130 } else if (status != noErr) { | |
131 DLOG(ERROR) | |
132 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
133 << status; | |
134 return; | |
135 } | |
136 | |
137 if (keyframe) { | |
138 const uint8_t* pset; | |
139 size_t pset_size; | |
140 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { | |
141 status = | |
142 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
143 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); | |
144 if (status != noErr) { | |
145 DLOG(ERROR) | |
146 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
147 << status; | |
148 return; | |
149 } | |
150 total_bytes += pset_size + nal_size_field_bytes; | |
151 } | |
152 } | |
153 | |
154 annexb_buffer->reserve(total_bytes); | |
155 | |
156 // Copy all parameter sets before keyframes. | |
157 if (keyframe) { | |
158 const uint8_t* pset; | |
159 size_t pset_size; | |
160 for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) { | |
161 status = | |
162 CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex( | |
163 fdesc, pset_i, &pset, &pset_size, nullptr, nullptr); | |
164 if (status != noErr) { | |
165 DLOG(ERROR) | |
166 << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: " | |
167 << status; | |
168 return; | |
169 } | |
170 static const char startcode_4[4] = {0, 0, 0, 1}; | |
171 annexb_buffer->append(startcode_4, sizeof(startcode_4)); | |
172 annexb_buffer->append(reinterpret_cast<const char*>(pset), pset_size); | |
173 } | |
174 } | |
175 | |
176 // Block buffers can be composed of non-contiguous chunks. For the sake of | |
177 // keeping this code simple, flatten non-contiguous block buffers. | |
178 base::ScopedCFTypeRef<CoreMediaGlue::CMBlockBufferRef> contiguous_bb( | |
179 bb, base::scoped_policy::RETAIN); | |
180 if (!CoreMediaGlue::CMBlockBufferIsRangeContiguous(bb, 0, 0)) { | |
181 contiguous_bb.reset(); | |
182 status = CoreMediaGlue::CMBlockBufferCreateContiguous( | |
183 kCFAllocatorDefault, bb, kCFAllocatorDefault, nullptr, 0, 0, 0, | |
184 contiguous_bb.InitializeInto()); | |
185 if (status != noErr) { | |
186 DLOG(ERROR) << " CMBlockBufferCreateContiguous failed: " << status; | |
187 return; | |
188 } | |
189 } | |
190 | |
191 // Copy all the NAL units. In the process convert them from AVCC format | |
192 // (length header) to AnnexB format (start code). | |
193 char* bb_data; | |
194 status = CoreMediaGlue::CMBlockBufferGetDataPointer(contiguous_bb, 0, nullptr, | |
195 nullptr, &bb_data); | |
196 if (status != noErr) { | |
197 DLOG(ERROR) << " CMBlockBufferGetDataPointer failed: " << status; | |
198 return; | |
199 } | |
200 | |
201 if (nal_size_field_bytes == 1) { | |
202 CopyNalsToAnnexB<uint8_t>(bb_data, bb_size, annexb_buffer); | |
203 } else if (nal_size_field_bytes == 2) { | |
204 CopyNalsToAnnexB<uint16_t>(bb_data, bb_size, annexb_buffer); | |
205 } else if (nal_size_field_bytes == 4) { | |
206 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer); | |
207 } else { | |
208 NOTREACHED(); | |
209 } | |
210 } | |
211 | |
212 } // namespace | 46 } // namespace |
213 | 47 |
214 class H264VideoToolboxEncoder::VideoFrameFactoryImpl | 48 class H264VideoToolboxEncoder::VideoFrameFactoryImpl |
215 : public base::RefCountedThreadSafe<VideoFrameFactoryImpl>, | 49 : public base::RefCountedThreadSafe<VideoFrameFactoryImpl>, |
216 public VideoFrameFactory { | 50 public VideoFrameFactory { |
217 public: | 51 public: |
218 // Type that proxies the VideoFrameFactory interface to this class. | 52 // Type that proxies the VideoFrameFactory interface to this class. |
219 class Proxy; | 53 class Proxy; |
220 | 54 |
221 VideoFrameFactoryImpl(const base::WeakPtr<H264VideoToolboxEncoder>& encoder, | 55 VideoFrameFactoryImpl(const base::WeakPtr<H264VideoToolboxEncoder>& encoder, |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 CastEnvironment::MAIN, FROM_HERE, | 218 CastEnvironment::MAIN, FROM_HERE, |
385 base::Bind(status_change_cb_, STATUS_CODEC_REINIT_PENDING)); | 219 base::Bind(status_change_cb_, STATUS_CODEC_REINIT_PENDING)); |
386 | 220 |
387 // Destroy the current session, if any. | 221 // Destroy the current session, if any. |
388 DestroyCompressionSession(); | 222 DestroyCompressionSession(); |
389 | 223 |
390 // On OS X, allow the hardware encoder. Don't require it, it does not support | 224 // On OS X, allow the hardware encoder. Don't require it, it does not support |
391 // all configurations (some of which are used for testing). | 225 // all configurations (some of which are used for testing). |
392 base::ScopedCFTypeRef<CFDictionaryRef> encoder_spec; | 226 base::ScopedCFTypeRef<CFDictionaryRef> encoder_spec; |
393 #if !defined(OS_IOS) | 227 #if !defined(OS_IOS) |
394 encoder_spec = DictionaryWithKeyValue( | 228 encoder_spec = video_toolbox::DictionaryWithKeyValue( |
395 videotoolbox_glue_ | 229 videotoolbox_glue_ |
396 ->kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder()
, | 230 ->kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder()
, |
397 kCFBooleanTrue); | 231 kCFBooleanTrue); |
398 #endif | 232 #endif |
399 | 233 |
400 // Force 420v so that clients can easily use these buffers as GPU textures. | 234 // Force 420v so that clients can easily use these buffers as GPU textures. |
401 const int format[] = { | 235 const int format[] = { |
402 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; | 236 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; |
403 | 237 |
404 // Keep these attachment settings in-sync with those in ConfigureSession(). | 238 // Keep these attachment settings in-sync with those in ConfigureSession(). |
405 CFTypeRef attachments_keys[] = {kCVImageBufferColorPrimariesKey, | 239 CFTypeRef attachments_keys[] = {kCVImageBufferColorPrimariesKey, |
406 kCVImageBufferTransferFunctionKey, | 240 kCVImageBufferTransferFunctionKey, |
407 kCVImageBufferYCbCrMatrixKey}; | 241 kCVImageBufferYCbCrMatrixKey}; |
408 CFTypeRef attachments_values[] = {kCVImageBufferColorPrimaries_ITU_R_709_2, | 242 CFTypeRef attachments_values[] = {kCVImageBufferColorPrimaries_ITU_R_709_2, |
409 kCVImageBufferTransferFunction_ITU_R_709_2, | 243 kCVImageBufferTransferFunction_ITU_R_709_2, |
410 kCVImageBufferYCbCrMatrix_ITU_R_709_2}; | 244 kCVImageBufferYCbCrMatrix_ITU_R_709_2}; |
411 CFTypeRef buffer_attributes_keys[] = {kCVPixelBufferPixelFormatTypeKey, | 245 CFTypeRef buffer_attributes_keys[] = {kCVPixelBufferPixelFormatTypeKey, |
412 kCVBufferPropagatedAttachmentsKey}; | 246 kCVBufferPropagatedAttachmentsKey}; |
413 CFTypeRef buffer_attributes_values[] = { | 247 CFTypeRef buffer_attributes_values[] = { |
414 ArrayWithIntegers(format, arraysize(format)).release(), | 248 video_toolbox::ArrayWithIntegers(format, arraysize(format)).release(), |
415 DictionaryWithKeysAndValues(attachments_keys, attachments_values, | 249 video_toolbox::DictionaryWithKeysAndValues( |
416 arraysize(attachments_keys)).release()}; | 250 attachments_keys, attachments_values, arraysize(attachments_keys)) |
| 251 .release()}; |
417 const base::ScopedCFTypeRef<CFDictionaryRef> buffer_attributes = | 252 const base::ScopedCFTypeRef<CFDictionaryRef> buffer_attributes = |
418 DictionaryWithKeysAndValues(buffer_attributes_keys, | 253 video_toolbox::DictionaryWithKeysAndValues( |
419 buffer_attributes_values, | 254 buffer_attributes_keys, buffer_attributes_values, |
420 arraysize(buffer_attributes_keys)); | 255 arraysize(buffer_attributes_keys)); |
421 for (auto& v : buffer_attributes_values) | 256 for (auto& v : buffer_attributes_values) |
422 CFRelease(v); | 257 CFRelease(v); |
423 | 258 |
424 // Create the compression session. | 259 // Create the compression session. |
425 | 260 |
426 // Note that the encoder object is given to the compression session as the | 261 // Note that the encoder object is given to the compression session as the |
427 // callback context using a raw pointer. The C API does not allow us to use a | 262 // callback context using a raw pointer. The C API does not allow us to use a |
428 // smart pointer, nor is this encoder ref counted. However, this is still | 263 // smart pointer, nor is this encoder ref counted. However, this is still |
429 // safe, because we 1) we own the compression session and 2) we tear it down | 264 // safe, because we 1) we own the compression session and 2) we tear it down |
430 // safely. When destructing the encoder, the compression session is flushed | 265 // safely. When destructing the encoder, the compression session is flushed |
(...skipping 26 matching lines...) Expand all Loading... |
457 base::scoped_policy::RETAIN); | 292 base::scoped_policy::RETAIN); |
458 video_frame_factory_->Update(pool, frame_size_); | 293 video_frame_factory_->Update(pool, frame_size_); |
459 | 294 |
460 // Notify that reinitialization is done. | 295 // Notify that reinitialization is done. |
461 cast_environment_->PostTask( | 296 cast_environment_->PostTask( |
462 CastEnvironment::MAIN, FROM_HERE, | 297 CastEnvironment::MAIN, FROM_HERE, |
463 base::Bind(status_change_cb_, STATUS_INITIALIZED)); | 298 base::Bind(status_change_cb_, STATUS_INITIALIZED)); |
464 } | 299 } |
465 | 300 |
466 void H264VideoToolboxEncoder::ConfigureCompressionSession() { | 301 void H264VideoToolboxEncoder::ConfigureCompressionSession() { |
467 SetSessionProperty( | 302 video_toolbox::SessionPropertySetter session_property_setter( |
| 303 compression_session_, videotoolbox_glue_); |
| 304 session_property_setter.SetSessionProperty( |
468 videotoolbox_glue_->kVTCompressionPropertyKey_ProfileLevel(), | 305 videotoolbox_glue_->kVTCompressionPropertyKey_ProfileLevel(), |
469 videotoolbox_glue_->kVTProfileLevel_H264_Main_AutoLevel()); | 306 videotoolbox_glue_->kVTProfileLevel_H264_Main_AutoLevel()); |
470 SetSessionProperty(videotoolbox_glue_->kVTCompressionPropertyKey_RealTime(), | 307 session_property_setter.SetSessionProperty( |
471 true); | 308 videotoolbox_glue_->kVTCompressionPropertyKey_RealTime(), true); |
472 SetSessionProperty( | 309 session_property_setter.SetSessionProperty( |
473 videotoolbox_glue_->kVTCompressionPropertyKey_AllowFrameReordering(), | 310 videotoolbox_glue_->kVTCompressionPropertyKey_AllowFrameReordering(), |
474 false); | 311 false); |
475 SetSessionProperty( | 312 session_property_setter.SetSessionProperty( |
476 videotoolbox_glue_->kVTCompressionPropertyKey_MaxKeyFrameInterval(), 240); | 313 videotoolbox_glue_->kVTCompressionPropertyKey_MaxKeyFrameInterval(), 240); |
477 SetSessionProperty( | 314 session_property_setter.SetSessionProperty( |
478 videotoolbox_glue_ | 315 videotoolbox_glue_ |
479 ->kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration(), | 316 ->kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration(), |
480 240); | 317 240); |
481 // TODO(jfroy): implement better bitrate control | 318 // TODO(jfroy): implement better bitrate control |
482 // https://crbug.com/425352 | 319 // https://crbug.com/425352 |
483 SetSessionProperty( | 320 session_property_setter.SetSessionProperty( |
484 videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(), | 321 videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(), |
485 (video_config_.min_bitrate + video_config_.max_bitrate) / 2); | 322 (video_config_.min_bitrate + video_config_.max_bitrate) / 2); |
486 SetSessionProperty( | 323 session_property_setter.SetSessionProperty( |
487 videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(), | 324 videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(), |
488 video_config_.max_frame_rate); | 325 video_config_.max_frame_rate); |
489 // Keep these attachment settings in-sync with those in Initialize(). | 326 // Keep these attachment settings in-sync with those in Initialize(). |
490 SetSessionProperty( | 327 session_property_setter.SetSessionProperty( |
491 videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(), | 328 videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(), |
492 kCVImageBufferColorPrimaries_ITU_R_709_2); | 329 kCVImageBufferColorPrimaries_ITU_R_709_2); |
493 SetSessionProperty( | 330 session_property_setter.SetSessionProperty( |
494 videotoolbox_glue_->kVTCompressionPropertyKey_TransferFunction(), | 331 videotoolbox_glue_->kVTCompressionPropertyKey_TransferFunction(), |
495 kCVImageBufferTransferFunction_ITU_R_709_2); | 332 kCVImageBufferTransferFunction_ITU_R_709_2); |
496 SetSessionProperty( | 333 session_property_setter.SetSessionProperty( |
497 videotoolbox_glue_->kVTCompressionPropertyKey_YCbCrMatrix(), | 334 videotoolbox_glue_->kVTCompressionPropertyKey_YCbCrMatrix(), |
498 kCVImageBufferYCbCrMatrix_ITU_R_709_2); | 335 kCVImageBufferYCbCrMatrix_ITU_R_709_2); |
499 if (video_config_.max_number_of_video_buffers_used > 0) { | 336 if (video_config_.max_number_of_video_buffers_used > 0) { |
500 SetSessionProperty( | 337 session_property_setter.SetSessionProperty( |
501 videotoolbox_glue_->kVTCompressionPropertyKey_MaxFrameDelayCount(), | 338 videotoolbox_glue_->kVTCompressionPropertyKey_MaxFrameDelayCount(), |
502 video_config_.max_number_of_video_buffers_used); | 339 video_config_.max_number_of_video_buffers_used); |
503 } | 340 } |
504 } | 341 } |
505 | 342 |
506 void H264VideoToolboxEncoder::DestroyCompressionSession() { | 343 void H264VideoToolboxEncoder::DestroyCompressionSession() { |
507 DCHECK(thread_checker_.CalledOnValidThread()); | 344 DCHECK(thread_checker_.CalledOnValidThread()); |
508 | 345 |
509 // If the compression session exists, invalidate it. This blocks until all | 346 // If the compression session exists, invalidate it. This blocks until all |
510 // pending output callbacks have returned and any internal threads have | 347 // pending output callbacks have returned and any internal threads have |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 // frame into memory visible by the hardware encoder. The VideoFrame's | 396 // frame into memory visible by the hardware encoder. The VideoFrame's |
560 // lifetime is extended for the lifetime of the returned CVPixelBuffer. | 397 // lifetime is extended for the lifetime of the returned CVPixelBuffer. |
561 auto pixel_buffer = media::WrapVideoFrameInCVPixelBuffer(*video_frame); | 398 auto pixel_buffer = media::WrapVideoFrameInCVPixelBuffer(*video_frame); |
562 if (!pixel_buffer) { | 399 if (!pixel_buffer) { |
563 DLOG(ERROR) << "WrapVideoFrameInCVPixelBuffer failed."; | 400 DLOG(ERROR) << "WrapVideoFrameInCVPixelBuffer failed."; |
564 return false; | 401 return false; |
565 } | 402 } |
566 | 403 |
567 // Convert the frame timestamp to CMTime. | 404 // Convert the frame timestamp to CMTime. |
568 auto timestamp_cm = CoreMediaGlue::CMTimeMake( | 405 auto timestamp_cm = CoreMediaGlue::CMTimeMake( |
569 (reference_time - base::TimeTicks()).InMicroseconds(), USEC_PER_SEC); | 406 video_frame->timestamp().InMicroseconds(), USEC_PER_SEC); |
570 | 407 |
571 // Wrap information we'll need after the frame is encoded in a heap object. | 408 // Wrap information we'll need after the frame is encoded in a heap object. |
572 // We'll get the pointer back from the VideoToolbox completion callback. | 409 // We'll get the pointer back from the VideoToolbox completion callback. |
573 scoped_ptr<InProgressFrameEncode> request(new InProgressFrameEncode( | 410 scoped_ptr<InProgressFrameEncode> request(new InProgressFrameEncode( |
574 RtpTimeTicks::FromTimeDelta(video_frame->timestamp(), kVideoFrequency), | 411 RtpTimeTicks::FromTimeDelta(video_frame->timestamp(), kVideoFrequency), |
575 reference_time, frame_encoded_callback)); | 412 reference_time, frame_encoded_callback)); |
576 | 413 |
577 // Build a suitable frame properties dictionary for keyframes. | 414 // Build a suitable frame properties dictionary for keyframes. |
578 base::ScopedCFTypeRef<CFDictionaryRef> frame_props; | 415 base::ScopedCFTypeRef<CFDictionaryRef> frame_props; |
579 if (encode_next_frame_as_keyframe_) { | 416 if (encode_next_frame_as_keyframe_) { |
580 frame_props = DictionaryWithKeyValue( | 417 frame_props = video_toolbox::DictionaryWithKeyValue( |
581 videotoolbox_glue_->kVTEncodeFrameOptionKey_ForceKeyFrame(), | 418 videotoolbox_glue_->kVTEncodeFrameOptionKey_ForceKeyFrame(), |
582 kCFBooleanTrue); | 419 kCFBooleanTrue); |
583 encode_next_frame_as_keyframe_ = false; | 420 encode_next_frame_as_keyframe_ = false; |
584 } | 421 } |
585 | 422 |
586 // Submit the frame to the compression session. The function returns as soon | 423 // Submit the frame to the compression session. The function returns as soon |
587 // as the frame has been enqueued. | 424 // as the frame has been enqueued. |
588 OSStatus status = videotoolbox_glue_->VTCompressionSessionEncodeFrame( | 425 OSStatus status = videotoolbox_glue_->VTCompressionSessionEncodeFrame( |
589 compression_session_, pixel_buffer, timestamp_cm, | 426 compression_session_, pixel_buffer, timestamp_cm, |
590 CoreMediaGlue::CMTime{0, 0, 0, 0}, frame_props, | 427 CoreMediaGlue::CMTime{0, 0, 0, 0}, frame_props, |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
666 // Reset the compression session only if the frame size is not zero (which | 503 // Reset the compression session only if the frame size is not zero (which |
667 // will obviously fail). It is possible for the frame size to be zero if no | 504 // will obviously fail). It is possible for the frame size to be zero if no |
668 // frame was submitted for encoding or requested from the video frame factory | 505 // frame was submitted for encoding or requested from the video frame factory |
669 // before suspension. | 506 // before suspension. |
670 if (!frame_size_.IsEmpty()) { | 507 if (!frame_size_.IsEmpty()) { |
671 VLOG(1) << "OnResume: Resetting compression session."; | 508 VLOG(1) << "OnResume: Resetting compression session."; |
672 ResetCompressionSession(); | 509 ResetCompressionSession(); |
673 } | 510 } |
674 } | 511 } |
675 | 512 |
676 bool H264VideoToolboxEncoder::SetSessionProperty(CFStringRef key, | |
677 int32_t value) { | |
678 base::ScopedCFTypeRef<CFNumberRef> cfvalue( | |
679 CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)); | |
680 return videotoolbox_glue_->VTSessionSetProperty(compression_session_, key, | |
681 cfvalue) == noErr; | |
682 } | |
683 | |
684 bool H264VideoToolboxEncoder::SetSessionProperty(CFStringRef key, bool value) { | |
685 CFBooleanRef cfvalue = (value) ? kCFBooleanTrue : kCFBooleanFalse; | |
686 return videotoolbox_glue_->VTSessionSetProperty(compression_session_, key, | |
687 cfvalue) == noErr; | |
688 } | |
689 | |
690 bool H264VideoToolboxEncoder::SetSessionProperty(CFStringRef key, | |
691 CFStringRef value) { | |
692 return videotoolbox_glue_->VTSessionSetProperty(compression_session_, key, | |
693 value) == noErr; | |
694 } | |
695 | |
696 void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque, | 513 void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque, |
697 void* request_opaque, | 514 void* request_opaque, |
698 OSStatus status, | 515 OSStatus status, |
699 VTEncodeInfoFlags info, | 516 VTEncodeInfoFlags info, |
700 CMSampleBufferRef sbuf) { | 517 CMSampleBufferRef sbuf) { |
701 auto encoder = reinterpret_cast<H264VideoToolboxEncoder*>(encoder_opaque); | 518 auto encoder = reinterpret_cast<H264VideoToolboxEncoder*>(encoder_opaque); |
702 const scoped_ptr<InProgressFrameEncode> request( | 519 const scoped_ptr<InProgressFrameEncode> request( |
703 reinterpret_cast<InProgressFrameEncode*>(request_opaque)); | 520 reinterpret_cast<InProgressFrameEncode*>(request_opaque)); |
704 bool keyframe = false; | 521 bool keyframe = false; |
705 bool has_frame_data = false; | 522 bool has_frame_data = false; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
744 // doesn't support the concept of forward-referencing frame dependencies or | 561 // doesn't support the concept of forward-referencing frame dependencies or |
745 // multiple frame dependencies; so pretend that all frames are only | 562 // multiple frame dependencies; so pretend that all frames are only |
746 // decodable after their immediately preceding frame is decoded. This will | 563 // decodable after their immediately preceding frame is decoded. This will |
747 // ensure a Cast receiver only attempts to decode the frames sequentially | 564 // ensure a Cast receiver only attempts to decode the frames sequentially |
748 // and in order. Furthermore, the encoder is configured to never use forward | 565 // and in order. Furthermore, the encoder is configured to never use forward |
749 // references (see |kVTCompressionPropertyKey_AllowFrameReordering|). There | 566 // references (see |kVTCompressionPropertyKey_AllowFrameReordering|). There |
750 // is no way to prevent multiple reference frames. | 567 // is no way to prevent multiple reference frames. |
751 encoded_frame->referenced_frame_id = frame_id - 1; | 568 encoded_frame->referenced_frame_id = frame_id - 1; |
752 } | 569 } |
753 | 570 |
754 if (has_frame_data) | 571 if (has_frame_data) { |
755 CopySampleBufferToAnnexBBuffer(sbuf, &encoded_frame->data, keyframe); | 572 video_toolbox::CopySampleBufferToAnnexBBuffer(sbuf, keyframe, |
| 573 &encoded_frame->data); |
| 574 } |
756 | 575 |
757 // TODO(miu): Compute and populate the |deadline_utilization| and | 576 // TODO(miu): Compute and populate the |deadline_utilization| and |
758 // |lossy_utilization| performance metrics in |encoded_frame|. | 577 // |lossy_utilization| performance metrics in |encoded_frame|. |
759 | 578 |
760 encoded_frame->encode_completion_time = | 579 encoded_frame->encode_completion_time = |
761 encoder->cast_environment_->Clock()->NowTicks(); | 580 encoder->cast_environment_->Clock()->NowTicks(); |
762 encoder->cast_environment_->PostTask( | 581 encoder->cast_environment_->PostTask( |
763 CastEnvironment::MAIN, FROM_HERE, | 582 CastEnvironment::MAIN, FROM_HERE, |
764 base::Bind(request->frame_encoded_callback, | 583 base::Bind(request->frame_encoded_callback, |
765 base::Passed(&encoded_frame))); | 584 base::Passed(&encoded_frame))); |
766 } | 585 } |
767 | 586 |
768 } // namespace cast | 587 } // namespace cast |
769 } // namespace media | 588 } // namespace media |
OLD | NEW |