ffmpeg / libavformat / rtpdec_qdm2.c @ 6cc7f139
History | View | Annotate | Download (10.4 KB)
1 |
/*
|
---|---|
2 |
* QDesign Music 2 (QDM2) payload for RTP
|
3 |
* Copyright (c) 2010 Ronald S. Bultje
|
4 |
*
|
5 |
* This file is part of Libav.
|
6 |
*
|
7 |
* Libav 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 |
* Libav 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 Libav; 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 RTP support for the QDM2 payload (todo: wiki)
|
25 |
* @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
26 |
*/
|
27 |
|
28 |
#include <string.h> |
29 |
#include "libavutil/intreadwrite.h" |
30 |
#include "libavcodec/avcodec.h" |
31 |
#include "rtp.h" |
32 |
#include "rtpdec.h" |
33 |
#include "rtpdec_formats.h" |
34 |
|
35 |
struct PayloadContext {
|
36 |
/** values read from the config header, used as packet headers */
|
37 |
//@{
|
38 |
int block_type; ///< superblock type, value 2 .. 8 |
39 |
int block_size; ///< from extradata, used as pkt length |
40 |
int subpkts_per_block; ///< max. nr. of subpackets to add per output buffer |
41 |
//@}
|
42 |
|
43 |
/** Temporary storage for superblock restoring, per packet ID (0x80 total) */
|
44 |
//@{
|
45 |
uint16_t len[0x80]; ///< how much the temporary buffer is filled |
46 |
uint8_t buf[0x80][0x800]; ///< the temporary storage buffer |
47 |
|
48 |
unsigned int cache; ///< number of data packets that we have cached right now |
49 |
unsigned int n_pkts; ///< number of RTP packets received since last packet output / config |
50 |
uint32_t timestamp; ///< timestamp of next-to-be-returned packet
|
51 |
//@}
|
52 |
}; |
53 |
|
54 |
/**
|
55 |
* Parses configuration (basically the codec-specific extradata) from
|
56 |
* a RTP config subpacket (starts with 0xff).
|
57 |
*
|
58 |
* Layout of the config subpacket (in bytes):
|
59 |
* 1: 0xFF <- config ID
|
60 |
* then an array {
|
61 |
* 1: size <- of the current item
|
62 |
* 1: item type <- 0 .. 4
|
63 |
* size-2: data <- data depends on the item type
|
64 |
* }
|
65 |
*
|
66 |
* Item 0 implies the end of the config subpacket, and has no data.
|
67 |
* Item 1 implies a stream configuration without extradata.
|
68 |
* Item 2 max. nr. of subpackets per superblock
|
69 |
* Item 3 superblock type for the stream
|
70 |
* Item 4 implies a stream configuration with extradata (size >= 0x1c).
|
71 |
*
|
72 |
* @return <0 on error, otherwise the number of bytes parsed from the
|
73 |
* input buffer.
|
74 |
*/
|
75 |
static int qdm2_parse_config(PayloadContext *qdm, AVStream *st, |
76 |
const uint8_t *buf, const uint8_t *end) |
77 |
{ |
78 |
const uint8_t *p = buf;
|
79 |
|
80 |
while (end - p >= 2) { |
81 |
unsigned int item_len = p[0], config_item = p[1]; |
82 |
|
83 |
if (item_len < 2 || end - p < item_len || config_item > 4) |
84 |
return AVERROR_INVALIDDATA;
|
85 |
|
86 |
switch (config_item) {
|
87 |
case 0: /* end of config block */ |
88 |
return p - buf + item_len;
|
89 |
case 1: /* stream without extradata */ |
90 |
/* FIXME: set default qdm->block_size */
|
91 |
break;
|
92 |
case 2: /**< subpackets per block */ |
93 |
if (item_len < 3) |
94 |
return AVERROR_INVALIDDATA;
|
95 |
qdm->subpkts_per_block = p[2];
|
96 |
break;
|
97 |
case 3: /* superblock type */ |
98 |
if (item_len < 4) |
99 |
return AVERROR_INVALIDDATA;
|
100 |
qdm->block_type = AV_RB16(p + 2);
|
101 |
break;
|
102 |
case 4: /* stream with extradata */ |
103 |
if (item_len < 30) |
104 |
return AVERROR_INVALIDDATA;
|
105 |
av_freep(&st->codec->extradata); |
106 |
st->codec->extradata_size = 26 + item_len;
|
107 |
if (!(st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE))) {
|
108 |
st->codec->extradata_size = 0;
|
109 |
return AVERROR(ENOMEM);
|
110 |
} |
111 |
AV_WB32(st->codec->extradata, 12);
|
112 |
memcpy(st->codec->extradata + 4, "frma", 4); |
113 |
memcpy(st->codec->extradata + 8, "QDM2", 4); |
114 |
AV_WB32(st->codec->extradata + 12, 6 + item_len); |
115 |
memcpy(st->codec->extradata + 16, "QDCA", 4); |
116 |
memcpy(st->codec->extradata + 20, p + 2, item_len - 2); |
117 |
AV_WB32(st->codec->extradata + 18 + item_len, 8); |
118 |
AV_WB32(st->codec->extradata + 22 + item_len, 0); |
119 |
|
120 |
qdm->block_size = AV_RB32(p + 26);
|
121 |
break;
|
122 |
} |
123 |
|
124 |
p += item_len; |
125 |
} |
126 |
|
127 |
return AVERROR(EAGAIN); /* not enough data */ |
128 |
} |
129 |
|
130 |
/**
|
131 |
* Parses a single subpacket. We store this subpacket in an intermediate
|
132 |
* buffer (position depends on the ID (byte[0]). When called, at least
|
133 |
* 4 bytes are available for reading (see qdm2_parse_packet()).
|
134 |
*
|
135 |
* Layout of a single subpacket (RTP packets commonly contain multiple
|
136 |
* such subpackets) - length in bytes:
|
137 |
* 1: ordering ID <- 0 .. 0x7F
|
138 |
* 1: subpacket type <- 0 .. 0x7F; value & 0x80 means subpacket length = 2 bytes, else 1 byte
|
139 |
* 1/2: subpacket length <- length of the data following the flags/length fields
|
140 |
* if (subpacket type & 0x7F) == 0x7F
|
141 |
* 1: subpacket type, higher bits
|
142 |
* size: subpacket data
|
143 |
*
|
144 |
* The subpackets come in randomly, and should be encapsulated into 1
|
145 |
* or more superblocks (containing qdm->subpkts_per_block subpackets
|
146 |
* each) per RTP packet, in order of ascending "ordering ID", see
|
147 |
* qdm2_restore_block().
|
148 |
*
|
149 |
* @return <0 on error, otherwise the number of bytes parsed from the
|
150 |
* input buffer.
|
151 |
*/
|
152 |
static int qdm2_parse_subpacket(PayloadContext *qdm, AVStream *st, |
153 |
const uint8_t *buf, const uint8_t *end) |
154 |
{ |
155 |
const uint8_t *p = buf;
|
156 |
unsigned int id, len, type, to_copy; |
157 |
|
158 |
/* parse header so we know the size of the header/data */
|
159 |
id = *p++; |
160 |
type = *p++; |
161 |
if (type & 0x80) { |
162 |
len = AV_RB16(p); |
163 |
p += 2;
|
164 |
type &= 0x7F;
|
165 |
} else
|
166 |
len = *p++; |
167 |
|
168 |
if (end - p < len + (type == 0x7F) || id >= 0x80) |
169 |
return AVERROR_INVALIDDATA;
|
170 |
if (type == 0x7F) |
171 |
type |= *p++ << 8;
|
172 |
|
173 |
/* copy data into a temporary buffer */
|
174 |
to_copy = FFMIN(len + (p - &buf[1]), 0x800 - qdm->len[id]); |
175 |
memcpy(&qdm->buf[id][qdm->len[id]], buf + 1, to_copy);
|
176 |
qdm->len[id] += to_copy; |
177 |
|
178 |
return p + len - buf;
|
179 |
} |
180 |
|
181 |
/**
|
182 |
* Adds a superblock header around a set of subpackets.
|
183 |
*
|
184 |
* @return <0 on error, else 0.
|
185 |
*/
|
186 |
static int qdm2_restore_block(PayloadContext *qdm, AVStream *st, AVPacket *pkt) |
187 |
{ |
188 |
int to_copy, n, res, include_csum;
|
189 |
uint8_t *p, *csum_pos = NULL;
|
190 |
|
191 |
/* create packet to hold subpkts into a superblock */
|
192 |
assert(qdm->cache > 0);
|
193 |
for (n = 0; n < 0x80; n++) |
194 |
if (qdm->len[n] > 0) |
195 |
break;
|
196 |
assert(n < 0x80);
|
197 |
|
198 |
if ((res = av_new_packet(pkt, qdm->block_size)) < 0) |
199 |
return res;
|
200 |
memset(pkt->data, 0, pkt->size);
|
201 |
pkt->stream_index = st->index; |
202 |
p = pkt->data; |
203 |
|
204 |
/* superblock header */
|
205 |
if (qdm->len[n] > 0xff) { |
206 |
*p++ = qdm->block_type | 0x80;
|
207 |
AV_WB16(p, qdm->len[n]); |
208 |
p += 2;
|
209 |
} else {
|
210 |
*p++ = qdm->block_type; |
211 |
*p++ = qdm->len[n]; |
212 |
} |
213 |
if ((include_csum = (qdm->block_type == 2 || qdm->block_type == 4))) { |
214 |
csum_pos = p; |
215 |
p += 2;
|
216 |
} |
217 |
|
218 |
/* subpacket data */
|
219 |
to_copy = FFMIN(qdm->len[n], pkt->size - (p - pkt->data)); |
220 |
memcpy(p, qdm->buf[n], to_copy); |
221 |
qdm->len[n] = 0;
|
222 |
|
223 |
/* checksum header */
|
224 |
if (include_csum) {
|
225 |
unsigned int total = 0; |
226 |
uint8_t *q; |
227 |
|
228 |
for (q = pkt->data; q < &pkt->data[qdm->block_size]; q++)
|
229 |
total += *q; |
230 |
AV_WB16(csum_pos, (uint16_t) total); |
231 |
} |
232 |
|
233 |
return 0; |
234 |
} |
235 |
|
236 |
/** return 0 on packet, no more left, 1 on packet, -1 on partial packet... */
|
237 |
static int qdm2_parse_packet(AVFormatContext *s, PayloadContext *qdm, |
238 |
AVStream *st, AVPacket *pkt, |
239 |
uint32_t *timestamp, |
240 |
const uint8_t *buf, int len, int flags) |
241 |
{ |
242 |
int res = AVERROR_INVALIDDATA, n;
|
243 |
const uint8_t *end = buf + len, *p = buf;
|
244 |
|
245 |
if (len > 0) { |
246 |
if (len < 2) |
247 |
return AVERROR_INVALIDDATA;
|
248 |
|
249 |
/* configuration block */
|
250 |
if (*p == 0xff) { |
251 |
if (qdm->n_pkts > 0) { |
252 |
av_log(s, AV_LOG_WARNING, |
253 |
"Out of sequence config - dropping queue\n");
|
254 |
qdm->n_pkts = 0;
|
255 |
memset(qdm->len, 0, sizeof(qdm->len)); |
256 |
} |
257 |
|
258 |
if ((res = qdm2_parse_config(qdm, st, ++p, end)) < 0) |
259 |
return res;
|
260 |
p += res; |
261 |
|
262 |
/* We set codec_id to CODEC_ID_NONE initially to
|
263 |
* delay decoder initialization since extradata is
|
264 |
* carried within the RTP stream, not SDP. Here,
|
265 |
* by setting codec_id to CODEC_ID_QDM2, we are signalling
|
266 |
* to the decoder that it is OK to initialize. */
|
267 |
st->codec->codec_id = CODEC_ID_QDM2; |
268 |
} |
269 |
|
270 |
/* subpackets */
|
271 |
while (end - p >= 4) { |
272 |
if ((res = qdm2_parse_subpacket(qdm, st, p, end)) < 0) |
273 |
return res;
|
274 |
p += res; |
275 |
} |
276 |
|
277 |
qdm->timestamp = *timestamp; |
278 |
if (++qdm->n_pkts < qdm->subpkts_per_block)
|
279 |
return AVERROR(EAGAIN);
|
280 |
qdm->cache = 0;
|
281 |
for (n = 0; n < 0x80; n++) |
282 |
if (qdm->len[n] > 0) |
283 |
qdm->cache++; |
284 |
} |
285 |
|
286 |
/* output the subpackets into freshly created superblock structures */
|
287 |
if (!qdm->cache || (res = qdm2_restore_block(qdm, st, pkt)) < 0) |
288 |
return res;
|
289 |
if (--qdm->cache == 0) |
290 |
qdm->n_pkts = 0;
|
291 |
|
292 |
*timestamp = qdm->timestamp; |
293 |
qdm->timestamp = RTP_NOTS_VALUE; |
294 |
|
295 |
return (qdm->cache > 0) ? 1 : 0; |
296 |
} |
297 |
|
298 |
static PayloadContext *qdm2_extradata_new(void) |
299 |
{ |
300 |
return av_mallocz(sizeof(PayloadContext)); |
301 |
} |
302 |
|
303 |
static void qdm2_extradata_free(PayloadContext *qdm) |
304 |
{ |
305 |
av_free(qdm); |
306 |
} |
307 |
|
308 |
RTPDynamicProtocolHandler ff_qdm2_dynamic_handler = { |
309 |
.enc_name = "X-QDM",
|
310 |
.codec_type = AVMEDIA_TYPE_AUDIO, |
311 |
.codec_id = CODEC_ID_NONE, |
312 |
.open = qdm2_extradata_new, |
313 |
.close = qdm2_extradata_free, |
314 |
.parse_packet = qdm2_parse_packet, |
315 |
}; |