rtpdec_asf.c 9.64 KB
Newer Older
1 2 3 4
/*
 * Microsoft RTP/ASF support.
 * Copyright (c) 2008 Ronald S. Bultje
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav is free software; you can redistribute it and/or
8 9 10 11
 * 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.
 *
12
 * Libav is distributed in the hope that it will be useful,
13 14 15 16 17
 * 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
18
 * License along with Libav; if not, write to the Free Software
19 20 21 22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
23
 * @file
24 25 26 27
 * @brief Microsoft RTP/ASF support
 * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
 */

28 29 30
#include "libavutil/base64.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
31
#include "rtp.h"
32
#include "rtpdec_formats.h"
33 34
#include "rtsp.h"
#include "asf.h"
35
#include "avio_internal.h"
36
#include "internal.h"
37

38 39 40 41 42 43 44
/**
 * From MSDN 2.2.1.4, we learn that ASF data packets over RTP should not
 * contain any padding. Unfortunately, the header min/max_pktsize are not
 * updated (thus making min_pktsize invalid). Here, we "fix" these faulty
 * min_pktsize values in the ASF file header.
 * @return 0 on success, <0 on failure (currently -1).
 */
45
static int rtp_asf_fix_header(uint8_t *buf, int len)
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
{
    uint8_t *p = buf, *end = buf + len;

    if (len < sizeof(ff_asf_guid) * 2 + 22 ||
        memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) {
        return -1;
    }
    p += sizeof(ff_asf_guid) + 14;
    do {
        uint64_t chunksize = AV_RL64(p + sizeof(ff_asf_guid));
        if (memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) {
            if (chunksize > end - p)
                return -1;
            p += chunksize;
            continue;
        }

        /* skip most of the file header, to min_pktsize */
        p += 6 * 8 + 3 * 4 + sizeof(ff_asf_guid) * 2;
        if (p + 8 <= end && AV_RL32(p) == AV_RL32(p + 4)) {
            /* and set that to zero */
            AV_WL32(p, 0);
            return 0;
        }
        break;
    } while (end - p >= sizeof(ff_asf_guid) + 8);

    return -1;
}

/**
77
 * The following code is basically a buffered AVIOContext,
78 79 80 81
 * with the added benefit of returning -EAGAIN (instead of 0)
 * on packet boundaries, such that the ASF demuxer can return
 * safely and resume business at the next packet.
 */
82
static int packetizer_read(void *opaque, uint8_t *buf, int buf_size)
83 84 85 86
{
    return AVERROR(EAGAIN);
}

87
static void init_packetizer(AVIOContext *pb, uint8_t *buf, int len)
88
{
89
    ffio_init_context(pb, buf, len, 0, NULL, packetizer_read, NULL, NULL);
90 91 92 93 94 95

    /* this "fills" the buffer with its current content */
    pb->pos     = len;
    pb->buf_end = buf + len;
}

96
int ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p)
97
{
98
    int ret = 0;
99
    if (av_strstart(p, "pgmpu:data:application/vnd.ms.wms-hdr.asfv1;base64,", &p)) {
100
        AVIOContext pb;
101
        RTSPState *rt = s->priv_data;
102
        AVDictionary *opts = NULL;
103 104
        int len = strlen(p) * 6 / 8;
        char *buf = av_mallocz(len);
105 106 107

        if (!buf)
            return AVERROR(ENOMEM);
108 109
        av_base64_decode(buf, p, len);

110 111 112 113
        if (rtp_asf_fix_header(buf, len) < 0)
            av_log(s, AV_LOG_ERROR,
                   "Failed to fix invalid RTSP-MS/ASF min_pktsize\n");
        init_packetizer(&pb, buf, len);
114
        if (rt->asf_ctx) {
115
            avformat_close_input(&rt->asf_ctx);
116
        }
117 118 119
        rt->asf_ctx = avformat_alloc_context();
        if (!rt->asf_ctx) {
            av_free(buf);
120
            return AVERROR(ENOMEM);
121
        }
122
        rt->asf_ctx->pb      = &pb;
123 124 125
        av_dict_set(&opts, "no_resync_search", "1", 0);
        ret = avformat_open_input(&rt->asf_ctx, "", &ff_asf_demuxer, &opts);
        av_dict_free(&opts);
126 127
        if (ret < 0) {
            av_free(buf);
128
            return ret;
129
        }
130
        av_dict_copy(&s->metadata, rt->asf_ctx->metadata, 0);
131
        rt->asf_pb_pos = avio_tell(&pb);
132 133 134
        av_free(buf);
        rt->asf_ctx->pb = NULL;
    }
135
    return ret;
136
}
137

138 139
static int asfrtp_parse_sdp_line(AVFormatContext *s, int stream_index,
                                 PayloadContext *asf, const char *line)
140
{
141 142
    if (stream_index < 0)
        return 0;
143 144 145 146 147 148 149 150 151 152
    if (av_strstart(line, "stream:", &line)) {
        RTSPState *rt = s->priv_data;

        s->streams[stream_index]->id = strtol(line, NULL, 10);

        if (rt->asf_ctx) {
            int i;

            for (i = 0; i < rt->asf_ctx->nb_streams; i++) {
                if (s->streams[stream_index]->id == rt->asf_ctx->streams[i]->id) {
153 154
                    avcodec_parameters_copy(s->streams[stream_index]->codecpar,
                                            rt->asf_ctx->streams[i]->codecpar);
155 156
                    s->streams[stream_index]->need_parsing =
                        rt->asf_ctx->streams[i]->need_parsing;
157
                    avpriv_set_pts_info(s->streams[stream_index], 32, 1, 1000);
158 159 160 161 162 163 164 165
                }
           }
        }
    }

    return 0;
}

