]> git.sesse.net Git - ffmpeg/blob - libavformat/rtpdec_mpeg12.c
avformat/rtpdec_mpeg12: add robust MPEG audio depacketization (RFC 5219)
[ffmpeg] / libavformat / rtpdec_mpeg12.c
1 /*
2  * Common code for the RTP depacketization of MPEG-1/2 formats.
3  * Copyright (c) 2002 Fabrice Bellard
4  *
5  * RTP parser for loss tolerant payload format for MP3 audio (RFC 5219)
6  * Copyright (c) 2015 Gilles Chanteperdrix <gch@xenomai.org>
7  *
8  * This file is part of FFmpeg.
9  *
10  * FFmpeg is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * FFmpeg is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with FFmpeg; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23  */
24
25 #include "libavutil/attributes.h"
26 #include "libavutil/intreadwrite.h"
27 #include "rtpdec_formats.h"
28
29 #define RTP_MPA_PAYLOAD_HEADER_SIZE 1
30
31 static av_cold int mpeg_init(AVFormatContext *ctx, int st_index, PayloadContext *data)
32 {
33     if (st_index < 0)
34         return 0;
35     ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_FULL;
36     return 0;
37 }
38
39 static int mpeg_parse_packet(AVFormatContext *ctx, PayloadContext *data,
40                              AVStream *st, AVPacket *pkt, uint32_t *timestamp,
41                              const uint8_t *buf, int len, uint16_t seq,
42                              int flags)
43 {
44     unsigned int h;
45     if (len <= 4)
46         return AVERROR_INVALIDDATA;
47     h    = AV_RB32(buf);
48     buf += 4;
49     len -= 4;
50     if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && h & (1 << 26)) {
51         /* MPEG-2 */
52         if (len <= 4)
53             return AVERROR_INVALIDDATA;
54         buf += 4;
55         len -= 4;
56     }
57     if (av_new_packet(pkt, len) < 0)
58         return AVERROR(ENOMEM);
59     memcpy(pkt->data, buf, len);
60     pkt->stream_index = st->index;
61     return 0;
62 }
63
64 RTPDynamicProtocolHandler ff_mpeg_audio_dynamic_handler = {
65     .codec_type        = AVMEDIA_TYPE_AUDIO,
66     .codec_id          = AV_CODEC_ID_MP3,
67     .init              = mpeg_init,
68     .parse_packet      = mpeg_parse_packet,
69     .static_payload_id = 14,
70 };
71
72 RTPDynamicProtocolHandler ff_mpeg_video_dynamic_handler = {
73     .codec_type        = AVMEDIA_TYPE_VIDEO,
74     .codec_id          = AV_CODEC_ID_MPEG2VIDEO,
75     .init              = mpeg_init,
76     .parse_packet      = mpeg_parse_packet,
77     .static_payload_id = 32,
78 };
79
80 /* MPA-ROBUST, RFC 5219 */
81 struct PayloadContext {
82     unsigned adu_size;
83     unsigned cur_size;
84     uint32_t timestamp;
85     uint8_t *split_buf;
86     int split_pos, split_buf_size, split_pkts;
87     AVIOContext *fragment;
88 };
89
90 static av_cold int mpa_robust_init(AVFormatContext *ctx, int st_index,
91                                 PayloadContext *data)
92 {
93     if (st_index < 0)
94         return 0;
95     ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_HEADERS;
96     return 0;
97 }
98
99 static PayloadContext *mpa_robust_new_context(void)
100 {
101         return av_mallocz(sizeof(PayloadContext));
102 }
103
104 static inline void free_fragment_if_needed(PayloadContext *data)
105 {
106     if (data->fragment) {
107         uint8_t *p;
108         avio_close_dyn_buf(data->fragment, &p);
109         av_free(p);
110         data->fragment = NULL;
111     }
112 }
113
114 static void mpa_robust_free_context(PayloadContext *data)
115 {
116     free_fragment_if_needed(data);
117     av_free(data);
118 }
119
120 static int mpa_robust_parse_rtp_header(AVFormatContext *ctx,
121                                         const uint8_t *buf, int len,
122                                         unsigned *adu_size, unsigned *cont)
123 {
124     unsigned header_size;
125
126     if (len < RTP_MPA_PAYLOAD_HEADER_SIZE + 1) {
127         av_log(ctx, AV_LOG_ERROR, "Invalid %d bytes packet\n", len);
128         return AVERROR_INVALIDDATA;
129     }
130
131     *cont = !!(buf[0] & 0x80);
132     if (!(buf[0] & 0x40)) {
133         header_size = 1;
134         *adu_size = buf[0] & ~0xc0;
135     } else {
136         header_size = 2;
137         *adu_size = AV_RB16(buf) & ~0xc000;
138     }
139
140     return header_size;
141 }
142
143 static int mpa_robust_parse_packet(AVFormatContext *ctx, PayloadContext *data,
144                                 AVStream *st, AVPacket *pkt,
145                                 uint32_t *timestamp, const uint8_t *buf,
146                                 int len, uint16_t seq, int flags)
147 {
148     unsigned adu_size, continuation;
149     int err, header_size;
150
151     if (!buf) {
152         buf = &data->split_buf[data->split_pos];
153         len = data->split_buf_size - data->split_pos;
154
155         header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size,
156                                                 &continuation);
157         if (header_size < 0) {
158             av_freep(&data->split_buf);
159             return header_size;
160         }
161         buf += header_size;
162         len -= header_size;
163
164         if (continuation || adu_size > len) {
165             av_freep(&data->split_buf);
166             av_log(ctx, AV_LOG_ERROR, "Invalid frame\n");
167             return AVERROR_INVALIDDATA;
168         }
169
170         if (av_new_packet(pkt, adu_size)) {
171             av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
172             return AVERROR(ENOMEM);
173         }
174
175         pkt->stream_index = st->index;
176         memcpy(pkt->data, buf, adu_size);
177
178         data->split_pos = (buf - data->split_buf) + adu_size;
179
180         if (data->split_pos == data->split_buf_size) {
181             av_freep(&data->split_buf);
182             return 0;
183         }
184
185         return 1;
186     }
187
188
189     header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size,
190                                             &continuation);
191     if (header_size < 0)
192         return header_size;
193
194     buf += header_size;
195     len -= header_size;
196
197     if (!continuation && adu_size <= len) {
198         /* One or more complete frames */
199
200         if (av_new_packet(pkt, adu_size)) {
201             av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
202             return AVERROR(ENOMEM);
203         }
204
205         pkt->stream_index = st->index;
206         memcpy(pkt->data, buf, adu_size);
207
208         buf += adu_size;
209         len -= adu_size;
210         if (len) {
211             data->split_buf_size = len;
212             data->split_buf = av_malloc(data->split_buf_size);
213             data->split_pos = 0;
214             if (!data->split_buf) {
215                 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
216                 av_free_packet(pkt);
217                 return AVERROR(ENOMEM);
218             }
219             memcpy(data->split_buf, buf, data->split_buf_size);
220             return 1;
221         }
222         return 0;
223     } else if (!continuation) { /* && adu_size > len */
224         /* First fragment */
225         free_fragment_if_needed(data);
226
227         data->adu_size = adu_size;
228         data->cur_size = len;
229         data->timestamp = *timestamp;
230
231         err = avio_open_dyn_buf(&data->fragment);
232         if (err < 0)
233             return err;
234
235         avio_write(data->fragment, buf, len);
236         return AVERROR(EAGAIN);
237     }
238     /* else continuation == 1 */
239
240     /* Fragment other than first */
241     if (!data->fragment) {
242         av_log(ctx, AV_LOG_WARNING,
243             "Received packet without a start fragment; dropping.\n");
244         return AVERROR(EAGAIN);
245     }
246     if (adu_size = data->adu_size ||
247         data->timestamp != *timestamp) {
248         free_fragment_if_needed(data);
249         av_log(ctx, AV_LOG_ERROR, "Invalid packet received\n");
250         return AVERROR_INVALIDDATA;
251     }
252
253     avio_write(data->fragment, buf, len);
254     data->cur_size += len;
255
256     if (data->cur_size < data->adu_size)
257         return AVERROR(EAGAIN);
258
259     err = ff_rtp_finalize_packet(pkt, &data->fragment, st->index);
260     if (err < 0) {
261         av_log(ctx, AV_LOG_ERROR,
262                "Error occurred when getting fragment buffer.");
263         return err;
264     }
265
266     return 0;
267 }
268
269 RTPDynamicProtocolHandler ff_mpeg_audio_robust_dynamic_handler = {
270     .codec_type        = AVMEDIA_TYPE_AUDIO,
271     .codec_id          = AV_CODEC_ID_MP3ADU,
272     .init              = mpa_robust_init,
273     .alloc             = mpa_robust_new_context,
274     .free              = mpa_robust_free_context,
275     .parse_packet      = mpa_robust_parse_packet,
276     .enc_name          = "mpa-robust",
277 };