rtpdec_mpeg4.c 7.66 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/**
 * Common code for the RTP depacketization of MPEG-4 formats.
 * Copyright (c) 2010 Fabrice Bellard
 *                    Romain Degez
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * @brief MPEG4 / RTP Code
 * @author Fabrice Bellard
 * @author Romain Degez
 */

30
#include "rtpdec_formats.h"
31
#include "internal.h"
32
#include "libavutil/avstring.h"
33
#include "libavcodec/get_bits.h"
34 35
#include <strings.h>

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
/** Structure listing useful vars to parse RTP packet payload*/
struct PayloadContext
{
    int sizelength;
    int indexlength;
    int indexdeltalength;
    int profile_level_id;
    int streamtype;
    int objecttype;
    char *mode;

    /** mpeg 4 AU headers */
    struct AUHeaders {
        int size;
        int index;
        int cts_flag;
        int cts;
        int dts_flag;
        int dts;
        int rap_flag;
        int streamstate;
    } *au_headers;
    int au_headers_allocated;
    int nb_au_headers;
    int au_headers_length_bytes;
    int cur_au_index;
};
63

64 65 66 67 68 69 70 71 72 73 74 75
typedef struct {
    const char *str;
    uint16_t    type;
    uint32_t    offset;
} AttrNameMap;

/* All known fmtp parameters and the corresponding RTPAttrTypeEnum */
#define ATTR_NAME_TYPE_INT 0
#define ATTR_NAME_TYPE_STR 1
static const AttrNameMap attr_names[]=
{
    { "SizeLength",       ATTR_NAME_TYPE_INT,
76
      offsetof(PayloadContext, sizelength) },
77
    { "IndexLength",      ATTR_NAME_TYPE_INT,
78
      offsetof(PayloadContext, indexlength) },
79
    { "IndexDeltaLength", ATTR_NAME_TYPE_INT,
80
      offsetof(PayloadContext, indexdeltalength) },
81
    { "profile-level-id", ATTR_NAME_TYPE_INT,
82
      offsetof(PayloadContext, profile_level_id) },
83
    { "StreamType",       ATTR_NAME_TYPE_INT,
84
      offsetof(PayloadContext, streamtype) },
85
    { "mode",             ATTR_NAME_TYPE_STR,
86
      offsetof(PayloadContext, mode) },
87 88 89
    { NULL, -1, -1 },
};

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
static PayloadContext *new_context(void)
{
    return av_mallocz(sizeof(PayloadContext));
}

static void free_context(PayloadContext * data)
{
    int i;
    for (i = 0; i < data->nb_au_headers; i++) {
         /* according to rtp_parse_mp4_au, we treat multiple
          * au headers as one, so nb_au_headers is always 1.
          * loop anyway in case this changes.
          * (note: changes done carelessly might lead to a double free)
          */
       av_free(&data->au_headers[i]);
    }
    av_free(data->mode);
    av_free(data);
}

110 111 112
static int parse_fmtp_config(AVCodecContext * codec, char *value)
{
    /* decode the hexa encoded parameter */
113
    int len = ff_hex_to_data(NULL, value);
114
    av_free(codec->extradata);
115 116 117 118
    codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE);
    if (!codec->extradata)
        return AVERROR(ENOMEM);
    codec->extradata_size = len;
119
    ff_hex_to_data(codec->extradata, value);
120 121 122
    return 0;
}

