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 FFmpeg.
00006  *
00007  * FFmpeg 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  * FFmpeg 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 FFmpeg; 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 "internal.h"
00030 #include "avio_internal.h"
00031 #include "rtp.h"
00032 #include "rtpdec.h"
00033 #include "isom.h"
00034 #include "libavcodec/get_bits.h"
00035 
00036 struct PayloadContext {
00037     AVPacket pkt;
00038     int bytes_per_frame, remaining;
00039     uint32_t timestamp;
00040 };
00041 
00042 static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt,
00043                                AVStream *st, AVPacket *pkt,
00044                                uint32_t *timestamp, const uint8_t *buf,
00045                                int len, int flags)
00046 {
00047     AVIOContext pb;
00048     GetBitContext gb;
00049     int packing_scheme, has_payload_desc, has_packet_info, alen,
00050         has_marker_bit = flags & RTP_FLAG_MARKER;
00051 
00052     if (qt->remaining) {
00053         int num = qt->pkt.size / qt->bytes_per_frame;
00054 
00055         if (av_new_packet(pkt, qt->bytes_per_frame))
00056             return AVERROR(ENOMEM);
00057         pkt->stream_index = st->index;
00058         pkt->flags        = qt->pkt.flags;
00059         memcpy(pkt->data,
00060                &qt->pkt.data[(num - qt->remaining) * qt->bytes_per_frame],
00061                qt->bytes_per_frame);
00062         if (--qt->remaining == 0) {
00063             av_freep(&qt->pkt.data);
00064             qt->pkt.size = 0;
00065         }
00066         return qt->remaining > 0;
00067     }
00068 
00073     init_get_bits(&gb, buf, len << 3);
00074     ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
00075 
00076     if (len < 4)
00077         return AVERROR_INVALIDDATA;
00078 
00079     skip_bits(&gb, 4); // version
00080     if ((packing_scheme = get_bits(&gb, 2)) == 0)
00081         return AVERROR_INVALIDDATA;
00082     if (get_bits1(&gb))
00083         flags          |= RTP_FLAG_KEY;
00084     has_payload_desc    = get_bits1(&gb);
00085     has_packet_info     = get_bits1(&gb);
00086     skip_bits(&gb, 23); // reserved:7, cache payload info:1, payload ID:15
00087 
00088     if (has_payload_desc) {
00089         int data_len, pos, is_start, is_finish;
00090         uint32_t tag;
00091 
00092         pos = get_bits_count(&gb) >> 3;
00093         if (pos + 12 > len)
00094             return AVERROR_INVALIDDATA;
00095 
00096         skip_bits(&gb, 2); // has non-I frames:1, is sparse:1
00097         is_start  = get_bits1(&gb);
00098         is_finish = get_bits1(&gb);
00099         if (!is_start || !is_finish) {
00100             av_log_missing_feature(s, "RTP-X-QT with payload description "
00101                                       "split over several packets", 1);
00102             return AVERROR(ENOSYS);
00103         }
00104         skip_bits(&gb, 12); // reserved
00105         data_len = get_bits(&gb, 16);
00106 
00107         avio_seek(&pb, pos + 4, SEEK_SET);
00108         tag = avio_rl32(&pb);
00109         if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
00110                  tag != MKTAG('v','i','d','e')) ||
00111             (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
00112                  tag != MKTAG('s','o','u','n')))
00113             return AVERROR_INVALIDDATA;
00114         avpriv_set_pts_info(st, 32, 1, avio_rb32(&pb));
00115 
00116         if (pos + data_len > len)
00117             return AVERROR_INVALIDDATA;
00118         /* TLVs */
00119         while (avio_tell(&pb) + 4 < pos + data_len) {
00120             int tlv_len = avio_rb16(&pb);
00121             tag = avio_rl16(&pb);
00122             if (avio_tell(&pb) + tlv_len > pos + data_len)
00123                 return AVERROR_INVALIDDATA;
00124 
00125 #define MKTAG16(a,b) MKTAG(a,b,0,0)
00126             switch (tag) {
00127             case MKTAG16('s','d'): {
00128                 MOVStreamContext *msc;
00129                 void *priv_data = st->priv_data;
00130                 int nb_streams = s->nb_streams;
00131                 MOVContext *mc = av_mallocz(sizeof(*mc));
00132                 if (!mc)
00133                     return AVERROR(ENOMEM);
00134                 mc->fc = s;
00135                 st->priv_data = msc = av_mallocz(sizeof(MOVStreamContext));
00136                 if (!msc) {
00137                     av_free(mc);
00138                     st->priv_data = priv_data;
00139                     return AVERROR(ENOMEM);
00140                 }
00141                 /* ff_mov_read_stsd_entries updates stream s->nb_streams-1,
00142                  * so set it temporarily to indicate which stream to update. */
00143                 s->nb_streams = st->index + 1;
00144                 ff_mov_read_stsd_entries(mc, &pb, 1);
00145                 qt->bytes_per_frame = msc->bytes_per_frame;
00146                 av_free(msc);
00147                 av_free(mc);
00148                 st->priv_data = priv_data;
00149                 s->nb_streams = nb_streams;
00150                 break;
00151             }
00152             default:
00153                 avio_skip(&pb, tlv_len);
00154                 break;
00155             }
00156         }
00157 
00158         /* 32-bit alignment */
00159         avio_skip(&pb, ((avio_tell(&pb) + 3) & ~3) - avio_tell(&pb));
00160     } else
00161         avio_seek(&pb, 4, SEEK_SET);
00162 
00163     if (has_packet_info) {
00164         av_log_missing_feature(s, "RTP-X-QT with packet specific info", 1);
00165         return AVERROR(ENOSYS);
00166     }
00167 
00168     alen = len - avio_tell(&pb);
00169     if (alen <= 0)
00170         return AVERROR_INVALIDDATA;
00171 
00172     switch (packing_scheme) {
00173     case 3: /* one data packet spread over 1 or multiple RTP packets */
00174         if (qt->pkt.size > 0 && qt->timestamp == *timestamp) {
00175             qt->pkt.data = av_realloc(qt->pkt.data, qt->pkt.size + alen +
00176                                       FF_INPUT_BUFFER_PADDING_SIZE);
00177         } else {
00178             av_freep(&qt->pkt.data);
00179             av_init_packet(&qt->pkt);
00180             qt->pkt.data = av_malloc(alen + FF_INPUT_BUFFER_PADDING_SIZE);
00181             qt->pkt.size = 0;
00182             qt->timestamp = *timestamp;
00183         }
00184         if (!qt->pkt.data)
00185             return AVERROR(ENOMEM);
00186         memcpy(qt->pkt.data + qt->pkt.size, buf + avio_tell(&pb), alen);
00187         qt->pkt.size += alen;
00188         if (has_marker_bit) {
00189             *pkt = qt->pkt;
00190             qt->pkt.size = 0;
00191             qt->pkt.data = NULL;
00192             pkt->flags        = flags & RTP_FLAG_KEY ? AV_PKT_FLAG_KEY : 0;
00193             pkt->stream_index = st->index;
00194             pkt->destruct     = av_destruct_packet;
00195             memset(pkt->data + pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
00196             return 0;
00197         }
00198         return AVERROR(EAGAIN);
00199 
00200     case 1: /* constant packet size, multiple packets per RTP packet */
00201         if (qt->bytes_per_frame == 0 ||
00202             alen % qt->bytes_per_frame != 0)
00203             return AVERROR_INVALIDDATA; /* wrongly padded */
00204         qt->remaining = (alen / qt->bytes_per_frame) - 1;
00205         if (av_new_packet(pkt, qt->bytes_per_frame))
00206             return AVERROR(ENOMEM);
00207         memcpy(pkt->data, buf + avio_tell(&pb), qt->bytes_per_frame);
00208         pkt->flags = flags & RTP_FLAG_KEY ? AV_PKT_FLAG_KEY : 0;
00209         pkt->stream_index = st->index;
00210         if (qt->remaining > 0) {
00211             av_freep(&qt->pkt.data);
00212             qt->pkt.data = av_malloc(qt->remaining * qt->bytes_per_frame);
00213             if (!qt->pkt.data) {
00214                 av_free_packet(pkt);
00215                 return AVERROR(ENOMEM);
00216             }
00217             qt->pkt.size = qt->remaining * qt->bytes_per_frame;
00218             memcpy(qt->pkt.data,
00219                    buf + avio_tell(&pb) + qt->bytes_per_frame,
00220                    qt->remaining * qt->bytes_per_frame);
00221             qt->pkt.flags = pkt->flags;
00222             return 1;
00223         }
00224         return 0;
00225 
00226     default:  /* unimplemented */
00227         av_log_missing_feature(NULL, "RTP-X-QT with packing scheme 2", 1);
00228         return AVERROR(ENOSYS);
00229     }
00230 }
00231 
00232 static PayloadContext *qt_rtp_new(void)
00233 {
00234     return av_mallocz(sizeof(PayloadContext));
00235 }
00236 
00237 static void qt_rtp_free(PayloadContext *qt)
00238 {
00239     av_freep(&qt->pkt.data);
00240     av_free(qt);
00241 }
00242 
00243 #define RTP_QT_HANDLER(m, n, s, t) \
00244 RTPDynamicProtocolHandler ff_ ## m ## _rtp_ ## n ## _handler = { \
00245     .enc_name         = s, \
00246     .codec_type       = t, \
00247     .codec_id         = CODEC_ID_NONE, \
00248     .alloc            = qt_rtp_new,    \
00249     .free             = qt_rtp_free,   \
00250     .parse_packet     = qt_rtp_parse_packet, \
00251 }
00252 
00253 RTP_QT_HANDLER(qt,        vid, "X-QT",        AVMEDIA_TYPE_VIDEO);
00254 RTP_QT_HANDLER(qt,        aud, "X-QT",        AVMEDIA_TYPE_AUDIO);
00255 RTP_QT_HANDLER(quicktime, vid, "X-QUICKTIME", AVMEDIA_TYPE_VIDEO);
00256 RTP_QT_HANDLER(quicktime, aud, "X-QUICKTIME", AVMEDIA_TYPE_AUDIO);