FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/rtpdec_qt.c
Date: 2026-09-27 22:20:00
Exec Total Coverage
Lines: 0 148 0.0%
Functions: 0 3 0.0%
Branches: 0 71 0.0%

Line Branch Exec Source
1 /*
2 * RTP/Quicktime support.
3 * Copyright (c) 2009 Ronald S. Bultje
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 * @file
24 * @brief Quicktime-style RTP support
25 * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
26 */
27
28 #include "libavutil/mem.h"
29 #include "avformat.h"
30 #include "internal.h"
31 #include "avio_internal.h"
32 #include "rtp.h"
33 #include "rtpdec.h"
34 #include "isom.h"
35 #include "libavcodec/get_bits.h"
36
37 struct PayloadContext {
38 AVPacket *pkt;
39 int bytes_per_frame, remaining;
40 uint32_t timestamp;
41 };
42
43 ✗ static av_cold int qt_rtp_init(AVFormatContext *ctx, int st_index,
44 PayloadContext *qt)
45 {
46 ✗ qt->pkt = av_packet_alloc();
47 ✗ if (!qt->pkt)
48 ✗ return AVERROR(ENOMEM);
49
50 ✗ return 0;
51 }
52
53 ✗ static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt,
54 AVStream *st, AVPacket *pkt,
55 uint32_t *timestamp, const uint8_t *buf,
56 int len, uint16_t seq, int flags)
57 {
58 FFIOContext pb0;
59 ✗ AVIOContext *const pb = &pb0.pub;
60 GetBitContext gb;
61 int packing_scheme, has_payload_desc, has_packet_info, alen,
62 ✗ has_marker_bit = flags & RTP_FLAG_MARKER,
63 keyframe, ret;
64
65 ✗ if (qt->remaining) {
66 ✗ int num = qt->pkt->size / qt->bytes_per_frame;
67
68 ✗ if ((ret = av_new_packet(pkt, qt->bytes_per_frame)) < 0)
69 ✗ return ret;
70 ✗ pkt->stream_index = st->index;
71 ✗ pkt->flags = qt->pkt->flags;
72 ✗ memcpy(pkt->data,
73 ✗ &qt->pkt->data[(num - qt->remaining) * qt->bytes_per_frame],
74 ✗ qt->bytes_per_frame);
75 ✗ if (--qt->remaining == 0) {
76 ✗ av_freep(&qt->pkt->data);
77 ✗ qt->pkt->size = 0;
78 }
79 ✗ return qt->remaining > 0;
80 }
81
82 /**
83 * The RTP payload is described in:
84 * http://developer.apple.com/quicktime/icefloe/dispatch026.html
85 */
86 ✗ ret = init_get_bits(&gb, buf, len << 3);
87 ✗ if (ret < 0)
88 ✗ return ret;
89 ✗ ffio_init_read_context(&pb0, buf, len);
90
91 ✗ if (len < 4)
92 ✗ return AVERROR_INVALIDDATA;
93
94 ✗ skip_bits(&gb, 4); // version
95 ✗ if ((packing_scheme = get_bits(&gb, 2)) == 0)
96 ✗ return AVERROR_INVALIDDATA;
97 ✗ keyframe = get_bits1(&gb);
98 ✗ has_payload_desc = get_bits1(&gb);
99 ✗ has_packet_info = get_bits1(&gb);
100 ✗ skip_bits(&gb, 23); // reserved:7, cache payload info:1, payload ID:15
101
102 ✗ if (has_payload_desc) {
103 int data_len, pos, is_start, is_finish;
104 uint32_t tag;
105
106 ✗ pos = get_bits_count(&gb) >> 3;
107 ✗ if (pos + 12 > len)
108 ✗ return AVERROR_INVALIDDATA;
109
110 ✗ skip_bits(&gb, 2); // has non-I-frames:1, is sparse:1
111 ✗ is_start = get_bits1(&gb);
112 ✗ is_finish = get_bits1(&gb);
113 ✗ if (!is_start || !is_finish) {
114 ✗ avpriv_request_sample(s, "RTP-X-QT with payload description "
115 "split over several packets");
116 ✗ return AVERROR_PATCHWELCOME;
117 }
118 ✗ skip_bits(&gb, 12); // reserved
119 ✗ data_len = get_bits(&gb, 16);
120
121 ✗ avio_seek(pb, pos + 4, SEEK_SET);
122 ✗ tag = avio_rl32(pb);
123 ✗ if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
124 ✗ tag != MKTAG('v','i','d','e')) ||
125 ✗ (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
126 tag != MKTAG('s','o','u','n')))
127 ✗ return AVERROR_INVALIDDATA;
128 ✗ avpriv_set_pts_info(st, 32, 1, avio_rb32(pb));
129
130 ✗ if (pos + data_len > len)
131 ✗ return AVERROR_INVALIDDATA;
132 /* TLVs */
133 ✗ while (avio_tell(pb) + 4 < pos + data_len) {
134 ✗ int tlv_len = avio_rb16(pb);
135 ✗ tag = avio_rl16(pb);
136 ✗ if (avio_tell(pb) + tlv_len > pos + data_len)
137 ✗ return AVERROR_INVALIDDATA;
138
139 #define MKTAG16(a,b) MKTAG(a,b,0,0)
140 ✗ switch (tag) {
141 ✗ case MKTAG16('s','d'): {
142 MOVStreamContext *msc;
143 ✗ void *priv_data = st->priv_data;
144 ✗ int nb_streams = s->nb_streams;
145 ✗ MOVContext *mc = av_mallocz(sizeof(*mc));
146 ✗ if (!mc)
147 ✗ return AVERROR(ENOMEM);
148 ✗ mc->fc = s;
149 ✗ st->priv_data = msc = av_mallocz(sizeof(MOVStreamContext));
150 ✗ if (!msc) {
151 ✗ av_free(mc);
152 ✗ st->priv_data = priv_data;
153 ✗ return AVERROR(ENOMEM);
154 }
155 /* ff_mov_read_stsd_entries updates stream s->nb_streams-1,
156 * so set it temporarily to indicate which stream to update. */
157 ✗ s->nb_streams = st->index + 1;
158 ✗ ff_mov_read_stsd_entries(mc, pb, 1);
159 ✗ qt->bytes_per_frame = msc->bytes_per_frame;
160 ✗ av_free(msc);
161 ✗ av_free(mc);
162 ✗ st->priv_data = priv_data;
163 ✗ s->nb_streams = nb_streams;
164 ✗ break;
165 }
166 ✗ default:
167 ✗ avio_skip(pb, tlv_len);
168 ✗ break;
169 }
170 }
171
172 /* 32-bit alignment */
173 ✗ avio_skip(pb, ((avio_tell(pb) + 3) & ~3) - avio_tell(pb));
174 } else
175 ✗ avio_seek(pb, 4, SEEK_SET);
176
177 ✗ if (has_packet_info) {
178 ✗ avpriv_request_sample(s, "RTP-X-QT with packet-specific info");
179 ✗ return AVERROR_PATCHWELCOME;
180 }
181
182 ✗ alen = len - avio_tell(pb);
183 ✗ if (alen <= 0)
184 ✗ return AVERROR_INVALIDDATA;
185
186 ✗ switch (packing_scheme) {
187 ✗ case 3: /* one data packet spread over 1 or multiple RTP packets */
188 ✗ if (qt->pkt->size > 0 && qt->timestamp == *timestamp) {
189 int err;
190 ✗ if ((err = av_reallocp(&qt->pkt->data, qt->pkt->size + alen +
191 AV_INPUT_BUFFER_PADDING_SIZE)) < 0) {
192 ✗ qt->pkt->size = 0;
193 ✗ return err;
194 }
195 } else {
196 ✗ av_freep(&qt->pkt->data);
197 ✗ av_packet_unref(qt->pkt);
198 ✗ qt->pkt->data = av_realloc(NULL, alen + AV_INPUT_BUFFER_PADDING_SIZE);
199 ✗ if (!qt->pkt->data)
200 ✗ return AVERROR(ENOMEM);
201 ✗ qt->pkt->size = 0;
202 ✗ qt->timestamp = *timestamp;
203 }
204 ✗ memcpy(qt->pkt->data + qt->pkt->size, buf + avio_tell(pb), alen);
205 ✗ qt->pkt->size += alen;
206 ✗ if (has_marker_bit) {
207 ✗ int ret = av_packet_from_data(pkt, qt->pkt->data, qt->pkt->size);
208 ✗ if (ret < 0)
209 ✗ return ret;
210
211 ✗ qt->pkt->size = 0;
212 ✗ qt->pkt->data = NULL;
213 ✗ pkt->flags = keyframe ? AV_PKT_FLAG_KEY : 0;
214 ✗ pkt->stream_index = st->index;
215 ✗ memset(pkt->data + pkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
216 ✗ return 0;
217 }
218 ✗ return AVERROR(EAGAIN);
219
220 ✗ case 1: /* constant packet size, multiple packets per RTP packet */
221 ✗ if (qt->bytes_per_frame == 0 ||
222 ✗ alen % qt->bytes_per_frame != 0)
223 ✗ return AVERROR_INVALIDDATA; /* wrongly padded */
224 ✗ qt->remaining = (alen / qt->bytes_per_frame) - 1;
225 ✗ if ((ret = av_new_packet(pkt, qt->bytes_per_frame)) < 0)
226 ✗ return ret;
227 ✗ memcpy(pkt->data, buf + avio_tell(pb), qt->bytes_per_frame);
228 ✗ pkt->flags = keyframe ? AV_PKT_FLAG_KEY : 0;
229 ✗ pkt->stream_index = st->index;
230 ✗ if (qt->remaining > 0) {
231 ✗ av_freep(&qt->pkt->data);
232 ✗ qt->pkt->data = av_realloc(NULL, qt->remaining * qt->bytes_per_frame);
233 ✗ if (!qt->pkt->data) {
234 ✗ av_packet_unref(pkt);
235 ✗ return AVERROR(ENOMEM);
236 }
237 ✗ qt->pkt->size = qt->remaining * qt->bytes_per_frame;
238 ✗ memcpy(qt->pkt->data,
239 ✗ buf + avio_tell(pb) + qt->bytes_per_frame,
240 ✗ qt->remaining * qt->bytes_per_frame);
241 ✗ qt->pkt->flags = pkt->flags;
242 ✗ return 1;
243 }
244 ✗ return 0;
245
246 ✗ default: /* unimplemented */
247 ✗ avpriv_request_sample(NULL, "RTP-X-QT with packing scheme 2");
248 ✗ return AVERROR_PATCHWELCOME;
249 }
250 }
251
252 ✗ static void qt_rtp_close(PayloadContext *qt)
253 {
254 ✗ av_freep(&qt->pkt->data);
255 ✗ av_packet_free(&qt->pkt);
256 ✗ }
257
258 #define RTP_QT_HANDLER(m, n, s, t) \
259 const RTPDynamicProtocolHandler ff_ ## m ## _rtp_ ## n ## _handler = { \
260 .enc_name = s, \
261 .codec_type = t, \
262 .codec_id = AV_CODEC_ID_NONE, \
263 .priv_data_size = sizeof(PayloadContext), \
264 .init = qt_rtp_init, \
265 .close = qt_rtp_close, \
266 .parse_packet = qt_rtp_parse_packet, \
267 }
268
269 RTP_QT_HANDLER(qt, vid, "X-QT", AVMEDIA_TYPE_VIDEO);
270 RTP_QT_HANDLER(qt, aud, "X-QT", AVMEDIA_TYPE_AUDIO);
271 RTP_QT_HANDLER(quicktime, vid, "X-QUICKTIME", AVMEDIA_TYPE_VIDEO);
272 RTP_QT_HANDLER(quicktime, aud, "X-QUICKTIME", AVMEDIA_TYPE_AUDIO);
273