166
struct PayloadContext {
167
    AVIOContext *pktbuf, pb;
Eli Friedman's avatar
Eli Friedman committed
168
    uint8_t *buf;
169 170 171 172 173 174 175
};

/**
 * @return 0 when a packet was written into /p pkt, and no more data is left;
 *         1 when a packet was written into /p pkt, and more packets might be left;
 *        <0 when not enough data was provided to return a full packet, or on error.
 */
176 177 178
static int asfrtp_parse_packet(AVFormatContext *s, PayloadContext *asf,
                               AVStream *st, AVPacket *pkt,
                               uint32_t *timestamp,
179 180
                               const uint8_t *buf, int len, uint16_t seq,
                               int flags)
181
{
182
    AVIOContext *pb = &asf->pb;
183 184 185 186 187 188 189
    int res, mflags, len_off;
    RTSPState *rt = s->priv_data;

    if (!rt->asf_ctx)
        return -1;

    if (len > 0) {
190
        int off, out_len = 0;
191 192 193 194

        if (len < 4)
            return -1;

195 196
        av_freep(&asf->buf);

197
        ffio_init_context(pb, buf, len, 0, NULL, NULL, NULL, NULL);
198

199 200
        while (avio_tell(pb) + 4 < len) {
            int start_off = avio_tell(pb);
201

202 203
            mflags = avio_r8(pb);
            len_off = avio_rb24(pb);
204
            if (mflags & 0x20)   /**< relative timestamp */
205
                avio_skip(pb, 4);
206
            if (mflags & 0x10)   /**< has duration */
207
                avio_skip(pb, 4);
208
            if (mflags & 0x8)    /**< has location ID */
209
                avio_skip(pb, 4);
210
            off = avio_tell(pb);
211 212 213 214 215 216 217 218

            if (!(mflags & 0x40)) {
                /**
                 * If 0x40 is not set, the len_off field specifies an offset
                 * of this packet's payload data in the complete (reassembled)
                 * ASF packet. This is used to spread one ASF packet over
                 * multiple RTP packets.
                 */
219
                if (asf->pktbuf && len_off != avio_tell(asf->pktbuf)) {
220
                    ffio_free_dyn_buf(&asf->pktbuf);
221 222
                }
                if (!len_off && !asf->pktbuf &&
223
                    (res = avio_open_dyn_buf(&asf->pktbuf)) < 0)
224 225 226 227
                    return res;
                if (!asf->pktbuf)
                    return AVERROR(EIO);

228
                avio_write(asf->pktbuf, buf + off, len - off);
229
                avio_skip(pb, len - off);
230 231
                if (!(flags & RTP_FLAG_MARKER))
                    return -1;
232
                out_len     = avio_close_dyn_buf(asf->pktbuf, &asf->buf);
233
                asf->pktbuf = NULL;
234 235 236 237 238 239 240 241 242 243 244 245
            } else {
                /**
                 * If 0x40 is set, the len_off field specifies the length of
                 * the next ASF packet that can be read from this payload
                 * data alone. This is commonly the same as the payload size,
                 * but could be less in case of packet splitting (i.e.
                 * multiple ASF packets in one RTP packet).
                 */

                int cur_len = start_off + len_off - off;
                int prev_len = out_len;
                out_len += cur_len;
246 247
                if (FFMIN(cur_len, len - off) < 0)
                    return -1;
248 249
                if ((res = av_reallocp(&asf->buf, out_len)) < 0)
                    return res;
250 251
                memcpy(asf->buf + prev_len, buf + off,
                       FFMIN(cur_len, len - off));
252
                avio_skip(pb, cur_len);
253 254 255 256 257 258 259 260 261 262 263 264
            }
        }

        init_packetizer(pb, asf->buf, out_len);
        pb->pos += rt->asf_pb_pos;
        pb->eof_reached = 0;
        rt->asf_ctx->pb = pb;
    }

    for (;;) {
        int i;

265
        res = ff_read_packet(rt->asf_ctx, pkt);
266
        rt->asf_pb_pos = avio_tell(pb);
267 268 269 270 271 272 273 274
        if (res != 0)
            break;
        for (i = 0; i < s->nb_streams; i++) {
            if (s->streams[i]->id == rt->asf_ctx->streams[pkt->stream_index]->id) {
                pkt->stream_index = i;
                return 1; // FIXME: return 0 if last packet
            }
        }
275
        av_packet_unref(pkt);
276 277 278 279 280
    }

    return res == 1 ? -1 : res;
}

281
static void asfrtp_close_context(PayloadContext *asf)
282
{
283
    ffio_free_dyn_buf(&asf->pktbuf);
284 285 286
    av_freep(&asf->buf);
}

287 288
#define RTP_ASF_HANDLER(n, s, t) \
RTPDynamicProtocolHandler ff_ms_rtp_ ## n ## _handler = { \
289 290
    .enc_name         = s, \
    .codec_type       = t, \
291
    .codec_id         = AV_CODEC_ID_NONE, \
292
    .priv_data_size   = sizeof(PayloadContext), \
293
    .parse_sdp_a_line = asfrtp_parse_sdp_line, \
294
    .close            = asfrtp_close_context, \
295
    .parse_packet     = asfrtp_parse_packet,   \
296
}
297

298 299
RTP_ASF_HANDLER(asf_pfv, "x-asf-pf",  AVMEDIA_TYPE_VIDEO);
RTP_ASF_HANDLER(asf_pfa, "x-asf-pf",  AVMEDIA_TYPE_AUDIO);