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

Side by Side Diff: webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.mm

Issue 2258103003: iOS H264VideoToolBoxEncoder: Stop scaling native CVPixelBuffers (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Remove enable_scaling Created 4 years, 4 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 webrtc::VideoRotation rotation; 148 webrtc::VideoRotation rotation;
149 }; 149 };
150 150
151 // We receive I420Frames as input, but we need to feed CVPixelBuffers into the 151 // We receive I420Frames as input, but we need to feed CVPixelBuffers into the
152 // encoder. This performs the copy and format conversion. 152 // encoder. This performs the copy and format conversion.
153 // TODO(tkchin): See if encoder will accept i420 frames and compare performance. 153 // TODO(tkchin): See if encoder will accept i420 frames and compare performance.
154 bool CopyVideoFrameToPixelBuffer( 154 bool CopyVideoFrameToPixelBuffer(
155 const rtc::scoped_refptr<webrtc::VideoFrameBuffer>& frame, 155 const rtc::scoped_refptr<webrtc::VideoFrameBuffer>& frame,
156 CVPixelBufferRef pixel_buffer) { 156 CVPixelBufferRef pixel_buffer) {
157 RTC_DCHECK(pixel_buffer); 157 RTC_DCHECK(pixel_buffer);
158 RTC_DCHECK(CVPixelBufferGetPixelFormatType(pixel_buffer) == 158 RTC_DCHECK_EQ(CVPixelBufferGetPixelFormatType(pixel_buffer),
159 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange); 159 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange);
160 RTC_DCHECK(CVPixelBufferGetHeightOfPlane(pixel_buffer, 0) == 160 RTC_DCHECK_EQ(CVPixelBufferGetHeightOfPlane(pixel_buffer, 0),
161 static_cast<size_t>(frame->height())); 161 static_cast<size_t>(frame->height()));
162 RTC_DCHECK(CVPixelBufferGetWidthOfPlane(pixel_buffer, 0) == 162 RTC_DCHECK_EQ(CVPixelBufferGetWidthOfPlane(pixel_buffer, 0),
163 static_cast<size_t>(frame->width())); 163 static_cast<size_t>(frame->width()));
164 164
165 CVReturn cvRet = CVPixelBufferLockBaseAddress(pixel_buffer, 0); 165 CVReturn cvRet = CVPixelBufferLockBaseAddress(pixel_buffer, 0);
166 if (cvRet != kCVReturnSuccess) { 166 if (cvRet != kCVReturnSuccess) {
167 LOG(LS_ERROR) << "Failed to lock base address: " << cvRet; 167 LOG(LS_ERROR) << "Failed to lock base address: " << cvRet;
168 return false; 168 return false;
169 } 169 }
170 uint8_t* dst_y = reinterpret_cast<uint8_t*>( 170 uint8_t* dst_y = reinterpret_cast<uint8_t*>(
171 CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 0)); 171 CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 0));
172 int dst_stride_y = CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 0); 172 int dst_stride_y = CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 0);
173 uint8_t* dst_uv = reinterpret_cast<uint8_t*>( 173 uint8_t* dst_uv = reinterpret_cast<uint8_t*>(
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
211 // .5 is set as a mininum to prevent overcompensating for large temporary 211 // .5 is set as a mininum to prevent overcompensating for large temporary
212 // overshoots. We don't want to degrade video quality too badly. 212 // overshoots. We don't want to degrade video quality too badly.
213 // .95 is set to prevent oscillations. When a lower bitrate is set on the 213 // .95 is set to prevent oscillations. When a lower bitrate is set on the
214 // encoder than previously set, its output seems to have a brief period of 214 // encoder than previously set, its output seems to have a brief period of
215 // drastically reduced bitrate, so we want to avoid that. In steady state 215 // drastically reduced bitrate, so we want to avoid that. In steady state
216 // conditions, 0.95 seems to give us better overall bitrate over long periods 216 // conditions, 0.95 seems to give us better overall bitrate over long periods
217 // of time. 217 // of time.
218 H264VideoToolboxEncoder::H264VideoToolboxEncoder() 218 H264VideoToolboxEncoder::H264VideoToolboxEncoder()
219 : callback_(nullptr), 219 : callback_(nullptr),
220 compression_session_(nullptr), 220 compression_session_(nullptr),
221 bitrate_adjuster_(Clock::GetRealTimeClock(), .5, .95), 221 bitrate_adjuster_(Clock::GetRealTimeClock(), .5, .95) {
222 enable_scaling_(true) {
223 #if defined(WEBRTC_IOS)
224 if ([UIDevice deviceType] == RTCDeviceTypeIPhone4S) {
225 enable_scaling_ = false;
226 }
227 #endif
228 } 222 }
229 223
230 H264VideoToolboxEncoder::~H264VideoToolboxEncoder() { 224 H264VideoToolboxEncoder::~H264VideoToolboxEncoder() {
231 DestroyCompressionSession(); 225 DestroyCompressionSession();
232 } 226 }
233 227
234 int H264VideoToolboxEncoder::InitEncode(const VideoCodec* codec_settings, 228 int H264VideoToolboxEncoder::InitEncode(const VideoCodec* codec_settings,
235 int number_of_cores, 229 int number_of_cores,
236 size_t max_payload_size) { 230 size_t max_payload_size) {
237 RTC_DCHECK(codec_settings); 231 RTC_DCHECK(codec_settings);
238 RTC_DCHECK_EQ(codec_settings->codecType, kVideoCodecH264); 232 RTC_DCHECK_EQ(codec_settings->codecType, kVideoCodecH264);
239 width_ = codec_settings->width;
240 height_ = codec_settings->height;
241 { 233 {
242 rtc::CritScope lock(&quality_scaler_crit_); 234 rtc::CritScope lock(&quality_scaler_crit_);
243 quality_scaler_.Init(QualityScaler::kLowH264QpThreshold, 235 quality_scaler_.Init(QualityScaler::kLowH264QpThreshold,
244 QualityScaler::kBadH264QpThreshold, 236 QualityScaler::kBadH264QpThreshold,
245 codec_settings->startBitrate, codec_settings->width, 237 codec_settings->startBitrate, codec_settings->width,
246 codec_settings->height, codec_settings->maxFramerate); 238 codec_settings->height, codec_settings->maxFramerate);
247 QualityScaler::Resolution res = quality_scaler_.GetScaledResolution(); 239 QualityScaler::Resolution res = quality_scaler_.GetScaledResolution();
248 // TODO(tkchin): We may need to enforce width/height dimension restrictions 240 // TODO(tkchin): We may need to enforce width/height dimension restrictions
249 // to match what the encoder supports. 241 // to match what the encoder supports.
250 if (enable_scaling_) { 242 width_ = res.width;
251 width_ = res.width; 243 height_ = res.height;
252 height_ = res.height;
253 }
254 } 244 }
255 // We can only set average bitrate on the HW encoder. 245 // We can only set average bitrate on the HW encoder.
256 target_bitrate_bps_ = codec_settings->startBitrate; 246 target_bitrate_bps_ = codec_settings->startBitrate;
257 bitrate_adjuster_.SetTargetBitrateBps(target_bitrate_bps_); 247 bitrate_adjuster_.SetTargetBitrateBps(target_bitrate_bps_);
258 248
259 // TODO(tkchin): Try setting payload size via 249 // TODO(tkchin): Try setting payload size via
260 // kVTCompressionPropertyKey_MaxH264SliceBytes. 250 // kVTCompressionPropertyKey_MaxH264SliceBytes.
261 251
262 return ResetCompressionSession(); 252 return ResetCompressionSession();
263 } 253 }
264 254
265 rtc::scoped_refptr<VideoFrameBuffer>
266 H264VideoToolboxEncoder::GetScaledBufferOnEncode(
267 const rtc::scoped_refptr<VideoFrameBuffer>& frame) {
268 rtc::CritScope lock(&quality_scaler_crit_);
269 quality_scaler_.OnEncodeFrame(frame->width(), frame->height());
270 if (!frame->native_handle())
271 return quality_scaler_.GetScaledBuffer(frame);
272
273 // Handle native (CVImageRef) scaling.
274 const QualityScaler::Resolution res = quality_scaler_.GetScaledResolution();
275 if (!enable_scaling_ ||
276 (res.width == frame->width() && res.height == frame->height()))
277 return frame;
278 // TODO(magjed): Implement efficient CVImageRef -> CVImageRef scaling instead
279 // of doing it via I420.
280 return quality_scaler_.GetScaledBuffer(frame->NativeToI420Buffer());
281 }
282
283 int H264VideoToolboxEncoder::Encode( 255 int H264VideoToolboxEncoder::Encode(
284 const VideoFrame& frame, 256 const VideoFrame& frame,
285 const CodecSpecificInfo* codec_specific_info, 257 const CodecSpecificInfo* codec_specific_info,
286 const std::vector<FrameType>* frame_types) { 258 const std::vector<FrameType>* frame_types) {
287 RTC_DCHECK(!frame.IsZeroSize()); 259 RTC_DCHECK(!frame.IsZeroSize());
288 if (!callback_ || !compression_session_) { 260 if (!callback_ || !compression_session_) {
289 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; 261 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
290 } 262 }
291 #if defined(WEBRTC_IOS) 263 #if defined(WEBRTC_IOS)
292 if (!RTCIsUIApplicationActive()) { 264 if (!RTCIsUIApplicationActive()) {
293 // Ignore all encode requests when app isn't active. In this state, the 265 // Ignore all encode requests when app isn't active. In this state, the
294 // hardware encoder has been invalidated by the OS. 266 // hardware encoder has been invalidated by the OS.
295 return WEBRTC_VIDEO_CODEC_OK; 267 return WEBRTC_VIDEO_CODEC_OK;
296 } 268 }
297 #endif 269 #endif
298 bool is_keyframe_required = false; 270 bool is_keyframe_required = false;
299 rtc::scoped_refptr<VideoFrameBuffer> input_image(
300 GetScaledBufferOnEncode(frame.video_frame_buffer()));
301 271
302 if (input_image->width() != width_ || input_image->height() != height_) { 272 quality_scaler_.OnEncodeFrame(frame.width(), frame.height());
303 width_ = input_image->width(); 273 const QualityScaler::Resolution scaled_res =
304 height_ = input_image->height(); 274 quality_scaler_.GetScaledResolution();
275
276 if (scaled_res.width != width_ || scaled_res.height != height_) {
277 width_ = scaled_res.width;
278 height_ = scaled_res.height;
305 int ret = ResetCompressionSession(); 279 int ret = ResetCompressionSession();
306 if (ret < 0) 280 if (ret < 0)
307 return ret; 281 return ret;
308 } 282 }
309 283
310 // Get a pixel buffer from the pool and copy frame data over. 284 // Get a pixel buffer from the pool and copy frame data over.
311 CVPixelBufferPoolRef pixel_buffer_pool = 285 CVPixelBufferPoolRef pixel_buffer_pool =
312 VTCompressionSessionGetPixelBufferPool(compression_session_); 286 VTCompressionSessionGetPixelBufferPool(compression_session_);
313 #if defined(WEBRTC_IOS) 287 #if defined(WEBRTC_IOS)
314 if (!pixel_buffer_pool) { 288 if (!pixel_buffer_pool) {
315 // Kind of a hack. On backgrounding, the compression session seems to get 289 // Kind of a hack. On backgrounding, the compression session seems to get
316 // invalidated, which causes this pool call to fail when the application 290 // invalidated, which causes this pool call to fail when the application
317 // is foregrounded and frames are being sent for encoding again. 291 // is foregrounded and frames are being sent for encoding again.
318 // Resetting the session when this happens fixes the issue. 292 // Resetting the session when this happens fixes the issue.
319 // In addition we request a keyframe so video can recover quickly. 293 // In addition we request a keyframe so video can recover quickly.
320 ResetCompressionSession(); 294 ResetCompressionSession();
321 pixel_buffer_pool = 295 pixel_buffer_pool =
322 VTCompressionSessionGetPixelBufferPool(compression_session_); 296 VTCompressionSessionGetPixelBufferPool(compression_session_);
323 is_keyframe_required = true; 297 is_keyframe_required = true;
324 LOG(LS_INFO) << "Resetting compression session due to invalid pool."; 298 LOG(LS_INFO) << "Resetting compression session due to invalid pool.";
325 } 299 }
326 #endif 300 #endif
327 301
328 CVPixelBufferRef pixel_buffer = 302 CVPixelBufferRef pixel_buffer = static_cast<CVPixelBufferRef>(
329 static_cast<CVPixelBufferRef>(input_image->native_handle()); 303 frame.video_frame_buffer()->native_handle());
330 if (pixel_buffer) { 304 if (pixel_buffer) {
305 // This pixel buffer might have a higher resolution than what the
306 // compression session is configured to. The compression session can handle
307 // that and will output encoded frames in the configured resolution
308 // regardless of the input pixel buffer resolution.
331 CVBufferRetain(pixel_buffer); 309 CVBufferRetain(pixel_buffer);
332 pixel_buffer_pool = nullptr; 310 pixel_buffer_pool = nullptr;
333 } else { 311 } else {
334 if (!pixel_buffer_pool) { 312 if (!pixel_buffer_pool) {
335 LOG(LS_ERROR) << "Failed to get pixel buffer pool."; 313 LOG(LS_ERROR) << "Failed to get pixel buffer pool.";
336 return WEBRTC_VIDEO_CODEC_ERROR; 314 return WEBRTC_VIDEO_CODEC_ERROR;
337 } 315 }
338 CVReturn ret = CVPixelBufferPoolCreatePixelBuffer( 316 CVReturn ret = CVPixelBufferPoolCreatePixelBuffer(
339 nullptr, pixel_buffer_pool, &pixel_buffer); 317 nullptr, pixel_buffer_pool, &pixel_buffer);
340 if (ret != kCVReturnSuccess) { 318 if (ret != kCVReturnSuccess) {
341 LOG(LS_ERROR) << "Failed to create pixel buffer: " << ret; 319 LOG(LS_ERROR) << "Failed to create pixel buffer: " << ret;
342 // We probably want to drop frames here, since failure probably means 320 // We probably want to drop frames here, since failure probably means
343 // that the pool is empty. 321 // that the pool is empty.
344 return WEBRTC_VIDEO_CODEC_ERROR; 322 return WEBRTC_VIDEO_CODEC_ERROR;
345 } 323 }
346 RTC_DCHECK(pixel_buffer); 324 RTC_DCHECK(pixel_buffer);
347 if (!internal::CopyVideoFrameToPixelBuffer(input_image, pixel_buffer)) { 325 // TODO(magjed): Optimize by merging scaling and NV12 pixel buffer
326 // conversion once libyuv::MergeUVPlanes is available.
327 rtc::scoped_refptr<VideoFrameBuffer> scaled_i420_buffer =
328 quality_scaler_.GetScaledBuffer(frame.video_frame_buffer());
329 if (!internal::CopyVideoFrameToPixelBuffer(scaled_i420_buffer,
330 pixel_buffer)) {
348 LOG(LS_ERROR) << "Failed to copy frame data."; 331 LOG(LS_ERROR) << "Failed to copy frame data.";
349 CVBufferRelease(pixel_buffer); 332 CVBufferRelease(pixel_buffer);
350 return WEBRTC_VIDEO_CODEC_ERROR; 333 return WEBRTC_VIDEO_CODEC_ERROR;
351 } 334 }
352 } 335 }
353 336
354 // Check if we need a keyframe. 337 // Check if we need a keyframe.
355 if (!is_keyframe_required && frame_types) { 338 if (!is_keyframe_required && frame_types) {
356 for (auto frame_type : *frame_types) { 339 for (auto frame_type : *frame_types) {
357 if (frame_type == kVideoFrameKey) { 340 if (frame_type == kVideoFrameKey) {
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after
644 if (result != 0) { 627 if (result != 0) {
645 LOG(LS_ERROR) << "Encode callback failed: " << result; 628 LOG(LS_ERROR) << "Encode callback failed: " << result;
646 return; 629 return;
647 } 630 }
648 bitrate_adjuster_.Update(frame._size); 631 bitrate_adjuster_.Update(frame._size);
649 } 632 }
650 633
651 } // namespace webrtc 634 } // namespace webrtc
652 635
653 #endif // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED) 636 #endif // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698