yading@11: /* yading@11: * Microsoft RTP/ASF support. yading@11: * Copyright (c) 2008 Ronald S. Bultje yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * @brief Microsoft RTP/ASF support yading@11: * @author Ronald S. Bultje yading@11: */ yading@11: yading@11: #include "libavutil/base64.h" yading@11: #include "libavutil/avstring.h" yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "rtp.h" yading@11: #include "rtpdec_formats.h" yading@11: #include "rtsp.h" yading@11: #include "asf.h" yading@11: #include "avio_internal.h" yading@11: #include "internal.h" yading@11: yading@11: /** yading@11: * From MSDN 2.2.1.4, we learn that ASF data packets over RTP should not yading@11: * contain any padding. Unfortunately, the header min/max_pktsize are not yading@11: * updated (thus making min_pktsize invalid). Here, we "fix" these faulty yading@11: * min_pktsize values in the ASF file header. yading@11: * @return 0 on success, <0 on failure (currently -1). yading@11: */ yading@11: static int rtp_asf_fix_header(uint8_t *buf, int len) yading@11: { yading@11: uint8_t *p = buf, *end = buf + len; yading@11: yading@11: if (len < sizeof(ff_asf_guid) * 2 + 22 || yading@11: memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) { yading@11: return -1; yading@11: } yading@11: p += sizeof(ff_asf_guid) + 14; yading@11: do { yading@11: uint64_t chunksize = AV_RL64(p + sizeof(ff_asf_guid)); yading@11: if (memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) { yading@11: if (chunksize > end - p) yading@11: return -1; yading@11: p += chunksize; yading@11: continue; yading@11: } yading@11: yading@11: /* skip most of the file header, to min_pktsize */ yading@11: p += 6 * 8 + 3 * 4 + sizeof(ff_asf_guid) * 2; yading@11: if (p + 8 <= end && AV_RL32(p) == AV_RL32(p + 4)) { yading@11: /* and set that to zero */ yading@11: AV_WL32(p, 0); yading@11: return 0; yading@11: } yading@11: break; yading@11: } while (end - p >= sizeof(ff_asf_guid) + 8); yading@11: yading@11: return -1; yading@11: } yading@11: yading@11: /** yading@11: * The following code is basically a buffered AVIOContext, yading@11: * with the added benefit of returning -EAGAIN (instead of 0) yading@11: * on packet boundaries, such that the ASF demuxer can return yading@11: * safely and resume business at the next packet. yading@11: */ yading@11: static int packetizer_read(void *opaque, uint8_t *buf, int buf_size) yading@11: { yading@11: return AVERROR(EAGAIN); yading@11: } yading@11: yading@11: static void init_packetizer(AVIOContext *pb, uint8_t *buf, int len) yading@11: { yading@11: ffio_init_context(pb, buf, len, 0, NULL, packetizer_read, NULL, NULL); yading@11: yading@11: /* this "fills" the buffer with its current content */ yading@11: pb->pos = len; yading@11: pb->buf_end = buf + len; yading@11: } yading@11: yading@11: int ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p) yading@11: { yading@11: int ret = 0; yading@11: if (av_strstart(p, "pgmpu:data:application/vnd.ms.wms-hdr.asfv1;base64,", &p)) { yading@11: AVIOContext pb; yading@11: RTSPState *rt = s->priv_data; yading@11: AVDictionary *opts = NULL; yading@11: int len = strlen(p) * 6 / 8; yading@11: char *buf = av_mallocz(len); yading@11: av_base64_decode(buf, p, len); yading@11: yading@11: if (rtp_asf_fix_header(buf, len) < 0) yading@11: av_log(s, AV_LOG_ERROR, yading@11: "Failed to fix invalid RTSP-MS/ASF min_pktsize\n"); yading@11: init_packetizer(&pb, buf, len); yading@11: if (rt->asf_ctx) { yading@11: avformat_close_input(&rt->asf_ctx); yading@11: } yading@11: if (!(rt->asf_ctx = avformat_alloc_context())) yading@11: return AVERROR(ENOMEM); yading@11: rt->asf_ctx->pb = &pb; yading@11: av_dict_set(&opts, "no_resync_search", "1", 0); yading@11: ret = avformat_open_input(&rt->asf_ctx, "", &ff_asf_demuxer, &opts); yading@11: av_dict_free(&opts); yading@11: if (ret < 0) yading@11: return ret; yading@11: av_dict_copy(&s->metadata, rt->asf_ctx->metadata, 0); yading@11: rt->asf_pb_pos = avio_tell(&pb); yading@11: av_free(buf); yading@11: rt->asf_ctx->pb = NULL; yading@11: } yading@11: return ret; yading@11: } yading@11: yading@11: static int asfrtp_parse_sdp_line(AVFormatContext *s, int stream_index, yading@11: PayloadContext *asf, const char *line) yading@11: { yading@11: if (stream_index < 0) yading@11: return 0; yading@11: if (av_strstart(line, "stream:", &line)) { yading@11: RTSPState *rt = s->priv_data; yading@11: yading@11: s->streams[stream_index]->id = strtol(line, NULL, 10); yading@11: yading@11: if (rt->asf_ctx) { yading@11: int i; yading@11: yading@11: for (i = 0; i < rt->asf_ctx->nb_streams; i++) { yading@11: if (s->streams[stream_index]->id == rt->asf_ctx->streams[i]->id) { yading@11: *s->streams[stream_index]->codec = yading@11: *rt->asf_ctx->streams[i]->codec; yading@11: rt->asf_ctx->streams[i]->codec->extradata_size = 0; yading@11: rt->asf_ctx->streams[i]->codec->extradata = NULL; yading@11: avpriv_set_pts_info(s->streams[stream_index], 32, 1, 1000); yading@11: } yading@11: } yading@11: } yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: struct PayloadContext { yading@11: AVIOContext *pktbuf, pb; yading@11: uint8_t *buf; yading@11: }; yading@11: yading@11: /** yading@11: * @return 0 when a packet was written into /p pkt, and no more data is left; yading@11: * 1 when a packet was written into /p pkt, and more packets might be left; yading@11: * <0 when not enough data was provided to return a full packet, or on error. yading@11: */ yading@11: static int asfrtp_parse_packet(AVFormatContext *s, PayloadContext *asf, yading@11: AVStream *st, AVPacket *pkt, yading@11: uint32_t *timestamp, yading@11: const uint8_t *buf, int len, uint16_t seq, yading@11: int flags) yading@11: { yading@11: AVIOContext *pb = &asf->pb; yading@11: int res, mflags, len_off; yading@11: RTSPState *rt = s->priv_data; yading@11: yading@11: if (!rt->asf_ctx) yading@11: return -1; yading@11: yading@11: if (len > 0) { yading@11: int off, out_len = 0; yading@11: yading@11: if (len < 4) yading@11: return -1; yading@11: yading@11: av_freep(&asf->buf); yading@11: yading@11: ffio_init_context(pb, buf, len, 0, NULL, NULL, NULL, NULL); yading@11: yading@11: while (avio_tell(pb) + 4 < len) { yading@11: int start_off = avio_tell(pb); yading@11: yading@11: mflags = avio_r8(pb); yading@11: if (mflags & 0x80) yading@11: flags |= RTP_FLAG_KEY; yading@11: len_off = avio_rb24(pb); yading@11: if (mflags & 0x20) /**< relative timestamp */ yading@11: avio_skip(pb, 4); yading@11: if (mflags & 0x10) /**< has duration */ yading@11: avio_skip(pb, 4); yading@11: if (mflags & 0x8) /**< has location ID */ yading@11: avio_skip(pb, 4); yading@11: off = avio_tell(pb); yading@11: yading@11: if (!(mflags & 0x40)) { yading@11: /** yading@11: * If 0x40 is not set, the len_off field specifies an offset yading@11: * of this packet's payload data in the complete (reassembled) yading@11: * ASF packet. This is used to spread one ASF packet over yading@11: * multiple RTP packets. yading@11: */ yading@11: if (asf->pktbuf && len_off != avio_tell(asf->pktbuf)) { yading@11: uint8_t *p; yading@11: avio_close_dyn_buf(asf->pktbuf, &p); yading@11: asf->pktbuf = NULL; yading@11: av_free(p); yading@11: } yading@11: if (!len_off && !asf->pktbuf && yading@11: (res = avio_open_dyn_buf(&asf->pktbuf)) < 0) yading@11: return res; yading@11: if (!asf->pktbuf) yading@11: return AVERROR(EIO); yading@11: yading@11: avio_write(asf->pktbuf, buf + off, len - off); yading@11: avio_skip(pb, len - off); yading@11: if (!(flags & RTP_FLAG_MARKER)) yading@11: return -1; yading@11: out_len = avio_close_dyn_buf(asf->pktbuf, &asf->buf); yading@11: asf->pktbuf = NULL; yading@11: } else { yading@11: /** yading@11: * If 0x40 is set, the len_off field specifies the length of yading@11: * the next ASF packet that can be read from this payload yading@11: * data alone. This is commonly the same as the payload size, yading@11: * but could be less in case of packet splitting (i.e. yading@11: * multiple ASF packets in one RTP packet). yading@11: */ yading@11: yading@11: int cur_len = start_off + len_off - off; yading@11: int prev_len = out_len; yading@11: void *newmem; yading@11: out_len += cur_len; yading@11: if (FFMIN(cur_len, len - off) < 0) yading@11: return -1; yading@11: newmem = av_realloc(asf->buf, out_len); yading@11: if (!newmem) yading@11: return -1; yading@11: asf->buf = newmem; yading@11: memcpy(asf->buf + prev_len, buf + off, yading@11: FFMIN(cur_len, len - off)); yading@11: avio_skip(pb, cur_len); yading@11: } yading@11: } yading@11: yading@11: init_packetizer(pb, asf->buf, out_len); yading@11: pb->pos += rt->asf_pb_pos; yading@11: pb->eof_reached = 0; yading@11: rt->asf_ctx->pb = pb; yading@11: } yading@11: yading@11: for (;;) { yading@11: int i; yading@11: yading@11: res = ff_read_packet(rt->asf_ctx, pkt); yading@11: rt->asf_pb_pos = avio_tell(pb); yading@11: if (res != 0) yading@11: break; yading@11: for (i = 0; i < s->nb_streams; i++) { yading@11: if (s->streams[i]->id == rt->asf_ctx->streams[pkt->stream_index]->id) { yading@11: pkt->stream_index = i; yading@11: return 1; // FIXME: return 0 if last packet yading@11: } yading@11: } yading@11: av_free_packet(pkt); yading@11: } yading@11: yading@11: return res == 1 ? -1 : res; yading@11: } yading@11: yading@11: static PayloadContext *asfrtp_new_context(void) yading@11: { yading@11: return av_mallocz(sizeof(PayloadContext)); yading@11: } yading@11: yading@11: static void asfrtp_free_context(PayloadContext *asf) yading@11: { yading@11: if (asf->pktbuf) { yading@11: uint8_t *p = NULL; yading@11: avio_close_dyn_buf(asf->pktbuf, &p); yading@11: asf->pktbuf = NULL; yading@11: av_free(p); yading@11: } yading@11: av_freep(&asf->buf); yading@11: av_free(asf); yading@11: } yading@11: yading@11: #define RTP_ASF_HANDLER(n, s, t) \ yading@11: RTPDynamicProtocolHandler ff_ms_rtp_ ## n ## _handler = { \ yading@11: .enc_name = s, \ yading@11: .codec_type = t, \ yading@11: .codec_id = AV_CODEC_ID_NONE, \ yading@11: .parse_sdp_a_line = asfrtp_parse_sdp_line, \ yading@11: .alloc = asfrtp_new_context, \ yading@11: .free = asfrtp_free_context, \ yading@11: .parse_packet = asfrtp_parse_packet, \ yading@11: } yading@11: yading@11: RTP_ASF_HANDLER(asf_pfv, "x-asf-pf", AVMEDIA_TYPE_VIDEO); yading@11: RTP_ASF_HANDLER(asf_pfa, "x-asf-pf", AVMEDIA_TYPE_AUDIO);