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

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

Powered by Google App Engine
This is Rietveld 408576698