• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
mpa_robust_close_context(PayloadContext * data)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 
mpa_robust_parse_rtp_header(AVFormatContext * ctx,const uint8_t * buf,int len,unsigned * adu_size,unsigned * cont)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 
mpa_robust_parse_packet(AVFormatContext * ctx,PayloadContext * data,AVStream * st,AVPacket * pkt,uint32_t * timestamp,const uint8_t * buf,int len,uint16_t seq,int flags)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 ((err = av_new_packet(pkt, adu_size)) < 0) {
94             av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
95             return err;
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 ((err = av_new_packet(pkt, adu_size)) < 0) {
124             av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
125             return err;
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 const 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