Libav 0.7.1
|
00001 /* 00002 * QDesign Music 2 (QDM2) payload for RTP 00003 * Copyright (c) 2010 Ronald S. Bultje 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00028 #include <string.h> 00029 #include "libavutil/intreadwrite.h" 00030 #include "libavcodec/avcodec.h" 00031 #include "rtp.h" 00032 #include "rtpdec.h" 00033 #include "rtpdec_formats.h" 00034 00035 struct PayloadContext { 00038 int block_type; 00039 int block_size; 00040 int subpkts_per_block; 00041 00042 00045 uint16_t len[0x80]; 00046 uint8_t buf[0x80][0x800]; 00047 00048 unsigned int cache; 00049 unsigned int n_pkts; 00050 uint32_t timestamp; 00051 00052 }; 00053 00075 static int qdm2_parse_config(PayloadContext *qdm, AVStream *st, 00076 const uint8_t *buf, const uint8_t *end) 00077 { 00078 const uint8_t *p = buf; 00079 00080 while (end - p >= 2) { 00081 unsigned int item_len = p[0], config_item = p[1]; 00082 00083 if (item_len < 2 || end - p < item_len || config_item > 4) 00084 return AVERROR_INVALIDDATA; 00085 00086 switch (config_item) { 00087 case 0: /* end of config block */ 00088 return p - buf + item_len; 00089 case 1: /* stream without extradata */ 00090 /* FIXME: set default qdm->block_size */ 00091 break; 00092 case 2: 00093 if (item_len < 3) 00094 return AVERROR_INVALIDDATA; 00095 qdm->subpkts_per_block = p[2]; 00096 break; 00097 case 3: /* superblock type */ 00098 if (item_len < 4) 00099 return AVERROR_INVALIDDATA; 00100 qdm->block_type = AV_RB16(p + 2); 00101 break; 00102 case 4: /* stream with extradata */ 00103 if (item_len < 30) 00104 return AVERROR_INVALIDDATA; 00105 av_freep(&st->codec->extradata); 00106 st->codec->extradata_size = 26 + item_len; 00107 if (!(st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE))) { 00108 st->codec->extradata_size = 0; 00109 return AVERROR(ENOMEM); 00110 } 00111 AV_WB32(st->codec->extradata, 12); 00112 memcpy(st->codec->extradata + 4, "frma", 4); 00113 memcpy(st->codec->extradata + 8, "QDM2", 4); 00114 AV_WB32(st->codec->extradata + 12, 6 + item_len); 00115 memcpy(st->codec->extradata + 16, "QDCA", 4); 00116 memcpy(st->codec->extradata + 20, p + 2, item_len - 2); 00117 AV_WB32(st->codec->extradata + 18 + item_len, 8); 00118 AV_WB32(st->codec->extradata + 22 + item_len, 0); 00119 00120 qdm->block_size = AV_RB32(p + 26); 00121 break; 00122 } 00123 00124 p += item_len; 00125 } 00126 00127 return AVERROR(EAGAIN); /* not enough data */ 00128 } 00129 00152 static int qdm2_parse_subpacket(PayloadContext *qdm, AVStream *st, 00153 const uint8_t *buf, const uint8_t *end) 00154 { 00155 const uint8_t *p = buf; 00156 unsigned int id, len, type, to_copy; 00157 00158 /* parse header so we know the size of the header/data */ 00159 id = *p++; 00160 type = *p++; 00161 if (type & 0x80) { 00162 len = AV_RB16(p); 00163 p += 2; 00164 type &= 0x7F; 00165 } else 00166 len = *p++; 00167 00168 if (end - p < len + (type == 0x7F) || id >= 0x80) 00169 return AVERROR_INVALIDDATA; 00170 if (type == 0x7F) 00171 type |= *p++ << 8; 00172 00173 /* copy data into a temporary buffer */ 00174 to_copy = FFMIN(len + (p - &buf[1]), 0x800 - qdm->len[id]); 00175 memcpy(&qdm->buf[id][qdm->len[id]], buf + 1, to_copy); 00176 qdm->len[id] += to_copy; 00177 00178 return p + len - buf; 00179 } 00180 00186 static int qdm2_restore_block(PayloadContext *qdm, AVStream *st, AVPacket *pkt) 00187 { 00188 int to_copy, n, res, include_csum; 00189 uint8_t *p, *csum_pos = NULL; 00190 00191 /* create packet to hold subpkts into a superblock */ 00192 assert(qdm->cache > 0); 00193 for (n = 0; n < 0x80; n++) 00194 if (qdm->len[n] > 0) 00195 break; 00196 assert(n < 0x80); 00197 00198 if ((res = av_new_packet(pkt, qdm->block_size)) < 0) 00199 return res; 00200 memset(pkt->data, 0, pkt->size); 00201 pkt->stream_index = st->index; 00202 p = pkt->data; 00203 00204 /* superblock header */ 00205 if (qdm->len[n] > 0xff) { 00206 *p++ = qdm->block_type | 0x80; 00207 AV_WB16(p, qdm->len[n]); 00208 p += 2; 00209 } else { 00210 *p++ = qdm->block_type; 00211 *p++ = qdm->len[n]; 00212 } 00213 if ((include_csum = (qdm->block_type == 2 || qdm->block_type == 4))) { 00214 csum_pos = p; 00215 p += 2; 00216 } 00217 00218 /* subpacket data */ 00219 to_copy = FFMIN(qdm->len[n], pkt->size - (p - pkt->data)); 00220 memcpy(p, qdm->buf[n], to_copy); 00221 qdm->len[n] = 0; 00222 00223 /* checksum header */ 00224 if (include_csum) { 00225 unsigned int total = 0; 00226 uint8_t *q; 00227 00228 for (q = pkt->data; q < &pkt->data[qdm->block_size]; q++) 00229 total += *q; 00230 AV_WB16(csum_pos, (uint16_t) total); 00231 } 00232 00233 return 0; 00234 } 00235 00237 static int qdm2_parse_packet(AVFormatContext *s, PayloadContext *qdm, 00238 AVStream *st, AVPacket *pkt, 00239 uint32_t *timestamp, 00240 const uint8_t *buf, int len, int flags) 00241 { 00242 int res = AVERROR_INVALIDDATA, n; 00243 const uint8_t *end = buf + len, *p = buf; 00244 00245 if (len > 0) { 00246 if (len < 2) 00247 return AVERROR_INVALIDDATA; 00248 00249 /* configuration block */ 00250 if (*p == 0xff) { 00251 if (qdm->n_pkts > 0) { 00252 av_log(s, AV_LOG_WARNING, 00253 "Out of sequence config - dropping queue\n"); 00254 qdm->n_pkts = 0; 00255 memset(qdm->len, 0, sizeof(qdm->len)); 00256 } 00257 00258 if ((res = qdm2_parse_config(qdm, st, ++p, end)) < 0) 00259 return res; 00260 p += res; 00261 00262 /* We set codec_id to CODEC_ID_NONE initially to 00263 * delay decoder initialization since extradata is 00264 * carried within the RTP stream, not SDP. Here, 00265 * by setting codec_id to CODEC_ID_QDM2, we are signalling 00266 * to the decoder that it is OK to initialize. */ 00267 st->codec->codec_id = CODEC_ID_QDM2; 00268 } 00269 if (st->codec->codec_id == CODEC_ID_NONE) 00270 return AVERROR(EAGAIN); 00271 00272 /* subpackets */ 00273 while (end - p >= 4) { 00274 if ((res = qdm2_parse_subpacket(qdm, st, p, end)) < 0) 00275 return res; 00276 p += res; 00277 } 00278 00279 qdm->timestamp = *timestamp; 00280 if (++qdm->n_pkts < qdm->subpkts_per_block) 00281 return AVERROR(EAGAIN); 00282 qdm->cache = 0; 00283 for (n = 0; n < 0x80; n++) 00284 if (qdm->len[n] > 0) 00285 qdm->cache++; 00286 } 00287 00288 /* output the subpackets into freshly created superblock structures */ 00289 if (!qdm->cache || (res = qdm2_restore_block(qdm, st, pkt)) < 0) 00290 return res; 00291 if (--qdm->cache == 0) 00292 qdm->n_pkts = 0; 00293 00294 *timestamp = qdm->timestamp; 00295 qdm->timestamp = RTP_NOTS_VALUE; 00296 00297 return (qdm->cache > 0) ? 1 : 0; 00298 } 00299 00300 static PayloadContext *qdm2_extradata_new(void) 00301 { 00302 return av_mallocz(sizeof(PayloadContext)); 00303 } 00304 00305 static void qdm2_extradata_free(PayloadContext *qdm) 00306 { 00307 av_free(qdm); 00308 } 00309 00310 RTPDynamicProtocolHandler ff_qdm2_dynamic_handler = { 00311 .enc_name = "X-QDM", 00312 .codec_type = AVMEDIA_TYPE_AUDIO, 00313 .codec_id = CODEC_ID_NONE, 00314 .alloc = qdm2_extradata_new, 00315 .free = qdm2_extradata_free, 00316 .parse_packet = qdm2_parse_packet, 00317 };