123
static int rtp_parse_mp4_au(PayloadContext *data, const uint8_t *buf)
124 125 126 127 128 129 130 131 132 133 134
{
    int au_headers_length, au_header_size, i;
    GetBitContext getbitcontext;

    /* decode the first 2 bytes where the AUHeader sections are stored
       length in bits */
    au_headers_length = AV_RB16(buf);

    if (au_headers_length > RTP_MAX_PACKET_LENGTH)
      return -1;

135
    data->au_headers_length_bytes = (au_headers_length + 7) / 8;
136 137 138 139

    /* skip AU headers length section (2 bytes) */
    buf += 2;

140
    init_get_bits(&getbitcontext, buf, data->au_headers_length_bytes * 8);
141 142

    /* XXX: Wrong if optionnal additional sections are present (cts, dts etc...) */
143
    au_header_size = data->sizelength + data->indexlength;
144 145 146
    if (au_header_size <= 0 || (au_headers_length % au_header_size != 0))
        return -1;

147 148 149 150 151
    data->nb_au_headers = au_headers_length / au_header_size;
    if (!data->au_headers || data->au_headers_allocated < data->nb_au_headers) {
        av_free(data->au_headers);
        data->au_headers = av_malloc(sizeof(struct AUHeaders) * data->nb_au_headers);
        data->au_headers_allocated = data->nb_au_headers;
152 153 154 155 156
    }

    /* XXX: We handle multiple AU Section as only one (need to fix this for interleaving)
       In my test, the FAAD decoder does not behave correctly when sending each AU one by one
       but does when sending the whole as one big packet...  */
157 158 159 160 161
    data->au_headers[0].size = 0;
    data->au_headers[0].index = 0;
    for (i = 0; i < data->nb_au_headers; ++i) {
        data->au_headers[0].size += get_bits_long(&getbitcontext, data->sizelength);
        data->au_headers[0].index = get_bits_long(&getbitcontext, data->indexlength);
162 163
    }

164
    data->nb_au_headers = 1;
165 166 167 168 169 170 171

    return 0;
}


/* Follows RFC 3640 */
static int aac_parse_packet(AVFormatContext *ctx,
172
                            PayloadContext *data,
173 174 175 176 177
                            AVStream *st,
                            AVPacket *pkt,
                            uint32_t *timestamp,
                            const uint8_t *buf, int len, int flags)
{
178
    if (rtp_parse_mp4_au(data, buf))
179 180
        return -1;

181 182
    buf += data->au_headers_length_bytes + 2;
    len -= data->au_headers_length_bytes + 2;
183 184 185

    /* XXX: Fixme we only handle the case where rtp_parse_mp4_au define
                    one au_header */
186 187
    av_new_packet(pkt, data->au_headers[0].size);
    memcpy(pkt->data, buf, data->au_headers[0].size);
188 189 190 191 192

    pkt->stream_index = st->index;
    return 0;
}

193 194
static int parse_fmtp(AVStream *stream, PayloadContext *data,
                      char *attr, char *value)
195
{
196 197 198
    AVCodecContext *codec = stream->codec;
    int res, i;

Josh Allmann's avatar
Josh Allmann committed
199 200
    if (!strcmp(attr, "config")) {
        res = parse_fmtp_config(codec, value);
201

Josh Allmann's avatar
Josh Allmann committed
202 203 204
        if (res < 0)
            return res;
    }
205

Josh Allmann's avatar
Josh Allmann committed
206 207 208 209 210 211 212 213 214 215
    if (codec->codec_id == CODEC_ID_AAC) {
        /* Looking for a known attribute */
        for (i = 0; attr_names[i].str; ++i) {
            if (!strcasecmp(attr, attr_names[i].str)) {
                if (attr_names[i].type == ATTR_NAME_TYPE_INT) {
                    *(int *)((char *)data+
                        attr_names[i].offset) = atoi(value);
                } else if (attr_names[i].type == ATTR_NAME_TYPE_STR)
                    *(char **)((char *)data+
                        attr_names[i].offset) = av_strdup(value);
216
            }
217
        }
Josh Allmann's avatar
Josh Allmann committed
218 219 220
    }
    return 0;
}
221

222 223 224 225 226 227 228
static int parse_sdp_line(AVFormatContext *s, int st_index,
                          PayloadContext *data, const char *line)
{
    const char *p;

    if (av_strstart(line, "fmtp:", &p))
        return ff_parse_fmtp(s->streams[st_index], data, p, parse_fmtp);
229

230
    return 0;
231 232 233 234 235 236 237
}

RTPDynamicProtocolHandler ff_mp4v_es_dynamic_handler = {
    .enc_name           = "MP4V-ES",
    .codec_type         = AVMEDIA_TYPE_VIDEO,
    .codec_id           = CODEC_ID_MPEG4,
    .parse_sdp_a_line   = parse_sdp_line,
238 239
    .alloc              = NULL,
    .free               = NULL,
240 241 242 243 244 245 246 247
    .parse_packet       = NULL
};

RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler = {
    .enc_name           = "mpeg4-generic",
    .codec_type         = AVMEDIA_TYPE_AUDIO,
    .codec_id           = CODEC_ID_AAC,
    .parse_sdp_a_line   = parse_sdp_line,
248 249
    .alloc              = new_context,
    .free               = free_context,
250
    .parse_packet       = aac_parse_packet
251
};