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