FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/rtpdec_rfc4175.c
Date: 2024-04-19 17:50:32
Exec Total Coverage
Lines: 0 168 0.0%
Functions: 0 5 0.0%
Branches: 0 118 0.0%

Line Branch Exec Source
1 /*
2 * RTP Depacketization of RAW video (TR-03)
3 * Copyright (c) 2016 Savoir-faire Linux, Inc
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 /* Development sponsored by CBC/Radio-Canada */
23
24 #include "avio_internal.h"
25 #include "rtpdec_formats.h"
26 #include "libavutil/avstring.h"
27 #include "libavutil/mem.h"
28 #include "libavutil/pixdesc.h"
29 #include "libavutil/parseutils.h"
30
31 struct PayloadContext {
32 char *sampling;
33 AVRational framerate;
34 int depth;
35 int width;
36 int height;
37 int interlaced;
38 int field;
39
40 uint8_t *frame;
41 unsigned int frame_size;
42 unsigned int pgroup; /* size of the pixel group in bytes */
43 unsigned int xinc;
44
45 uint32_t timestamp;
46 };
47
48 static int rfc4175_parse_format(AVStream *stream, PayloadContext *data)
49 {
50 enum AVPixelFormat pixfmt;
51 int tag;
52 const AVPixFmtDescriptor *desc;
53
54 if (!strncmp(data->sampling, "YCbCr-4:2:2", 11)) {
55 tag = MKTAG('U', 'Y', 'V', 'Y');
56 data->xinc = 2;
57
58 if (data->depth == 8) {
59 data->pgroup = 4;
60 pixfmt = AV_PIX_FMT_UYVY422;
61 stream->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
62 } else if (data->depth == 10) {
63 data->pgroup = 5;
64 pixfmt = AV_PIX_FMT_YUV422P10;
65 stream->codecpar->codec_id = AV_CODEC_ID_BITPACKED;
66 } else {
67 return AVERROR_INVALIDDATA;
68 }
69 } else if (!strncmp(data->sampling, "YCbCr-4:2:0", 11)) {
70 tag = MKTAG('I', '4', '2', '0');
71 data->xinc = 4;
72
73 if (data->depth == 8) {
74 data->pgroup = 6;
75 pixfmt = AV_PIX_FMT_YUV420P;
76 stream->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
77 } else {
78 return AVERROR_INVALIDDATA;
79 }
80 } else if (!strncmp(data->sampling, "RGB", 3)) {
81 tag = MKTAG('R', 'G', 'B', 24);
82 if (data->depth == 8) {
83 data->xinc = 1;
84 data->pgroup = 3;
85 pixfmt = AV_PIX_FMT_RGB24;
86 stream->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
87 } else {
88 return AVERROR_INVALIDDATA;
89 }
90 } else if (!strncmp(data->sampling, "BGR", 3)) {
91 tag = MKTAG('B', 'G', 'R', 24);
92 if (data->depth == 8) {
93 data->xinc = 1;
94 data->pgroup = 3;
95 pixfmt = AV_PIX_FMT_BGR24;
96 stream->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
97 } else {
98 return AVERROR_INVALIDDATA;
99 }
100 } else {
101 return AVERROR_INVALIDDATA;
102 }
103
104 desc = av_pix_fmt_desc_get(pixfmt);
105 stream->codecpar->format = pixfmt;
106 stream->codecpar->codec_tag = tag;
107 stream->codecpar->bits_per_coded_sample = av_get_bits_per_pixel(desc);
108 data->frame_size = data->width * data->height * data->pgroup / data->xinc;
109
110 if (data->interlaced)
111 stream->codecpar->field_order = AV_FIELD_TT;
112 else
113 stream->codecpar->field_order = AV_FIELD_PROGRESSIVE;
114
115 if (data->framerate.den > 0) {
116 stream->avg_frame_rate = data->framerate;
117 stream->codecpar->bit_rate = data->frame_size * av_q2d(data->framerate) * 8;
118 }
119
120 return 0;
121 }
122
123 static int rfc4175_parse_fmtp(AVFormatContext *s, AVStream *stream,
124 PayloadContext *data, const char *attr,
125 const char *value)
126 {
127 if (!strncmp(attr, "width", 5))
128 data->width = atoi(value);
129 else if (!strncmp(attr, "height", 6))
130 data->height = atoi(value);
131 else if (!strncmp(attr, "sampling", 8))
132 data->sampling = av_strdup(value);
133 else if (!strncmp(attr, "depth", 5))
134 data->depth = atoi(value);
135 else if (!strncmp(attr, "interlace", 9))
136 data->interlaced = 1;
137 else if (!strncmp(attr, "exactframerate", 14)) {
138 if (av_parse_video_rate(&data->framerate, value) < 0)
139 return AVERROR(EINVAL);
140 } else if (!strncmp(attr, "TCS", 3)) {
141 if (!strncmp(value, "SDR", 3))
142 stream->codecpar->color_trc = AVCOL_TRC_BT709;
143 else if (!strncmp(value, "PQ", 2))
144 stream->codecpar->color_trc = AVCOL_TRC_SMPTE2084;
145 else if (!strncmp(value, "HLG", 3))
146 stream->codecpar->color_trc = AVCOL_TRC_ARIB_STD_B67;
147 else if (!strncmp(value, "LINEAR", 6))
148 stream->codecpar->color_trc = AVCOL_TRC_LINEAR;
149 else if (!strncmp(value, "ST428-1", 7))
150 stream->codecpar->color_trc = AVCOL_TRC_SMPTEST428_1;
151 else
152 stream->codecpar->color_trc = AVCOL_TRC_UNSPECIFIED;
153 } else if (!strncmp(attr, "colorimetry", 11)) {
154 if (!strncmp(value, "BT601", 5)) {
155 stream->codecpar->color_primaries = AVCOL_PRI_BT470BG;
156 stream->codecpar->color_space = AVCOL_SPC_BT470BG;
157 } else if (!strncmp(value, "BT709", 5)) {
158 stream->codecpar->color_primaries = AVCOL_PRI_BT709;
159 stream->codecpar->color_space = AVCOL_SPC_BT709;
160 } else if (!strncmp(value, "BT2020", 6)) {
161 stream->codecpar->color_primaries = AVCOL_PRI_BT2020;
162 stream->codecpar->color_space = AVCOL_SPC_BT2020_NCL;
163 }
164 } else if (!strncmp(attr, "RANGE", 5)) {
165 if (!strncmp(value, "NARROW", 6))
166 stream->codecpar->color_range = AVCOL_RANGE_MPEG;
167 else if (!strncmp(value, "FULL", 4))
168 stream->codecpar->color_range = AVCOL_RANGE_JPEG;
169 }
170
171 return 0;
172 }
173
174 static int rfc4175_parse_sdp_line(AVFormatContext *s, int st_index,
175 PayloadContext *data, const char *line)
176 {
177 const char *p;
178
179 if (st_index < 0)
180 return 0;
181
182 if (av_strstart(line, "fmtp:", &p)) {
183 AVStream *stream = s->streams[st_index];
184 int ret = ff_parse_fmtp(s, stream, data, p, rfc4175_parse_fmtp);
185
186 if (ret < 0)
187 return ret;
188
189
190 if (!data->sampling || !data->depth || !data->width || !data->height)
191 return AVERROR(EINVAL);
192
193 stream->codecpar->width = data->width;
194 stream->codecpar->height = data->height;
195
196 ret = rfc4175_parse_format(stream, data);
197 av_freep(&data->sampling);
198
199 return ret;
200 }
201
202 return 0;
203 }
204
205 static int rfc4175_finalize_packet(PayloadContext *data, AVPacket *pkt,
206 int stream_index)
207 {
208 int ret = 0;
209
210 pkt->stream_index = stream_index;
211 if (!data->interlaced || data->field) {
212 ret = av_packet_from_data(pkt, data->frame, data->frame_size);
213 if (ret < 0) {
214 av_freep(&data->frame);
215 }
216 data->frame = NULL;
217 }
218
219 data->field = 0;
220
221 return ret;
222 }
223
224 static int rfc4175_handle_packet(AVFormatContext *ctx, PayloadContext *data,
225 AVStream *st, AVPacket *pkt, uint32_t *timestamp,
226 const uint8_t * buf, int len,
227 uint16_t seq, int flags)
228 {
229 int length, line, offset, cont, field;
230 const uint8_t *headers = buf + 2; /* skip extended seqnum */
231 const uint8_t *payload = buf + 2;
232 int payload_len = len - 2;
233 int missed_last_packet = 0;
234
235 uint8_t *dest;
236
237 if (*timestamp != data->timestamp) {
238 if (data->frame && (!data->interlaced || data->field)) {
239 /*
240 * if we're here, it means that we missed the cue to return
241 * the previous AVPacket, that cue being the RTP_FLAG_MARKER
242 * in the last packet of either the previous frame (progressive)
243 * or the previous second field (interlace). Let's finalize the
244 * previous frame (or pair of fields) anyway by filling the AVPacket.
245 */
246 av_log(ctx, AV_LOG_ERROR, "Missed previous RTP Marker\n");
247 missed_last_packet = 1;
248 rfc4175_finalize_packet(data, pkt, st->index);
249 }
250
251 if (!data->frame)
252 data->frame = av_malloc(data->frame_size);
253
254 data->timestamp = *timestamp;
255
256 if (!data->frame) {
257 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
258 return AVERROR(ENOMEM);
259 }
260 }
261
262 /*
263 * looks for the 'Continuation bit' in scan lines' headers
264 * to find where data start
265 */
266 do {
267 if (payload_len < 6)
268 return AVERROR_INVALIDDATA;
269
270 cont = payload[4] & 0x80;
271 payload += 6;
272 payload_len -= 6;
273 } while (cont);
274
275 /* and now iterate over every scan lines */
276 do {
277 int copy_offset;
278
279 if (payload_len < data->pgroup)
280 return AVERROR_INVALIDDATA;
281
282 length = (headers[0] << 8) | headers[1];
283 field = (headers[2] & 0x80) >> 7;
284 line = ((headers[2] & 0x7f) << 8) | headers[3];
285 offset = ((headers[4] & 0x7f) << 8) | headers[5];
286 cont = headers[4] & 0x80;
287 headers += 6;
288 data->field = field;
289
290 if (!data->pgroup || length % data->pgroup)
291 return AVERROR_INVALIDDATA;
292
293 if (length > payload_len)
294 length = payload_len;
295
296 if (data->interlaced)
297 line = 2 * line + field;
298
299 /* prevent ill-formed packets to write after buffer's end */
300 copy_offset = (line * data->width + offset) * data->pgroup / data->xinc;
301 if (copy_offset + length > data->frame_size || !data->frame)
302 return AVERROR_INVALIDDATA;
303
304 dest = data->frame + copy_offset;
305 memcpy(dest, payload, length);
306
307 payload += length;
308 payload_len -= length;
309 } while (cont);
310
311 if ((flags & RTP_FLAG_MARKER)) {
312 return rfc4175_finalize_packet(data, pkt, st->index);
313 } else if (missed_last_packet) {
314 return 0;
315 }
316
317 return AVERROR(EAGAIN);
318 }
319
320 const RTPDynamicProtocolHandler ff_rfc4175_rtp_handler = {
321 .enc_name = "raw",
322 .codec_type = AVMEDIA_TYPE_VIDEO,
323 .codec_id = AV_CODEC_ID_NONE,
324 .priv_data_size = sizeof(PayloadContext),
325 .parse_sdp_a_line = rfc4175_parse_sdp_line,
326 .parse_packet = rfc4175_handle_packet,
327 };
328