Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * RTP Depacketization of MP4A-LATM, RFC 3016 | |
3 | * Copyright (c) 2010 Martin Storsjo | |
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 | #include "rtpdec_formats.h" | |
23 | #include "internal.h" | |
24 | #include "libavutil/avstring.h" | |
25 | #include "libavcodec/get_bits.h" | |
26 | ||
27 | struct PayloadContext { | |
28 | AVIOContext *dyn_buf; | |
29 | uint8_t *buf; | |
30 | int pos, len; | |
31 | uint32_t timestamp; | |
32 | }; | |
33 | ||
34 | static PayloadContext *latm_new_context(void) | |
35 | { | |
36 | return av_mallocz(sizeof(PayloadContext)); | |
37 | } | |
38 | ||
39 | static void latm_free_context(PayloadContext *data) | |
40 | { | |
41 | if (!data) | |
42 | return; | |
43 | if (data->dyn_buf) { | |
44 | uint8_t *p; | |
45 | avio_close_dyn_buf(data->dyn_buf, &p); | |
46 | av_free(p); | |
47 | } | |
48 | av_free(data->buf); | |
49 | av_free(data); | |
50 | } | |
51 | ||
52 | static int latm_parse_packet(AVFormatContext *ctx, PayloadContext *data, | |
53 | AVStream *st, AVPacket *pkt, uint32_t *timestamp, | |
54 | const uint8_t *buf, int len, uint16_t seq, | |
55 | int flags) | |
56 | { | |
57 | int ret, cur_len; | |
58 | ||
59 | if (buf) { | |
60 | if (!data->dyn_buf || data->timestamp != *timestamp) { | |
61 | av_freep(&data->buf); | |
62 | if (data->dyn_buf) | |
63 | avio_close_dyn_buf(data->dyn_buf, &data->buf); | |
64 | data->dyn_buf = NULL; | |
65 | av_freep(&data->buf); | |
66 | ||
67 | data->timestamp = *timestamp; | |
68 | if ((ret = avio_open_dyn_buf(&data->dyn_buf)) < 0) | |
69 | return ret; | |
70 | } | |
71 | avio_write(data->dyn_buf, buf, len); | |
72 | ||
73 | if (!(flags & RTP_FLAG_MARKER)) | |
74 | return AVERROR(EAGAIN); | |
75 | av_free(data->buf); | |
76 | data->len = avio_close_dyn_buf(data->dyn_buf, &data->buf); | |
77 | data->dyn_buf = NULL; | |
78 | data->pos = 0; | |
79 | } | |
80 | ||
81 | if (!data->buf) { | |
82 | av_log(ctx, AV_LOG_ERROR, "No data available yet\n"); | |
83 | return AVERROR(EIO); | |
84 | } | |
85 | ||
86 | cur_len = 0; | |
87 | while (data->pos < data->len) { | |
88 | uint8_t val = data->buf[data->pos++]; | |
89 | cur_len += val; | |
90 | if (val != 0xff) | |
91 | break; | |
92 | } | |
93 | if (data->pos + cur_len > data->len) { | |
94 | av_log(ctx, AV_LOG_ERROR, "Malformed LATM packet\n"); | |
95 | return AVERROR(EIO); | |
96 | } | |
97 | ||
98 | if ((ret = av_new_packet(pkt, cur_len)) < 0) | |
99 | return ret; | |
100 | memcpy(pkt->data, data->buf + data->pos, cur_len); | |
101 | data->pos += cur_len; | |
102 | pkt->stream_index = st->index; | |
103 | return data->pos < data->len; | |
104 | } | |
105 | ||
106 | static int parse_fmtp_config(AVStream *st, char *value) | |
107 | { | |
108 | int len = ff_hex_to_data(NULL, value), i, ret = 0; | |
109 | GetBitContext gb; | |
110 | uint8_t *config; | |
111 | int audio_mux_version, same_time_framing, num_programs, num_layers; | |
112 | ||
113 | /* Pad this buffer, too, to avoid out of bounds reads with get_bits below */ | |
114 | config = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); | |
115 | if (!config) | |
116 | return AVERROR(ENOMEM); | |
117 | ff_hex_to_data(config, value); | |
118 | init_get_bits(&gb, config, len*8); | |
119 | audio_mux_version = get_bits(&gb, 1); | |
120 | same_time_framing = get_bits(&gb, 1); | |
121 | skip_bits(&gb, 6); /* num_sub_frames */ | |
122 | num_programs = get_bits(&gb, 4); | |
123 | num_layers = get_bits(&gb, 3); | |
124 | if (audio_mux_version != 0 || same_time_framing != 1 || num_programs != 0 || | |
125 | num_layers != 0) { | |
126 | av_log(NULL, AV_LOG_WARNING, "Unsupported LATM config (%d,%d,%d,%d)\n", | |
127 | audio_mux_version, same_time_framing, | |
128 | num_programs, num_layers); | |
129 | ret = AVERROR_PATCHWELCOME; | |
130 | goto end; | |
131 | } | |
132 | av_freep(&st->codec->extradata); | |
133 | if (ff_alloc_extradata(st->codec, (get_bits_left(&gb) + 7)/8)) { | |
134 | ret = AVERROR(ENOMEM); | |
135 | goto end; | |
136 | } | |
137 | for (i = 0; i < st->codec->extradata_size; i++) | |
138 | st->codec->extradata[i] = get_bits(&gb, 8); | |
139 | ||
140 | end: | |
141 | av_free(config); | |
142 | return ret; | |
143 | } | |
144 | ||
145 | static int parse_fmtp(AVFormatContext *s, | |
146 | AVStream *stream, PayloadContext *data, | |
147 | char *attr, char *value) | |
148 | { | |
149 | int res; | |
150 | ||
151 | if (!strcmp(attr, "config")) { | |
152 | res = parse_fmtp_config(stream, value); | |
153 | if (res < 0) | |
154 | return res; | |
155 | } else if (!strcmp(attr, "cpresent")) { | |
156 | int cpresent = atoi(value); | |
157 | if (cpresent != 0) | |
158 | avpriv_request_sample(s, | |
159 | "RTP MP4A-LATM with in-band configuration"); | |
160 | } | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static int latm_parse_sdp_line(AVFormatContext *s, int st_index, | |
166 | PayloadContext *data, const char *line) | |
167 | { | |
168 | const char *p; | |
169 | ||
170 | if (st_index < 0) | |
171 | return 0; | |
172 | ||
173 | if (av_strstart(line, "fmtp:", &p)) | |
174 | return ff_parse_fmtp(s, s->streams[st_index], data, p, parse_fmtp); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | RTPDynamicProtocolHandler ff_mp4a_latm_dynamic_handler = { | |
180 | .enc_name = "MP4A-LATM", | |
181 | .codec_type = AVMEDIA_TYPE_AUDIO, | |
182 | .codec_id = AV_CODEC_ID_AAC, | |
183 | .parse_sdp_a_line = latm_parse_sdp_line, | |
184 | .alloc = latm_new_context, | |
185 | .free = latm_free_context, | |
186 | .parse_packet = latm_parse_packet | |
187 | }; |