2 * RTP parser for HEVC/H.265 payload format (draft version 6)
3 * Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.com>
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "libavutil/avstring.h"
24 #include "libavutil/base64.h"
29 #define RTP_HEVC_PAYLOAD_HEADER_SIZE 2
30 #define RTP_HEVC_FU_HEADER_SIZE 1
31 #define RTP_HEVC_DONL_FIELD_SIZE 2
32 #define HEVC_SPECIFIED_NAL_UNIT_TYPES 48
34 /* SDP out-of-band signaling data */
35 struct PayloadContext
{
38 uint8_t *sps
, *pps
, *vps
, *sei
;
39 int sps_size
, pps_size
, vps_size
, sei_size
;
42 static const uint8_t start_sequence
[] = { 0x00, 0x00, 0x00, 0x01 };
44 static av_cold PayloadContext
*hevc_new_context(void)
46 return av_mallocz(sizeof(PayloadContext
));
49 static av_cold
void hevc_free_context(PayloadContext
*data
)
54 static av_cold
int hevc_init(AVFormatContext
*ctx
, int st_index
,
57 av_dlog(ctx
, "hevc_init() for stream %d\n", st_index
);
62 ctx
->streams
[st_index
]->need_parsing
= AVSTREAM_PARSE_FULL
;
67 static av_cold
int hevc_sdp_parse_fmtp_config(AVFormatContext
*s
,
69 PayloadContext
*hevc_data
,
70 char *attr
, char *value
)
72 /* profile-space: 0-3 */
73 /* profile-id: 0-31 */
74 if (!strcmp(attr
, "profile-id")) {
75 hevc_data
->profile_id
= atoi(value
);
76 av_dlog(s
, "SDP: found profile-id: %d\n", hevc_data
->profile_id
);
81 /* interop-constraints: [base16] */
82 /* profile-compatibility-indicator: [base16] */
83 /* sprop-sub-layer-id: 0-6, defines highest possible value for TID, default: 6 */
84 /* recv-sub-layer-id: 0-6 */
85 /* max-recv-level-id: 0-255 */
86 /* tx-mode: MSM,SSM */
87 /* sprop-vps: [base64] */
88 /* sprop-sps: [base64] */
89 /* sprop-pps: [base64] */
90 /* sprop-sei: [base64] */
91 if (!strcmp(attr
, "sprop-vps") || !strcmp(attr
, "sprop-sps") ||
92 !strcmp(attr
, "sprop-pps") || !strcmp(attr
, "sprop-sei")) {
95 if (!strcmp(attr
, "sprop-vps")) {
96 data_ptr
= &hevc_data
->vps
;
97 size_ptr
= &hevc_data
->vps_size
;
98 } else if (!strcmp(attr
, "sprop-sps")) {
99 data_ptr
= &hevc_data
->sps
;
100 size_ptr
= &hevc_data
->sps_size
;
101 } else if (!strcmp(attr
, "sprop-pps")) {
102 data_ptr
= &hevc_data
->pps
;
103 size_ptr
= &hevc_data
->pps_size
;
104 } else if (!strcmp(attr
, "sprop-sei")) {
105 data_ptr
= &hevc_data
->sei
;
106 size_ptr
= &hevc_data
->sei_size
;
110 char base64packet
[1024];
111 uint8_t decoded_packet
[1024];
112 int decoded_packet_size
;
113 char *dst
= base64packet
;
115 while (*value
&& *value
!= ',' &&
116 (dst
- base64packet
) < sizeof(base64packet
) - 1) {
124 decoded_packet_size
= av_base64_decode(decoded_packet
, base64packet
,
125 sizeof(decoded_packet
));
126 if (decoded_packet_size
> 0) {
127 uint8_t *tmp
= av_realloc(*data_ptr
, decoded_packet_size
+
128 sizeof(start_sequence
) + *size_ptr
);
130 av_log(s
, AV_LOG_ERROR
,
131 "Unable to allocate memory for extradata!\n");
132 return AVERROR(ENOMEM
);
136 memcpy(*data_ptr
+ *size_ptr
, start_sequence
,
137 sizeof(start_sequence
));
138 memcpy(*data_ptr
+ *size_ptr
+ sizeof(start_sequence
),
139 decoded_packet
, decoded_packet_size
);
141 *size_ptr
+= sizeof(start_sequence
) + decoded_packet_size
;
146 /* max-lsr, max-lps, max-cpb, max-dpb, max-br, max-tr, max-tc */
149 /* sprop-max-don-diff: 0-32767
151 When the RTP stream depends on one or more other RTP
152 streams (in this case tx-mode MUST be equal to "MSM" and
153 MSM is in use), this parameter MUST be present and the
154 value MUST be greater than 0.
156 if (!strcmp(attr
, "sprop-max-don-diff")) {
158 hevc_data
->using_donl_field
= 1;
159 av_dlog(s
, "Found sprop-max-don-diff in SDP, DON field usage is: %d\n",
160 hevc_data
->using_donl_field
);
163 /* sprop-depack-buf-nalus: 0-32767 */
164 if (!strcmp(attr
, "sprop-depack-buf-nalus")) {
166 hevc_data
->using_donl_field
= 1;
167 av_dlog(s
, "Found sprop-depack-buf-nalus in SDP, DON field usage is: %d\n",
168 hevc_data
->using_donl_field
);
171 /* sprop-depack-buf-bytes: 0-4294967295 */
173 /* sprop-segmentation-id: 0-3 */
174 /* sprop-spatial-segmentation-idc: [base16] */
175 /* dec-parallel-ca: */
181 static av_cold
int hevc_parse_sdp_line(AVFormatContext
*ctx
, int st_index
,
182 PayloadContext
*hevc_data
, const char *line
)
184 AVStream
*current_stream
;
185 AVCodecContext
*codec
;
186 const char *sdp_line_ptr
= line
;
191 current_stream
= ctx
->streams
[st_index
];
192 codec
= current_stream
->codec
;
194 if (av_strstart(sdp_line_ptr
, "framesize:", &sdp_line_ptr
)) {
195 char str_video_width
[50];
196 char *str_video_width_ptr
= str_video_width
;
199 * parse "a=framesize:96 320-240"
203 while (*sdp_line_ptr
&& *sdp_line_ptr
== ' ')
205 /* ignore RTP payload ID */
206 while (*sdp_line_ptr
&& *sdp_line_ptr
!= ' ')
209 while (*sdp_line_ptr
&& *sdp_line_ptr
== ' ')
211 /* extract the actual video resolution description */
212 while (*sdp_line_ptr
&& *sdp_line_ptr
!= '-' &&
213 (str_video_width_ptr
- str_video_width
) < sizeof(str_video_width
) - 1)
214 *str_video_width_ptr
++ = *sdp_line_ptr
++;
215 /* add trailing zero byte */
216 *str_video_width_ptr
= '\0';
218 /* determine the width value */
219 codec
->width
= atoi(str_video_width
);
220 /* jump beyond the "-" and determine the height value */
221 codec
->height
= atoi(sdp_line_ptr
+ 1);
222 } else if (av_strstart(sdp_line_ptr
, "fmtp:", &sdp_line_ptr
)) {
223 int ret
= ff_parse_fmtp(ctx
, current_stream
, hevc_data
, sdp_line_ptr
,
224 hevc_sdp_parse_fmtp_config
);
225 if (hevc_data
->vps_size
|| hevc_data
->sps_size
||
226 hevc_data
->pps_size
|| hevc_data
->sei_size
) {
227 av_freep(&codec
->extradata
);
228 codec
->extradata_size
= hevc_data
->vps_size
+ hevc_data
->sps_size
+
229 hevc_data
->pps_size
+ hevc_data
->sei_size
;
230 codec
->extradata
= av_malloc(codec
->extradata_size
+
231 FF_INPUT_BUFFER_PADDING_SIZE
);
232 if (!codec
->extradata
) {
233 ret
= AVERROR(ENOMEM
);
234 codec
->extradata_size
= 0;
237 memcpy(codec
->extradata
+ pos
, hevc_data
->vps
, hevc_data
->vps_size
);
238 pos
+= hevc_data
->vps_size
;
239 memcpy(codec
->extradata
+ pos
, hevc_data
->sps
, hevc_data
->sps_size
);
240 pos
+= hevc_data
->sps_size
;
241 memcpy(codec
->extradata
+ pos
, hevc_data
->pps
, hevc_data
->pps_size
);
242 pos
+= hevc_data
->pps_size
;
243 memcpy(codec
->extradata
+ pos
, hevc_data
->sei
, hevc_data
->sei_size
);
244 pos
+= hevc_data
->sei_size
;
245 memset(codec
->extradata
+ pos
, 0, FF_INPUT_BUFFER_PADDING_SIZE
);
248 av_freep(&hevc_data
->vps
);
249 av_freep(&hevc_data
->sps
);
250 av_freep(&hevc_data
->pps
);
251 av_freep(&hevc_data
->sei
);
252 hevc_data
->vps_size
= 0;
253 hevc_data
->sps_size
= 0;
254 hevc_data
->pps_size
= 0;
255 hevc_data
->sei_size
= 0;
263 static int hevc_handle_packet(AVFormatContext
*ctx
, PayloadContext
*rtp_hevc_ctx
,
264 AVStream
*st
, AVPacket
*pkt
, uint32_t *timestamp
,
265 const uint8_t *buf
, int len
, uint16_t seq
,
268 const uint8_t *rtp_pl
= buf
;
269 int tid
, lid
, nal_type
;
270 int first_fragment
, last_fragment
, fu_type
;
271 uint8_t new_nal_header
[2];
274 /* sanity check for size of input packet: 1 byte payload at least */
275 if (len
< RTP_HEVC_PAYLOAD_HEADER_SIZE
+ 1) {
276 av_log(ctx
, AV_LOG_ERROR
, "Too short RTP/HEVC packet, got %d bytes\n", len
);
277 return AVERROR_INVALIDDATA
;
281 decode the HEVC payload header according to section 4 of draft version 6:
284 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
285 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
286 |F| Type | LayerId | TID |
287 +-------------+-----------------+
289 Forbidden zero (F): 1 bit
290 NAL unit type (Type): 6 bits
291 NUH layer ID (LayerId): 6 bits
292 NUH temporal ID plus 1 (TID): 3 bits
294 nal_type
= (buf
[0] >> 1) & 0x3f;
295 lid
= ((buf
[0] << 5) & 0x20) | ((buf
[1] >> 3) & 0x1f);
298 /* sanity check for correct layer ID */
300 /* future scalable or 3D video coding extensions */
301 avpriv_report_missing_feature(ctx
, "Multi-layer HEVC coding\n");
302 return AVERROR_PATCHWELCOME
;
305 /* sanity check for correct temporal ID */
307 av_log(ctx
, AV_LOG_ERROR
, "Illegal temporal ID in RTP/HEVC packet\n");
308 return AVERROR_INVALIDDATA
;
311 /* sanity check for correct NAL unit type */
313 av_log(ctx
, AV_LOG_ERROR
, "Unsupported (HEVC) NAL type (%d)\n", nal_type
);
314 return AVERROR_INVALIDDATA
;
318 /* aggregated packets (AP) */
320 /* pass the HEVC payload header */
321 buf
+= RTP_HEVC_PAYLOAD_HEADER_SIZE
;
322 len
-= RTP_HEVC_PAYLOAD_HEADER_SIZE
;
324 /* pass the HEVC DONL field */
325 if (rtp_hevc_ctx
->using_donl_field
) {
326 buf
+= RTP_HEVC_DONL_FIELD_SIZE
;
327 len
-= RTP_HEVC_DONL_FIELD_SIZE
;
331 /* video parameter set (VPS) */
333 /* sequence parameter set (SPS) */
335 /* picture parameter set (PPS) */
337 /* supplemental enhancement information (SEI) */
339 /* single NAL unit packet */
341 /* sanity check for size of input packet: 1 byte payload at least */
343 av_log(ctx
, AV_LOG_ERROR
,
344 "Too short RTP/HEVC packet, got %d bytes of NAL unit type %d\n",
346 return AVERROR_INVALIDDATA
;
349 /* create A/V packet */
350 if ((res
= av_new_packet(pkt
, sizeof(start_sequence
) + len
)) < 0)
352 /* A/V packet: copy start sequence */
353 memcpy(pkt
->data
, start_sequence
, sizeof(start_sequence
));
354 /* A/V packet: copy NAL unit data */
355 memcpy(pkt
->data
+ sizeof(start_sequence
), buf
, len
);
358 /* fragmentation unit (FU) */
360 /* pass the HEVC payload header */
361 buf
+= RTP_HEVC_PAYLOAD_HEADER_SIZE
;
362 len
-= RTP_HEVC_PAYLOAD_HEADER_SIZE
;
372 Start fragment (S): 1 bit
373 End fragment (E): 1 bit
376 first_fragment
= buf
[0] & 0x80;
377 last_fragment
= buf
[0] & 0x40;
378 fu_type
= buf
[0] & 0x3f;
380 /* pass the HEVC FU header */
381 buf
+= RTP_HEVC_FU_HEADER_SIZE
;
382 len
-= RTP_HEVC_FU_HEADER_SIZE
;
384 /* pass the HEVC DONL field */
385 if (rtp_hevc_ctx
->using_donl_field
) {
386 buf
+= RTP_HEVC_DONL_FIELD_SIZE
;
387 len
-= RTP_HEVC_DONL_FIELD_SIZE
;
390 av_dlog(ctx
, " FU type %d with %d bytes\n", fu_type
, len
);
392 /* sanity check for size of input packet: 1 byte payload at least */
394 new_nal_header
[0] = (rtp_pl
[0] & 0x81) | (fu_type
<< 1);
395 new_nal_header
[1] = rtp_pl
[1];
397 /* start fragment vs. subsequent fragments */
398 if (first_fragment
) {
399 if (!last_fragment
) {
400 /* create A/V packet which is big enough */
401 if ((res
= av_new_packet(pkt
, sizeof(start_sequence
) + sizeof(new_nal_header
) + len
)) < 0)
403 /* A/V packet: copy start sequence */
404 memcpy(pkt
->data
, start_sequence
, sizeof(start_sequence
));
405 /* A/V packet: copy new NAL header */
406 memcpy(pkt
->data
+ sizeof(start_sequence
), new_nal_header
, sizeof(new_nal_header
));
407 /* A/V packet: copy NAL unit data */
408 memcpy(pkt
->data
+ sizeof(start_sequence
) + sizeof(new_nal_header
), buf
, len
);
410 av_log(ctx
, AV_LOG_ERROR
, "Illegal combination of S and E bit in RTP/HEVC packet\n");
411 res
= AVERROR_INVALIDDATA
;
414 /* create A/V packet */
415 if ((res
= av_new_packet(pkt
, len
)) < 0)
417 /* A/V packet: copy NAL unit data */
418 memcpy(pkt
->data
, buf
, len
);
422 av_log(ctx
, AV_LOG_ERROR
,
423 "Too short RTP/HEVC packet, got %d bytes of NAL unit type %d\n",
425 res
= AVERROR_INVALIDDATA
;
427 res
= AVERROR(EAGAIN
);
434 /* Temporal scalability control information (TSCI) */
435 avpriv_report_missing_feature(ctx
, "PACI packets for RTP/HEVC\n");
436 res
= AVERROR_PATCHWELCOME
;
440 pkt
->stream_index
= st
->index
;
445 RTPDynamicProtocolHandler ff_hevc_dynamic_handler
= {
447 .codec_type
= AVMEDIA_TYPE_VIDEO
,
448 .codec_id
= AV_CODEC_ID_HEVC
,
450 .parse_sdp_a_line
= hevc_parse_sdp_line
,
451 .alloc
= hevc_new_context
,
452 .free
= hevc_free_context
,
453 .parse_packet
= hevc_handle_packet