Libav 0.7.1
libavformat/rtpdec_qt.c
Go to the documentation of this file.
00001 /*
00002  * RTP/Quicktime support.
00003  * Copyright (c) 2009 Ronald S. Bultje
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00028 #include "avformat.h"
00029 #include "avio_internal.h"
00030 #include "rtp.h"
00031 #include "rtpdec.h"
00032 #include "isom.h"
00033 #include "libavcodec/get_bits.h"
00034 
00035 struct PayloadContext {
00036     AVPacket pkt;
00037     int bytes_per_frame, remaining;
00038     uint32_t timestamp;
00039 };
00040 
00041 static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt,
00042                                AVStream *st, AVPacket *pkt,
00043                                uint32_t *timestamp, const uint8_t *buf,
00044                                int len, int flags)
00045 {
00046     AVIOContext pb;
00047     GetBitContext gb;
00048     int packing_scheme, has_payload_desc, has_packet_info, alen,
00049         has_marker_bit = flags & RTP_FLAG_MARKER;
00050 
00051     if (qt->remaining) {
00052         int num = qt->pkt.size / qt->bytes_per_frame;
00053 
00054         if (av_new_packet(pkt, qt->bytes_per_frame))
00055             return AVERROR(ENOMEM);
00056         pkt->stream_index = st->index;
00057         pkt->flags        = qt->pkt.flags;
00058         memcpy(pkt->data,
00059                &qt->pkt.data[(num - qt->remaining) * qt->bytes_per_frame],
00060                qt->bytes_per_frame);
00061         if (--qt->remaining == 0) {
00062             av_freep(&qt->pkt.data);
00063             qt->pkt.size = 0;
00064         }
00065         return qt->remaining > 0;
00066     }
00067 
00072     init_get_bits(&gb, buf, len << 3);
00073     ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
00074 
00075     if (len < 4)
00076         return AVERROR_INVALIDDATA;
00077 
00078     skip_bits(&gb, 4); // version
00079     if ((packing_scheme = get_bits(&gb, 2)) == 0)
00080         return AVERROR_INVALIDDATA;
00081     if (get_bits1(&gb))
00082         flags          |= RTP_FLAG_KEY;
00083     has_payload_desc    = get_bits1(&gb);
00084     has_packet_info     = get_bits1(&gb);
00085     skip_bits(&gb, 23); // reserved:7, cache payload info:1, payload ID:15
00086 
00087     if (has_payload_desc) {
00088         int data_len, pos, is_start, is_finish;
00089         uint32_t tag;
00090 
00091         pos = get_bits_count(&gb) >> 3;
00092         if (pos + 12 > len)
00093             return AVERROR_INVALIDDATA;
00094 
00095         skip_bits(&gb, 2); // has non-I frames:1, is sparse:1
00096         is_start  = get_bits1(&gb);
00097         is_finish = get_bits1(&gb);
00098         if (!is_start || !is_finish) {
00099             av_log_missing_feature(s, "RTP-X-QT with payload description "
00100                                       "split over several packets", 1);
00101             return AVERROR(ENOSYS);
00102         }
00103         skip_bits(&gb, 12); // reserved
00104         data_len = get_bits(&gb, 16);
00105 
00106         avio_seek(&pb, pos + 4, SEEK_SET);
00107         tag = avio_rl32(&pb);
00108         if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
00109                  tag != MKTAG('v','i','d','e')) ||
00110             (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
00111                  tag != MKTAG('s','o','u','n')))
00112             return AVERROR_INVALIDDATA;
00113         av_set_pts_info(st, 32, 1, avio_rb32(&pb));
00114 
00115         if (pos + data_len > len)
00116             return AVERROR_INVALIDDATA;
00117         /* TLVs */
00118         while (avio_tell(&pb) + 4 < pos + data_len) {
00119             int tlv_len = avio_rb16(&pb);
00120             tag = avio_rl16(&pb);
00121             if (avio_tell(&pb) + tlv_len > pos + data_len)
00122                 return AVERROR_INVALIDDATA;
00123 
00124 #define MKTAG16(a,b) MKTAG(a,b,0,0)
00125             switch (tag) {
00126             case MKTAG16('s','d'): {
00127                 MOVStreamContext *msc;
00128                 void *priv_data = st->priv_data;
00129                 int nb_streams = s->nb_streams;
00130                 MOVContext *mc = av_mallocz(sizeof(*mc));
00131                 if (!mc)
00132                     return AVERROR(ENOMEM);
00133                 mc->fc = s;
00134                 st->priv_data = msc = av_mallocz(sizeof(MOVStreamContext));
00135                 if (!msc) {
00136                     av_free(mc);
00137                     st->priv_data = priv_data;
00138                     return AVERROR(ENOMEM);
00139                 }
00140                 /* ff_mov_read_stsd_entries updates stream s->nb_streams-1,
00141                  * so set it temporarily to indicate which stream to update. */
00142                 s->nb_streams = st->index + 1;
00143                 ff_mov_read_stsd_entries(mc, &pb, 1);
00144                 qt->bytes_per_frame = msc->bytes_per_frame;
00145                 av_free(msc);
00146                 av_free(mc);
00147                 st->priv_data = priv_data;
00148                 s->nb_streams = nb_streams;
00149                 break;
00150             }
00151             default:
00152                 avio_skip(&pb, tlv_len);
00153                 break;
00154             }
00155         }
00156 
00157         /* 32-bit alignment */
00158         avio_skip(&pb, ((avio_tell(&pb) + 3) & ~3) - avio_tell(&pb));
00159     } else
00160         avio_seek(&pb, 4, SEEK_SET);
00161 
00162     if (has_packet_info) {
00163         av_log_missing_feature(s, "RTP-X-QT with packet specific info", 1);
00164         return AVERROR(ENOSYS);
00165     }
00166 
00167     alen = len - avio_tell(&pb);
00168     if (alen <= 0)
00169         return AVERROR_INVALIDDATA;
00170 
00171     switch (packing_scheme) {
00172     case 3: /* one data packet spread over 1 or multiple RTP packets */
00173         if (qt->pkt.size > 0 && qt->timestamp == *timestamp) {
00174             qt->pkt.data = av_realloc(qt->pkt.data, qt->pkt.size + alen +
00175                                       FF_INPUT_BUFFER_PADDING_SIZE);
00176         } else {
00177             av_freep(&qt->pkt.data);
00178             av_init_packet(&qt->pkt);
00179             qt->pkt.data = av_malloc(alen + FF_INPUT_BUFFER_PADDING_SIZE);
00180             qt->pkt.size = 0;
00181             qt->timestamp = *timestamp;
00182         }
00183         if (!qt->pkt.data)
00184             return AVERROR(ENOMEM);
00185         memcpy(qt->pkt.data + qt->pkt.size, buf + avio_tell(&pb), alen);
00186         qt->pkt.size += alen;
00187         if (has_marker_bit) {
00188             *pkt = qt->pkt;
00189             qt->pkt.size = 0;
00190             qt->pkt.data = NULL;
00191             pkt->flags        = flags & RTP_FLAG_KEY ? AV_PKT_FLAG_KEY : 0;
00192             pkt->stream_index = st->index;
00193             pkt->destruct     = av_destruct_packet;
00194             memset(pkt->data + pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
00195             return 0;
00196         }
00197         return AVERROR(EAGAIN);
00198 
00199     case 1: /* constant packet size, multiple packets per RTP packet */
00200         if (qt->bytes_per_frame == 0 ||
00201             alen % qt->bytes_per_frame != 0)
00202             return AVERROR_INVALIDDATA; /* wrongly padded */
00203         qt->remaining = (alen / qt->bytes_per_frame) - 1;
00204         if (av_new_packet(pkt, qt->bytes_per_frame))
00205             return AVERROR(ENOMEM);
00206         memcpy(pkt->data, buf + avio_tell(&pb), qt->bytes_per_frame);
00207         pkt->flags = flags & RTP_FLAG_KEY ? AV_PKT_FLAG_KEY : 0;
00208         pkt->stream_index = st->index;
00209         if (qt->remaining > 0) {
00210             av_freep(&qt->pkt.data);
00211             qt->pkt.data = av_malloc(qt->remaining * qt->bytes_per_frame);
00212             if (!qt->pkt.data) {
00213                 av_free_packet(pkt);
00214                 return AVERROR(ENOMEM);
00215             }
00216             qt->pkt.size = qt->remaining * qt->bytes_per_frame;
00217             memcpy(qt->pkt.data,
00218                    buf + avio_tell(&pb) + qt->bytes_per_frame,
00219                    qt->remaining * qt->bytes_per_frame);
00220             qt->pkt.flags = pkt->flags;
00221             return 1;
00222         }
00223         return 0;
00224 
00225     default:  /* unimplemented */
00226         av_log_missing_feature(NULL, "RTP-X-QT with packing scheme 2", 1);
00227         return AVERROR(ENOSYS);
00228     }
00229 }
00230 
00231 static PayloadContext *qt_rtp_new(void)
00232 {
00233     return av_mallocz(sizeof(PayloadContext));
00234 }
00235 
00236 static void qt_rtp_free(PayloadContext *qt)
00237 {
00238     av_freep(&qt->pkt.data);
00239     av_free(qt);
00240 }
00241 
00242 #define RTP_QT_HANDLER(m, n, s, t) \
00243 RTPDynamicProtocolHandler ff_ ## m ## _rtp_ ## n ## _handler = { \
00244     .enc_name         = s, \
00245     .codec_type       = t, \
00246     .codec_id         = CODEC_ID_NONE, \
00247     .alloc            = qt_rtp_new,    \
00248     .free             = qt_rtp_free,   \
00249     .parse_packet     = qt_rtp_parse_packet, \
00250 }
00251 
00252 RTP_QT_HANDLER(qt,        vid, "X-QT",        AVMEDIA_TYPE_VIDEO);
00253 RTP_QT_HANDLER(qt,        aud, "X-QT",        AVMEDIA_TYPE_AUDIO);
00254 RTP_QT_HANDLER(quicktime, vid, "X-QUICKTIME", AVMEDIA_TYPE_VIDEO);
00255 RTP_QT_HANDLER(quicktime, aud, "X-QUICKTIME", AVMEDIA_TYPE_AUDIO);