| /* | |
| * RTP/Quicktime support. | |
| * Copyright (c) 2009 Ronald S. Bultje | |
| * | |
| * This file is part of FFmpeg. | |
| * | |
| * FFmpeg is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * FFmpeg is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with FFmpeg; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| */ | |
| /** | |
| * @file | |
| * @brief Quicktime-style RTP support | |
| * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net> | |
| */ | |
| #include "avformat.h" | |
| #include "internal.h" | |
| #include "avio_internal.h" | |
| #include "rtp.h" | |
| #include "rtpdec.h" | |
| #include "isom.h" | |
| #include "libavcodec/get_bits.h" | |
| struct PayloadContext { | |
| AVPacket *pkt; | |
| int bytes_per_frame, remaining; | |
| uint32_t timestamp; | |
| }; | |
| static av_cold int qt_rtp_init(AVFormatContext *ctx, int st_index, | |
| PayloadContext *qt) | |
| { | |
| qt->pkt = av_packet_alloc(); | |
| if (!qt->pkt) | |
| return AVERROR(ENOMEM); | |
| return 0; | |
| } | |
| static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt, | |
| AVStream *st, AVPacket *pkt, | |
| uint32_t *timestamp, const uint8_t *buf, | |
| int len, uint16_t seq, int flags) | |
| { | |
| FFIOContext pb0; | |
| AVIOContext *const pb = &pb0.pub; | |
| GetBitContext gb; | |
| int packing_scheme, has_payload_desc, has_packet_info, alen, | |
| has_marker_bit = flags & RTP_FLAG_MARKER, | |
| keyframe, ret; | |
| if (qt->remaining) { | |
| int num = qt->pkt->size / qt->bytes_per_frame; | |
| if ((ret = av_new_packet(pkt, qt->bytes_per_frame)) < 0) | |
| return ret; | |
| pkt->stream_index = st->index; | |
| pkt->flags = qt->pkt->flags; | |
| memcpy(pkt->data, | |
| &qt->pkt->data[(num - qt->remaining) * qt->bytes_per_frame], | |
| qt->bytes_per_frame); | |
| if (--qt->remaining == 0) { | |
| av_freep(&qt->pkt->data); | |
| qt->pkt->size = 0; | |
| } | |
| return qt->remaining > 0; | |
| } | |
| /** | |
| * The RTP payload is described in: | |
| * http://developer.apple.com/quicktime/icefloe/dispatch026.html | |
| */ | |
| ret = init_get_bits(&gb, buf, len << 3); | |
| if (ret < 0) | |
| return ret; | |
| ffio_init_read_context(&pb0, buf, len); | |
| if (len < 4) | |
| return AVERROR_INVALIDDATA; | |
| skip_bits(&gb, 4); // version | |
| if ((packing_scheme = get_bits(&gb, 2)) == 0) | |
| return AVERROR_INVALIDDATA; | |
| keyframe = get_bits1(&gb); | |
| has_payload_desc = get_bits1(&gb); | |
| has_packet_info = get_bits1(&gb); | |
| skip_bits(&gb, 23); // reserved:7, cache payload info:1, payload ID:15 | |
| if (has_payload_desc) { | |
| int data_len, pos, is_start, is_finish; | |
| uint32_t tag; | |
| pos = get_bits_count(&gb) >> 3; | |
| if (pos + 12 > len) | |
| return AVERROR_INVALIDDATA; | |
| skip_bits(&gb, 2); // has non-I-frames:1, is sparse:1 | |
| is_start = get_bits1(&gb); | |
| is_finish = get_bits1(&gb); | |
| if (!is_start || !is_finish) { | |
| avpriv_request_sample(s, "RTP-X-QT with payload description " | |
| "split over several packets"); | |
| return AVERROR_PATCHWELCOME; | |
| } | |
| skip_bits(&gb, 12); // reserved | |
| data_len = get_bits(&gb, 16); | |
| avio_seek(pb, pos + 4, SEEK_SET); | |
| tag = avio_rl32(pb); | |
| if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && | |
| tag != MKTAG('v','i','d','e')) || | |
| (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && | |
| tag != MKTAG('s','o','u','n'))) | |
| return AVERROR_INVALIDDATA; | |
| avpriv_set_pts_info(st, 32, 1, avio_rb32(pb)); | |
| if (pos + data_len > len) | |
| return AVERROR_INVALIDDATA; | |
| /* TLVs */ | |
| while (avio_tell(pb) + 4 < pos + data_len) { | |
| int tlv_len = avio_rb16(pb); | |
| tag = avio_rl16(pb); | |
| if (avio_tell(pb) + tlv_len > pos + data_len) | |
| return AVERROR_INVALIDDATA; | |
| #define MKTAG16(a,b) MKTAG(a,b,0,0) | |
| switch (tag) { | |
| case MKTAG16('s','d'): { | |
| MOVStreamContext *msc; | |
| void *priv_data = st->priv_data; | |
| int nb_streams = s->nb_streams; | |
| MOVContext *mc = av_mallocz(sizeof(*mc)); | |
| if (!mc) | |
| return AVERROR(ENOMEM); | |
| mc->fc = s; | |
| st->priv_data = msc = av_mallocz(sizeof(MOVStreamContext)); | |
| if (!msc) { | |
| av_free(mc); | |
| st->priv_data = priv_data; | |
| return AVERROR(ENOMEM); | |
| } | |
| /* ff_mov_read_stsd_entries updates stream s->nb_streams-1, | |
| * so set it temporarily to indicate which stream to update. */ | |
| s->nb_streams = st->index + 1; | |
| ff_mov_read_stsd_entries(mc, pb, 1); | |
| qt->bytes_per_frame = msc->bytes_per_frame; | |
| av_free(msc); | |
| av_free(mc); | |
| st->priv_data = priv_data; | |
| s->nb_streams = nb_streams; | |
| break; | |
| } | |
| default: | |
| avio_skip(pb, tlv_len); | |
| break; | |
| } | |
| } | |
| /* 32-bit alignment */ | |
| avio_skip(pb, ((avio_tell(pb) + 3) & ~3) - avio_tell(pb)); | |
| } else | |
| avio_seek(pb, 4, SEEK_SET); | |
| if (has_packet_info) { | |
| avpriv_request_sample(s, "RTP-X-QT with packet-specific info"); | |
| return AVERROR_PATCHWELCOME; | |
| } | |
| alen = len - avio_tell(pb); | |
| if (alen <= 0) | |
| return AVERROR_INVALIDDATA; | |
| switch (packing_scheme) { | |
| case 3: /* one data packet spread over 1 or multiple RTP packets */ | |
| if (qt->pkt->size > 0 && qt->timestamp == *timestamp) { | |
| int err; | |
| if ((err = av_reallocp(&qt->pkt->data, qt->pkt->size + alen + | |
| AV_INPUT_BUFFER_PADDING_SIZE)) < 0) { | |
| qt->pkt->size = 0; | |
| return err; | |
| } | |
| } else { | |
| av_freep(&qt->pkt->data); | |
| av_packet_unref(qt->pkt); | |
| qt->pkt->data = av_realloc(NULL, alen + AV_INPUT_BUFFER_PADDING_SIZE); | |
| if (!qt->pkt->data) | |
| return AVERROR(ENOMEM); | |
| qt->pkt->size = 0; | |
| qt->timestamp = *timestamp; | |
| } | |
| memcpy(qt->pkt->data + qt->pkt->size, buf + avio_tell(pb), alen); | |
| qt->pkt->size += alen; | |
| if (has_marker_bit) { | |
| int ret = av_packet_from_data(pkt, qt->pkt->data, qt->pkt->size); | |
| if (ret < 0) | |
| return ret; | |
| qt->pkt->size = 0; | |
| qt->pkt->data = NULL; | |
| pkt->flags = keyframe ? AV_PKT_FLAG_KEY : 0; | |
| pkt->stream_index = st->index; | |
| memset(pkt->data + pkt->size, 0, AV_INPUT_BUFFER_PADDING_SIZE); | |
| return 0; | |
| } | |
| return AVERROR(EAGAIN); | |
| case 1: /* constant packet size, multiple packets per RTP packet */ | |
| if (qt->bytes_per_frame == 0 || | |
| alen % qt->bytes_per_frame != 0) | |
| return AVERROR_INVALIDDATA; /* wrongly padded */ | |
| qt->remaining = (alen / qt->bytes_per_frame) - 1; | |
| if ((ret = av_new_packet(pkt, qt->bytes_per_frame)) < 0) | |
| return ret; | |
| memcpy(pkt->data, buf + avio_tell(pb), qt->bytes_per_frame); | |
| pkt->flags = keyframe ? AV_PKT_FLAG_KEY : 0; | |
| pkt->stream_index = st->index; | |
| if (qt->remaining > 0) { | |
| av_freep(&qt->pkt->data); | |
| qt->pkt->data = av_realloc(NULL, qt->remaining * qt->bytes_per_frame); | |
| if (!qt->pkt->data) { | |
| av_packet_unref(pkt); | |
| return AVERROR(ENOMEM); | |
| } | |
| qt->pkt->size = qt->remaining * qt->bytes_per_frame; | |
| memcpy(qt->pkt->data, | |
| buf + avio_tell(pb) + qt->bytes_per_frame, | |
| qt->remaining * qt->bytes_per_frame); | |
| qt->pkt->flags = pkt->flags; | |
| return 1; | |
| } | |
| return 0; | |
| default: /* unimplemented */ | |
| avpriv_request_sample(NULL, "RTP-X-QT with packing scheme 2"); | |
| return AVERROR_PATCHWELCOME; | |
| } | |
| } | |
| static void qt_rtp_close(PayloadContext *qt) | |
| { | |
| av_freep(&qt->pkt->data); | |
| av_packet_free(&qt->pkt); | |
| } | |
| #define RTP_QT_HANDLER(m, n, s, t) \ | |
| const RTPDynamicProtocolHandler ff_ ## m ## _rtp_ ## n ## _handler = { \ | |
| .enc_name = s, \ | |
| .codec_type = t, \ | |
| .codec_id = AV_CODEC_ID_NONE, \ | |
| .priv_data_size = sizeof(PayloadContext), \ | |
| .init = qt_rtp_init, \ | |
| .close = qt_rtp_close, \ | |
| .parse_packet = qt_rtp_parse_packet, \ | |
| } | |
| RTP_QT_HANDLER(qt, vid, "X-QT", AVMEDIA_TYPE_VIDEO); | |
| RTP_QT_HANDLER(qt, aud, "X-QT", AVMEDIA_TYPE_AUDIO); | |
| RTP_QT_HANDLER(quicktime, vid, "X-QUICKTIME", AVMEDIA_TYPE_VIDEO); | |
| RTP_QT_HANDLER(quicktime, aud, "X-QUICKTIME", AVMEDIA_TYPE_AUDIO); |