yading@11: /* yading@11: * RTP Depacketization of MP4A-LATM, RFC 3016 yading@11: * Copyright (c) 2010 Martin Storsjo yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "rtpdec_formats.h" yading@11: #include "internal.h" yading@11: #include "libavutil/avstring.h" yading@11: #include "libavcodec/get_bits.h" yading@11: yading@11: struct PayloadContext { yading@11: AVIOContext *dyn_buf; yading@11: uint8_t *buf; yading@11: int pos, len; yading@11: uint32_t timestamp; yading@11: }; yading@11: yading@11: static PayloadContext *latm_new_context(void) yading@11: { yading@11: return av_mallocz(sizeof(PayloadContext)); yading@11: } yading@11: yading@11: static void latm_free_context(PayloadContext *data) yading@11: { yading@11: if (!data) yading@11: return; yading@11: if (data->dyn_buf) { yading@11: uint8_t *p; yading@11: avio_close_dyn_buf(data->dyn_buf, &p); yading@11: av_free(p); yading@11: } yading@11: av_free(data->buf); yading@11: av_free(data); yading@11: } yading@11: yading@11: static int latm_parse_packet(AVFormatContext *ctx, PayloadContext *data, yading@11: AVStream *st, AVPacket *pkt, uint32_t *timestamp, yading@11: const uint8_t *buf, int len, uint16_t seq, yading@11: int flags) yading@11: { yading@11: int ret, cur_len; yading@11: yading@11: if (buf) { yading@11: if (!data->dyn_buf || data->timestamp != *timestamp) { yading@11: av_freep(&data->buf); yading@11: if (data->dyn_buf) yading@11: avio_close_dyn_buf(data->dyn_buf, &data->buf); yading@11: data->dyn_buf = NULL; yading@11: av_freep(&data->buf); yading@11: yading@11: data->timestamp = *timestamp; yading@11: if ((ret = avio_open_dyn_buf(&data->dyn_buf)) < 0) yading@11: return ret; yading@11: } yading@11: avio_write(data->dyn_buf, buf, len); yading@11: yading@11: if (!(flags & RTP_FLAG_MARKER)) yading@11: return AVERROR(EAGAIN); yading@11: av_free(data->buf); yading@11: data->len = avio_close_dyn_buf(data->dyn_buf, &data->buf); yading@11: data->dyn_buf = NULL; yading@11: data->pos = 0; yading@11: } yading@11: yading@11: if (!data->buf) { yading@11: av_log(ctx, AV_LOG_ERROR, "No data available yet\n"); yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: cur_len = 0; yading@11: while (data->pos < data->len) { yading@11: uint8_t val = data->buf[data->pos++]; yading@11: cur_len += val; yading@11: if (val != 0xff) yading@11: break; yading@11: } yading@11: if (data->pos + cur_len > data->len) { yading@11: av_log(ctx, AV_LOG_ERROR, "Malformed LATM packet\n"); yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: if ((ret = av_new_packet(pkt, cur_len)) < 0) yading@11: return ret; yading@11: memcpy(pkt->data, data->buf + data->pos, cur_len); yading@11: data->pos += cur_len; yading@11: pkt->stream_index = st->index; yading@11: return data->pos < data->len; yading@11: } yading@11: yading@11: static int parse_fmtp_config(AVStream *st, char *value) yading@11: { yading@11: int len = ff_hex_to_data(NULL, value), i, ret = 0; yading@11: GetBitContext gb; yading@11: uint8_t *config; yading@11: int audio_mux_version, same_time_framing, num_programs, num_layers; yading@11: yading@11: /* Pad this buffer, too, to avoid out of bounds reads with get_bits below */ yading@11: config = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!config) yading@11: return AVERROR(ENOMEM); yading@11: ff_hex_to_data(config, value); yading@11: init_get_bits(&gb, config, len*8); yading@11: audio_mux_version = get_bits(&gb, 1); yading@11: same_time_framing = get_bits(&gb, 1); yading@11: skip_bits(&gb, 6); /* num_sub_frames */ yading@11: num_programs = get_bits(&gb, 4); yading@11: num_layers = get_bits(&gb, 3); yading@11: if (audio_mux_version != 0 || same_time_framing != 1 || num_programs != 0 || yading@11: num_layers != 0) { yading@11: av_log(NULL, AV_LOG_WARNING, "Unsupported LATM config (%d,%d,%d,%d)\n", yading@11: audio_mux_version, same_time_framing, yading@11: num_programs, num_layers); yading@11: ret = AVERROR_PATCHWELCOME; yading@11: goto end; yading@11: } yading@11: av_freep(&st->codec->extradata); yading@11: st->codec->extradata_size = (get_bits_left(&gb) + 7)/8; yading@11: st->codec->extradata = av_mallocz(st->codec->extradata_size + yading@11: FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!st->codec->extradata) { yading@11: ret = AVERROR(ENOMEM); yading@11: goto end; yading@11: } yading@11: for (i = 0; i < st->codec->extradata_size; i++) yading@11: st->codec->extradata[i] = get_bits(&gb, 8); yading@11: yading@11: end: yading@11: av_free(config); yading@11: return ret; yading@11: } yading@11: yading@11: static int parse_fmtp(AVStream *stream, PayloadContext *data, yading@11: char *attr, char *value) yading@11: { yading@11: int res; yading@11: yading@11: if (!strcmp(attr, "config")) { yading@11: res = parse_fmtp_config(stream, value); yading@11: if (res < 0) yading@11: return res; yading@11: } else if (!strcmp(attr, "cpresent")) { yading@11: int cpresent = atoi(value); yading@11: if (cpresent != 0) yading@11: avpriv_request_sample(NULL, yading@11: "RTP MP4A-LATM with in-band configuration"); yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int latm_parse_sdp_line(AVFormatContext *s, int st_index, yading@11: PayloadContext *data, const char *line) yading@11: { yading@11: const char *p; yading@11: yading@11: if (st_index < 0) yading@11: return 0; yading@11: yading@11: if (av_strstart(line, "fmtp:", &p)) yading@11: return ff_parse_fmtp(s->streams[st_index], data, p, parse_fmtp); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: RTPDynamicProtocolHandler ff_mp4a_latm_dynamic_handler = { yading@11: .enc_name = "MP4A-LATM", yading@11: .codec_type = AVMEDIA_TYPE_AUDIO, yading@11: .codec_id = AV_CODEC_ID_AAC, yading@11: .parse_sdp_a_line = latm_parse_sdp_line, yading@11: .alloc = latm_new_context, yading@11: .free = latm_free_context, yading@11: .parse_packet = latm_parse_packet yading@11: };