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/avstring.h"
27 #include "libavutil/pixdesc.h"
29 struct PayloadContext {
36 unsigned int frame_size;
37 unsigned int pgroup; /* size of the pixel group in bytes */
43 static int rfc4175_parse_format(AVStream *stream, PayloadContext *data)
45 enum AVPixelFormat pixfmt = AV_PIX_FMT_NONE;
46 int bits_per_sample = 0;
49 if (!strncmp(data->sampling, "YCbCr-4:2:2", 11)) {
50 tag = MKTAG('U', 'Y', 'V', 'Y');
53 if (data->depth == 8) {
56 pixfmt = AV_PIX_FMT_UYVY422;
57 } else if (data->depth == 10) {
60 pixfmt = AV_PIX_FMT_YUV422P10;
62 return AVERROR_INVALIDDATA;
65 return AVERROR_INVALIDDATA;
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;
76 static int rfc4175_parse_fmtp(AVFormatContext *s, AVStream *stream,
77 PayloadContext *data, const char *attr,
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);
92 static int rfc4175_parse_sdp_line(AVFormatContext *s, int st_index,
93 PayloadContext *data, const char *line)
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);
108 if (!data->sampling || !data->depth || !data->width || !data->height)
111 stream->codecpar->width = data->width;
112 stream->codecpar->height = data->height;
114 ret = rfc4175_parse_format(stream, data);
115 av_freep(&data->sampling);
123 static int rfc4175_finalize_packet(PayloadContext *data, AVPacket *pkt,
128 pkt->stream_index = stream_index;
129 ret = av_packet_from_data(pkt, data->frame, data->frame_size);
131 av_freep(&data->frame);
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)
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;
152 if (*timestamp != data->timestamp) {
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.
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);
166 data->frame = av_malloc(data->frame_size);
168 data->timestamp = *timestamp;
171 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
172 return AVERROR(ENOMEM);
177 * looks for the 'Continuation bit' in scan lines' headers
178 * to find where data start
182 return AVERROR_INVALIDDATA;
184 cont = payload[4] & 0x80;
189 /* and now iterate over every scan lines */
193 if (payload_len < data->pgroup)
194 return AVERROR_INVALIDDATA;
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;
202 if (length % data->pgroup)
203 return AVERROR_INVALIDDATA;
205 if (length > payload_len)
206 length = payload_len;
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;
213 dest = data->frame + copy_offset;
214 memcpy(dest, payload, length);
217 payload_len -= length;
220 if ((flags & RTP_FLAG_MARKER)) {
221 return rfc4175_finalize_packet(data, pkt, st->index);
222 } else if (missed_last_packet) {
226 return AVERROR(EAGAIN);
229 RTPDynamicProtocolHandler ff_rfc4175_rtp_handler = {
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,