Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6147 | serge | 1 | /* |
2 | * RTP parser for HEVC/H.265 payload format (draft version 6) |
||
3 | * Copyright (c) 2014 Thomas Volkert |
||
4 | * |
||
5 | * This file is part of FFmpeg. |
||
6 | * |
||
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. |
||
11 | * |
||
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. |
||
16 | * |
||
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 |
||
20 | * |
||
21 | */ |
||
22 | |||
23 | #include "libavutil/avassert.h" |
||
24 | #include "libavutil/avstring.h" |
||
25 | #include "libavutil/base64.h" |
||
26 | #include "libavcodec/get_bits.h" |
||
27 | |||
28 | #include "avformat.h" |
||
29 | #include "rtpdec.h" |
||
30 | #include "rtpdec_formats.h" |
||
31 | |||
32 | #define RTP_HEVC_PAYLOAD_HEADER_SIZE 2 |
||
33 | #define RTP_HEVC_FU_HEADER_SIZE 1 |
||
34 | #define RTP_HEVC_DONL_FIELD_SIZE 2 |
||
35 | #define RTP_HEVC_DOND_FIELD_SIZE 1 |
||
36 | #define RTP_HEVC_AP_NALU_LENGTH_FIELD_SIZE 2 |
||
37 | #define HEVC_SPECIFIED_NAL_UNIT_TYPES 48 |
||
38 | |||
39 | /* SDP out-of-band signaling data */ |
||
40 | struct PayloadContext { |
||
41 | int using_donl_field; |
||
42 | int profile_id; |
||
43 | uint8_t *sps, *pps, *vps, *sei; |
||
44 | int sps_size, pps_size, vps_size, sei_size; |
||
45 | }; |
||
46 | |||
47 | static const uint8_t start_sequence[] = { 0x00, 0x00, 0x00, 0x01 }; |
||
48 | |||
49 | static av_cold int hevc_sdp_parse_fmtp_config(AVFormatContext *s, |
||
50 | AVStream *stream, |
||
51 | PayloadContext *hevc_data, |
||
52 | const char *attr, const char *value) |
||
53 | { |
||
54 | /* profile-space: 0-3 */ |
||
55 | /* profile-id: 0-31 */ |
||
56 | if (!strcmp(attr, "profile-id")) { |
||
57 | hevc_data->profile_id = atoi(value); |
||
58 | av_log(s, AV_LOG_TRACE, "SDP: found profile-id: %d\n", hevc_data->profile_id); |
||
59 | } |
||
60 | |||
61 | /* tier-flag: 0-1 */ |
||
62 | /* level-id: 0-255 */ |
||
63 | /* interop-constraints: [base16] */ |
||
64 | /* profile-compatibility-indicator: [base16] */ |
||
65 | /* sprop-sub-layer-id: 0-6, defines highest possible value for TID, default: 6 */ |
||
66 | /* recv-sub-layer-id: 0-6 */ |
||
67 | /* max-recv-level-id: 0-255 */ |
||
68 | /* tx-mode: MSM,SSM */ |
||
69 | /* sprop-vps: [base64] */ |
||
70 | /* sprop-sps: [base64] */ |
||
71 | /* sprop-pps: [base64] */ |
||
72 | /* sprop-sei: [base64] */ |
||
73 | if (!strcmp(attr, "sprop-vps") || !strcmp(attr, "sprop-sps") || |
||
74 | !strcmp(attr, "sprop-pps") || !strcmp(attr, "sprop-sei")) { |
||
75 | uint8_t **data_ptr = NULL; |
||
76 | int *size_ptr = NULL; |
||
77 | if (!strcmp(attr, "sprop-vps")) { |
||
78 | data_ptr = &hevc_data->vps; |
||
79 | size_ptr = &hevc_data->vps_size; |
||
80 | } else if (!strcmp(attr, "sprop-sps")) { |
||
81 | data_ptr = &hevc_data->sps; |
||
82 | size_ptr = &hevc_data->sps_size; |
||
83 | } else if (!strcmp(attr, "sprop-pps")) { |
||
84 | data_ptr = &hevc_data->pps; |
||
85 | size_ptr = &hevc_data->pps_size; |
||
86 | } else if (!strcmp(attr, "sprop-sei")) { |
||
87 | data_ptr = &hevc_data->sei; |
||
88 | size_ptr = &hevc_data->sei_size; |
||
89 | } else |
||
90 | av_assert0(0); |
||
91 | |||
92 | ff_h264_parse_sprop_parameter_sets(s, data_ptr, |
||
93 | size_ptr, value); |
||
94 | } |
||
95 | |||
96 | /* max-lsr, max-lps, max-cpb, max-dpb, max-br, max-tr, max-tc */ |
||
97 | /* max-fps */ |
||
98 | |||
99 | /* sprop-max-don-diff: 0-32767 |
||
100 | |||
101 | When the RTP stream depends on one or more other RTP |
||
102 | streams (in this case tx-mode MUST be equal to "MSM" and |
||
103 | MSM is in use), this parameter MUST be present and the |
||
104 | value MUST be greater than 0. |
||
105 | */ |
||
106 | if (!strcmp(attr, "sprop-max-don-diff")) { |
||
107 | if (atoi(value) > 0) |
||
108 | hevc_data->using_donl_field = 1; |
||
109 | av_log(s, AV_LOG_TRACE, "Found sprop-max-don-diff in SDP, DON field usage is: %d\n", |
||
110 | hevc_data->using_donl_field); |
||
111 | } |
||
112 | |||
113 | /* sprop-depack-buf-nalus: 0-32767 */ |
||
114 | if (!strcmp(attr, "sprop-depack-buf-nalus")) { |
||
115 | if (atoi(value) > 0) |
||
116 | hevc_data->using_donl_field = 1; |
||
117 | av_log(s, AV_LOG_TRACE, "Found sprop-depack-buf-nalus in SDP, DON field usage is: %d\n", |
||
118 | hevc_data->using_donl_field); |
||
119 | } |
||
120 | |||
121 | /* sprop-depack-buf-bytes: 0-4294967295 */ |
||
122 | /* depack-buf-cap */ |
||
123 | /* sprop-segmentation-id: 0-3 */ |
||
124 | /* sprop-spatial-segmentation-idc: [base16] */ |
||
125 | /* dec-parallel-ca: */ |
||
126 | /* include-dph */ |
||
127 | |||
128 | return 0; |
||
129 | } |
||
130 | |||
131 | static av_cold int hevc_parse_sdp_line(AVFormatContext *ctx, int st_index, |
||
132 | PayloadContext *hevc_data, const char *line) |
||
133 | { |
||
134 | AVStream *current_stream; |
||
135 | AVCodecContext *codec; |
||
136 | const char *sdp_line_ptr = line; |
||
137 | |||
138 | if (st_index < 0) |
||
139 | return 0; |
||
140 | |||
141 | current_stream = ctx->streams[st_index]; |
||
142 | codec = current_stream->codec; |
||
143 | |||
144 | if (av_strstart(sdp_line_ptr, "framesize:", &sdp_line_ptr)) { |
||
145 | ff_h264_parse_framesize(codec, sdp_line_ptr); |
||
146 | } else if (av_strstart(sdp_line_ptr, "fmtp:", &sdp_line_ptr)) { |
||
147 | int ret = ff_parse_fmtp(ctx, current_stream, hevc_data, sdp_line_ptr, |
||
148 | hevc_sdp_parse_fmtp_config); |
||
149 | if (hevc_data->vps_size || hevc_data->sps_size || |
||
150 | hevc_data->pps_size || hevc_data->sei_size) { |
||
151 | av_freep(&codec->extradata); |
||
152 | codec->extradata_size = hevc_data->vps_size + hevc_data->sps_size + |
||
153 | hevc_data->pps_size + hevc_data->sei_size; |
||
154 | codec->extradata = av_malloc(codec->extradata_size + |
||
155 | AV_INPUT_BUFFER_PADDING_SIZE); |
||
156 | if (!codec->extradata) { |
||
157 | ret = AVERROR(ENOMEM); |
||
158 | codec->extradata_size = 0; |
||
159 | } else { |
||
160 | int pos = 0; |
||
161 | memcpy(codec->extradata + pos, hevc_data->vps, hevc_data->vps_size); |
||
162 | pos += hevc_data->vps_size; |
||
163 | memcpy(codec->extradata + pos, hevc_data->sps, hevc_data->sps_size); |
||
164 | pos += hevc_data->sps_size; |
||
165 | memcpy(codec->extradata + pos, hevc_data->pps, hevc_data->pps_size); |
||
166 | pos += hevc_data->pps_size; |
||
167 | memcpy(codec->extradata + pos, hevc_data->sei, hevc_data->sei_size); |
||
168 | pos += hevc_data->sei_size; |
||
169 | memset(codec->extradata + pos, 0, AV_INPUT_BUFFER_PADDING_SIZE); |
||
170 | } |
||
171 | |||
172 | av_freep(&hevc_data->vps); |
||
173 | av_freep(&hevc_data->sps); |
||
174 | av_freep(&hevc_data->pps); |
||
175 | av_freep(&hevc_data->sei); |
||
176 | hevc_data->vps_size = 0; |
||
177 | hevc_data->sps_size = 0; |
||
178 | hevc_data->pps_size = 0; |
||
179 | hevc_data->sei_size = 0; |
||
180 | } |
||
181 | return ret; |
||
182 | } |
||
183 | |||
184 | return 0; |
||
185 | } |
||
186 | |||
187 | static int hevc_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_hevc_ctx, |
||
188 | AVStream *st, AVPacket *pkt, uint32_t *timestamp, |
||
189 | const uint8_t *buf, int len, uint16_t seq, |
||
190 | int flags) |
||
191 | { |
||
192 | const uint8_t *rtp_pl = buf; |
||
193 | int tid, lid, nal_type; |
||
194 | int first_fragment, last_fragment, fu_type; |
||
195 | uint8_t new_nal_header[2]; |
||
196 | int res = 0; |
||
197 | |||
198 | /* sanity check for size of input packet: 1 byte payload at least */ |
||
199 | if (len < RTP_HEVC_PAYLOAD_HEADER_SIZE + 1) { |
||
200 | av_log(ctx, AV_LOG_ERROR, "Too short RTP/HEVC packet, got %d bytes\n", len); |
||
201 | return AVERROR_INVALIDDATA; |
||
202 | } |
||
203 | |||
204 | /* |
||
205 | * decode the HEVC payload header according to section 4 of draft version 6: |
||
206 | * |
||
207 | * 0 1 |
||
208 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 |
||
209 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
||
210 | * |F| Type | LayerId | TID | |
||
211 | * +-------------+-----------------+ |
||
212 | * |
||
213 | * Forbidden zero (F): 1 bit |
||
214 | * NAL unit type (Type): 6 bits |
||
215 | * NUH layer ID (LayerId): 6 bits |
||
216 | * NUH temporal ID plus 1 (TID): 3 bits |
||
217 | */ |
||
218 | nal_type = (buf[0] >> 1) & 0x3f; |
||
219 | lid = ((buf[0] << 5) & 0x20) | ((buf[1] >> 3) & 0x1f); |
||
220 | tid = buf[1] & 0x07; |
||
221 | |||
222 | /* sanity check for correct layer ID */ |
||
223 | if (lid) { |
||
224 | /* future scalable or 3D video coding extensions */ |
||
225 | avpriv_report_missing_feature(ctx, "Multi-layer HEVC coding\n"); |
||
226 | return AVERROR_PATCHWELCOME; |
||
227 | } |
||
228 | |||
229 | /* sanity check for correct temporal ID */ |
||
230 | if (!tid) { |
||
231 | av_log(ctx, AV_LOG_ERROR, "Illegal temporal ID in RTP/HEVC packet\n"); |
||
232 | return AVERROR_INVALIDDATA; |
||
233 | } |
||
234 | |||
235 | /* sanity check for correct NAL unit type */ |
||
236 | if (nal_type > 50) { |
||
237 | av_log(ctx, AV_LOG_ERROR, "Unsupported (HEVC) NAL type (%d)\n", nal_type); |
||
238 | return AVERROR_INVALIDDATA; |
||
239 | } |
||
240 | |||
241 | switch (nal_type) { |
||
242 | /* video parameter set (VPS) */ |
||
243 | case 32: |
||
244 | /* sequence parameter set (SPS) */ |
||
245 | case 33: |
||
246 | /* picture parameter set (PPS) */ |
||
247 | case 34: |
||
248 | /* supplemental enhancement information (SEI) */ |
||
249 | case 39: |
||
250 | /* single NAL unit packet */ |
||
251 | default: |
||
252 | /* create A/V packet */ |
||
253 | if ((res = av_new_packet(pkt, sizeof(start_sequence) + len)) < 0) |
||
254 | return res; |
||
255 | /* A/V packet: copy start sequence */ |
||
256 | memcpy(pkt->data, start_sequence, sizeof(start_sequence)); |
||
257 | /* A/V packet: copy NAL unit data */ |
||
258 | memcpy(pkt->data + sizeof(start_sequence), buf, len); |
||
259 | |||
260 | break; |
||
261 | /* aggregated packet (AP) - with two or more NAL units */ |
||
262 | case 48: |
||
263 | /* pass the HEVC payload header */ |
||
264 | buf += RTP_HEVC_PAYLOAD_HEADER_SIZE; |
||
265 | len -= RTP_HEVC_PAYLOAD_HEADER_SIZE; |
||
266 | |||
267 | /* pass the HEVC DONL field */ |
||
268 | if (rtp_hevc_ctx->using_donl_field) { |
||
269 | buf += RTP_HEVC_DONL_FIELD_SIZE; |
||
270 | len -= RTP_HEVC_DONL_FIELD_SIZE; |
||
271 | } |
||
272 | |||
273 | res = ff_h264_handle_aggregated_packet(ctx, rtp_hevc_ctx, pkt, buf, len, |
||
274 | rtp_hevc_ctx->using_donl_field ? |
||
275 | RTP_HEVC_DOND_FIELD_SIZE : 0, |
||
276 | NULL, 0); |
||
277 | if (res < 0) |
||
278 | return res; |
||
279 | break; |
||
280 | /* fragmentation unit (FU) */ |
||
281 | case 49: |
||
282 | /* pass the HEVC payload header */ |
||
283 | buf += RTP_HEVC_PAYLOAD_HEADER_SIZE; |
||
284 | len -= RTP_HEVC_PAYLOAD_HEADER_SIZE; |
||
285 | |||
286 | /* |
||
287 | * decode the FU header |
||
288 | * |
||
289 | * 0 1 2 3 4 5 6 7 |
||
290 | * +-+-+-+-+-+-+-+-+ |
||
291 | * |S|E| FuType | |
||
292 | * +---------------+ |
||
293 | * |
||
294 | * Start fragment (S): 1 bit |
||
295 | * End fragment (E): 1 bit |
||
296 | * FuType: 6 bits |
||
297 | */ |
||
298 | first_fragment = buf[0] & 0x80; |
||
299 | last_fragment = buf[0] & 0x40; |
||
300 | fu_type = buf[0] & 0x3f; |
||
301 | |||
302 | /* pass the HEVC FU header */ |
||
303 | buf += RTP_HEVC_FU_HEADER_SIZE; |
||
304 | len -= RTP_HEVC_FU_HEADER_SIZE; |
||
305 | |||
306 | /* pass the HEVC DONL field */ |
||
307 | if (rtp_hevc_ctx->using_donl_field) { |
||
308 | buf += RTP_HEVC_DONL_FIELD_SIZE; |
||
309 | len -= RTP_HEVC_DONL_FIELD_SIZE; |
||
310 | } |
||
311 | |||
312 | av_log(ctx, AV_LOG_TRACE, " FU type %d with %d bytes\n", fu_type, len); |
||
313 | |||
314 | /* sanity check for size of input packet: 1 byte payload at least */ |
||
315 | if (len <= 0) { |
||
316 | if (len < 0) { |
||
317 | av_log(ctx, AV_LOG_ERROR, |
||
318 | "Too short RTP/HEVC packet, got %d bytes of NAL unit type %d\n", |
||
319 | len, nal_type); |
||
320 | return AVERROR_INVALIDDATA; |
||
321 | } else { |
||
322 | return AVERROR(EAGAIN); |
||
323 | } |
||
324 | } |
||
325 | |||
326 | if (first_fragment && last_fragment) { |
||
327 | av_log(ctx, AV_LOG_ERROR, "Illegal combination of S and E bit in RTP/HEVC packet\n"); |
||
328 | return AVERROR_INVALIDDATA; |
||
329 | } |
||
330 | |||
331 | new_nal_header[0] = (rtp_pl[0] & 0x81) | (fu_type << 1); |
||
332 | new_nal_header[1] = rtp_pl[1]; |
||
333 | |||
334 | res = ff_h264_handle_frag_packet(pkt, buf, len, first_fragment, |
||
335 | new_nal_header, sizeof(new_nal_header)); |
||
336 | |||
337 | break; |
||
338 | /* PACI packet */ |
||
339 | case 50: |
||
340 | /* Temporal scalability control information (TSCI) */ |
||
341 | avpriv_report_missing_feature(ctx, "PACI packets for RTP/HEVC\n"); |
||
342 | res = AVERROR_PATCHWELCOME; |
||
343 | break; |
||
344 | } |
||
345 | |||
346 | pkt->stream_index = st->index; |
||
347 | |||
348 | return res; |
||
349 | } |
||
350 | |||
351 | RTPDynamicProtocolHandler ff_hevc_dynamic_handler = { |
||
352 | .enc_name = "H265", |
||
353 | .codec_type = AVMEDIA_TYPE_VIDEO, |
||
354 | .codec_id = AV_CODEC_ID_HEVC, |
||
355 | .need_parsing = AVSTREAM_PARSE_FULL, |
||
356 | .priv_data_size = sizeof(PayloadContext), |
||
357 | .parse_sdp_a_line = hevc_parse_sdp_line, |
||
358 | .parse_packet = hevc_handle_packet, |
||
359 | };><>>=>>>><>>> |