]> git.sesse.net Git - ffmpeg/blob - libavformat/rtpdec_mpa_robust.c
Merge commit 'db158f0dd217cf839be8af195d66cf49a76537a8'
[ffmpeg] / libavformat / rtpdec_mpa_robust.c
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 "rtpdec_formats.h"
26
27 struct PayloadContext {
28     unsigned adu_size;
29     unsigned cur_size;
30     uint32_t timestamp;
31     uint8_t *split_buf;
32     int split_pos, split_buf_size, split_pkts;
33     AVIOContext *fragment;
34 };
35
36 static av_cold int mpa_robust_init(AVFormatContext *ctx, int st_index,
37                                    PayloadContext *data)
38 {
39     if (st_index < 0)
40         return 0;
41     ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_HEADERS;
42     return 0;
43 }
44
45 static PayloadContext *mpa_robust_new_context(void)
46 {
47     return av_mallocz(sizeof(PayloadContext));
48 }
49
50 static inline void free_fragment(PayloadContext *data)
51 {
52     if (data->fragment) {
53         uint8_t *p;
54         avio_close_dyn_buf(data->fragment, &p);
55         av_free(p);
56         data->fragment = NULL;
57     }
58 }
59
60 static void mpa_robust_free_context(PayloadContext *data)
61 {
62     free_fragment(data);
63     av_free(data->split_buf);
64     av_free(data);
65 }
66
67 static int mpa_robust_parse_rtp_header(AVFormatContext *ctx,
68                                        const uint8_t *buf, int len,
69                                        unsigned *adu_size, unsigned *cont)
70 {
71     unsigned header_size;
72
73     if (len < 2) {
74         av_log(ctx, AV_LOG_ERROR, "Invalid %d bytes packet\n", len);
75         return AVERROR_INVALIDDATA;
76     }
77
78     *cont = !!(buf[0] & 0x80);
79     if (!(buf[0] & 0x40)) {
80         header_size = 1;
81         *adu_size = buf[0] & ~0xc0;
82     } else {
83         header_size = 2;
84         *adu_size = AV_RB16(buf) & ~0xc000;
85     }
86
87     return header_size;
88 }
89
90 static int mpa_robust_parse_packet(AVFormatContext *ctx, PayloadContext *data,
91                                    AVStream *st, AVPacket *pkt,
92                                    uint32_t *timestamp, const uint8_t *buf,
93                                    int len, uint16_t seq, int flags)
94 {
95     unsigned adu_size, continuation;
96     int err, header_size;
97
98     if (!buf) {
99         buf = &data->split_buf[data->split_pos];
100         len = data->split_buf_size - data->split_pos;
101
102         header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size,
103                                                   &continuation);
104         if (header_size < 0) {
105             av_freep(&data->split_buf);
106             return header_size;
107         }
108         buf += header_size;
109         len -= header_size;
110
111         if (continuation || adu_size > len) {
112             av_freep(&data->split_buf);
113             av_log(ctx, AV_LOG_ERROR, "Invalid frame\n");
114             return AVERROR_INVALIDDATA;
115         }
116
117         if (av_new_packet(pkt, adu_size)) {
118             av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
119             return AVERROR(ENOMEM);
120         }
121
122         pkt->stream_index = st->index;
123         memcpy(pkt->data, buf, adu_size);
124
125         data->split_pos += adu_size;
126
127         if (data->split_pos == data->split_buf_size) {
128             av_freep(&data->split_buf);
129             return 0;
130         }
131
132         return 1;
133     }
134
135
136     header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size,
137                                               &continuation);
138     if (header_size < 0)
139         return header_size;
140
141     buf += header_size;
142     len -= header_size;
143
144     if (!continuation && adu_size <= len) {
145         /* One or more complete frames */
146
147         if (av_new_packet(pkt, adu_size)) {
148             av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
149             return AVERROR(ENOMEM);
150         }
151
152         pkt->stream_index = st->index;
153         memcpy(pkt->data, buf, adu_size);
154
155         buf += adu_size;
156         len -= adu_size;
157         if (len) {
158             data->split_buf_size = len;
159             data->split_buf = av_malloc(data->split_buf_size);
160             data->split_pos = 0;
161             if (!data->split_buf) {
162                 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
163                 av_free_packet(pkt);
164                 return AVERROR(ENOMEM);
165             }
166             memcpy(data->split_buf, buf, data->split_buf_size);
167             return 1;
168         }
169         return 0;
170     } else if (!continuation) { /* && adu_size > len */
171         /* First fragment */
172         free_fragment(data);
173
174         data->adu_size = adu_size;
175         data->cur_size = len;
176         data->timestamp = *timestamp;
177
178         err = avio_open_dyn_buf(&data->fragment);
179         if (err < 0)
180             return err;
181
182         avio_write(data->fragment, buf, len);
183         return AVERROR(EAGAIN);
184     }
185     /* else continuation == 1 */
186
187     /* Fragment other than first */
188     if (!data->fragment) {
189         av_log(ctx, AV_LOG_WARNING,
190             "Received packet without a start fragment; dropping.\n");
191         return AVERROR(EAGAIN);
192     }
193     if (adu_size = data->adu_size ||
194         data->timestamp != *timestamp) {
195         free_fragment(data);
196         av_log(ctx, AV_LOG_ERROR, "Invalid packet received\n");
197         return AVERROR_INVALIDDATA;
198     }
199
200     avio_write(data->fragment, buf, len);
201     data->cur_size += len;
202
203     if (data->cur_size < data->adu_size)
204         return AVERROR(EAGAIN);
205
206     err = ff_rtp_finalize_packet(pkt, &data->fragment, st->index);
207     if (err < 0) {
208         av_log(ctx, AV_LOG_ERROR,
209                "Error occurred when getting fragment buffer.\n");
210         return err;
211     }
212
213     return 0;
214 }
215
216 RTPDynamicProtocolHandler ff_mpeg_audio_robust_dynamic_handler = {
217     .codec_type        = AVMEDIA_TYPE_AUDIO,
218     .codec_id          = AV_CODEC_ID_MP3ADU,
219     .init              = mpa_robust_init,
220     .alloc             = mpa_robust_new_context,
221     .free              = mpa_robust_free_context,
222     .parse_packet      = mpa_robust_parse_packet,
223     .enc_name          = "mpa-robust",
224 };