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 */ |
11 | 11 |
12 #include "webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h" | 12 #include "webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h" |
13 | 13 |
14 #include <limits> | 14 #include <limits> |
15 | 15 |
16 #include "third_party/openh264/src/codec/api/svc/codec_api.h" | 16 #include "third_party/openh264/src/codec/api/svc/codec_api.h" |
17 #include "third_party/openh264/src/codec/api/svc/codec_app_def.h" | 17 #include "third_party/openh264/src/codec/api/svc/codec_app_def.h" |
18 #include "third_party/openh264/src/codec/api/svc/codec_def.h" | 18 #include "third_party/openh264/src/codec/api/svc/codec_def.h" |
19 | 19 |
20 #include "webrtc/base/checks.h" | 20 #include "webrtc/base/checks.h" |
21 #include "webrtc/base/logging.h" | 21 #include "webrtc/base/logging.h" |
22 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" | 22 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" |
23 #include "webrtc/system_wrappers/include/metrics.h" | |
23 | 24 |
24 namespace webrtc { | 25 namespace webrtc { |
25 | 26 |
26 namespace { | 27 namespace { |
27 | 28 |
28 const bool kOpenH264EncoderDetailedLogging = false; | 29 const bool kOpenH264EncoderDetailedLogging = false; |
29 | 30 |
30 int NumberOfThreads(int width, int height, int number_of_cores) { | 31 int NumberOfThreads(int width, int height, int number_of_cores) { |
31 // TODO(hbos): In Chromium, multiple threads do not work with sandbox on Mac, | 32 // TODO(hbos): In Chromium, multiple threads do not work with sandbox on Mac, |
32 // see crbug.com/583348. Until further investigated, only use one thread. | 33 // see crbug.com/583348. Until further investigated, only use one thread. |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
133 } | 134 } |
134 // Copy the entire layer's data (including start codes). | 135 // Copy the entire layer's data (including start codes). |
135 memcpy(encoded_image->_buffer + encoded_image->_length, | 136 memcpy(encoded_image->_buffer + encoded_image->_length, |
136 layerInfo.pBsBuf, | 137 layerInfo.pBsBuf, |
137 layer_len); | 138 layer_len); |
138 encoded_image->_length += layer_len; | 139 encoded_image->_length += layer_len; |
139 } | 140 } |
140 } | 141 } |
141 | 142 |
142 H264EncoderImpl::H264EncoderImpl() | 143 H264EncoderImpl::H264EncoderImpl() |
143 : openh264_encoder_(nullptr), | 144 : has_reported_init_(false), |
145 has_reported_error_(false), | |
146 openh264_encoder_(nullptr), | |
144 encoded_image_callback_(nullptr) { | 147 encoded_image_callback_(nullptr) { |
145 } | 148 } |
146 | 149 |
147 H264EncoderImpl::~H264EncoderImpl() { | 150 H264EncoderImpl::~H264EncoderImpl() { |
148 Release(); | 151 Release(); |
149 } | 152 } |
150 | 153 |
151 int32_t H264EncoderImpl::InitEncode(const VideoCodec* codec_settings, | 154 int32_t H264EncoderImpl::InitEncode(const VideoCodec* codec_settings, |
152 int32_t number_of_cores, | 155 int32_t number_of_cores, |
153 size_t /*max_payload_size*/) { | 156 size_t /*max_payload_size*/) { |
157 ReportInit(); | |
154 if (!codec_settings || | 158 if (!codec_settings || |
155 codec_settings->codecType != kVideoCodecH264) { | 159 codec_settings->codecType != kVideoCodecH264) { |
160 ReportError(); | |
156 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 161 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
157 } | 162 } |
158 if (codec_settings->maxFramerate == 0) | 163 if (codec_settings->maxFramerate == 0) { |
164 ReportError(); | |
159 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 165 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
160 if (codec_settings->width < 1 || codec_settings->height < 1) | 166 } |
167 if (codec_settings->width < 1 || codec_settings->height < 1) { | |
168 ReportError(); | |
161 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 169 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
170 } | |
162 | 171 |
163 int32_t release_ret = Release(); | 172 int32_t release_ret = Release(); |
164 if (release_ret != WEBRTC_VIDEO_CODEC_OK) | 173 if (release_ret != WEBRTC_VIDEO_CODEC_OK) { |
174 ReportError(); | |
165 return release_ret; | 175 return release_ret; |
176 } | |
166 RTC_DCHECK(!openh264_encoder_); | 177 RTC_DCHECK(!openh264_encoder_); |
167 | 178 |
168 // Create encoder. | 179 // Create encoder. |
169 if (WelsCreateSVCEncoder(&openh264_encoder_) != 0) { | 180 if (WelsCreateSVCEncoder(&openh264_encoder_) != 0) { |
170 // Failed to create encoder. | 181 // Failed to create encoder. |
171 LOG(LS_ERROR) << "Failed to create OpenH264 encoder"; | 182 LOG(LS_ERROR) << "Failed to create OpenH264 encoder"; |
172 RTC_DCHECK(!openh264_encoder_); | 183 RTC_DCHECK(!openh264_encoder_); |
184 ReportError(); | |
173 return WEBRTC_VIDEO_CODEC_ERROR; | 185 return WEBRTC_VIDEO_CODEC_ERROR; |
174 } | 186 } |
175 RTC_DCHECK(openh264_encoder_); | 187 RTC_DCHECK(openh264_encoder_); |
176 if (kOpenH264EncoderDetailedLogging) { | 188 if (kOpenH264EncoderDetailedLogging) { |
177 int trace_level = WELS_LOG_DETAIL; | 189 int trace_level = WELS_LOG_DETAIL; |
178 openh264_encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL, | 190 openh264_encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL, |
179 &trace_level); | 191 &trace_level); |
180 } | 192 } |
181 // else WELS_LOG_DEFAULT is used by default. | 193 // else WELS_LOG_DEFAULT is used by default. |
182 | 194 |
183 codec_settings_ = *codec_settings; | 195 codec_settings_ = *codec_settings; |
184 if (codec_settings_.targetBitrate == 0) | 196 if (codec_settings_.targetBitrate == 0) |
185 codec_settings_.targetBitrate = codec_settings_.startBitrate; | 197 codec_settings_.targetBitrate = codec_settings_.startBitrate; |
186 | 198 |
187 // Initialization parameters. | 199 // Initialization parameters. |
188 // There are two ways to initialize. There is SEncParamBase (cleared with | 200 // There are two ways to initialize. There is SEncParamBase (cleared with |
189 // memset(&p, 0, sizeof(SEncParamBase)) used in Initialize, and SEncParamExt | 201 // memset(&p, 0, sizeof(SEncParamBase)) used in Initialize, and SEncParamExt |
190 // which is a superset of SEncParamBase (cleared with GetDefaultParams) used | 202 // which is a superset of SEncParamBase (cleared with GetDefaultParams) used |
191 // in InitializeExt. | 203 // in InitializeExt. |
192 SEncParamExt init_params; | 204 SEncParamExt init_params; |
193 openh264_encoder_->GetDefaultParams(&init_params); | 205 openh264_encoder_->GetDefaultParams(&init_params); |
194 if (codec_settings_.mode == kRealtimeVideo) { | 206 if (codec_settings_.mode == kRealtimeVideo) { |
195 init_params.iUsageType = CAMERA_VIDEO_REAL_TIME; | 207 init_params.iUsageType = CAMERA_VIDEO_REAL_TIME; |
196 } else if (codec_settings_.mode == kScreensharing) { | 208 } else if (codec_settings_.mode == kScreensharing) { |
197 init_params.iUsageType = SCREEN_CONTENT_REAL_TIME; | 209 init_params.iUsageType = SCREEN_CONTENT_REAL_TIME; |
198 } else { | 210 } else { |
211 ReportError(); | |
199 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 212 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
200 } | 213 } |
201 init_params.iPicWidth = codec_settings_.width; | 214 init_params.iPicWidth = codec_settings_.width; |
202 init_params.iPicHeight = codec_settings_.height; | 215 init_params.iPicHeight = codec_settings_.height; |
203 // |init_params| uses bit/s, |codec_settings_| uses kbit/s. | 216 // |init_params| uses bit/s, |codec_settings_| uses kbit/s. |
204 init_params.iTargetBitrate = codec_settings_.targetBitrate * 1000; | 217 init_params.iTargetBitrate = codec_settings_.targetBitrate * 1000; |
205 init_params.iMaxBitrate = codec_settings_.maxBitrate * 1000; | 218 init_params.iMaxBitrate = codec_settings_.maxBitrate * 1000; |
206 // Rate Control mode | 219 // Rate Control mode |
207 init_params.iRCMode = RC_BITRATE_MODE; | 220 init_params.iRCMode = RC_BITRATE_MODE; |
208 init_params.fMaxFrameRate = static_cast<float>(codec_settings_.maxFramerate); | 221 init_params.fMaxFrameRate = static_cast<float>(codec_settings_.maxFramerate); |
(...skipping 20 matching lines...) Expand all Loading... | |
229 init_params.sSpatialLayers[0].fFrameRate = init_params.fMaxFrameRate; | 242 init_params.sSpatialLayers[0].fFrameRate = init_params.fMaxFrameRate; |
230 init_params.sSpatialLayers[0].iSpatialBitrate = init_params.iTargetBitrate; | 243 init_params.sSpatialLayers[0].iSpatialBitrate = init_params.iTargetBitrate; |
231 init_params.sSpatialLayers[0].iMaxSpatialBitrate = init_params.iMaxBitrate; | 244 init_params.sSpatialLayers[0].iMaxSpatialBitrate = init_params.iMaxBitrate; |
232 // Slice num according to number of threads. | 245 // Slice num according to number of threads. |
233 init_params.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_AUTO_SLICE; | 246 init_params.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_AUTO_SLICE; |
234 | 247 |
235 // Initialize. | 248 // Initialize. |
236 if (openh264_encoder_->InitializeExt(&init_params) != 0) { | 249 if (openh264_encoder_->InitializeExt(&init_params) != 0) { |
237 LOG(LS_ERROR) << "Failed to initialize OpenH264 encoder"; | 250 LOG(LS_ERROR) << "Failed to initialize OpenH264 encoder"; |
238 Release(); | 251 Release(); |
252 ReportError(); | |
239 return WEBRTC_VIDEO_CODEC_ERROR; | 253 return WEBRTC_VIDEO_CODEC_ERROR; |
240 } | 254 } |
241 int video_format = EVideoFormatType::videoFormatI420; | 255 int video_format = EVideoFormatType::videoFormatI420; |
242 openh264_encoder_->SetOption(ENCODER_OPTION_DATAFORMAT, | 256 openh264_encoder_->SetOption(ENCODER_OPTION_DATAFORMAT, |
243 &video_format); | 257 &video_format); |
244 | 258 |
245 // Initialize encoded image. Default buffer size: size of unencoded data. | 259 // Initialize encoded image. Default buffer size: size of unencoded data. |
246 encoded_image_._size = CalcBufferSize( | 260 encoded_image_._size = CalcBufferSize( |
247 kI420, codec_settings_.width, codec_settings_.height); | 261 kI420, codec_settings_.width, codec_settings_.height); |
248 encoded_image_._buffer = new uint8_t[encoded_image_._size]; | 262 encoded_image_._buffer = new uint8_t[encoded_image_._size]; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
292 &target_bitrate); | 306 &target_bitrate); |
293 float max_framerate = static_cast<float>(codec_settings_.maxFramerate); | 307 float max_framerate = static_cast<float>(codec_settings_.maxFramerate); |
294 openh264_encoder_->SetOption(ENCODER_OPTION_FRAME_RATE, | 308 openh264_encoder_->SetOption(ENCODER_OPTION_FRAME_RATE, |
295 &max_framerate); | 309 &max_framerate); |
296 return WEBRTC_VIDEO_CODEC_OK; | 310 return WEBRTC_VIDEO_CODEC_OK; |
297 } | 311 } |
298 | 312 |
299 int32_t H264EncoderImpl::Encode( | 313 int32_t H264EncoderImpl::Encode( |
300 const VideoFrame& frame, const CodecSpecificInfo* codec_specific_info, | 314 const VideoFrame& frame, const CodecSpecificInfo* codec_specific_info, |
301 const std::vector<FrameType>* frame_types) { | 315 const std::vector<FrameType>* frame_types) { |
302 if (!IsInitialized()) | 316 if (!IsInitialized()) { |
317 ReportError(); | |
303 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 318 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
304 if (frame.IsZeroSize()) | 319 } |
320 if (frame.IsZeroSize()) { | |
321 ReportError(); | |
305 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 322 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
323 } | |
306 if (!encoded_image_callback_) { | 324 if (!encoded_image_callback_) { |
307 LOG(LS_WARNING) << "InitEncode() has been called, but a callback function " | 325 LOG(LS_WARNING) << "InitEncode() has been called, but a callback function " |
308 << "has not been set with RegisterEncodeCompleteCallback()"; | 326 << "has not been set with RegisterEncodeCompleteCallback()"; |
327 ReportError(); | |
309 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 328 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
310 } | 329 } |
311 if (frame.width() != codec_settings_.width || | 330 if (frame.width() != codec_settings_.width || |
312 frame.height() != codec_settings_.height) { | 331 frame.height() != codec_settings_.height) { |
313 LOG(LS_WARNING) << "Encoder initialized for " << codec_settings_.width | 332 LOG(LS_WARNING) << "Encoder initialized for " << codec_settings_.width |
314 << "x" << codec_settings_.height << " but trying to encode " | 333 << "x" << codec_settings_.height << " but trying to encode " |
315 << frame.width() << "x" << frame.height() << " frame."; | 334 << frame.width() << "x" << frame.height() << " frame."; |
335 ReportError(); | |
316 return WEBRTC_VIDEO_CODEC_ERR_SIZE; | 336 return WEBRTC_VIDEO_CODEC_ERR_SIZE; |
317 } | 337 } |
318 | 338 |
319 bool force_key_frame = false; | 339 bool force_key_frame = false; |
320 if (frame_types != nullptr) { | 340 if (frame_types != nullptr) { |
321 // We only support a single stream. | 341 // We only support a single stream. |
322 RTC_DCHECK_EQ(frame_types->size(), static_cast<size_t>(1)); | 342 RTC_DCHECK_EQ(frame_types->size(), static_cast<size_t>(1)); |
323 // Skip frame? | 343 // Skip frame? |
324 if ((*frame_types)[0] == kEmptyFrame) { | 344 if ((*frame_types)[0] == kEmptyFrame) { |
325 return WEBRTC_VIDEO_CODEC_OK; | 345 return WEBRTC_VIDEO_CODEC_OK; |
(...skipping 24 matching lines...) Expand all Loading... | |
350 | 370 |
351 // EncodeFrame output. | 371 // EncodeFrame output. |
352 SFrameBSInfo info; | 372 SFrameBSInfo info; |
353 memset(&info, 0, sizeof(SFrameBSInfo)); | 373 memset(&info, 0, sizeof(SFrameBSInfo)); |
354 | 374 |
355 // Encode! | 375 // Encode! |
356 int enc_ret = openh264_encoder_->EncodeFrame(&picture, &info); | 376 int enc_ret = openh264_encoder_->EncodeFrame(&picture, &info); |
357 if (enc_ret != 0) { | 377 if (enc_ret != 0) { |
358 LOG(LS_ERROR) << "OpenH264 frame encoding failed, EncodeFrame returned " | 378 LOG(LS_ERROR) << "OpenH264 frame encoding failed, EncodeFrame returned " |
359 << enc_ret << "."; | 379 << enc_ret << "."; |
380 ReportError(); | |
360 return WEBRTC_VIDEO_CODEC_ERROR; | 381 return WEBRTC_VIDEO_CODEC_ERROR; |
361 } | 382 } |
362 | 383 |
363 encoded_image_._encodedWidth = frame.width(); | 384 encoded_image_._encodedWidth = frame.width(); |
364 encoded_image_._encodedHeight = frame.height(); | 385 encoded_image_._encodedHeight = frame.height(); |
365 encoded_image_._timeStamp = frame.timestamp(); | 386 encoded_image_._timeStamp = frame.timestamp(); |
366 encoded_image_.ntp_time_ms_ = frame.ntp_time_ms(); | 387 encoded_image_.ntp_time_ms_ = frame.ntp_time_ms(); |
367 encoded_image_.capture_time_ms_ = frame.render_time_ms(); | 388 encoded_image_.capture_time_ms_ = frame.render_time_ms(); |
368 encoded_image_._frameType = EVideoFrameType_to_FrameType(info.eFrameType); | 389 encoded_image_._frameType = EVideoFrameType_to_FrameType(info.eFrameType); |
369 | 390 |
(...skipping 12 matching lines...) Expand all Loading... | |
382 &codec_specific, | 403 &codec_specific, |
383 &frag_header); | 404 &frag_header); |
384 } | 405 } |
385 return WEBRTC_VIDEO_CODEC_OK; | 406 return WEBRTC_VIDEO_CODEC_OK; |
386 } | 407 } |
387 | 408 |
388 bool H264EncoderImpl::IsInitialized() const { | 409 bool H264EncoderImpl::IsInitialized() const { |
389 return openh264_encoder_ != nullptr; | 410 return openh264_encoder_ != nullptr; |
390 } | 411 } |
391 | 412 |
413 void H264EncoderImpl::ReportInit() { | |
414 if (has_reported_init_) | |
415 return; | |
416 RTC_HISTOGRAM_COUNTS("WebRTC.Video.H264EncoderImpl.Init", | |
hlundin-webrtc
2016/02/23 13:55:54
Same again.
hbos
2016/02/23 14:43:26
Done.
| |
417 1 /* sample */, | |
418 1 /* min */, 1 /* max */, | |
419 1 /* bucket count */); | |
420 has_reported_init_ = true; | |
421 } | |
422 | |
423 void H264EncoderImpl::ReportError() { | |
424 if (has_reported_error_) | |
425 return; | |
426 RTC_HISTOGRAM_COUNTS("WebRTC.Video.H264EncoderImpl.Error", | |
427 1 /* sample */, | |
428 1 /* min */, 1 /* max */, | |
429 1 /* bucket count */); | |
430 has_reported_error_ = true; | |
431 } | |
432 | |
392 int32_t H264EncoderImpl::SetChannelParameters( | 433 int32_t H264EncoderImpl::SetChannelParameters( |
393 uint32_t packet_loss, int64_t rtt) { | 434 uint32_t packet_loss, int64_t rtt) { |
394 return WEBRTC_VIDEO_CODEC_OK; | 435 return WEBRTC_VIDEO_CODEC_OK; |
395 } | 436 } |
396 | 437 |
397 int32_t H264EncoderImpl::SetPeriodicKeyFrames(bool enable) { | 438 int32_t H264EncoderImpl::SetPeriodicKeyFrames(bool enable) { |
398 return WEBRTC_VIDEO_CODEC_OK; | 439 return WEBRTC_VIDEO_CODEC_OK; |
399 } | 440 } |
400 | 441 |
401 void H264EncoderImpl::OnDroppedFrame() { | 442 void H264EncoderImpl::OnDroppedFrame() { |
402 } | 443 } |
403 | 444 |
404 } // namespace webrtc | 445 } // namespace webrtc |
OLD | NEW |