OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 * | 9 * |
10 */ | 10 */ |
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 | 243 |
244 H264VideoToolboxEncoder::~H264VideoToolboxEncoder() { | 244 H264VideoToolboxEncoder::~H264VideoToolboxEncoder() { |
245 DestroyCompressionSession(); | 245 DestroyCompressionSession(); |
246 } | 246 } |
247 | 247 |
248 int H264VideoToolboxEncoder::InitEncode(const VideoCodec* codec_settings, | 248 int H264VideoToolboxEncoder::InitEncode(const VideoCodec* codec_settings, |
249 int number_of_cores, | 249 int number_of_cores, |
250 size_t max_payload_size) { | 250 size_t max_payload_size) { |
251 RTC_DCHECK(codec_settings); | 251 RTC_DCHECK(codec_settings); |
252 RTC_DCHECK_EQ(codec_settings->codecType, kVideoCodecH264); | 252 RTC_DCHECK_EQ(codec_settings->codecType, kVideoCodecH264); |
253 { | 253 |
254 rtc::CritScope lock(&quality_scaler_crit_); | 254 width_ = codec_settings->width; |
255 quality_scaler_.Init(internal::kLowH264QpThreshold, | 255 height_ = codec_settings->height; |
256 internal::kHighH264QpThreshold, | |
257 codec_settings->startBitrate, codec_settings->width, | |
258 codec_settings->height, codec_settings->maxFramerate); | |
259 QualityScaler::Resolution res = quality_scaler_.GetScaledResolution(); | |
260 // TODO(tkchin): We may need to enforce width/height dimension restrictions | |
261 // to match what the encoder supports. | |
262 width_ = res.width; | |
263 height_ = res.height; | |
264 } | |
265 // We can only set average bitrate on the HW encoder. | 256 // We can only set average bitrate on the HW encoder. |
266 target_bitrate_bps_ = codec_settings->startBitrate; | 257 target_bitrate_bps_ = codec_settings->startBitrate; |
267 bitrate_adjuster_.SetTargetBitrateBps(target_bitrate_bps_); | 258 bitrate_adjuster_.SetTargetBitrateBps(target_bitrate_bps_); |
268 | 259 |
269 // TODO(tkchin): Try setting payload size via | 260 // TODO(tkchin): Try setting payload size via |
270 // kVTCompressionPropertyKey_MaxH264SliceBytes. | 261 // kVTCompressionPropertyKey_MaxH264SliceBytes. |
271 | 262 |
272 return ResetCompressionSession(); | 263 return ResetCompressionSession(); |
273 } | 264 } |
274 | 265 |
275 int H264VideoToolboxEncoder::Encode( | 266 int H264VideoToolboxEncoder::Encode( |
276 const VideoFrame& frame, | 267 const VideoFrame& frame, |
277 const CodecSpecificInfo* codec_specific_info, | 268 const CodecSpecificInfo* codec_specific_info, |
278 const std::vector<FrameType>* frame_types) { | 269 const std::vector<FrameType>* frame_types) { |
| 270 // |input_frame| size should always match codec settings. |
| 271 RTC_DCHECK_EQ(frame.width(), width_); |
| 272 RTC_DCHECK_EQ(frame.height(), height_); |
279 RTC_DCHECK(!frame.IsZeroSize()); | 273 RTC_DCHECK(!frame.IsZeroSize()); |
280 if (!callback_ || !compression_session_) { | 274 if (!callback_ || !compression_session_) { |
281 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 275 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
282 } | 276 } |
283 #if defined(WEBRTC_IOS) | 277 #if defined(WEBRTC_IOS) |
284 if (!RTCIsUIApplicationActive()) { | 278 if (!RTCIsUIApplicationActive()) { |
285 // Ignore all encode requests when app isn't active. In this state, the | 279 // Ignore all encode requests when app isn't active. In this state, the |
286 // hardware encoder has been invalidated by the OS. | 280 // hardware encoder has been invalidated by the OS. |
287 return WEBRTC_VIDEO_CODEC_OK; | 281 return WEBRTC_VIDEO_CODEC_OK; |
288 } | 282 } |
289 #endif | 283 #endif |
290 bool is_keyframe_required = false; | 284 bool is_keyframe_required = false; |
291 | 285 |
292 quality_scaler_.OnEncodeFrame(frame.width(), frame.height()); | |
293 const QualityScaler::Resolution scaled_res = | |
294 quality_scaler_.GetScaledResolution(); | |
295 | |
296 if (scaled_res.width != width_ || scaled_res.height != height_) { | |
297 width_ = scaled_res.width; | |
298 height_ = scaled_res.height; | |
299 int ret = ResetCompressionSession(); | |
300 if (ret < 0) | |
301 return ret; | |
302 } | |
303 | |
304 // Get a pixel buffer from the pool and copy frame data over. | 286 // Get a pixel buffer from the pool and copy frame data over. |
305 CVPixelBufferPoolRef pixel_buffer_pool = | 287 CVPixelBufferPoolRef pixel_buffer_pool = |
306 VTCompressionSessionGetPixelBufferPool(compression_session_); | 288 VTCompressionSessionGetPixelBufferPool(compression_session_); |
307 #if defined(WEBRTC_IOS) | 289 #if defined(WEBRTC_IOS) |
308 if (!pixel_buffer_pool) { | 290 if (!pixel_buffer_pool) { |
309 // Kind of a hack. On backgrounding, the compression session seems to get | 291 // Kind of a hack. On backgrounding, the compression session seems to get |
310 // invalidated, which causes this pool call to fail when the application | 292 // invalidated, which causes this pool call to fail when the application |
311 // is foregrounded and frames are being sent for encoding again. | 293 // is foregrounded and frames are being sent for encoding again. |
312 // Resetting the session when this happens fixes the issue. | 294 // Resetting the session when this happens fixes the issue. |
313 // In addition we request a keyframe so video can recover quickly. | 295 // In addition we request a keyframe so video can recover quickly. |
(...skipping 26 matching lines...) Expand all Loading... |
340 if (!core_video_frame_buffer->CropAndScaleTo(&nv12_scale_buffer_, | 322 if (!core_video_frame_buffer->CropAndScaleTo(&nv12_scale_buffer_, |
341 pixel_buffer)) { | 323 pixel_buffer)) { |
342 return WEBRTC_VIDEO_CODEC_ERROR; | 324 return WEBRTC_VIDEO_CODEC_ERROR; |
343 } | 325 } |
344 } | 326 } |
345 } else { | 327 } else { |
346 pixel_buffer = internal::CreatePixelBuffer(pixel_buffer_pool); | 328 pixel_buffer = internal::CreatePixelBuffer(pixel_buffer_pool); |
347 if (!pixel_buffer) { | 329 if (!pixel_buffer) { |
348 return WEBRTC_VIDEO_CODEC_ERROR; | 330 return WEBRTC_VIDEO_CODEC_ERROR; |
349 } | 331 } |
350 // TODO(magjed): Optimize by merging scaling and NV12 pixel buffer | 332 RTC_DCHECK(pixel_buffer); |
351 // conversion once libyuv::MergeUVPlanes is available. | 333 if (!internal::CopyVideoFrameToPixelBuffer(frame.video_frame_buffer(), |
352 rtc::scoped_refptr<VideoFrameBuffer> scaled_i420_buffer = | |
353 quality_scaler_.GetScaledBuffer(frame.video_frame_buffer()); | |
354 if (!internal::CopyVideoFrameToPixelBuffer(scaled_i420_buffer, | |
355 pixel_buffer)) { | 334 pixel_buffer)) { |
356 LOG(LS_ERROR) << "Failed to copy frame data."; | 335 LOG(LS_ERROR) << "Failed to copy frame data."; |
357 CVBufferRelease(pixel_buffer); | 336 CVBufferRelease(pixel_buffer); |
358 return WEBRTC_VIDEO_CODEC_ERROR; | 337 return WEBRTC_VIDEO_CODEC_ERROR; |
359 } | 338 } |
360 } | 339 } |
361 | 340 |
362 // Check if we need a keyframe. | 341 // Check if we need a keyframe. |
363 if (!is_keyframe_required && frame_types) { | 342 if (!is_keyframe_required && frame_types) { |
364 for (auto frame_type : *frame_types) { | 343 for (auto frame_type : *frame_types) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 } | 379 } |
401 return WEBRTC_VIDEO_CODEC_OK; | 380 return WEBRTC_VIDEO_CODEC_OK; |
402 } | 381 } |
403 | 382 |
404 int H264VideoToolboxEncoder::RegisterEncodeCompleteCallback( | 383 int H264VideoToolboxEncoder::RegisterEncodeCompleteCallback( |
405 EncodedImageCallback* callback) { | 384 EncodedImageCallback* callback) { |
406 callback_ = callback; | 385 callback_ = callback; |
407 return WEBRTC_VIDEO_CODEC_OK; | 386 return WEBRTC_VIDEO_CODEC_OK; |
408 } | 387 } |
409 | 388 |
410 void H264VideoToolboxEncoder::OnDroppedFrame() { | |
411 rtc::CritScope lock(&quality_scaler_crit_); | |
412 quality_scaler_.ReportDroppedFrame(); | |
413 } | |
414 | |
415 int H264VideoToolboxEncoder::SetChannelParameters(uint32_t packet_loss, | 389 int H264VideoToolboxEncoder::SetChannelParameters(uint32_t packet_loss, |
416 int64_t rtt) { | 390 int64_t rtt) { |
417 // Encoder doesn't know anything about packet loss or rtt so just return. | 391 // Encoder doesn't know anything about packet loss or rtt so just return. |
418 return WEBRTC_VIDEO_CODEC_OK; | 392 return WEBRTC_VIDEO_CODEC_OK; |
419 } | 393 } |
420 | 394 |
421 int H264VideoToolboxEncoder::SetRates(uint32_t new_bitrate_kbit, | 395 int H264VideoToolboxEncoder::SetRates(uint32_t new_bitrate_kbit, |
422 uint32_t frame_rate) { | 396 uint32_t frame_rate) { |
423 target_bitrate_bps_ = 1000 * new_bitrate_kbit; | 397 target_bitrate_bps_ = 1000 * new_bitrate_kbit; |
424 bitrate_adjuster_.SetTargetBitrateBps(target_bitrate_bps_); | 398 bitrate_adjuster_.SetTargetBitrateBps(target_bitrate_bps_); |
425 SetBitrateBps(bitrate_adjuster_.GetAdjustedBitrateBps()); | 399 SetBitrateBps(bitrate_adjuster_.GetAdjustedBitrateBps()); |
426 | |
427 rtc::CritScope lock(&quality_scaler_crit_); | |
428 quality_scaler_.ReportFramerate(frame_rate); | |
429 | |
430 return WEBRTC_VIDEO_CODEC_OK; | 400 return WEBRTC_VIDEO_CODEC_OK; |
431 } | 401 } |
432 | 402 |
433 int H264VideoToolboxEncoder::Release() { | 403 int H264VideoToolboxEncoder::Release() { |
434 // Need to reset so that the session is invalidated and won't use the | 404 // Need to reset so that the session is invalidated and won't use the |
435 // callback anymore. Do not remove callback until the session is invalidated | 405 // callback anymore. Do not remove callback until the session is invalidated |
436 // since async encoder callbacks can occur until invalidation. | 406 // since async encoder callbacks can occur until invalidation. |
437 int ret = ResetCompressionSession(); | 407 int ret = ResetCompressionSession(); |
438 callback_ = nullptr; | 408 callback_ = nullptr; |
439 return ret; | 409 return ret; |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
592 int32_t height, | 562 int32_t height, |
593 int64_t render_time_ms, | 563 int64_t render_time_ms, |
594 uint32_t timestamp, | 564 uint32_t timestamp, |
595 VideoRotation rotation) { | 565 VideoRotation rotation) { |
596 if (status != noErr) { | 566 if (status != noErr) { |
597 LOG(LS_ERROR) << "H264 encode failed."; | 567 LOG(LS_ERROR) << "H264 encode failed."; |
598 return; | 568 return; |
599 } | 569 } |
600 if (info_flags & kVTEncodeInfo_FrameDropped) { | 570 if (info_flags & kVTEncodeInfo_FrameDropped) { |
601 LOG(LS_INFO) << "H264 encode dropped frame."; | 571 LOG(LS_INFO) << "H264 encode dropped frame."; |
602 rtc::CritScope lock(&quality_scaler_crit_); | |
603 quality_scaler_.ReportDroppedFrame(); | |
604 return; | 572 return; |
605 } | 573 } |
606 | 574 |
607 bool is_keyframe = false; | 575 bool is_keyframe = false; |
608 CFArrayRef attachments = | 576 CFArrayRef attachments = |
609 CMSampleBufferGetSampleAttachmentsArray(sample_buffer, 0); | 577 CMSampleBufferGetSampleAttachmentsArray(sample_buffer, 0); |
610 if (attachments != nullptr && CFArrayGetCount(attachments)) { | 578 if (attachments != nullptr && CFArrayGetCount(attachments)) { |
611 CFDictionaryRef attachment = | 579 CFDictionaryRef attachment = |
612 static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(attachments, 0)); | 580 static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(attachments, 0)); |
613 is_keyframe = | 581 is_keyframe = |
(...skipping 21 matching lines...) Expand all Loading... |
635 frame._encodedWidth = width; | 603 frame._encodedWidth = width; |
636 frame._encodedHeight = height; | 604 frame._encodedHeight = height; |
637 frame._completeFrame = true; | 605 frame._completeFrame = true; |
638 frame._frameType = | 606 frame._frameType = |
639 is_keyframe ? webrtc::kVideoFrameKey : webrtc::kVideoFrameDelta; | 607 is_keyframe ? webrtc::kVideoFrameKey : webrtc::kVideoFrameDelta; |
640 frame.capture_time_ms_ = render_time_ms; | 608 frame.capture_time_ms_ = render_time_ms; |
641 frame._timeStamp = timestamp; | 609 frame._timeStamp = timestamp; |
642 frame.rotation_ = rotation; | 610 frame.rotation_ = rotation; |
643 | 611 |
644 h264_bitstream_parser_.ParseBitstream(buffer->data(), buffer->size()); | 612 h264_bitstream_parser_.ParseBitstream(buffer->data(), buffer->size()); |
645 int qp; | 613 h264_bitstream_parser_.GetLastSliceQp(&frame.qp_); |
646 if (h264_bitstream_parser_.GetLastSliceQp(&qp)) { | |
647 rtc::CritScope lock(&quality_scaler_crit_); | |
648 quality_scaler_.ReportQP(qp); | |
649 } | |
650 | 614 |
651 EncodedImageCallback::Result result = | 615 EncodedImageCallback::Result res = |
652 callback_->OnEncodedImage(frame, &codec_specific_info, header.get()); | 616 callback_->OnEncodedImage(frame, &codec_specific_info, header.get()); |
653 if (result.error != EncodedImageCallback::Result::OK) { | 617 if (res.error != EncodedImageCallback::Result::OK) { |
654 LOG(LS_ERROR) << "Encode callback failed: " << result.error; | 618 LOG(LS_ERROR) << "Encode callback failed: " << res.error; |
655 return; | 619 return; |
656 } | 620 } |
657 bitrate_adjuster_.Update(frame._size); | 621 bitrate_adjuster_.Update(frame._size); |
658 } | 622 } |
659 | 623 |
| 624 QualityScaler::Settings H264VideoToolboxEncoder::GetQPThresholds() const { |
| 625 return QualityScaler::Settings(true, internal::kLowH264QpThreshold, |
| 626 internal::kHighH264QpThreshold); |
| 627 } |
660 } // namespace webrtc | 628 } // namespace webrtc |
OLD | NEW |