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