Commit | Line | Data |
---|---|---|
887af2aa JA |
1 | /* |
2 | * RTP Theora Protocol | |
3 | * Copyright (c) 2010 Josh Allmann | |
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 | /** | |
23 | * @file libavformat/rtpdec_theora.c | |
24 | * @brief Theora / RTP Code | |
25 | * @author Josh Allmann <joshua.allmann@gmail.com> | |
26 | */ | |
27 | ||
28 | #include "libavutil/avstring.h" | |
29 | #include "libavutil/base64.h" | |
30 | #include "libavcodec/bytestream.h" | |
31 | ||
32 | #include <assert.h> | |
33 | ||
34 | #include "rtpdec.h" | |
06a36faf | 35 | #include "rtpdec_xiph.h" |
887af2aa JA |
36 | |
37 | /** | |
38 | * RTP/Theora specific private data. | |
39 | */ | |
40 | struct PayloadContext { | |
41 | unsigned ident; ///< 24-bit stream configuration identifier | |
42 | uint32_t timestamp; | |
43 | ByteIOContext* fragment; ///< buffer for split payloads | |
44 | }; | |
45 | ||
46 | static PayloadContext *theora_new_context(void) | |
47 | { | |
48 | return av_mallocz(sizeof(PayloadContext)); | |
49 | } | |
50 | ||
51 | static inline void free_fragment_if_needed(PayloadContext * data) | |
52 | { | |
53 | if (data->fragment) { | |
54 | uint8_t* p; | |
55 | url_close_dyn_buf(data->fragment, &p); | |
56 | av_free(p); | |
57 | data->fragment = NULL; | |
58 | } | |
59 | } | |
60 | ||
61 | static void theora_free_context(PayloadContext * data) | |
62 | { | |
63 | free_fragment_if_needed(data); | |
64 | av_free(data); | |
65 | } | |
66 | ||
67 | static int theora_handle_packet(AVFormatContext * ctx, | |
68 | PayloadContext * data, | |
69 | AVStream * st, | |
70 | AVPacket * pkt, | |
71 | uint32_t * timestamp, | |
72 | const uint8_t * buf, int len, int flags) | |
73 | { | |
74 | ||
75 | int ident, fragmented, tdt, num_pkts, pkt_len; | |
76 | ||
77 | if (len < 6) { | |
78 | av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len); | |
79 | return AVERROR_INVALIDDATA; | |
80 | } | |
81 | ||
82 | // read theora rtp headers | |
83 | ident = AV_RB24(buf); | |
84 | fragmented = buf[3] >> 6; | |
85 | tdt = (buf[3] >> 4) & 3; | |
86 | num_pkts = buf[3] & 7; | |
87 | pkt_len = AV_RB16(buf + 4); | |
88 | ||
89 | if (pkt_len > len - 6) { | |
90 | av_log(ctx, AV_LOG_ERROR, | |
91 | "Invalid packet length %d in %d byte packet\n", pkt_len, | |
92 | len); | |
93 | return AVERROR_INVALIDDATA; | |
94 | } | |
95 | ||
96 | if (ident != data->ident) { | |
97 | av_log(ctx, AV_LOG_ERROR, | |
98 | "Unimplemented Theora SDP configuration change detected\n"); | |
99 | return AVERROR_PATCHWELCOME; | |
100 | } | |
101 | ||
102 | if (tdt) { | |
103 | av_log(ctx, AV_LOG_ERROR, | |
104 | "Unimplemented RTP Theora packet settings (%d,%d,%d)\n", | |
105 | fragmented, tdt, num_pkts); | |
106 | return AVERROR_PATCHWELCOME; | |
107 | } | |
108 | ||
109 | buf += 6; // move past header bits | |
110 | len -= 6; | |
111 | ||
112 | if (fragmented == 0) { | |
113 | // whole frame(s) | |
114 | int i, data_len, write_len; | |
115 | buf -= 2; | |
116 | len += 2; | |
117 | ||
118 | // fast first pass to calculate total length | |
119 | for (i = 0, data_len = 0; (i < num_pkts) && (len >= 2); i++) { | |
120 | int off = data_len + (i << 1); | |
121 | pkt_len = AV_RB16(buf + off); | |
122 | data_len += pkt_len; | |
123 | len -= pkt_len + 2; | |
124 | } | |
125 | ||
126 | if (len < 0 || i < num_pkts) { | |
127 | av_log(ctx, AV_LOG_ERROR, | |
128 | "Bad packet: %d bytes left at frame %d of %d\n", | |
129 | len, i, num_pkts); | |
130 | return AVERROR_INVALIDDATA; | |
131 | } | |
132 | ||
133 | if (av_new_packet(pkt, data_len)) { | |
134 | av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); | |
135 | return AVERROR_NOMEM; | |
136 | } | |
137 | pkt->stream_index = st->index; | |
138 | ||
139 | // concatenate frames | |
140 | for (i = 0, write_len = 0; write_len < data_len; i++) { | |
141 | pkt_len = AV_RB16(buf); | |
142 | buf += 2; | |
143 | memcpy(pkt->data + write_len, buf, pkt_len); | |
144 | write_len += pkt_len; | |
145 | buf += pkt_len; | |
146 | } | |
147 | assert(write_len == data_len); | |
148 | ||
149 | return 0; | |
150 | ||
151 | } else if (fragmented == 1) { | |
152 | // start of theora data fragment | |
153 | int res; | |
154 | ||
155 | // end packet has been lost somewhere, so drop buffered data | |
156 | free_fragment_if_needed(data); | |
157 | ||
158 | if((res = url_open_dyn_buf(&data->fragment)) < 0) | |
159 | return res; | |
160 | ||
161 | put_buffer(data->fragment, buf, pkt_len); | |
162 | data->timestamp = *timestamp; | |
163 | ||
164 | } else { | |
165 | assert(fragmented < 4); | |
166 | if (data->timestamp != *timestamp) { | |
167 | // skip if fragmented timestamp is incorrect; | |
168 | // a start packet has been lost somewhere | |
169 | free_fragment_if_needed(data); | |
170 | av_log(ctx, AV_LOG_ERROR, "RTP timestamps don't match!\n"); | |
171 | return AVERROR_INVALIDDATA; | |
172 | } | |
173 | ||
174 | // copy data to fragment buffer | |
175 | put_buffer(data->fragment, buf, pkt_len); | |
176 | ||
177 | if (fragmented == 3) { | |
178 | // end of theora data packet | |
179 | uint8_t* theora_data; | |
180 | int frame_size = url_close_dyn_buf(data->fragment, &theora_data); | |
181 | ||
182 | if (frame_size < 0) { | |
183 | av_log(ctx, AV_LOG_ERROR, | |
184 | "Error occurred when getting fragment buffer."); | |
185 | return frame_size; | |
186 | } | |
187 | ||
188 | if (av_new_packet(pkt, frame_size)) { | |
189 | av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); | |
190 | return AVERROR_NOMEM; | |
191 | } | |
192 | ||
193 | memcpy(pkt->data, theora_data, frame_size); | |
194 | pkt->stream_index = st->index; | |
195 | ||
196 | av_free(theora_data); | |
197 | data->fragment = NULL; | |
198 | ||
199 | return 0; | |
200 | } | |
201 | } | |
202 | ||
203 | return AVERROR(EAGAIN); | |
204 | } | |
205 | ||
206 | /** | |
207 | * Length encoding described in RFC5215 section 3.1.1. | |
208 | */ | |
209 | static int get_base128(const uint8_t ** buf, const uint8_t * buf_end) | |
210 | { | |
211 | int n = 0; | |
212 | for (; *buf < buf_end; ++*buf) { | |
213 | n <<= 7; | |
214 | n += **buf & 0x7f; | |
215 | if (!(**buf & 0x80)) { | |
216 | ++*buf; | |
217 | return n; | |
218 | } | |
219 | } | |
220 | return 0; | |
221 | } | |
222 | ||
223 | /** | |
224 | * Based off parse_packed_headers in Vorbis RTP | |
225 | */ | |
226 | static unsigned int | |
227 | parse_packed_headers(const uint8_t * packed_headers, | |
228 | const uint8_t * packed_headers_end, | |
229 | AVCodecContext * codec, PayloadContext * theora_data) | |
230 | { | |
231 | ||
232 | unsigned num_packed, num_headers, length, length1, length2, extradata_alloc; | |
233 | uint8_t *ptr; | |
234 | ||
235 | if (packed_headers_end - packed_headers < 9) { | |
236 | av_log(codec, AV_LOG_ERROR, | |
237 | "Invalid %d byte packed header.", | |
238 | packed_headers_end - packed_headers); | |
239 | return AVERROR_INVALIDDATA; | |
240 | } | |
241 | ||
242 | num_packed = bytestream_get_be32(&packed_headers); | |
243 | theora_data->ident = bytestream_get_be24(&packed_headers); | |
244 | length = bytestream_get_be16(&packed_headers); | |
245 | num_headers = get_base128(&packed_headers, packed_headers_end); | |
246 | length1 = get_base128(&packed_headers, packed_headers_end); | |
247 | length2 = get_base128(&packed_headers, packed_headers_end); | |
248 | ||
249 | if (num_packed != 1 || num_headers > 3) { | |
250 | av_log(codec, AV_LOG_ERROR, | |
251 | "Unimplemented number of headers: %d packed headers, %d headers\n", | |
252 | num_packed, num_headers); | |
253 | return AVERROR_PATCHWELCOME; | |
254 | } | |
255 | ||
256 | if (packed_headers_end - packed_headers != length || | |
257 | length1 > length || length2 > length - length1) { | |
258 | av_log(codec, AV_LOG_ERROR, | |
259 | "Bad packed header lengths (%d,%d,%d,%d)\n", length1, | |
260 | length2, packed_headers_end - packed_headers, length); | |
261 | return AVERROR_INVALIDDATA; | |
262 | } | |
263 | ||
264 | /* allocate extra space: | |
265 | * -- length/255 +2 for xiphlacing | |
266 | * -- one for the '2' marker | |
267 | * -- FF_INPUT_BUFFER_PADDING_SIZE required */ | |
268 | extradata_alloc = length + length/255 + 3 + FF_INPUT_BUFFER_PADDING_SIZE; | |
269 | ||
270 | ptr = codec->extradata = av_malloc(extradata_alloc); | |
271 | if (!ptr) { | |
272 | av_log(codec, AV_LOG_ERROR, "Out of memory\n"); | |
273 | return AVERROR_NOMEM; | |
274 | } | |
275 | *ptr++ = 2; | |
276 | ptr += av_xiphlacing(ptr, length1); | |
277 | ptr += av_xiphlacing(ptr, length2); | |
278 | memcpy(ptr, packed_headers, length); | |
279 | ptr += length; | |
280 | codec->extradata_size = ptr - codec->extradata; | |
281 | // clear out remaining parts of the buffer | |
282 | memset(ptr, 0, extradata_alloc - codec->extradata_size); | |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
287 | static int theora_parse_fmtp_pair(AVCodecContext * codec, | |
288 | PayloadContext *theora_data, | |
289 | char *attr, char *value) | |
290 | { | |
291 | int result = 0; | |
292 | ||
293 | if (!strcmp(attr, "sampling")) { | |
294 | return AVERROR_PATCHWELCOME; | |
295 | } else if (!strcmp(attr, "width")) { | |
296 | /* This is an integer between 1 and 1048561 | |
297 | * and MUST be in multiples of 16. */ | |
298 | codec->width = atoi(value); | |
299 | return 0; | |
300 | } else if (!strcmp(attr, "height")) { | |
301 | /* This is an integer between 1 and 1048561 | |
302 | * and MUST be in multiples of 16. */ | |
303 | codec->height = atoi(value); | |
304 | return 0; | |
305 | } else if (!strcmp(attr, "delivery-method")) { | |
306 | /* Possible values are: inline, in_band, out_band/specific_name. */ | |
307 | return AVERROR_PATCHWELCOME; | |
308 | } else if (!strcmp(attr, "configuration-uri")) { | |
309 | /* NOTE: configuration-uri is supported only under 2 conditions: | |
310 | *--after the delivery-method tag | |
311 | * --with a delivery-method value of out_band */ | |
312 | return AVERROR_PATCHWELCOME; | |
313 | } else if (!strcmp(attr, "configuration")) { | |
314 | /* NOTE: configuration is supported only AFTER the delivery-method tag | |
315 | * The configuration value is a base64 encoded packed header */ | |
316 | uint8_t *decoded_packet = NULL; | |
317 | int packet_size; | |
318 | size_t decoded_alloc = strlen(value) / 4 * 3 + 4; | |
319 | ||
320 | if (decoded_alloc <= INT_MAX) { | |
321 | decoded_packet = av_malloc(decoded_alloc); | |
322 | if (decoded_packet) { | |
323 | packet_size = | |
324 | av_base64_decode(decoded_packet, value, decoded_alloc); | |
325 | ||
326 | result = parse_packed_headers | |
327 | (decoded_packet, decoded_packet + packet_size, codec, | |
328 | theora_data); | |
329 | } else { | |
330 | av_log(codec, AV_LOG_ERROR, | |
331 | "Out of memory while decoding SDP configuration.\n"); | |
332 | result = AVERROR_NOMEM; | |
333 | } | |
334 | } else { | |
335 | av_log(codec, AV_LOG_ERROR, "Packet too large\n"); | |
336 | result = AVERROR_INVALIDDATA; | |
337 | } | |
338 | av_free(decoded_packet); | |
339 | } | |
340 | return result; | |
341 | } | |
342 | ||
343 | static int theora_parse_sdp_line(AVFormatContext *s, int st_index, | |
344 | PayloadContext *data, const char *line) | |
345 | { | |
346 | const char *p; | |
347 | char *value; | |
348 | char attr[25]; | |
349 | int value_size = strlen(line), attr_size = sizeof(attr), res = 0; | |
350 | AVCodecContext* codec = s->streams[st_index]->codec; | |
351 | ||
352 | assert(codec->id == CODEC_ID_THEORA); | |
353 | assert(data); | |
354 | ||
355 | if (!(value = av_malloc(value_size))) { | |
356 | av_log(codec, AV_LOG_ERROR, "Out of memory\n"); | |
357 | return AVERROR_NOMEM; | |
358 | } | |
359 | ||
360 | if (av_strstart(line, "fmtp:", &p)) { | |
361 | // remove protocol identifier | |
362 | while (*p && *p == ' ') p++; // strip spaces | |
363 | while (*p && *p != ' ') p++; // eat protocol identifier | |
364 | while (*p && *p == ' ') p++; // strip trailing spaces | |
365 | ||
366 | while (ff_rtsp_next_attr_and_value(&p, | |
367 | attr, attr_size, | |
368 | value, value_size)) { | |
369 | res = theora_parse_fmtp_pair(codec, data, attr, value); | |
370 | if (res < 0 && res != AVERROR_PATCHWELCOME) | |
371 | return res; | |
372 | } | |
373 | } | |
374 | ||
375 | av_free(value); | |
376 | return 0; | |
377 | } | |
378 | ||
379 | RTPDynamicProtocolHandler ff_theora_dynamic_handler = { | |
380 | .enc_name = "theora", | |
72415b2a | 381 | .codec_type = AVMEDIA_TYPE_VIDEO, |
887af2aa JA |
382 | .codec_id = CODEC_ID_THEORA, |
383 | .parse_sdp_a_line = theora_parse_sdp_line, | |
384 | .open = theora_new_context, | |
385 | .close = theora_free_context, | |
386 | .parse_packet = theora_handle_packet | |
387 | }; |