• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavformat/rtpdec_xiph.c

Go to the documentation of this file.
00001 /*
00002  * Xiph RTP Protocols
00003  * Copyright (c) 2009 Colin McQuillian
00004  * Copyright (c) 2010 Josh Allmann
00005  *
00006  * This file is part of FFmpeg.
00007  *
00008  * FFmpeg is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * FFmpeg is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with FFmpeg; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00030 #include "libavutil/avstring.h"
00031 #include "libavutil/base64.h"
00032 #include "libavcodec/bytestream.h"
00033 
00034 #include <assert.h>
00035 
00036 #include "rtpdec.h"
00037 #include "rtpdec_xiph.h"
00038 
00042 struct PayloadContext {
00043     unsigned ident;             
00044     uint32_t timestamp;
00045     ByteIOContext* fragment;    
00046 };
00047 
00048 static PayloadContext *xiph_new_context(void)
00049 {
00050     return av_mallocz(sizeof(PayloadContext));
00051 }
00052 
00053 static inline void free_fragment_if_needed(PayloadContext * data)
00054 {
00055     if (data->fragment) {
00056         uint8_t* p;
00057         url_close_dyn_buf(data->fragment, &p);
00058         av_free(p);
00059         data->fragment = NULL;
00060     }
00061 }
00062 
00063 static void xiph_free_context(PayloadContext * data)
00064 {
00065     free_fragment_if_needed(data);
00066     av_free(data);
00067 }
00068 
00069 static int xiph_handle_packet(AVFormatContext * ctx,
00070                               PayloadContext * data,
00071                               AVStream * st,
00072                               AVPacket * pkt,
00073                               uint32_t * timestamp,
00074                               const uint8_t * buf, int len, int flags)
00075 {
00076 
00077     int ident, fragmented, tdt, num_pkts, pkt_len;
00078 
00079     if (len < 6) {
00080         av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len);
00081         return AVERROR_INVALIDDATA;
00082     }
00083 
00084     // read xiph rtp headers
00085     ident       = AV_RB24(buf);
00086     fragmented  = buf[3] >> 6;
00087     tdt         = (buf[3] >> 4) & 3;
00088     num_pkts    = buf[3] & 7;
00089     pkt_len     = AV_RB16(buf + 4);
00090 
00091     if (pkt_len > len - 6) {
00092         av_log(ctx, AV_LOG_ERROR,
00093                "Invalid packet length %d in %d byte packet\n", pkt_len,
00094                len);
00095         return AVERROR_INVALIDDATA;
00096     }
00097 
00098     if (ident != data->ident) {
00099         av_log(ctx, AV_LOG_ERROR,
00100                "Unimplemented Xiph SDP configuration change detected\n");
00101         return AVERROR_PATCHWELCOME;
00102     }
00103 
00104     if (tdt) {
00105         av_log(ctx, AV_LOG_ERROR,
00106                "Unimplemented RTP Xiph packet settings (%d,%d,%d)\n",
00107                fragmented, tdt, num_pkts);
00108         return AVERROR_PATCHWELCOME;
00109     }
00110 
00111     buf += 6; // move past header bits
00112     len -= 6;
00113 
00114     if (fragmented == 0) {
00115         // whole frame(s)
00116         int i, data_len, write_len;
00117         buf -= 2;
00118         len += 2;
00119 
00120         // fast first pass to calculate total length
00121         for (i = 0, data_len = 0;  (i < num_pkts) && (len >= 2);  i++) {
00122             int off   = data_len + (i << 1);
00123             pkt_len   = AV_RB16(buf + off);
00124             data_len += pkt_len;
00125             len      -= pkt_len + 2;
00126         }
00127 
00128         if (len < 0 || i < num_pkts) {
00129             av_log(ctx, AV_LOG_ERROR,
00130                    "Bad packet: %d bytes left at frame %d of %d\n",
00131                    len, i, num_pkts);
00132             return AVERROR_INVALIDDATA;
00133         }
00134 
00135         if (av_new_packet(pkt, data_len)) {
00136             av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
00137             return AVERROR(ENOMEM);
00138         }
00139         pkt->stream_index = st->index;
00140 
00141         // concatenate frames
00142         for (i = 0, write_len = 0; write_len < data_len; i++) {
00143             pkt_len = AV_RB16(buf);
00144             buf += 2;
00145             memcpy(pkt->data + write_len, buf, pkt_len);
00146             write_len += pkt_len;
00147             buf += pkt_len;
00148         }
00149         assert(write_len == data_len);
00150 
00151         return 0;
00152 
00153     } else if (fragmented == 1) {
00154         // start of xiph data fragment
00155         int res;
00156 
00157         // end packet has been lost somewhere, so drop buffered data
00158         free_fragment_if_needed(data);
00159 
00160         if((res = url_open_dyn_buf(&data->fragment)) < 0)
00161             return res;
00162 
00163         put_buffer(data->fragment, buf, pkt_len);
00164         data->timestamp = *timestamp;
00165 
00166     } else {
00167         assert(fragmented < 4);
00168         if (data->timestamp != *timestamp) {
00169             // skip if fragmented timestamp is incorrect;
00170             // a start packet has been lost somewhere
00171             free_fragment_if_needed(data);
00172             av_log(ctx, AV_LOG_ERROR, "RTP timestamps don't match!\n");
00173             return AVERROR_INVALIDDATA;
00174         }
00175 
00176         // copy data to fragment buffer
00177         put_buffer(data->fragment, buf, pkt_len);
00178 
00179         if (fragmented == 3) {
00180             // end of xiph data packet
00181             uint8_t* xiph_data;
00182             int frame_size = url_close_dyn_buf(data->fragment, &xiph_data);
00183 
00184             if (frame_size < 0) {
00185                 av_log(ctx, AV_LOG_ERROR,
00186                        "Error occurred when getting fragment buffer.");
00187                 return frame_size;
00188             }
00189 
00190             if (av_new_packet(pkt, frame_size)) {
00191                 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
00192                 return AVERROR(ENOMEM);
00193             }
00194 
00195             memcpy(pkt->data, xiph_data, frame_size);
00196             pkt->stream_index = st->index;
00197 
00198             av_free(xiph_data);
00199             data->fragment = NULL;
00200 
00201             return 0;
00202         }
00203     }
00204 
00205    return AVERROR(EAGAIN);
00206 }
00207 
00211 static int get_base128(const uint8_t ** buf, const uint8_t * buf_end)
00212 {
00213     int n = 0;
00214     for (; *buf < buf_end; ++*buf) {
00215         n <<= 7;
00216         n += **buf & 0x7f;
00217         if (!(**buf & 0x80)) {
00218             ++*buf;
00219             return n;
00220         }
00221     }
00222     return 0;
00223 }
00224 
00228 static unsigned int
00229 parse_packed_headers(const uint8_t * packed_headers,
00230                      const uint8_t * packed_headers_end,
00231                      AVCodecContext * codec, PayloadContext * xiph_data)
00232 {
00233 
00234     unsigned num_packed, num_headers, length, length1, length2, extradata_alloc;
00235     uint8_t *ptr;
00236 
00237     if (packed_headers_end - packed_headers < 9) {
00238         av_log(codec, AV_LOG_ERROR,
00239                "Invalid %d byte packed header.",
00240                packed_headers_end - packed_headers);
00241         return AVERROR_INVALIDDATA;
00242     }
00243 
00244     num_packed         = bytestream_get_be32(&packed_headers);
00245     xiph_data->ident   = bytestream_get_be24(&packed_headers);
00246     length             = bytestream_get_be16(&packed_headers);
00247     num_headers        = get_base128(&packed_headers, packed_headers_end);
00248     length1            = get_base128(&packed_headers, packed_headers_end);
00249     length2            = get_base128(&packed_headers, packed_headers_end);
00250 
00251     if (num_packed != 1 || num_headers > 3) {
00252         av_log(codec, AV_LOG_ERROR,
00253                "Unimplemented number of headers: %d packed headers, %d headers\n",
00254                num_packed, num_headers);
00255         return AVERROR_PATCHWELCOME;
00256     }
00257 
00258     if (packed_headers_end - packed_headers != length ||
00259         length1 > length || length2 > length - length1) {
00260         av_log(codec, AV_LOG_ERROR,
00261                "Bad packed header lengths (%d,%d,%d,%d)\n", length1,
00262                length2, packed_headers_end - packed_headers, length);
00263         return AVERROR_INVALIDDATA;
00264     }
00265 
00266     /* allocate extra space:
00267      * -- length/255 +2 for xiphlacing
00268      * -- one for the '2' marker
00269      * -- FF_INPUT_BUFFER_PADDING_SIZE required */
00270     extradata_alloc = length + length/255 + 3 + FF_INPUT_BUFFER_PADDING_SIZE;
00271 
00272     ptr = codec->extradata = av_malloc(extradata_alloc);
00273     if (!ptr) {
00274         av_log(codec, AV_LOG_ERROR, "Out of memory\n");
00275         return AVERROR(ENOMEM);
00276     }
00277     *ptr++ = 2;
00278     ptr += av_xiphlacing(ptr, length1);
00279     ptr += av_xiphlacing(ptr, length2);
00280     memcpy(ptr, packed_headers, length);
00281     ptr += length;
00282     codec->extradata_size = ptr - codec->extradata;
00283     // clear out remaining parts of the buffer
00284     memset(ptr, 0, extradata_alloc - codec->extradata_size);
00285 
00286     return 0;
00287 }
00288 
00289 static int xiph_parse_fmtp_pair(AVCodecContext * codec,
00290                                 PayloadContext *xiph_data,
00291                                 char *attr, char *value)
00292 {
00293     int result = 0;
00294 
00295     if (!strcmp(attr, "sampling")) {
00296         return AVERROR_PATCHWELCOME;
00297     } else if (!strcmp(attr, "width")) {
00298         /* This is an integer between 1 and 1048561
00299          * and MUST be in multiples of 16. */
00300         codec->width = atoi(value);
00301         return 0;
00302     } else if (!strcmp(attr, "height")) {
00303         /* This is an integer between 1 and 1048561
00304          * and MUST be in multiples of 16. */
00305         codec->height = atoi(value);
00306         return 0;
00307     } else if (!strcmp(attr, "delivery-method")) {
00308         /* Possible values are: inline, in_band, out_band/specific_name. */
00309         return AVERROR_PATCHWELCOME;
00310     } else if (!strcmp(attr, "configuration-uri")) {
00311         /* NOTE: configuration-uri is supported only under 2 conditions:
00312          *--after the delivery-method tag
00313          * --with a delivery-method value of out_band */
00314         return AVERROR_PATCHWELCOME;
00315     } else if (!strcmp(attr, "configuration")) {
00316         /* NOTE: configuration is supported only AFTER the delivery-method tag
00317          * The configuration value is a base64 encoded packed header */
00318         uint8_t *decoded_packet = NULL;
00319         int packet_size;
00320         size_t decoded_alloc = strlen(value) / 4 * 3 + 4;
00321 
00322         if (decoded_alloc <= INT_MAX) {
00323             decoded_packet = av_malloc(decoded_alloc);
00324             if (decoded_packet) {
00325                 packet_size =
00326                     av_base64_decode(decoded_packet, value, decoded_alloc);
00327 
00328                 result = parse_packed_headers
00329                     (decoded_packet, decoded_packet + packet_size, codec,
00330                     xiph_data);
00331             } else {
00332                 av_log(codec, AV_LOG_ERROR,
00333                        "Out of memory while decoding SDP configuration.\n");
00334                 result = AVERROR(ENOMEM);
00335             }
00336         } else {
00337             av_log(codec, AV_LOG_ERROR, "Packet too large\n");
00338             result = AVERROR_INVALIDDATA;
00339         }
00340         av_free(decoded_packet);
00341     }
00342     return result;
00343 }
00344 
00345 static int xiph_parse_sdp_line(AVFormatContext *s, int st_index,
00346                                  PayloadContext *data, const char *line)
00347 {
00348     const char *p;
00349     char *value;
00350     char attr[25];
00351     int value_size = strlen(line), attr_size = sizeof(attr), res = 0;
00352     AVCodecContext* codec = s->streams[st_index]->codec;
00353 
00354     assert(data);
00355 
00356     if (!(value = av_malloc(value_size))) {
00357         av_log(codec, AV_LOG_ERROR, "Out of memory\n");
00358         return AVERROR(ENOMEM);
00359     }
00360 
00361     if (av_strstart(line, "fmtp:", &p)) {
00362         // remove protocol identifier
00363         while (*p && *p == ' ') p++; // strip spaces
00364         while (*p && *p != ' ') p++; // eat protocol identifier
00365         while (*p && *p == ' ') p++; // strip trailing spaces
00366 
00367         while (ff_rtsp_next_attr_and_value(&p,
00368                                            attr, attr_size,
00369                                            value, value_size)) {
00370             res = xiph_parse_fmtp_pair(codec, data, attr, value);
00371             if (res < 0 && res != AVERROR_PATCHWELCOME)
00372                 return res;
00373         }
00374     }
00375 
00376     av_free(value);
00377     return 0;
00378 }
00379 
00380 RTPDynamicProtocolHandler ff_theora_dynamic_handler = {
00381     .enc_name         = "theora",
00382     .codec_type       = AVMEDIA_TYPE_VIDEO,
00383     .codec_id         = CODEC_ID_THEORA,
00384     .parse_sdp_a_line = xiph_parse_sdp_line,
00385     .open             = xiph_new_context,
00386     .close            = xiph_free_context,
00387     .parse_packet     = xiph_handle_packet
00388 };
00389 
00390 RTPDynamicProtocolHandler ff_vorbis_dynamic_handler = {
00391     .enc_name         = "vorbis",
00392     .codec_type       = AVMEDIA_TYPE_AUDIO,
00393     .codec_id         = CODEC_ID_VORBIS,
00394     .parse_sdp_a_line = xiph_parse_sdp_line,
00395     .open             = xiph_new_context,
00396     .close            = xiph_free_context,
00397     .parse_packet     = xiph_handle_packet
00398 };

Generated on Fri Sep 16 2011 17:17:50 for FFmpeg by  doxygen 1.7.1