OLD | NEW |
---|---|
(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 // OpenH264 | |
15 #include "codec_api.h" | |
16 #include "codec_app_def.h" | |
17 #include "codec_def.h" | |
18 | |
19 #include "webrtc/base/checks.h" | |
20 #include "webrtc/base/logging.h" | |
21 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" | |
22 | |
23 namespace webrtc { | |
24 | |
25 namespace { | |
26 const bool OPENH264_ENCODER_LOGGING = false; | |
27 } // anonymous namespace | |
28 | |
29 static VideoFrameType EVideoFrameType_to_VideoFrameType( | |
30 const EVideoFrameType& type) { | |
stefan-webrtc
2015/09/28 11:19:02
You should be able to pass this by value since it'
hbos
2015/09/30 15:35:18
Done.
| |
31 switch (type) { | |
32 case videoFrameTypeInvalid: | |
33 case videoFrameTypeSkip: | |
34 return kSkipFrame; | |
stefan-webrtc
2015/09/28 11:19:02
I think we can send videoFrameTypeSkip as a kDelta
hbos
2015/09/30 15:35:18
I don't think the upper layers are notified at all
stefan-webrtc
2015/10/01 08:19:30
Well, kSkipFrame and an h.264 skip-encoded frame i
hbos
2015/10/01 12:19:45
Acknowledged.
| |
35 case videoFrameTypeIDR: | |
noahric
2015/09/25 00:11:31
IDR == kKeyFrame, right?
Also SPS/PPS are treated
stefan-webrtc
2015/09/28 11:19:02
Agree. videoFrameTypeI should however probably be
hbos
2015/09/28 11:33:20
An I-frame is a key frame, and all IDR-frames are
hbos
2015/09/30 15:35:18
@stefan: Ok, kDeltaFrame for videoFrameTypeI it is
| |
36 case videoFrameTypeI: | |
37 case videoFrameTypeP: | |
38 case videoFrameTypeIPMixed: | |
39 return kDeltaFrame; | |
40 default: | |
41 LOG(LS_WARNING) << "Unknown EVideoFrameType: " << type; | |
42 return kDeltaFrame; | |
43 } | |
44 } | |
45 | |
46 // Copies the encoded bytes from |info| to |encoded_image| and updates the | |
47 // fragmentation information of |frag_header|. | |
48 // After OpenH264 encoding, the encoded bytes are stored in |info| spread out | |
49 // over a number of layers and "NAL units". Each NAL unit is a fragment starting | |
50 // with the four-byte NAL header {0,0,0,1}. To save bytes, the NAL headers are | |
noahric
2015/09/25 00:11:31
It's up to you, but it's probably not worth stripp
noahric
2015/09/25 00:11:31
For clarity, I would avoid calling those NAL heade
hbos
2015/09/28 11:33:20
First comment:
There are assumptions about the in
stefan-webrtc
2015/09/28 11:53:55
The RTP format for H264 specifies that start codes
hbos
2015/09/30 15:35:18
Oh I see, I misunderstood. I didn't considered not
| |
51 // excluded when copying to |encoded_image->_buffer|. However these headers must | |
52 // be included for an H264Decoder to be able to decode the data. | |
53 // When fragments are sent over a network, the receiving end must re-insert | |
54 // the NAL headers before each fragment. | |
55 // For image data encoded and decoded locally, RTPDefragmentize can be used | |
56 // to convert (EncodedImage, RTPFragmentationHeader) with NAL headers excluded | |
57 // to a decodable EncodedImage buffer with NAL headers included. | |
58 static void RTPFragmentize(EncodedImage* encoded_image, | |
59 const VideoFrame& frame, | |
60 SFrameBSInfo* info, | |
61 RTPFragmentationHeader* frag_header) { | |
62 // Calculate minimum buffer size required to hold encoded data. | |
63 size_t required_size = 0; | |
64 for (int iLayer = 0; iLayer < info->iLayerNum; ++iLayer) { | |
65 const SLayerBSInfo& layerInfo = info->sLayerInfo[iLayer]; | |
66 for (int iNal = 0; iNal < layerInfo.iNalCount; ++iNal) { | |
67 required_size += (layerInfo.pNalLengthInByte[iNal] - 4); | |
68 } | |
69 } | |
70 if (encoded_image->_size < required_size) { | |
71 // Increase buffer size. Allocate enough to hold an unencoded image, this | |
72 // should be more than enough to hold any encoded data of future frames of | |
73 // the same size (avoiding possible future reallocation due to variations in | |
74 // required size). | |
75 encoded_image->_size = CalcBufferSize( | |
76 VideoType::kI420, frame.width(), frame.height()); | |
77 if (encoded_image->_size < required_size) { | |
78 // Encoded data > unencoded data, wtf? Allocate required bytes. | |
noahric
2015/09/25 00:11:31
Consider logging this. You're right, it's pretty W
hbos
2015/09/28 11:33:20
Done.
| |
79 encoded_image->_size = required_size; | |
80 } | |
81 if (encoded_image->_buffer != nullptr) | |
82 delete[] encoded_image->_buffer; | |
noahric
2015/09/25 00:11:31
Consider storing the buffer separately and with sc
hbos
2015/09/28 11:33:20
Done.
| |
83 encoded_image->_buffer = new uint8_t[encoded_image->_size]; | |
84 } | |
85 | |
86 // Iterate layers and NAL units, copy encoded data to |encoded_image->_buffer| | |
87 // and note each NAL unit as a fragment, excluding its NAL header. | |
88 encoded_image->_length = 0; | |
89 std::vector<int> frags; | |
90 for (int iLayer = 0; iLayer < info->iLayerNum; ++iLayer) { | |
91 int iLayerLen = 0; | |
92 const SLayerBSInfo& layerInfo = info->sLayerInfo[iLayer]; | |
93 // Copy the layer data to |encoded_image->_buffer|, excluding the 4-byte | |
94 // NAL headers. | |
95 for (int iNal = 0; iNal < layerInfo.iNalCount; ++iNal) { | |
96 // Expecting NAL header constant {0,0,0,1}. | |
97 DCHECK_EQ(layerInfo.pBsBuf[iLayerLen+0], static_cast<unsigned char>(0)); | |
98 DCHECK_EQ(layerInfo.pBsBuf[iLayerLen+1], static_cast<unsigned char>(0)); | |
99 DCHECK_EQ(layerInfo.pBsBuf[iLayerLen+2], static_cast<unsigned char>(0)); | |
100 DCHECK_EQ(layerInfo.pBsBuf[iLayerLen+3], static_cast<unsigned char>(1)); | |
101 | |
102 memcpy(encoded_image->_buffer + encoded_image->_length, | |
103 layerInfo.pBsBuf + iLayerLen + 4, | |
stefan-webrtc
2015/09/28 11:19:02
Do you know if these layers are stored in one chun
hbos
2015/09/30 15:35:18
Done.
| |
104 (layerInfo.pNalLengthInByte[iNal] - 4) * sizeof(unsigned char)); | |
105 encoded_image->_length += (layerInfo.pNalLengthInByte[iNal] - 4); | |
106 frags.push_back(layerInfo.pNalLengthInByte[iNal] - 4); | |
107 | |
108 iLayerLen += layerInfo.pNalLengthInByte[iNal]; | |
109 } | |
110 } | |
111 | |
112 frag_header->VerifyAndAllocateFragmentationHeader(frags.size()); | |
113 for (size_t i = 0, off = 0; i < frags.size(); ++i) { | |
114 frag_header->fragmentationOffset[i] = off; | |
115 frag_header->fragmentationLength[i] = frags[i]; | |
116 off += frags[i]; | |
stefan-webrtc
2015/09/28 11:19:02
frags -> fragments
off -> offset
hbos
2015/09/30 15:35:18
Done.
| |
117 } | |
118 } | |
119 | |
120 void H264EncoderImpl::RTPDefragmentize( | |
stefan-webrtc
2015/09/28 11:19:02
Is this a test function? In that case I would move
hbos
2015/09/30 15:35:18
(Removed, no longer needed due to including start
| |
121 const EncodedImage& encoded_image, | |
122 const RTPFragmentationHeader* frag_header, | |
123 uint8_t* enc_buffer_with_nal, size_t enc_buffer_with_nal_length) { | |
124 DCHECK_GE(enc_buffer_with_nal_length, | |
125 RTPDefragmentizeBufferLengthWithNAL(encoded_image, frag_header)); | |
126 const unsigned char nal_header[] = { 0, 0, 0, 1 }; | |
127 for (size_t i = 0; i < frag_header->fragmentationVectorSize; ++i) { | |
128 DCHECK_LE(frag_header->fragmentationOffset[i] + | |
129 frag_header->fragmentationLength[i], | |
130 encoded_image._length); | |
131 | |
132 // Insert a NAL header constant {0,0,0,1}. | |
133 memcpy(enc_buffer_with_nal, nal_header, 4); | |
134 // Copy fragment data. | |
135 memcpy(enc_buffer_with_nal + 4, | |
136 encoded_image._buffer + frag_header->fragmentationOffset[i], | |
137 frag_header->fragmentationLength[i]); | |
138 | |
139 enc_buffer_with_nal += (4 + frag_header->fragmentationLength[i]); | |
140 } | |
141 } | |
142 | |
143 size_t H264EncoderImpl::RTPDefragmentizeBufferLengthWithNAL( | |
stefan-webrtc
2015/09/28 11:19:02
Same with this.
hbos
2015/09/30 15:35:18
(Removed, no longer needed due to including start
| |
144 const EncodedImage& encoded_image, | |
145 const webrtc::RTPFragmentationHeader* frag_header) { | |
146 return encoded_image._length + | |
147 4 * static_cast<size_t>(frag_header->fragmentationVectorSize); | |
148 } | |
149 | |
150 H264EncoderImpl::H264EncoderImpl() | |
151 : openh264_encoder_(nullptr), | |
152 encoded_image_callback_(nullptr) { | |
153 } | |
154 | |
155 H264EncoderImpl::~H264EncoderImpl() { | |
156 Release(); | |
157 } | |
158 | |
159 int32_t H264EncoderImpl::InitEncode(const VideoCodec* codec_settings, | |
160 int32_t /*number_of_cores*/, | |
161 size_t /*max_payload_size*/) { | |
162 if (!codec_settings || | |
163 codec_settings->codecType != VideoCodecType::kVideoCodecH264) { | |
164 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | |
165 } | |
166 if (codec_settings->maxFramerate == 0) | |
167 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | |
168 if (codec_settings->width < 1 || codec_settings->height < 1) | |
169 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | |
170 | |
171 int release_ret = Release(); | |
172 if (release_ret != WEBRTC_VIDEO_CODEC_OK) | |
173 return release_ret; | |
174 DCHECK(!openh264_encoder_); | |
175 | |
176 // Create encoder. | |
177 if (WelsCreateSVCEncoder(&openh264_encoder_) != 0) { | |
178 // Failed to create encoder. | |
179 LOG(LS_ERROR) << "Failed to create OpenH264 encoder"; | |
180 DCHECK(!openh264_encoder_); | |
181 return WEBRTC_VIDEO_CODEC_ERROR; | |
182 } | |
183 DCHECK(openh264_encoder_); | |
184 if (&codec_settings_ != codec_settings) | |
noahric
2015/09/25 00:11:31
This won't ever be false, will it?
hbos
2015/09/28 11:33:20
You're right. Fixed.
| |
185 codec_settings_ = *codec_settings; | |
186 | |
187 if (codec_settings_.targetBitrate == 0) | |
188 codec_settings_.targetBitrate = codec_settings_.startBitrate; | |
189 | |
190 // Note: H264 codec specifics are ignored: | |
191 // - codec_settings->codecSpecific.H264.frameDroppingOn | |
stefan-webrtc
2015/09/28 11:19:02
Have you taken a look to see if it's possible to c
hbos
2015/09/30 15:35:18
Done.
| |
192 // - codec_settings->codecSpecific.H264.keyFrameInterval | |
noahric
2015/09/25 00:11:31
Assuming it's set, you don't really want to skip i
stefan-webrtc
2015/09/28 11:19:02
Agree, we should use this.
hbos
2015/09/28 11:33:20
I don't know what to do with these. I believe the
hbos
2015/09/30 15:35:18
Using them both now to set SEncParamExt's bEnableF
| |
193 | |
194 // Initialization parameters. | |
195 // There are two ways to initialize. There is SEncParamBase (cleared with | |
196 // memset(&p, 0, sizeof(SEncParamBase)) used in Initialize, and SEncParamExt | |
197 // which is a superset of SEncParamBase (cleared with GetDefaultParams) used | |
198 // in InitializeExt. We use SEncParamBase/Initialize. | |
stefan-webrtc
2015/09/28 11:19:02
Is there a reason why we use those?
I think we sh
hbos
2015/09/30 15:35:18
Using SEncParamExt now. iMultileThreadIdc set to a
| |
199 SEncParamBase init_params; | |
200 memset(&init_params, 0, sizeof(SEncParamBase)); | |
201 if (codec_settings_.mode == kRealtimeVideo) { | |
202 init_params.iUsageType = CAMERA_VIDEO_REAL_TIME; | |
203 } else if (codec_settings_.mode == kScreensharing) { | |
204 init_params.iUsageType = SCREEN_CONTENT_REAL_TIME; | |
205 } else { | |
206 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | |
207 } | |
208 init_params.iPicWidth = codec_settings_.width; | |
209 init_params.iPicHeight = codec_settings_.height; | |
210 // iTargetBitrate is in bit/s, targetBitrate is in kbit/s. | |
211 init_params.iTargetBitrate = codec_settings_.targetBitrate * 1000; | |
212 // Rate Control mode | |
213 init_params.iRCMode = RC_QUALITY_MODE; | |
stefan-webrtc
2015/09/28 11:19:02
I suspect a better choice would be RC_BITRATE_MODE
hbos
2015/09/30 15:35:18
I've noticed that you get really laggy video if th
stefan-webrtc
2015/10/01 08:19:30
I think RC_BITRATE_MODE makes more sense actually.
hbos
2015/10/01 12:19:45
Acknowledged. Removed TODO comment.
| |
214 init_params.fMaxFrameRate = static_cast<float>(codec_settings_.maxFramerate); | |
noahric
2015/09/25 00:11:31
Not sure how to set these, but there are few other
stefan-webrtc
2015/09/28 11:19:02
CABAC must be false since we only support baseline
hbos
2015/09/28 11:33:20
Those parameters are available if SEncParamExt is
stefan-webrtc
2015/09/28 11:53:55
I think automatic is better. There is no reason th
hbos
2015/09/30 15:35:18
Using keyFrameInterval to set uiIntraPeriod.
| |
215 | |
216 // Initialize. | |
217 if (openh264_encoder_->Initialize(&init_params) != 0) { | |
218 // Failed to initialize. | |
stefan-webrtc
2015/09/28 11:19:02
No need for this comment.
hbos
2015/09/30 15:35:18
Done.
| |
219 LOG(LS_ERROR) << "Failed to initialize OpenH264 encoder"; | |
220 Release(); | |
221 return WEBRTC_VIDEO_CODEC_ERROR; | |
222 } | |
223 if (OPENH264_ENCODER_LOGGING) { | |
224 int trace_level = WELS_LOG_DETAIL; | |
225 openh264_encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL, | |
226 &trace_level); | |
noahric
2015/09/25 00:11:31
What's the default? If nothing, then consider sett
hbos
2015/09/28 11:33:20
It has a default value it uses, WELS_LOG_DEFAULT (
| |
227 } | |
228 | |
229 // Initialize encoded image. | |
230 // Default buffer size: size of unencoded data (should be large enough). | |
231 encoded_image_._size = CalcBufferSize( | |
232 VideoType::kI420, codec_settings_.width, codec_settings_.height); | |
233 encoded_image_._buffer = new uint8_t[encoded_image_._size]; | |
234 encoded_image_._completeFrame = true; | |
235 encoded_image_._encodedWidth = 0; | |
236 encoded_image_._encodedHeight = 0; | |
237 encoded_image_._length = 0; | |
238 return WEBRTC_VIDEO_CODEC_OK; | |
239 } | |
240 | |
241 int32_t H264EncoderImpl::Release() { | |
242 if (openh264_encoder_) { | |
243 int uninit_ret = openh264_encoder_->Uninitialize(); | |
244 if (uninit_ret != 0) { | |
245 LOG(LS_WARNING) << "OpenH264 encoder's Uninitialize() returned " | |
246 << "unsuccessful: " << uninit_ret; | |
247 } | |
248 WelsDestroySVCEncoder(openh264_encoder_); | |
249 openh264_encoder_ = nullptr; | |
250 } | |
251 if (encoded_image_._buffer != nullptr) { | |
252 delete[] encoded_image_._buffer; | |
253 encoded_image_._buffer = nullptr; | |
254 } | |
255 return WEBRTC_VIDEO_CODEC_OK; | |
256 } | |
257 | |
258 int32_t H264EncoderImpl::RegisterEncodeCompleteCallback( | |
259 EncodedImageCallback* callback) { | |
260 encoded_image_callback_ = callback; | |
261 return WEBRTC_VIDEO_CODEC_OK; | |
262 } | |
263 | |
264 int32_t H264EncoderImpl::SetRates(uint32_t bitrate, uint32_t framerate) { | |
265 if (bitrate <= 0 || framerate <= 0) { | |
266 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | |
267 } | |
268 codec_settings_.targetBitrate = bitrate; | |
269 codec_settings_.maxFramerate = framerate; | |
270 return WEBRTC_VIDEO_CODEC_OK; | |
271 } | |
272 | |
273 int32_t H264EncoderImpl::Encode( | |
274 const VideoFrame& frame, const CodecSpecificInfo* codec_specific_info, | |
275 const std::vector<VideoFrameType>* frame_types) { | |
276 if (!IsInitialized()) | |
277 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
278 if (frame.IsZeroSize()) | |
279 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | |
280 if (!encoded_image_callback_) { | |
281 LOG(LS_WARNING) << "InitEncode() has been called, but a callback function " | |
282 << "has not been set with RegisterEncodeCompleteCallback()"; | |
283 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
284 } | |
285 | |
286 // Make |codec_settings_|'s size reflect the latest frame's size. | |
287 if (codec_settings_.width != frame.width() || | |
noahric
2015/09/25 00:11:31
That won't be right if simulcast is used, will it?
hbos
2015/09/28 11:33:20
Sorry, I'm not sure I follow your simulcast commen
| |
288 codec_settings_.height != frame.height()) { | |
289 codec_settings_.width = frame.width(); | |
290 codec_settings_.height = frame.height(); | |
291 } | |
292 | |
293 // Set encoder options. | |
294 int video_format = EVideoFormatType::videoFormatI420; | |
295 openh264_encoder_->SetOption(ENCODER_OPTION_DATAFORMAT, | |
296 &video_format); | |
297 SBitrateInfo target_bitrate; | |
298 memset(&target_bitrate, 0, sizeof(SBitrateInfo)); | |
299 target_bitrate.iLayer = SPATIAL_LAYER_ALL, | |
300 target_bitrate.iBitrate = codec_settings_.targetBitrate * 1000; | |
301 openh264_encoder_->SetOption(ENCODER_OPTION_BITRATE, | |
302 &target_bitrate); | |
noahric
2015/09/25 00:11:31
Why not just set these in SetRates?
hbos
2015/09/28 11:33:20
Done.
| |
303 float max_framerate = static_cast<float>(codec_settings_.maxFramerate); | |
304 openh264_encoder_->SetOption(ENCODER_OPTION_FRAME_RATE, | |
305 &max_framerate); | |
306 | |
307 // EncodeFrame input. | |
308 SSourcePicture picture; | |
309 memset(&picture, 0, sizeof(SSourcePicture)); | |
310 picture.iPicWidth = frame.width(); | |
311 picture.iPicHeight = frame.height(); | |
312 picture.iColorFormat = video_format; | |
313 picture.uiTimeStamp = frame.timestamp(); | |
314 picture.iStride[0] = frame.stride(kYPlane); | |
315 picture.iStride[1] = frame.stride(kUPlane); | |
316 picture.iStride[2] = frame.stride(kVPlane); | |
317 picture.pData[0] = const_cast<uint8_t*>(frame.buffer(kYPlane)); | |
318 picture.pData[1] = const_cast<uint8_t*>(frame.buffer(kUPlane)); | |
319 picture.pData[2] = const_cast<uint8_t*>(frame.buffer(kVPlane)); | |
320 | |
321 // EncodeFrame output. | |
322 SFrameBSInfo info; | |
323 memset(&info, 0, sizeof(SFrameBSInfo)); | |
324 | |
325 // Encode! | |
326 if (openh264_encoder_->EncodeFrame(&picture, &info) != 0) { | |
noahric
2015/09/25 00:11:30
It's probably worth capturing the return and loggi
hbos
2015/09/28 11:33:20
Done.
| |
327 LOG(LS_ERROR) << "OpenH264 frame encoding failed (EncodeFrame)"; | |
328 return WEBRTC_VIDEO_CODEC_ERROR; | |
329 } | |
330 | |
331 encoded_image_._encodedWidth = frame.width(); | |
332 encoded_image_._encodedHeight = frame.height(); | |
333 encoded_image_._timeStamp = frame.timestamp(); | |
334 encoded_image_.capture_time_ms_ = frame.render_time_ms(); | |
335 encoded_image_._frameType = EVideoFrameType_to_VideoFrameType( | |
336 info.eFrameType); | |
337 | |
338 // Split encoded image up into fragments. This also updates |encoded_image_|. | |
339 RTPFragmentationHeader frag_header; | |
340 RTPFragmentize(&encoded_image_, frame, &info, &frag_header); | |
341 | |
342 // Encoder can skip frames to save bandwidth in which case | |
343 // |encoded_image_._length| == 0. | |
344 if (encoded_image_._length > 0) { | |
345 // Deliver encoded image. | |
346 encoded_image_callback_->Encoded(encoded_image_, codec_specific_info, | |
347 &frag_header); | |
348 } | |
349 return WEBRTC_VIDEO_CODEC_OK; | |
350 } | |
351 | |
352 bool H264EncoderImpl::IsInitialized() { | |
353 return openh264_encoder_ != nullptr; | |
354 } | |
355 | |
356 int32_t H264EncoderImpl::SetChannelParameters( | |
357 uint32_t packet_loss, int64_t rtt) { | |
358 return WEBRTC_VIDEO_CODEC_OK; | |
359 } | |
360 | |
361 int32_t H264EncoderImpl::SetPeriodicKeyFrames(bool enable) { | |
362 return WEBRTC_VIDEO_CODEC_OK; | |
363 } | |
364 | |
365 int32_t H264EncoderImpl::CodecConfigParameters(uint8_t* buffer, int32_t size) { | |
366 return WEBRTC_VIDEO_CODEC_OK; | |
367 } | |
368 | |
369 void H264EncoderImpl::OnDroppedFrame() { | |
370 } | |
371 | |
372 } // namespace webrtc | |
OLD | NEW |