LCOV - code coverage report
Current view: top level - libavformat - rtpdec_qdm2.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 121 0.0 %
Date: 2017-12-18 06:23:41 Functions: 0 4 0.0 %

          Line data    Source code
       1             : /*
       2             :  * QDesign Music 2 (QDM2) payload for RTP
       3             :  * Copyright (c) 2010 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 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/avassert.h"
      30             : #include "libavutil/intreadwrite.h"
      31             : #include "libavcodec/avcodec.h"
      32             : #include "internal.h"
      33             : #include "rtp.h"
      34             : #include "rtpdec.h"
      35             : #include "rtpdec_formats.h"
      36             : 
      37             : struct PayloadContext {
      38             :     /** values read from the config header, used as packet headers */
      39             :     //@{
      40             :     int block_type;            ///< superblock type, value 2 .. 8
      41             :     int block_size;            ///< from extradata, used as pkt length
      42             :     int subpkts_per_block;     ///< max. nr. of subpackets to add per output buffer
      43             :     //@}
      44             : 
      45             :     /** Temporary storage for superblock restoring, per packet ID (0x80 total) */
      46             :     //@{
      47             :     uint16_t len[0x80];        ///< how much the temporary buffer is filled
      48             :     uint8_t  buf[0x80][0x800]; ///< the temporary storage buffer
      49             : 
      50             :     unsigned int cache;        ///< number of data packets that we have cached right now
      51             :     unsigned int n_pkts;       ///< number of RTP packets received since last packet output / config
      52             :     uint32_t timestamp;        ///< timestamp of next-to-be-returned packet
      53             :     //@}
      54             : };
      55             : 
      56             : /**
      57             :  * Parse configuration (basically the codec-specific extradata) from
      58             :  * an RTP config subpacket (starts with 0xff).
      59             :  *
      60             :  * Layout of the config subpacket (in bytes):
      61             :  * 1: 0xFF          <- config ID
      62             :  * then an array {
      63             :  *     1: size      <- of the current item
      64             :  *     1: item type <- 0 .. 4
      65             :  *     size-2: data <- data depends on the item type
      66             :  * }
      67             :  *
      68             :  * Item 0 implies the end of the config subpacket, and has no data.
      69             :  * Item 1 implies a stream configuration without extradata.
      70             :  * Item 2 max. nr. of subpackets per superblock
      71             :  * Item 3 superblock type for the stream
      72             :  * Item 4 implies a stream configuration with extradata (size >= 0x1c).
      73             :  *
      74             :  * @return <0 on error, otherwise the number of bytes parsed from the
      75             :  *         input buffer.
      76             :  */
      77           0 : static int qdm2_parse_config(PayloadContext *qdm, AVStream *st,
      78             :                              const uint8_t *buf, const uint8_t *end)
      79             : {
      80           0 :     const uint8_t *p = buf;
      81             : 
      82           0 :     while (end - p >= 2) {
      83           0 :         unsigned int item_len = p[0], config_item = p[1];
      84             : 
      85           0 :         if (item_len < 2 || end - p < item_len || config_item > 4)
      86           0 :             return AVERROR_INVALIDDATA;
      87             : 
      88           0 :         switch (config_item) {
      89           0 :             case 0: /* end of config block */
      90           0 :                 return p - buf + item_len;
      91           0 :             case 1: /* stream without extradata */
      92             :                 /* FIXME: set default qdm->block_size */
      93           0 :                 break;
      94           0 :             case 2: /**< subpackets per block */
      95           0 :                 if (item_len < 3)
      96           0 :                     return AVERROR_INVALIDDATA;
      97           0 :                 qdm->subpkts_per_block = p[2];
      98           0 :                 break;
      99           0 :             case 3: /* superblock type */
     100           0 :                 if (item_len < 4)
     101           0 :                     return AVERROR_INVALIDDATA;
     102           0 :                 qdm->block_type = AV_RB16(p + 2);
     103           0 :                 break;
     104           0 :             case 4: /* stream with extradata */
     105           0 :                 if (item_len < 30)
     106           0 :                     return AVERROR_INVALIDDATA;
     107           0 :                 av_freep(&st->codecpar->extradata);
     108           0 :                 if (ff_alloc_extradata(st->codecpar, 26 + item_len)) {
     109           0 :                     return AVERROR(ENOMEM);
     110             :                 }
     111           0 :                 AV_WB32(st->codecpar->extradata, 12);
     112           0 :                 memcpy(st->codecpar->extradata + 4, "frma", 4);
     113           0 :                 memcpy(st->codecpar->extradata + 8, "QDM2", 4);
     114           0 :                 AV_WB32(st->codecpar->extradata + 12, 6 + item_len);
     115           0 :                 memcpy(st->codecpar->extradata + 16, "QDCA", 4);
     116           0 :                 memcpy(st->codecpar->extradata + 20, p + 2, item_len - 2);
     117           0 :                 AV_WB32(st->codecpar->extradata + 18 + item_len, 8);
     118           0 :                 AV_WB32(st->codecpar->extradata + 22 + item_len, 0);
     119             : 
     120           0 :                 qdm->block_size = AV_RB32(p + 26);
     121           0 :                 break;
     122             :         }
     123             : 
     124           0 :         p += item_len;
     125             :     }
     126             : 
     127           0 :     return AVERROR(EAGAIN); /* not enough data */
     128             : }
     129             : 
     130             : /**
     131             :  * Parse 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           0 : static int qdm2_parse_subpacket(PayloadContext *qdm, AVStream *st,
     153             :                                 const uint8_t *buf, const uint8_t *end)
     154             : {
     155           0 :     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           0 :     id       = *p++;
     160           0 :     type     = *p++;
     161           0 :     if (type & 0x80) {
     162           0 :         len   = AV_RB16(p);
     163           0 :         p    += 2;
     164           0 :         type &= 0x7F;
     165             :     } else
     166           0 :         len = *p++;
     167             : 
     168           0 :     if (end - p < len + (type == 0x7F) || id >= 0x80)
     169           0 :         return AVERROR_INVALIDDATA;
     170           0 :     if (type == 0x7F)
     171           0 :         type |= *p++ << 8;
     172             : 
     173             :     /* copy data into a temporary buffer */
     174           0 :     to_copy = FFMIN(len + (p - &buf[1]), 0x800 - qdm->len[id]);
     175           0 :     memcpy(&qdm->buf[id][qdm->len[id]], buf + 1, to_copy);
     176           0 :     qdm->len[id] += to_copy;
     177             : 
     178           0 :     return p + len - buf;
     179             : }
     180             : 
     181             : /**
     182             :  * Add a superblock header around a set of subpackets.
     183             :  *
     184             :  * @return <0 on error, else 0.
     185             :  */
     186           0 : static int qdm2_restore_block(PayloadContext *qdm, AVStream *st, AVPacket *pkt)
     187             : {
     188             :     int to_copy, n, res, include_csum;
     189           0 :     uint8_t *p, *csum_pos = NULL;
     190             : 
     191             :     /* create packet to hold subpkts into a superblock */
     192           0 :     av_assert0(qdm->cache > 0);
     193           0 :     for (n = 0; n < 0x80; n++)
     194           0 :         if (qdm->len[n] > 0)
     195           0 :             break;
     196           0 :     av_assert0(n < 0x80);
     197             : 
     198           0 :     if ((res = av_new_packet(pkt, qdm->block_size)) < 0)
     199           0 :         return res;
     200           0 :     memset(pkt->data, 0, pkt->size);
     201           0 :     pkt->stream_index  = st->index;
     202           0 :     p                  = pkt->data;
     203             : 
     204             :     /* superblock header */
     205           0 :     if (qdm->len[n] > 0xff) {
     206           0 :         *p++ = qdm->block_type | 0x80;
     207           0 :         AV_WB16(p, qdm->len[n]);
     208           0 :         p   += 2;
     209             :     } else {
     210           0 :         *p++ = qdm->block_type;
     211           0 :         *p++ = qdm->len[n];
     212             :     }
     213           0 :     if ((include_csum = (qdm->block_type == 2 || qdm->block_type == 4))) {
     214           0 :         csum_pos = p;
     215           0 :         p       += 2;
     216             :     }
     217             : 
     218             :     /* subpacket data */
     219           0 :     to_copy = FFMIN(qdm->len[n], pkt->size - (p - pkt->data));
     220           0 :     memcpy(p, qdm->buf[n], to_copy);
     221           0 :     qdm->len[n] = 0;
     222             : 
     223             :     /* checksum header */
     224           0 :     if (include_csum) {
     225           0 :         unsigned int total = 0;
     226             :         uint8_t *q;
     227             : 
     228           0 :         for (q = pkt->data; q < &pkt->data[qdm->block_size]; q++)
     229           0 :             total += *q;
     230           0 :         AV_WB16(csum_pos, (uint16_t) total);
     231             :     }
     232             : 
     233           0 :     return 0;
     234             : }
     235             : 
     236             : /** return 0 on packet, no more left, 1 on packet, -1 on partial packet... */
     237           0 : 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, uint16_t seq,
     241             :                              int flags)
     242             : {
     243           0 :     int res = AVERROR_INVALIDDATA, n;
     244           0 :     const uint8_t *end = buf + len, *p = buf;
     245             : 
     246           0 :     if (len > 0) {
     247           0 :         if (len < 2)
     248           0 :             return AVERROR_INVALIDDATA;
     249             : 
     250             :         /* configuration block */
     251           0 :         if (*p == 0xff) {
     252           0 :             if (qdm->n_pkts > 0) {
     253           0 :                 av_log(s, AV_LOG_WARNING,
     254             :                        "Out of sequence config - dropping queue\n");
     255           0 :                 qdm->n_pkts = 0;
     256           0 :                 memset(qdm->len, 0, sizeof(qdm->len));
     257             :             }
     258             : 
     259           0 :             if ((res = qdm2_parse_config(qdm, st, ++p, end)) < 0)
     260           0 :                 return res;
     261           0 :             p += res;
     262             : 
     263             :             /* We set codec_id to AV_CODEC_ID_NONE initially to
     264             :              * delay decoder initialization since extradata is
     265             :              * carried within the RTP stream, not SDP. Here,
     266             :              * by setting codec_id to AV_CODEC_ID_QDM2, we are signalling
     267             :              * to the decoder that it is OK to initialize. */
     268           0 :             st->codecpar->codec_id = AV_CODEC_ID_QDM2;
     269             :         }
     270           0 :         if (st->codecpar->codec_id == AV_CODEC_ID_NONE)
     271           0 :             return AVERROR(EAGAIN);
     272             : 
     273             :         /* subpackets */
     274           0 :         while (end - p >= 4) {
     275           0 :             if ((res = qdm2_parse_subpacket(qdm, st, p, end)) < 0)
     276           0 :                 return res;
     277           0 :             p += res;
     278             :         }
     279             : 
     280           0 :         qdm->timestamp = *timestamp;
     281           0 :         if (++qdm->n_pkts < qdm->subpkts_per_block)
     282           0 :             return AVERROR(EAGAIN);
     283           0 :         qdm->cache = 0;
     284           0 :         for (n = 0; n < 0x80; n++)
     285           0 :             if (qdm->len[n] > 0)
     286           0 :                 qdm->cache++;
     287             :     }
     288             : 
     289             :     /* output the subpackets into freshly created superblock structures */
     290           0 :     if (!qdm->cache || (res = qdm2_restore_block(qdm, st, pkt)) < 0)
     291           0 :         return res;
     292           0 :     if (--qdm->cache == 0)
     293           0 :         qdm->n_pkts = 0;
     294             : 
     295           0 :     *timestamp = qdm->timestamp;
     296           0 :     qdm->timestamp = RTP_NOTS_VALUE;
     297             : 
     298           0 :     return (qdm->cache > 0) ? 1 : 0;
     299             : }
     300             : 
     301             : RTPDynamicProtocolHandler ff_qdm2_dynamic_handler = {
     302             :     .enc_name         = "X-QDM",
     303             :     .codec_type       = AVMEDIA_TYPE_AUDIO,
     304             :     .codec_id         = AV_CODEC_ID_NONE,
     305             :     .priv_data_size   = sizeof(PayloadContext),
     306             :     .parse_packet     = qdm2_parse_packet,
     307             : };

Generated by: LCOV version 1.13