blob: 86c8958d5cef85abeb4a05b7528826868c6f3aaa
1 | /* |
2 | * RTP parser for loss tolerant payload format for MP3 audio (RFC 5219) |
3 | * Copyright (c) 2015 Gilles Chanteperdrix <gch@xenomai.org> |
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 "libavutil/attributes.h" |
23 | #include "libavutil/intreadwrite.h" |
24 | |
25 | #include "avio_internal.h" |
26 | #include "rtpdec_formats.h" |
27 | |
28 | struct PayloadContext { |
29 | unsigned adu_size; |
30 | unsigned cur_size; |
31 | uint32_t timestamp; |
32 | uint8_t *split_buf; |
33 | int split_pos, split_buf_size, split_pkts; |
34 | AVIOContext *fragment; |
35 | }; |
36 | |
37 | static void mpa_robust_close_context(PayloadContext *data) |
38 | { |
39 | ffio_free_dyn_buf(&data->fragment); |
40 | av_free(data->split_buf); |
41 | } |
42 | |
43 | static int mpa_robust_parse_rtp_header(AVFormatContext *ctx, |
44 | const uint8_t *buf, int len, |
45 | unsigned *adu_size, unsigned *cont) |
46 | { |
47 | unsigned header_size; |
48 | |
49 | if (len < 2) { |
50 | av_log(ctx, AV_LOG_ERROR, "Invalid %d bytes packet\n", len); |
51 | return AVERROR_INVALIDDATA; |
52 | } |
53 | |
54 | *cont = !!(buf[0] & 0x80); |
55 | if (!(buf[0] & 0x40)) { |
56 | header_size = 1; |
57 | *adu_size = buf[0] & ~0xc0; |
58 | } else { |
59 | header_size = 2; |
60 | *adu_size = AV_RB16(buf) & ~0xc000; |
61 | } |
62 | |
63 | return header_size; |
64 | } |
65 | |
66 | static int mpa_robust_parse_packet(AVFormatContext *ctx, PayloadContext *data, |
67 | AVStream *st, AVPacket *pkt, |
68 | uint32_t *timestamp, const uint8_t *buf, |
69 | int len, uint16_t seq, int flags) |
70 | { |
71 | unsigned adu_size, continuation; |
72 | int err, header_size; |
73 | |
74 | if (!buf) { |
75 | buf = &data->split_buf[data->split_pos]; |
76 | len = data->split_buf_size - data->split_pos; |
77 | |
78 | header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size, |
79 | &continuation); |
80 | if (header_size < 0) { |
81 | av_freep(&data->split_buf); |
82 | return header_size; |
83 | } |
84 | buf += header_size; |
85 | len -= header_size; |
86 | |
87 | if (continuation || adu_size > len) { |
88 | av_freep(&data->split_buf); |
89 | av_log(ctx, AV_LOG_ERROR, "Invalid frame\n"); |
90 | return AVERROR_INVALIDDATA; |
91 | } |
92 | |
93 | if (av_new_packet(pkt, adu_size)) { |
94 | av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); |
95 | return AVERROR(ENOMEM); |
96 | } |
97 | |
98 | pkt->stream_index = st->index; |
99 | memcpy(pkt->data, buf, adu_size); |
100 | |
101 | data->split_pos += header_size + adu_size; |
102 | |
103 | if (data->split_pos == data->split_buf_size) { |
104 | av_freep(&data->split_buf); |
105 | return 0; |
106 | } |
107 | |
108 | return 1; |
109 | } |
110 | |
111 | |
112 | header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size, |
113 | &continuation); |
114 | if (header_size < 0) |
115 | return header_size; |
116 | |
117 | buf += header_size; |
118 | len -= header_size; |
119 | |
120 | if (!continuation && adu_size <= len) { |
121 | /* One or more complete frames */ |
122 | |
123 | if (av_new_packet(pkt, adu_size)) { |
124 | av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); |
125 | return AVERROR(ENOMEM); |
126 | } |
127 | |
128 | pkt->stream_index = st->index; |
129 | memcpy(pkt->data, buf, adu_size); |
130 | |
131 | buf += adu_size; |
132 | len -= adu_size; |
133 | if (len) { |
134 | data->split_buf_size = len; |
135 | data->split_buf = av_malloc(data->split_buf_size); |
136 | data->split_pos = 0; |
137 | if (!data->split_buf) { |
138 | av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); |
139 | av_packet_unref(pkt); |
140 | return AVERROR(ENOMEM); |
141 | } |
142 | memcpy(data->split_buf, buf, data->split_buf_size); |
143 | return 1; |
144 | } |
145 | return 0; |
146 | } else if (!continuation) { /* && adu_size > len */ |
147 | /* First fragment */ |
148 | ffio_free_dyn_buf(&data->fragment); |
149 | |
150 | data->adu_size = adu_size; |
151 | data->cur_size = len; |
152 | data->timestamp = *timestamp; |
153 | |
154 | err = avio_open_dyn_buf(&data->fragment); |
155 | if (err < 0) |
156 | return err; |
157 | |
158 | avio_write(data->fragment, buf, len); |
159 | return AVERROR(EAGAIN); |
160 | } |
161 | /* else continuation == 1 */ |
162 | |
163 | /* Fragment other than first */ |
164 | if (!data->fragment) { |
165 | av_log(ctx, AV_LOG_WARNING, |
166 | "Received packet without a start fragment; dropping.\n"); |
167 | return AVERROR(EAGAIN); |
168 | } |
169 | if (adu_size != data->adu_size || |
170 | data->timestamp != *timestamp) { |
171 | ffio_free_dyn_buf(&data->fragment); |
172 | av_log(ctx, AV_LOG_ERROR, "Invalid packet received\n"); |
173 | return AVERROR_INVALIDDATA; |
174 | } |
175 | |
176 | avio_write(data->fragment, buf, len); |
177 | data->cur_size += len; |
178 | |
179 | if (data->cur_size < data->adu_size) |
180 | return AVERROR(EAGAIN); |
181 | |
182 | err = ff_rtp_finalize_packet(pkt, &data->fragment, st->index); |
183 | if (err < 0) { |
184 | av_log(ctx, AV_LOG_ERROR, |
185 | "Error occurred when getting fragment buffer.\n"); |
186 | return err; |
187 | } |
188 | |
189 | return 0; |
190 | } |
191 | |
192 | RTPDynamicProtocolHandler ff_mpeg_audio_robust_dynamic_handler = { |
193 | .enc_name = "mpa-robust", |
194 | .codec_type = AVMEDIA_TYPE_AUDIO, |
195 | .codec_id = AV_CODEC_ID_MP3ADU, |
196 | .need_parsing = AVSTREAM_PARSE_HEADERS, |
197 | .priv_data_size = sizeof(PayloadContext), |
198 | .close = mpa_robust_close_context, |
199 | .parse_packet = mpa_robust_parse_packet, |
200 | }; |
201 |