2 * RTP Depacketization of RAW video (TR-03)
3 * Copyright (c) 2016 Savoir-faire Linux, Inc
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
22 /* Development sponsored by CBC/Radio-Canada */
24 #include "avio_internal.h"
25 #include "rtpdec_formats.h"
26 #include "libavutil/avassert.h"
27 #include "libavutil/avstring.h"
28 #include "libavutil/imgutils.h"
29 #include "libavutil/mem.h"
30 #include "libavutil/pixdesc.h"
31 #include "libavutil/parseutils.h"
33 struct PayloadContext
{
43 unsigned int frame_size
;
44 unsigned int pgroup
; /* size of the pixel group in bytes */
50 static int rfc4175_parse_format(AVStream
*stream
, PayloadContext
*data
)
52 enum AVPixelFormat pixfmt
;
54 const AVPixFmtDescriptor
*desc
;
56 if (!strncmp(data
->sampling
, "YCbCr-4:2:2", 11)) {
57 tag
= MKTAG('U', 'Y', 'V', 'Y');
60 if (data
->depth
== 8) {
62 pixfmt
= AV_PIX_FMT_UYVY422
;
63 stream
->codecpar
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
64 } else if (data
->depth
== 10) {
66 pixfmt
= AV_PIX_FMT_YUV422P10
;
67 stream
->codecpar
->codec_id
= AV_CODEC_ID_BITPACKED
;
69 return AVERROR_INVALIDDATA
;
71 } else if (!strncmp(data
->sampling
, "YCbCr-4:2:0", 11)) {
72 tag
= MKTAG('I', '4', '2', '0');
75 if (data
->depth
== 8) {
77 pixfmt
= AV_PIX_FMT_YUV420P
;
78 stream
->codecpar
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
80 return AVERROR_INVALIDDATA
;
82 } else if (!strncmp(data
->sampling
, "RGB", 3)) {
83 tag
= MKTAG('R', 'G', 'B', 24);
84 if (data
->depth
== 8) {
87 pixfmt
= AV_PIX_FMT_RGB24
;
88 stream
->codecpar
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
90 return AVERROR_INVALIDDATA
;
92 } else if (!strncmp(data
->sampling
, "BGR", 3)) {
93 tag
= MKTAG('B', 'G', 'R', 24);
94 if (data
->depth
== 8) {
97 pixfmt
= AV_PIX_FMT_BGR24
;
98 stream
->codecpar
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
100 return AVERROR_INVALIDDATA
;
103 return AVERROR_INVALIDDATA
;
106 desc
= av_pix_fmt_desc_get(pixfmt
);
107 stream
->codecpar
->format
= pixfmt
;
108 stream
->codecpar
->codec_tag
= tag
;
109 stream
->codecpar
->bits_per_coded_sample
= av_get_bits_per_pixel(desc
);
110 data
->frame_size
= data
->width
* data
->height
* data
->pgroup
/ data
->xinc
;
112 if (data
->interlaced
)
113 stream
->codecpar
->field_order
= AV_FIELD_TT
;
115 stream
->codecpar
->field_order
= AV_FIELD_PROGRESSIVE
;
117 if (data
->framerate
.den
> 0) {
118 stream
->avg_frame_rate
= data
->framerate
;
119 stream
->codecpar
->bit_rate
= data
->frame_size
* av_q2d(data
->framerate
) * 8;
125 static int rfc4175_parse_fmtp(AVFormatContext
*s
, AVStream
*stream
,
126 PayloadContext
*data
, const char *attr
,
129 if (!strncmp(attr
, "width", 5))
130 data
->width
= atoi(value
);
131 else if (!strncmp(attr
, "height", 6))
132 data
->height
= atoi(value
);
133 else if (data
->sampling
== NULL
&& !strncmp(attr
, "sampling", 8))
134 data
->sampling
= av_strdup(value
);
135 else if (!strncmp(attr
, "depth", 5))
136 data
->depth
= atoi(value
);
137 else if (!strncmp(attr
, "interlace", 9))
138 data
->interlaced
= 1;
139 else if (!strncmp(attr
, "exactframerate", 14)) {
140 if (av_parse_video_rate(&data
->framerate
, value
) < 0)
141 return AVERROR(EINVAL
);
142 } else if (!strncmp(attr
, "TCS", 3)) {
143 if (!strncmp(value
, "SDR", 3))
144 stream
->codecpar
->color_trc
= AVCOL_TRC_BT709
;
145 else if (!strncmp(value
, "PQ", 2))
146 stream
->codecpar
->color_trc
= AVCOL_TRC_SMPTE2084
;
147 else if (!strncmp(value
, "HLG", 3))
148 stream
->codecpar
->color_trc
= AVCOL_TRC_ARIB_STD_B67
;
149 else if (!strncmp(value
, "LINEAR", 6))
150 stream
->codecpar
->color_trc
= AVCOL_TRC_LINEAR
;
151 else if (!strncmp(value
, "ST428-1", 7))
152 stream
->codecpar
->color_trc
= AVCOL_TRC_SMPTEST428_1
;
154 stream
->codecpar
->color_trc
= AVCOL_TRC_UNSPECIFIED
;
155 } else if (!strncmp(attr
, "colorimetry", 11)) {
156 if (!strncmp(value
, "BT601", 5)) {
157 stream
->codecpar
->color_primaries
= AVCOL_PRI_BT470BG
;
158 stream
->codecpar
->color_space
= AVCOL_SPC_BT470BG
;
159 } else if (!strncmp(value
, "BT709", 5)) {
160 stream
->codecpar
->color_primaries
= AVCOL_PRI_BT709
;
161 stream
->codecpar
->color_space
= AVCOL_SPC_BT709
;
162 } else if (!strncmp(value
, "BT2020", 6)) {
163 stream
->codecpar
->color_primaries
= AVCOL_PRI_BT2020
;
164 stream
->codecpar
->color_space
= AVCOL_SPC_BT2020_NCL
;
166 } else if (!strncmp(attr
, "RANGE", 5)) {
167 if (!strncmp(value
, "NARROW", 6))
168 stream
->codecpar
->color_range
= AVCOL_RANGE_MPEG
;
169 else if (!strncmp(value
, "FULL", 4))
170 stream
->codecpar
->color_range
= AVCOL_RANGE_JPEG
;
176 static int rfc4175_parse_sdp_line(AVFormatContext
*s
, int st_index
,
177 PayloadContext
*data_arg
, const char *line
)
184 av_assert0(!data_arg
->sampling
);
186 if (av_strstart(line
, "fmtp:", &p
)) {
187 AVStream
*stream
= s
->streams
[st_index
];
188 PayloadContext data0
= *data_arg
, *data
= &data0
;
189 int ret
= ff_parse_fmtp(s
, stream
, data
, p
, rfc4175_parse_fmtp
);
191 if (!data
->sampling
|| !data
->depth
|| !data
->width
|| !data
->height
)
192 ret
= AVERROR(EINVAL
);
197 ret
= av_image_check_size(data
->width
, data
->height
, 0, s
);
201 stream
->codecpar
->width
= data
->width
;
202 stream
->codecpar
->height
= data
->height
;
204 ret
= rfc4175_parse_format(stream
, data
);
205 av_freep(&data
->sampling
);
209 av_freep(&data
->sampling
);
216 static int rfc4175_finalize_packet(PayloadContext
*data
, AVPacket
*pkt
,
221 pkt
->stream_index
= stream_index
;
222 if (!data
->interlaced
|| data
->field
) {
223 ret
= av_packet_from_data(pkt
, data
->frame
, data
->frame_size
);
225 av_freep(&data
->frame
);
235 static int rfc4175_handle_packet(AVFormatContext
*ctx
, PayloadContext
*data
,
236 AVStream
*st
, AVPacket
*pkt
, uint32_t *timestamp
,
237 const uint8_t * buf
, int len
,
238 uint16_t seq
, int flags
)
240 int length
, line
, offset
, cont
, field
;
241 const uint8_t *headers
= buf
+ 2; /* skip extended seqnum */
242 const uint8_t *payload
= buf
+ 2;
243 int payload_len
= len
- 2;
244 int missed_last_packet
= 0;
248 if (*timestamp
!= data
->timestamp
) {
249 if (data
->frame
&& (!data
->interlaced
|| data
->field
)) {
251 * if we're here, it means that we missed the cue to return
252 * the previous AVPacket, that cue being the RTP_FLAG_MARKER
253 * in the last packet of either the previous frame (progressive)
254 * or the previous second field (interlace). Let's finalize the
255 * previous frame (or pair of fields) anyway by filling the AVPacket.
257 av_log(ctx
, AV_LOG_ERROR
, "Missed previous RTP Marker\n");
258 missed_last_packet
= 1;
259 rfc4175_finalize_packet(data
, pkt
, st
->index
);
263 data
->frame
= av_malloc(data
->frame_size
);
265 data
->timestamp
= *timestamp
;
268 av_log(ctx
, AV_LOG_ERROR
, "Out of memory.\n");
269 return AVERROR(ENOMEM
);
274 * looks for the 'Continuation bit' in scan lines' headers
275 * to find where data start
279 return AVERROR_INVALIDDATA
;
281 cont
= payload
[4] & 0x80;
286 /* and now iterate over every scan lines */
290 if (payload_len
< data
->pgroup
)
291 return AVERROR_INVALIDDATA
;
293 length
= (headers
[0] << 8) | headers
[1];
294 field
= (headers
[2] & 0x80) >> 7;
295 line
= ((headers
[2] & 0x7f) << 8) | headers
[3];
296 offset
= ((headers
[4] & 0x7f) << 8) | headers
[5];
297 cont
= headers
[4] & 0x80;
301 if (!data
->pgroup
|| length
% data
->pgroup
)
302 return AVERROR_INVALIDDATA
;
304 if (length
> payload_len
)
305 length
= payload_len
;
307 if (data
->interlaced
)
308 line
= 2 * line
+ field
;
310 if (line
>= data
->height
)
311 return AVERROR_INVALIDDATA
;
313 /* prevent ill-formed packets to write after buffer's end */
314 copy_offset
= (line
* data
->width
+ offset
) * data
->pgroup
/ data
->xinc
;
315 if (copy_offset
+ length
> data
->frame_size
|| !data
->frame
)
316 return AVERROR_INVALIDDATA
;
318 dest
= data
->frame
+ copy_offset
;
319 memcpy(dest
, payload
, length
);
322 payload_len
-= length
;
325 if ((flags
& RTP_FLAG_MARKER
)) {
326 return rfc4175_finalize_packet(data
, pkt
, st
->index
);
327 } else if (missed_last_packet
) {
331 return AVERROR(EAGAIN
);
334 const RTPDynamicProtocolHandler ff_rfc4175_rtp_handler
= {
336 .codec_type
= AVMEDIA_TYPE_VIDEO
,
337 .codec_id
= AV_CODEC_ID_NONE
,
338 .priv_data_size
= sizeof(PayloadContext
),
339 .parse_sdp_a_line
= rfc4175_parse_sdp_line
,
340 .parse_packet
= rfc4175_handle_packet
,