Libav 0.7.1
|
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);