022cfec31351ed0c4bbf837d2330082643e1d42b
[ffmpeg.git] / 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 Libav.
6  *
7  * Libav 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  * Libav 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 Libav; 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_free_packet(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 };