| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * RTP/Quicktime support. | ||
| 3 | * Copyright (c) 2009 Ronald S. Bultje | ||
| 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 | ||
| 24 | * @brief Quicktime-style RTP support | ||
| 25 | * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net> | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include "libavutil/mem.h" | ||
| 29 | #include "avformat.h" | ||
| 30 | #include "internal.h" | ||
| 31 | #include "avio_internal.h" | ||
| 32 | #include "rtp.h" | ||
| 33 | #include "rtpdec.h" | ||
| 34 | #include "isom.h" | ||
| 35 | #include "libavcodec/get_bits.h" | ||
| 36 | |||
| 37 | struct PayloadContext { | ||
| 38 | AVPacket *pkt; | ||
| 39 | int bytes_per_frame, remaining; | ||
| 40 | uint32_t timestamp; | ||
| 41 | }; | ||
| 42 | |||
| 43 | ✗ | static av_cold int qt_rtp_init(AVFormatContext *ctx, int st_index, | |
| 44 | PayloadContext *qt) | ||
| 45 | { | ||
| 46 | ✗ | qt->pkt = av_packet_alloc(); | |
| 47 | ✗ | if (!qt->pkt) | |
| 48 | ✗ | return AVERROR(ENOMEM); | |
| 49 | |||
| 50 | ✗ | return 0; | |
| 51 | } | ||
| 52 | |||
| 53 | ✗ | static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt, | |
| 54 | AVStream *st, AVPacket *pkt, | ||
| 55 | uint32_t *timestamp, const uint8_t *buf, | ||
| 56 | int len, uint16_t seq, int flags) | ||
| 57 | { | ||
| 58 | FFIOContext pb0; | ||
| 59 | ✗ | AVIOContext *const pb = &pb0.pub; | |
| 60 | GetBitContext gb; | ||
| 61 | int packing_scheme, has_payload_desc, has_packet_info, alen, | ||
| 62 | ✗ | has_marker_bit = flags & RTP_FLAG_MARKER, | |
| 63 | keyframe, ret; | ||
| 64 | |||
| 65 | ✗ | if (qt->remaining) { | |
| 66 | ✗ | int num = qt->pkt->size / qt->bytes_per_frame; | |
| 67 | |||
| 68 | ✗ | if ((ret = av_new_packet(pkt, qt->bytes_per_frame)) < 0) | |
| 69 | ✗ | return ret; | |
| 70 | ✗ | pkt->stream_index = st->index; | |
| 71 | ✗ | pkt->flags = qt->pkt->flags; | |
| 72 | ✗ | memcpy(pkt->data, | |
| 73 | ✗ | &qt->pkt->data[(num - qt->remaining) * qt->bytes_per_frame], | |
| 74 | ✗ | qt->bytes_per_frame); | |
| 75 | ✗ | if (--qt->remaining == 0) { | |
| 76 | ✗ | av_freep(&qt->pkt->data); | |
| 77 | ✗ | qt->pkt->size = 0; | |
| 78 | } | ||
| 79 | ✗ | return qt->remaining > 0; | |
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * The RTP payload is described in: | ||
| 84 | * http://developer.apple.com/quicktime/icefloe/dispatch026.html | ||
| 85 | */ | ||
| 86 | ✗ | ret = init_get_bits(&gb, buf, len << 3); | |
| 87 | ✗ | if (ret < 0) | |
| 88 | ✗ | return ret; | |
| 89 | ✗ | ffio_init_read_context(&pb0, buf, len); | |
| 90 | |||
| 91 | ✗ | if (len < 4) | |
| 92 | ✗ | return AVERROR_INVALIDDATA; | |
| 93 | |||
| 94 | ✗ | skip_bits(&gb, 4); // version | |
| 95 | ✗ | if ((packing_scheme = get_bits(&gb, 2)) == 0) | |
| 96 | ✗ | return AVERROR_INVALIDDATA; | |
| 97 | ✗ | keyframe = get_bits1(&gb); | |
| 98 | ✗ | has_payload_desc = get_bits1(&gb); | |
| 99 | ✗ | has_packet_info = get_bits1(&gb); | |
| 100 | ✗ | skip_bits(&gb, 23); // reserved:7, cache payload info:1, payload ID:15 | |
| 101 | |||
| 102 | ✗ | if (has_payload_desc) { | |
| 103 | int data_len, pos, is_start, is_finish; | ||
| 104 | uint32_t tag; | ||
| 105 | |||
| 106 | ✗ | pos = get_bits_count(&gb) >> 3; | |
| 107 | ✗ | if (pos + 12 > len) | |
| 108 | ✗ | return AVERROR_INVALIDDATA; | |
| 109 | |||
| 110 | ✗ | skip_bits(&gb, 2); // has non-I-frames:1, is sparse:1 | |
| 111 | ✗ | is_start = get_bits1(&gb); | |
| 112 | ✗ | is_finish = get_bits1(&gb); | |
| 113 | ✗ | if (!is_start || !is_finish) { | |
| 114 | ✗ | avpriv_request_sample(s, "RTP-X-QT with payload description " | |
| 115 | "split over several packets"); | ||
| 116 | ✗ | return AVERROR_PATCHWELCOME; | |
| 117 | } | ||
| 118 | ✗ | skip_bits(&gb, 12); // reserved | |
| 119 | ✗ | data_len = get_bits(&gb, 16); | |
| 120 | |||
| 121 | ✗ | avio_seek(pb, pos + 4, SEEK_SET); | |
| 122 | ✗ | tag = avio_rl32(pb); | |
| 123 | ✗ | if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && | |
| 124 | ✗ | tag != MKTAG('v','i','d','e')) || | |
| 125 | ✗ | (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && | |
| 126 | tag != MKTAG('s','o','u','n'))) | ||
| 127 | ✗ | return AVERROR_INVALIDDATA; | |
| 128 | ✗ | avpriv_set_pts_info(st, 32, 1, avio_rb32(pb)); | |
| 129 | |||
| 130 | ✗ | if (pos + data_len > len) | |
| 131 | ✗ | return AVERROR_INVALIDDATA; | |
| 132 | /* TLVs */ | ||
| 133 | ✗ | while (avio_tell(pb) + 4 < pos + data_len) { | |
| 134 | ✗ | int tlv_len = avio_rb16(pb); | |
| 135 | ✗ | tag = avio_rl16(pb); | |
| 136 | ✗ | if (avio_tell(pb) + tlv_len > pos + data_len) | |
| 137 | ✗ | return AVERROR_INVALIDDATA; | |
| 138 | |||
| 139 | #define MKTAG16(a,b) MKTAG(a,b,0,0) | ||
| 140 | ✗ | switch (tag) { | |
| 141 | ✗ | case MKTAG16('s','d'): { | |
| 142 | MOVStreamContext *msc; | ||
| 143 | ✗ | void *priv_data = st->priv_data; | |
| 144 | ✗ | int nb_streams = s->nb_streams; | |
| 145 | ✗ | MOVContext *mc = av_mallocz(sizeof(*mc)); | |
| 146 | ✗ | if (!mc) | |
| 147 | ✗ | return AVERROR(ENOMEM); | |
| 148 | ✗ | mc->fc = s; | |
| 149 | ✗ | st->priv_data = msc = av_mallocz(sizeof(MOVStreamContext)); | |
| 150 | ✗ | if (!msc) { | |
| 151 | ✗ | av_free(mc); | |
| 152 | ✗ | st->priv_data = priv_data; | |
| 153 | ✗ | return AVERROR(ENOMEM); | |
| 154 | } | ||
| 155 | /* ff_mov_read_stsd_entries updates stream s->nb_streams-1, | ||
| 156 | * so set it temporarily to indicate which stream to update. */ | ||
| 157 | ✗ | s->nb_streams = st->index + 1; | |
| 158 | ✗ | ff_mov_read_stsd_entries(mc, pb, 1); | |
| 159 | ✗ | qt->bytes_per_frame = msc->bytes_per_frame; | |
| 160 | ✗ | av_free(msc); | |
| 161 | ✗ | av_free(mc); | |
| 162 | ✗ | st->priv_data = priv_data; | |
| 163 | ✗ | s->nb_streams = nb_streams; | |
| 164 | ✗ | break; | |
| 165 | } | ||
| 166 | ✗ | default: | |
| 167 | ✗ | avio_skip(pb, tlv_len); | |
| 168 | ✗ | break; | |
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | /* 32-bit alignment */ | ||
| 173 | ✗ | avio_skip(pb, ((avio_tell(pb) + 3) & ~3) - avio_tell(pb)); | |
| 174 | } else | ||
| 175 | ✗ | avio_seek(pb, 4, SEEK_SET); | |
| 176 | |||
| 177 | ✗ | if (has_packet_info) { | |
| 178 | ✗ | avpriv_request_sample(s, "RTP-X-QT with packet-specific info"); | |
| 179 | ✗ | return AVERROR_PATCHWELCOME; | |
| 180 | } | ||
| 181 | |||
| 182 | ✗ | alen = len - avio_tell(pb); | |
| 183 | ✗ | if (alen <= 0) | |
| 184 | ✗ | return AVERROR_INVALIDDATA; | |
| 185 | |||
| 186 | ✗ | switch (packing_scheme) { | |
| 187 | ✗ | case 3: /* one data packet spread over 1 or multiple RTP packets */ | |
| 188 | ✗ | if (qt->pkt->size > 0 && qt->timestamp == *timestamp) { | |
| 189 | int err; | ||
| 190 | ✗ | if ((err = av_reallocp(&qt->pkt->data, qt->pkt->size + alen + | |
| 191 | AV_INPUT_BUFFER_PADDING_SIZE)) < 0) { | ||
| 192 | ✗ | qt->pkt->size = 0; | |
| 193 | ✗ | return err; | |
| 194 | } | ||
| 195 | } else { | ||
| 196 | ✗ | av_freep(&qt->pkt->data); | |
| 197 | ✗ | av_packet_unref(qt->pkt); | |
| 198 | ✗ | qt->pkt->data = av_realloc(NULL, alen + AV_INPUT_BUFFER_PADDING_SIZE); | |
| 199 | ✗ | if (!qt->pkt->data) | |
| 200 | ✗ | return AVERROR(ENOMEM); | |
| 201 | ✗ | qt->pkt->size = 0; | |
| 202 | ✗ | qt->timestamp = *timestamp; | |
| 203 | } | ||
| 204 | ✗ | memcpy(qt->pkt->data + qt->pkt->size, buf + avio_tell(pb), alen); | |
| 205 | ✗ | qt->pkt->size += alen; | |
| 206 | ✗ | if (has_marker_bit) { | |
| 207 | ✗ | int ret = av_packet_from_data(pkt, qt->pkt->data, qt->pkt->size); | |
| 208 | ✗ | if (ret < 0) | |
| 209 | ✗ | return ret; | |
| 210 | |||
| 211 | ✗ | qt->pkt->size = 0; | |
| 212 | ✗ | qt->pkt->data = NULL; | |
| 213 | ✗ | pkt->flags = keyframe ? AV_PKT_FLAG_KEY : 0; | |
| 214 | ✗ | pkt->stream_index = st->index; | |
| 215 | ✗ | memset(pkt->data + pkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE); | |
| 216 | ✗ | return 0; | |
| 217 | } | ||
| 218 | ✗ | return AVERROR(EAGAIN); | |
| 219 | |||
| 220 | ✗ | case 1: /* constant packet size, multiple packets per RTP packet */ | |
| 221 | ✗ | if (qt->bytes_per_frame == 0 || | |
| 222 | ✗ | alen % qt->bytes_per_frame != 0) | |
| 223 | ✗ | return AVERROR_INVALIDDATA; /* wrongly padded */ | |
| 224 | ✗ | qt->remaining = (alen / qt->bytes_per_frame) - 1; | |
| 225 | ✗ | if ((ret = av_new_packet(pkt, qt->bytes_per_frame)) < 0) | |
| 226 | ✗ | return ret; | |
| 227 | ✗ | memcpy(pkt->data, buf + avio_tell(pb), qt->bytes_per_frame); | |
| 228 | ✗ | pkt->flags = keyframe ? AV_PKT_FLAG_KEY : 0; | |
| 229 | ✗ | pkt->stream_index = st->index; | |
| 230 | ✗ | if (qt->remaining > 0) { | |
| 231 | ✗ | av_freep(&qt->pkt->data); | |
| 232 | ✗ | qt->pkt->data = av_realloc(NULL, qt->remaining * qt->bytes_per_frame); | |
| 233 | ✗ | if (!qt->pkt->data) { | |
| 234 | ✗ | av_packet_unref(pkt); | |
| 235 | ✗ | return AVERROR(ENOMEM); | |
| 236 | } | ||
| 237 | ✗ | qt->pkt->size = qt->remaining * qt->bytes_per_frame; | |
| 238 | ✗ | memcpy(qt->pkt->data, | |
| 239 | ✗ | buf + avio_tell(pb) + qt->bytes_per_frame, | |
| 240 | ✗ | qt->remaining * qt->bytes_per_frame); | |
| 241 | ✗ | qt->pkt->flags = pkt->flags; | |
| 242 | ✗ | return 1; | |
| 243 | } | ||
| 244 | ✗ | return 0; | |
| 245 | |||
| 246 | ✗ | default: /* unimplemented */ | |
| 247 | ✗ | avpriv_request_sample(NULL, "RTP-X-QT with packing scheme 2"); | |
| 248 | ✗ | return AVERROR_PATCHWELCOME; | |
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | ✗ | static void qt_rtp_close(PayloadContext *qt) | |
| 253 | { | ||
| 254 | ✗ | av_freep(&qt->pkt->data); | |
| 255 | ✗ | av_packet_free(&qt->pkt); | |
| 256 | ✗ | } | |
| 257 | |||
| 258 | #define RTP_QT_HANDLER(m, n, s, t) \ | ||
| 259 | const RTPDynamicProtocolHandler ff_ ## m ## _rtp_ ## n ## _handler = { \ | ||
| 260 | .enc_name = s, \ | ||
| 261 | .codec_type = t, \ | ||
| 262 | .codec_id = AV_CODEC_ID_NONE, \ | ||
| 263 | .priv_data_size = sizeof(PayloadContext), \ | ||
| 264 | .init = qt_rtp_init, \ | ||
| 265 | .close = qt_rtp_close, \ | ||
| 266 | .parse_packet = qt_rtp_parse_packet, \ | ||
| 267 | } | ||
| 268 | |||
| 269 | RTP_QT_HANDLER(qt, vid, "X-QT", AVMEDIA_TYPE_VIDEO); | ||
| 270 | RTP_QT_HANDLER(qt, aud, "X-QT", AVMEDIA_TYPE_AUDIO); | ||
| 271 | RTP_QT_HANDLER(quicktime, vid, "X-QUICKTIME", AVMEDIA_TYPE_VIDEO); | ||
| 272 | RTP_QT_HANDLER(quicktime, aud, "X-QUICKTIME", AVMEDIA_TYPE_AUDIO); | ||
| 273 |