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/pixdesc.h"
28
29 struct PayloadContext {
30 char *sampling;
31 int depth;
32 int width;
33 int height;
34
35 uint8_t *frame;
36 unsigned int frame_size;
37 unsigned int pgroup; /* size of the pixel group in bytes */
38 unsigned int xinc;
39
40 uint32_t timestamp;
41 };
42
rfc4175_parse_format(AVStream * stream,PayloadContext * data)43 static int rfc4175_parse_format(AVStream *stream, PayloadContext *data)
44 {
45 enum AVPixelFormat pixfmt = AV_PIX_FMT_NONE;
46 int bits_per_sample = 0;
47 int tag = 0;
48
49 if (!strncmp(data->sampling, "YCbCr-4:2:2", 11)) {
50 tag = MKTAG('U', 'Y', 'V', 'Y');
51 data->xinc = 2;
52
53 if (data->depth == 8) {
54 data->pgroup = 4;
55 bits_per_sample = 16;
56 pixfmt = AV_PIX_FMT_UYVY422;
57 } else if (data->depth == 10) {
58 data->pgroup = 5;
59 bits_per_sample = 20;
60 pixfmt = AV_PIX_FMT_YUV422P10;
61 } else {
62 return AVERROR_INVALIDDATA;
63 }
64 } else {
65 return AVERROR_INVALIDDATA;
66 }
67
68 stream->codecpar->format = pixfmt;
69 stream->codecpar->codec_tag = tag;
70 stream->codecpar->bits_per_coded_sample = bits_per_sample;
71 data->frame_size = data->width * data->height * data->pgroup / data->xinc;
72
73 return 0;
74 }
75
rfc4175_parse_fmtp(AVFormatContext * s,AVStream * stream,PayloadContext * data,const char * attr,const char * value)76 static int rfc4175_parse_fmtp(AVFormatContext *s, AVStream *stream,
77 PayloadContext *data, const char *attr,
78 const char *value)
79 {
80 if (!strncmp(attr, "width", 5))
81 data->width = atoi(value);
82 else if (!strncmp(attr, "height", 6))
83 data->height = atoi(value);
84 else if (!strncmp(attr, "sampling", 8))
85 data->sampling = av_strdup(value);
86 else if (!strncmp(attr, "depth", 5))
87 data->depth = atoi(value);
88
89 return 0;
90 }
91
rfc4175_parse_sdp_line(AVFormatContext * s,int st_index,PayloadContext * data,const char * line)92 static int rfc4175_parse_sdp_line(AVFormatContext *s, int st_index,
93 PayloadContext *data, const char *line)
94 {
95 const char *p;
96
97 if (st_index < 0)
98 return 0;
99
100 if (av_strstart(line, "fmtp:", &p)) {
101 AVStream *stream = s->streams[st_index];
102 int ret = ff_parse_fmtp(s, stream, data, p, rfc4175_parse_fmtp);
103
104 if (ret < 0)
105 return ret;
106
107
108 if (!data->sampling || !data->depth || !data->width || !data->height)
109 return -1;
110
111 stream->codecpar->width = data->width;
112 stream->codecpar->height = data->height;
113
114 ret = rfc4175_parse_format(stream, data);
115 av_freep(&data->sampling);
116
117 return ret;
118 }
119
120 return 0;
121 }
122
rfc4175_finalize_packet(PayloadContext * data,AVPacket * pkt,int stream_index)123 static int rfc4175_finalize_packet(PayloadContext *data, AVPacket *pkt,
124 int stream_index)
125 {
126 int ret;
127
128 pkt->stream_index = stream_index;
129 ret = av_packet_from_data(pkt, data->frame, data->frame_size);
130 if (ret < 0) {
131 av_freep(&data->frame);
132 }
133
134 data->frame = NULL;
135
136 return ret;
137 }
138
rfc4175_handle_packet(AVFormatContext * ctx,PayloadContext * data,AVStream * st,AVPacket * pkt,uint32_t * timestamp,const uint8_t * buf,int len,uint16_t seq,int flags)139 static int rfc4175_handle_packet(AVFormatContext *ctx, PayloadContext *data,
140 AVStream *st, AVPacket *pkt, uint32_t *timestamp,
141 const uint8_t * buf, int len,
142 uint16_t seq, int flags)
143 {
144 int length, line, offset, cont;
145 const uint8_t *headers = buf + 2; /* skip extended seqnum */
146 const uint8_t *payload = buf + 2;
147 int payload_len = len - 2;
148 int missed_last_packet = 0;
149
150 uint8_t *dest;
151
152 if (*timestamp != data->timestamp) {
153 if (data->frame) {
154 /*
155 * if we're here, it means that two RTP packets didn't have the
156 * same timestamp, which is a sign that they were packets from two
157 * different frames, but we didn't get the flag RTP_FLAG_MARKER on
158 * the first one of these frames (last packet of a frame).
159 * Finalize the previous frame anyway by filling the AVPacket.
160 */
161 av_log(ctx, AV_LOG_ERROR, "Missed previous RTP Marker\n");
162 missed_last_packet = 1;
163 rfc4175_finalize_packet(data, pkt, st->index);
164 }
165
166 data->frame = av_malloc(data->frame_size);
167
168 data->timestamp = *timestamp;
169
170 if (!data->frame) {
171 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
172 return AVERROR(ENOMEM);
173 }
174 }
175
176 /*
177 * looks for the 'Continuation bit' in scan lines' headers
178 * to find where data start
179 */
180 do {
181 if (payload_len < 6)
182 return AVERROR_INVALIDDATA;
183
184 cont = payload[4] & 0x80;
185 payload += 6;
186 payload_len -= 6;
187 } while (cont);
188
189 /* and now iterate over every scan lines */
190 do {
191 int copy_offset;
192
193 if (payload_len < data->pgroup)
194 return AVERROR_INVALIDDATA;
195
196 length = (headers[0] << 8) | headers[1];
197 line = ((headers[2] & 0x7f) << 8) | headers[3];
198 offset = ((headers[4] & 0x7f) << 8) | headers[5];
199 cont = headers[4] & 0x80;
200 headers += 6;
201
202 if (length % data->pgroup)
203 return AVERROR_INVALIDDATA;
204
205 if (length > payload_len)
206 length = payload_len;
207
208 /* prevent ill-formed packets to write after buffer's end */
209 copy_offset = (line * data->width + offset) * data->pgroup / data->xinc;
210 if (copy_offset + length > data->frame_size)
211 return AVERROR_INVALIDDATA;
212
213 dest = data->frame + copy_offset;
214 memcpy(dest, payload, length);
215
216 payload += length;
217 payload_len -= length;
218 } while (cont);
219
220 if ((flags & RTP_FLAG_MARKER)) {
221 return rfc4175_finalize_packet(data, pkt, st->index);
222 } else if (missed_last_packet) {
223 return 0;
224 }
225
226 return AVERROR(EAGAIN);
227 }
228
229 const RTPDynamicProtocolHandler ff_rfc4175_rtp_handler = {
230 .enc_name = "raw",
231 .codec_type = AVMEDIA_TYPE_VIDEO,
232 .codec_id = AV_CODEC_ID_BITPACKED,
233 .priv_data_size = sizeof(PayloadContext),
234 .parse_sdp_a_line = rfc4175_parse_sdp_line,
235 .parse_packet = rfc4175_handle_packet,
236 };
237