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

Side by Side Diff: webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.cc

Issue 1306813009: H.264 video codec support using OpenH264/FFmpeg (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Rebase with master Created 4 years, 11 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
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 *
10 */
11
12 #include "webrtc/modules/video_coding/codecs/h264/h264_encoder_impl.h"
13
14 #include <limits>
15
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"
18 #include "third_party/openh264/src/codec/api/svc/codec_def.h"
19
20 #include "webrtc/base/checks.h"
21 #include "webrtc/base/logging.h"
22 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
23
24 namespace webrtc {
25
26 namespace {
27
28 const bool kOpenH264EncoderDetailedLogging = false;
29
30 int NumberOfThreads(int width, int height, int number_of_cores) {
31 if (width * height >= 1920 * 1080 && number_of_cores > 8) {
32 return 8; // 8 threads for 1080p on high perf machines.
33 } else if (width * height > 1280 * 960 && number_of_cores >= 6) {
34 return 3; // 3 threads for 1080p.
35 } else if (width * height > 640 * 480 && number_of_cores >= 3) {
36 return 2; // 2 threads for qHD/HD.
37 } else {
38 return 1; // 1 thread for VGA or less.
39 }
40 }
41
42 } // namespace
43
44 static FrameType EVideoFrameType_to_FrameType(EVideoFrameType type) {
45 switch (type) {
46 case videoFrameTypeInvalid:
47 return kEmptyFrame;
48 case videoFrameTypeIDR:
49 return kVideoFrameKey;
50 case videoFrameTypeSkip:
51 case videoFrameTypeI:
52 case videoFrameTypeP:
53 case videoFrameTypeIPMixed:
54 return kVideoFrameDelta;
55 default:
56 LOG(LS_WARNING) << "Unknown EVideoFrameType: " << type;
57 return kVideoFrameDelta;
58 }
59 }
60
61 // Helper method used by H264EncoderImpl::Encode.
62 // Copies the encoded bytes from |info| to |encoded_image| and updates the
63 // fragmentation information of |frag_header|. The |encoded_image->_buffer| may
64 // be deleted and reallocated if a bigger buffer is required.
65 //
66 // After OpenH264 encoding, the encoded bytes are stored in |info| spread out
67 // over a number of layers and "NAL units". Each NAL unit is a fragment starting
68 // with the four-byte start code {0,0,0,1}. All of this data (including the
69 // start codes) is copied to the |encoded_image->_buffer| and the |frag_header|
70 // is updated to point to each fragment, with offsets and lengths set as to
71 // exclude the start codes.
72 static void RtpFragmentize(EncodedImage* encoded_image,
73 rtc::scoped_ptr<uint8_t[]>* encoded_image_buffer,
74 const VideoFrame& frame,
75 SFrameBSInfo* info,
76 RTPFragmentationHeader* frag_header) {
77 // Calculate minimum buffer size required to hold encoded data.
78 size_t required_size = 0;
79 size_t fragments_count = 0;
80 for (int layer = 0; layer < info->iLayerNum; ++layer) {
81 const SLayerBSInfo& layerInfo = info->sLayerInfo[layer];
82 for (int nal = 0; nal < layerInfo.iNalCount; ++nal) {
83 RTC_CHECK_GE(layerInfo.pNalLengthInByte[nal], 0);
84 // Ensure |required_size| will not overflow.
85 RTC_CHECK_LE(static_cast<size_t>(layerInfo.pNalLengthInByte[nal]),
86 std::numeric_limits<size_t>::max() - required_size);
87 required_size += layerInfo.pNalLengthInByte[nal];
88 ++fragments_count;
89 }
90 }
91 if (encoded_image->_size < required_size) {
92 // Increase buffer size. Allocate enough to hold an unencoded image, this
93 // should be more than enough to hold any encoded data of future frames of
94 // the same size (avoiding possible future reallocation due to variations in
95 // required size).
96 encoded_image->_size = CalcBufferSize(kI420, frame.width(), frame.height());
97 if (encoded_image->_size < required_size) {
98 // Encoded data > unencoded data. Allocate required bytes.
99 LOG(LS_WARNING) << "Encoding produced more bytes than the original image "
100 << "data! Original bytes: " << encoded_image->_size
101 << ", encoded bytes: " << required_size << ".";
102 encoded_image->_size = required_size;
103 }
104 encoded_image->_buffer = new uint8_t[encoded_image->_size];
105 encoded_image_buffer->reset(encoded_image->_buffer);
106 }
107
108 // Iterate layers and NAL units, note each NAL unit as a fragment and copy
109 // the data to |encoded_image->_buffer|.
110 const uint8_t start_code[4] = {0, 0, 0, 1};
111 frag_header->VerifyAndAllocateFragmentationHeader(fragments_count);
112 size_t frag = 0;
113 encoded_image->_length = 0;
114 for (int layer = 0; layer < info->iLayerNum; ++layer) {
115 const SLayerBSInfo& layerInfo = info->sLayerInfo[layer];
116 // Iterate NAL units making up this layer, noting fragments.
117 size_t layer_len = 0;
118 for (int nal = 0; nal < layerInfo.iNalCount; ++nal, ++frag) {
palmer 2016/01/20 19:17:47 If I am reading this code right, and I am not sure
hbos_chromium 2016/01/21 22:31:49 Each layer contains a number of fragments. |fragme
119 RTC_DCHECK_EQ(layerInfo.pBsBuf[layer_len+0], start_code[0]);
120 RTC_DCHECK_EQ(layerInfo.pBsBuf[layer_len+1], start_code[1]);
121 RTC_DCHECK_EQ(layerInfo.pBsBuf[layer_len+2], start_code[2]);
122 RTC_DCHECK_EQ(layerInfo.pBsBuf[layer_len+3], start_code[3]);
123 frag_header->fragmentationOffset[frag] =
palmer 2016/01/20 19:17:47 Can you add a comment explaining why the arithmeti
hbos_chromium 2016/01/21 22:31:49 Done. Also since I DCHECK that it starts with a st
124 encoded_image->_length + layer_len + sizeof(start_code);
125 frag_header->fragmentationLength[frag] =
126 layerInfo.pNalLengthInByte[nal] - sizeof(start_code);
127 layer_len += layerInfo.pNalLengthInByte[nal];
128 }
129 // Copy the entire layer's data (including start codes).
130 memcpy(encoded_image->_buffer + encoded_image->_length,
131 layerInfo.pBsBuf,
132 layer_len);
133 encoded_image->_length += layer_len;
134 }
135 }
136
137 H264EncoderImpl::H264EncoderImpl()
138 : openh264_encoder_(nullptr),
139 encoded_image_callback_(nullptr) {
140 }
141
142 H264EncoderImpl::~H264EncoderImpl() {
143 Release();
144 }
145
146 int32_t H264EncoderImpl::InitEncode(const VideoCodec* codec_settings,
147 int32_t number_of_cores,
148 size_t /*max_payload_size*/) {
149 if (!codec_settings ||
150 codec_settings->codecType != kVideoCodecH264) {
151 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
152 }
153 if (codec_settings->maxFramerate == 0)
154 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
155 if (codec_settings->width < 1 || codec_settings->height < 1)
156 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
157
158 int32_t release_ret = Release();
159 if (release_ret != WEBRTC_VIDEO_CODEC_OK)
160 return release_ret;
161 RTC_DCHECK(!openh264_encoder_);
162
163 // Create encoder.
164 if (WelsCreateSVCEncoder(&openh264_encoder_) != 0) {
165 // Failed to create encoder.
166 LOG(LS_ERROR) << "Failed to create OpenH264 encoder";
167 RTC_DCHECK(!openh264_encoder_);
168 return WEBRTC_VIDEO_CODEC_ERROR;
169 }
170 RTC_DCHECK(openh264_encoder_);
171 if (kOpenH264EncoderDetailedLogging) {
172 int trace_level = WELS_LOG_DETAIL;
173 openh264_encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL,
174 &trace_level);
175 }
176 // else WELS_LOG_DEFAULT is used by default.
177
178 codec_settings_ = *codec_settings;
179 if (codec_settings_.targetBitrate == 0)
180 codec_settings_.targetBitrate = codec_settings_.startBitrate;
181
182 // Initialization parameters.
183 // There are two ways to initialize. There is SEncParamBase (cleared with
184 // memset(&p, 0, sizeof(SEncParamBase)) used in Initialize, and SEncParamExt
185 // which is a superset of SEncParamBase (cleared with GetDefaultParams) used
186 // in InitializeExt.
187 SEncParamExt init_params;
188 openh264_encoder_->GetDefaultParams(&init_params);
189 if (codec_settings_.mode == kRealtimeVideo) {
190 init_params.iUsageType = CAMERA_VIDEO_REAL_TIME;
191 } else if (codec_settings_.mode == kScreensharing) {
192 init_params.iUsageType = SCREEN_CONTENT_REAL_TIME;
193 } else {
194 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
195 }
196 init_params.iPicWidth = codec_settings_.width;
197 init_params.iPicHeight = codec_settings_.height;
198 // |init_params| uses bit/s, |codec_settings_| uses kbit/s.
199 init_params.iTargetBitrate = codec_settings_.targetBitrate * 1000;
200 init_params.iMaxBitrate = codec_settings_.maxBitrate * 1000;
201 // Rate Control mode
202 init_params.iRCMode = RC_BITRATE_MODE;
203 init_params.fMaxFrameRate = static_cast<float>(codec_settings_.maxFramerate);
204
205 // The following parameters are extension parameters (they're in SEncParamExt,
206 // not in SEncParamBase).
207 init_params.bEnableFrameSkip =
208 codec_settings_.codecSpecific.H264.frameDroppingOn;
209 // |uiIntraPeriod| - multiple of GOP size
210 // |keyFrameInterval| - number of frames
211 init_params.uiIntraPeriod =
212 codec_settings_.codecSpecific.H264.keyFrameInterval;
213 init_params.uiMaxNalSize = 0;
214 // Threading model: use auto.
215 // 0: auto (dynamic imp. internal encoder)
216 // 1: single thread (default value)
217 // >1: number of threads
218 init_params.iMultipleThreadIdc = NumberOfThreads(init_params.iPicWidth,
219 init_params.iPicHeight,
220 number_of_cores);
221 // The base spatial layer 0 is the only one we use.
222 init_params.sSpatialLayers[0].iVideoWidth = init_params.iPicWidth;
223 init_params.sSpatialLayers[0].iVideoHeight = init_params.iPicHeight;
224 init_params.sSpatialLayers[0].fFrameRate = init_params.fMaxFrameRate;
225 init_params.sSpatialLayers[0].iSpatialBitrate = init_params.iTargetBitrate;
226 init_params.sSpatialLayers[0].iMaxSpatialBitrate = init_params.iMaxBitrate;
227 // Slice num according to number of threads.
228 init_params.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_AUTO_SLICE;
229
230 // Initialize.
231 if (openh264_encoder_->InitializeExt(&init_params) != 0) {
232 LOG(LS_ERROR) << "Failed to initialize OpenH264 encoder";
233 Release();
234 return WEBRTC_VIDEO_CODEC_ERROR;
235 }
236 int video_format = EVideoFormatType::videoFormatI420;
237 openh264_encoder_->SetOption(ENCODER_OPTION_DATAFORMAT,
238 &video_format);
239
240 // Initialize encoded image. Default buffer size: size of unencoded data.
241 encoded_image_._size = CalcBufferSize(
242 kI420, codec_settings_.width, codec_settings_.height);
243 encoded_image_._buffer = new uint8_t[encoded_image_._size];
244 encoded_image_buffer_.reset(encoded_image_._buffer);
245 encoded_image_._completeFrame = true;
246 encoded_image_._encodedWidth = 0;
247 encoded_image_._encodedHeight = 0;
248 encoded_image_._length = 0;
249 return WEBRTC_VIDEO_CODEC_OK;
250 }
251
252 int32_t H264EncoderImpl::Release() {
253 if (openh264_encoder_) {
254 int uninit_ret = openh264_encoder_->Uninitialize();
255 if (uninit_ret != 0) {
256 LOG(LS_WARNING) << "OpenH264 encoder's Uninitialize() returned "
257 << "unsuccessful: " << uninit_ret;
258 }
259 WelsDestroySVCEncoder(openh264_encoder_);
260 openh264_encoder_ = nullptr;
261 }
262 if (encoded_image_._buffer != nullptr) {
263 encoded_image_._buffer = nullptr;
264 encoded_image_buffer_.reset();
265 }
266 return WEBRTC_VIDEO_CODEC_OK;
267 }
268
269 int32_t H264EncoderImpl::RegisterEncodeCompleteCallback(
270 EncodedImageCallback* callback) {
271 encoded_image_callback_ = callback;
272 return WEBRTC_VIDEO_CODEC_OK;
273 }
274
275 int32_t H264EncoderImpl::SetRates(uint32_t bitrate, uint32_t framerate) {
276 if (bitrate <= 0 || framerate <= 0) {
277 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
278 }
279 codec_settings_.targetBitrate = bitrate;
280 codec_settings_.maxFramerate = framerate;
281
282 SBitrateInfo target_bitrate;
283 memset(&target_bitrate, 0, sizeof(SBitrateInfo));
284 target_bitrate.iLayer = SPATIAL_LAYER_ALL,
285 target_bitrate.iBitrate = codec_settings_.targetBitrate * 1000;
286 openh264_encoder_->SetOption(ENCODER_OPTION_BITRATE,
287 &target_bitrate);
288 float max_framerate = static_cast<float>(codec_settings_.maxFramerate);
289 openh264_encoder_->SetOption(ENCODER_OPTION_FRAME_RATE,
290 &max_framerate);
291 return WEBRTC_VIDEO_CODEC_OK;
292 }
293
294 int32_t H264EncoderImpl::Encode(
295 const VideoFrame& frame, const CodecSpecificInfo* codec_specific_info,
296 const std::vector<FrameType>* frame_types) {
297 if (!IsInitialized())
298 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
299 if (frame.IsZeroSize())
300 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
301 if (!encoded_image_callback_) {
302 LOG(LS_WARNING) << "InitEncode() has been called, but a callback function "
303 << "has not been set with RegisterEncodeCompleteCallback()";
304 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
305 }
306 if (frame.width() != codec_settings_.width ||
307 frame.height() != codec_settings_.height) {
308 LOG(LS_WARNING) << "Encoder initialized for " << codec_settings_.width
309 << "x" << codec_settings_.height << " but trying to encode "
310 << frame.width() << "x" << frame.height() << " frame.";
311 return WEBRTC_VIDEO_CODEC_ERR_SIZE;
312 }
313
314 bool force_key_frame = false;
315 if (frame_types != nullptr) {
316 // We only support a single stream.
317 RTC_DCHECK_EQ(frame_types->size(), static_cast<size_t>(1));
318 // Skip frame?
319 if ((*frame_types)[0] == kEmptyFrame) {
320 return WEBRTC_VIDEO_CODEC_OK;
321 }
322 // Force key frame?
323 force_key_frame = (*frame_types)[0] == kVideoFrameKey;
324 }
325 if (force_key_frame) {
326 // Only need to call ForceIntraFrame when true. API doc says
327 // ForceIntraFrame(false) does nothing but really if you call it for every
328 // frame it introduces massive delays and lag in the video stream.
329 openh264_encoder_->ForceIntraFrame(true);
330 }
331
332 // EncodeFrame input.
333 SSourcePicture picture;
334 memset(&picture, 0, sizeof(SSourcePicture));
335 picture.iPicWidth = frame.width();
336 picture.iPicHeight = frame.height();
337 picture.iColorFormat = EVideoFormatType::videoFormatI420;
338 picture.uiTimeStamp = frame.ntp_time_ms();
339 picture.iStride[0] = frame.stride(kYPlane);
340 picture.iStride[1] = frame.stride(kUPlane);
341 picture.iStride[2] = frame.stride(kVPlane);
342 picture.pData[0] = const_cast<uint8_t*>(frame.buffer(kYPlane));
343 picture.pData[1] = const_cast<uint8_t*>(frame.buffer(kUPlane));
344 picture.pData[2] = const_cast<uint8_t*>(frame.buffer(kVPlane));
345
346 // EncodeFrame output.
347 SFrameBSInfo info;
348 memset(&info, 0, sizeof(SFrameBSInfo));
349
350 // Encode!
351 int enc_ret = openh264_encoder_->EncodeFrame(&picture, &info);
352 if (enc_ret != 0) {
353 LOG(LS_ERROR) << "OpenH264 frame encoding failed, EncodeFrame returned "
354 << enc_ret << ".";
355 return WEBRTC_VIDEO_CODEC_ERROR;
356 }
357
358 encoded_image_._encodedWidth = frame.width();
359 encoded_image_._encodedHeight = frame.height();
360 encoded_image_._timeStamp = frame.timestamp();
361 encoded_image_.ntp_time_ms_ = frame.ntp_time_ms();
362 encoded_image_.capture_time_ms_ = frame.render_time_ms();
363 encoded_image_._frameType = EVideoFrameType_to_FrameType(info.eFrameType);
364
365 // Split encoded image up into fragments. This also updates |encoded_image_|.
366 RTPFragmentationHeader frag_header;
367 RtpFragmentize(&encoded_image_, &encoded_image_buffer_, frame, &info,
368 &frag_header);
369
370 // Encoder can skip frames to save bandwidth in which case
371 // |encoded_image_._length| == 0.
372 if (encoded_image_._length > 0) {
373 // Deliver encoded image.
374 encoded_image_callback_->Encoded(encoded_image_, codec_specific_info,
375 &frag_header);
376 }
377 return WEBRTC_VIDEO_CODEC_OK;
378 }
379
380 bool H264EncoderImpl::IsInitialized() const {
381 return openh264_encoder_ != nullptr;
382 }
383
384 int32_t H264EncoderImpl::SetChannelParameters(
385 uint32_t packet_loss, int64_t rtt) {
386 return WEBRTC_VIDEO_CODEC_OK;
387 }
388
389 int32_t H264EncoderImpl::SetPeriodicKeyFrames(bool enable) {
390 return WEBRTC_VIDEO_CODEC_OK;
391 }
392
393 void H264EncoderImpl::OnDroppedFrame() {
394 }
395
396 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698