mmst.c 23.6 KB
Newer Older
1 2 3
/*
 * MMS protocol over TCP
 * Copyright (c) 2006,2007 Ryan Martell
4
 * Copyright (c) 2007 Björn Axelsson
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com>
 *
 * 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
 */
23 24 25 26 27 28 29 30

/* References
 * MMS protocol specification:
 *  [1]http://msdn.microsoft.com/en-us/library/cc234711(PROT.10).aspx
 * ASF specification. Revision 01.20.03.
 *  [2]http://msdn.microsoft.com/en-us/library/bb643323.aspx
 */

31
#include "avformat.h"
32
#include "mms.h"
33
#include "internal.h"
34
#include "avio_internal.h"
35 36 37
#include "libavutil/intreadwrite.h"
#include "libavcodec/bytestream.h"
#include "network.h"
38
#include "url.h"
39 40 41 42 43 44 45 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 77 78 79 80 81 82 83 84 85 86 87

#define LOCAL_ADDRESS 0xc0a80081    // FIXME get and use correct local ip address.
#define LOCAL_PORT    1037          // as above.
/** Client to server packet types. */
typedef enum {
    CS_PKT_INITIAL                  = 0x01,
    CS_PKT_PROTOCOL_SELECT          = 0x02,
    CS_PKT_MEDIA_FILE_REQUEST       = 0x05,
    CS_PKT_START_FROM_PKT_ID        = 0x07,
    CS_PKT_STREAM_PAUSE             = 0x09,
    CS_PKT_STREAM_CLOSE             = 0x0d,
    CS_PKT_MEDIA_HEADER_REQUEST     = 0x15,
    CS_PKT_TIMING_DATA_REQUEST      = 0x18,
    CS_PKT_USER_PASSWORD            = 0x1a,
    CS_PKT_KEEPALIVE                = 0x1b,
    CS_PKT_STREAM_ID_REQUEST        = 0x33,
} MMSCSPacketType;

/** Server to client packet types. */
typedef enum {
    /** Control packets. */
    /*@{*/
    SC_PKT_CLIENT_ACCEPTED          = 0x01,
    SC_PKT_PROTOCOL_ACCEPTED        = 0x02,
    SC_PKT_PROTOCOL_FAILED          = 0x03,
    SC_PKT_MEDIA_PKT_FOLLOWS        = 0x05,
    SC_PKT_MEDIA_FILE_DETAILS       = 0x06,
    SC_PKT_HEADER_REQUEST_ACCEPTED  = 0x11,
    SC_PKT_TIMING_TEST_REPLY        = 0x15,
    SC_PKT_PASSWORD_REQUIRED        = 0x1a,
    SC_PKT_KEEPALIVE                = 0x1b,
    SC_PKT_STREAM_STOPPED           = 0x1e,
    SC_PKT_STREAM_CHANGING          = 0x20,
    SC_PKT_STREAM_ID_ACCEPTED       = 0x21,
    /*@}*/

    /** Pseudo packets. */
    /*@{*/
    SC_PKT_CANCEL                   = -1,
    SC_PKT_NO_DATA                  = -2,
    /*@}*/

    /** Data packets. */
    /*@{*/
    SC_PKT_ASF_HEADER               = 0x010000,// make it bigger than 0xFF in case of
    SC_PKT_ASF_MEDIA                = 0x010001,// receiving false data packets.
    /*@}*/
} MMSSCPacketType;

88
typedef struct MMSTContext {
89 90 91 92 93 94 95 96 97 98
    MMSContext  mms;
    int outgoing_packet_seq;             ///< Outgoing packet sequence number.
    char path[256];                      ///< Path of the resource being asked for.
    char host[128];                      ///< Host of the resources.
    int incoming_packet_seq;             ///< Incoming packet sequence number.
    int incoming_flags;                  ///< Incoming packet flags.
    int packet_id;                       ///< Identifier for packets in the current stream.
    unsigned int header_packet_id;       ///< default is 2.
} MMSTContext;

99
/** Create MMST command packet header */
100
static void start_command_packet(MMSTContext *mmst, MMSCSPacketType packet_type)
101
{
102
    MMSContext *mms    = &mmst->mms;
103 104 105 106 107 108 109
    mms->write_out_ptr = mms->out_buffer;

    bytestream_put_le32(&mms->write_out_ptr, 1); // start sequence
    bytestream_put_le32(&mms->write_out_ptr, 0xb00bface);
    bytestream_put_le32(&mms->write_out_ptr, 0); // Length starts from after the protocol type bytes
    bytestream_put_le32(&mms->write_out_ptr, MKTAG('M','M','S',' '));
    bytestream_put_le32(&mms->write_out_ptr, 0);
110
    bytestream_put_le32(&mms->write_out_ptr, mmst->outgoing_packet_seq++);
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    bytestream_put_le64(&mms->write_out_ptr, 0); // timestamp
    bytestream_put_le32(&mms->write_out_ptr, 0);
    bytestream_put_le16(&mms->write_out_ptr, packet_type);
    bytestream_put_le16(&mms->write_out_ptr, 3); // direction to server
}

/** Add prefixes to MMST command packet. */
static void insert_command_prefixes(MMSContext *mms,
        uint32_t prefix1, uint32_t prefix2)
{
    bytestream_put_le32(&mms->write_out_ptr, prefix1); // first prefix
    bytestream_put_le32(&mms->write_out_ptr, prefix2); // second prefix
}

/** Send a prepared MMST command packet. */
126
static int send_command_packet(MMSTContext *mmst)
127
{
128
    MMSContext *mms  = &mmst->mms;
129
    int len= mms->write_out_ptr - mms->out_buffer;
130
    int exact_length = FFALIGN(len, 8);
131 132 133 134 135 136 137 138
    int first_length= exact_length - 16;
    int len8= first_length/8;
    int write_result;

    // update packet length fields.
    AV_WL32(mms->out_buffer + 8, first_length);
    AV_WL32(mms->out_buffer + 16, len8);
    AV_WL32(mms->out_buffer + 32, len8-2);
139
    memset(mms->write_out_ptr, 0, exact_length - len);
140 141

    // write it out.
142
    write_result= ffurl_write(mms->mms_hd, mms->out_buffer, exact_length);
143
    if(write_result != exact_length) {
144 145 146
        av_log(NULL, AV_LOG_ERROR,
               "Failed to write data of length %d: %d (%s)\n",
               exact_length, write_result,
147
               write_result < 0 ? strerror(AVUNERROR(write_result)) :
148
                   "The server closed the connection");
149
        return AVERROR(EIO);
150 151 152 153 154
    }

    return 0;
}

155
static int mms_put_utf16(MMSContext *mms, const uint8_t *src)
156
{
157
    AVIOContext bic;
158 159
    int size = mms->write_out_ptr - mms->out_buffer;
    int len;
160
    ffio_init_context(&bic, mms->write_out_ptr,
161 162
            sizeof(mms->out_buffer) - size, 1, NULL, NULL, NULL, NULL);

163
    len = avio_put_str16le(&bic, src);
164 165
    if (len < 0)
        return len;
166
    mms->write_out_ptr += len;
167
    return 0;
168 169
}

170
static int send_time_test_data(MMSTContext *mmst)
171
{
172
    start_command_packet(mmst, CS_PKT_TIMING_DATA_REQUEST);
173
    insert_command_prefixes(&mmst->mms, 0x00f0f0f0, 0x0004000b);
174
    return send_command_packet(mmst);
175 176
}

177
static int send_protocol_select(MMSTContext *mmst)
178
{
179
    int ret;
180
    char data_string[256];
181
    MMSContext *mms = &mmst->mms;
182

183
    start_command_packet(mmst, CS_PKT_PROTOCOL_SELECT);
184 185 186 187 188 189 190 191 192 193 194 195
    insert_command_prefixes(mms, 0, 0xffffffff);
    bytestream_put_le32(&mms->write_out_ptr, 0);          // maxFunnelBytes
    bytestream_put_le32(&mms->write_out_ptr, 0x00989680); // maxbitRate
    bytestream_put_le32(&mms->write_out_ptr, 2);          // funnelMode
    snprintf(data_string, sizeof(data_string), "\\\\%d.%d.%d.%d\\%s\\%d",
            (LOCAL_ADDRESS>>24)&0xff,
            (LOCAL_ADDRESS>>16)&0xff,
            (LOCAL_ADDRESS>>8)&0xff,
            LOCAL_ADDRESS&0xff,
            "TCP",                                        // or UDP
            LOCAL_PORT);

196 197
    if ((ret = mms_put_utf16(mms, data_string)) < 0)
        return ret;
198
    return send_command_packet(mmst);
199 200
}

201
static int send_media_file_request(MMSTContext *mmst)
202
{
203
    int ret;
204 205
    MMSContext *mms = &mmst->mms;
    start_command_packet(mmst, CS_PKT_MEDIA_FILE_REQUEST);
206 207 208
    insert_command_prefixes(mms, 1, 0xffffffff);
    bytestream_put_le32(&mms->write_out_ptr, 0);
    bytestream_put_le32(&mms->write_out_ptr, 0);
209 210
    if ((ret = mms_put_utf16(mms, mmst->path + 1)) < 0) // +1 for skip "/"
        return ret;
211

212
    return send_command_packet(mmst);
213 214
}

215
static void handle_packet_stream_changing_type(MMSTContext *mmst)
216
{
217
    MMSContext *mms = &mmst->mms;
218
    av_log(NULL, AV_LOG_TRACE, "Stream changing!\n");
219 220

    // 40 is the packet header size, 7 is the prefix size.
221
    mmst->header_packet_id= AV_RL32(mms->in_buffer + 40 + 7);
222
    av_log(NULL, AV_LOG_TRACE, "Changed header prefix to 0x%x", mmst->header_packet_id);
223 224
}

225
static int send_keepalive_packet(MMSTContext *mmst)
226 227
{
    // respond to a keepalive with a keepalive...
228 229 230
    start_command_packet(mmst, CS_PKT_KEEPALIVE);
    insert_command_prefixes(&mmst->mms, 1, 0x100FFFF);
    return send_command_packet(mmst);
231 232 233 234 235 236 237 238 239 240 241 242 243 244
}

/** Pad media packets smaller than max_packet_size and/or adjust read position
  * after a seek. */
static void pad_media_packet(MMSContext *mms)
{
    if(mms->remaining_in_len<mms->asf_packet_len) {
        int padding_size = mms->asf_packet_len - mms->remaining_in_len;
        memset(mms->in_buffer + mms->remaining_in_len, 0, padding_size);
        mms->remaining_in_len += padding_size;
    }
}

/** Read incoming MMST media, header or command packet. */
245
static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst)
246 247 248
{
    int read_result;
    MMSSCPacketType packet_type= -1;
249
    MMSContext *mms = &mmst->mms;
250
    for(;;) {
251
        read_result = ffurl_read_complete(mms->mms_hd, mms->in_buffer, 8);
252 253 254 255
        if (read_result != 8) {
            if(read_result < 0) {
                av_log(NULL, AV_LOG_ERROR,
                       "Error reading packet header: %d (%s)\n",
256
                       read_result, strerror(AVUNERROR(read_result)));
257 258 259 260 261 262 263 264 265
                packet_type = SC_PKT_CANCEL;
            } else {
                av_log(NULL, AV_LOG_ERROR,
                       "The server closed the connection\n");
                packet_type = SC_PKT_NO_DATA;
            }
            return packet_type;
        }

266 267
        // handle command packet.
        if(AV_RL32(mms->in_buffer + 4)==0xb00bface) {
268 269
            int length_remaining, hr;

270
            mmst->incoming_flags= mms->in_buffer[3];
271
            read_result= ffurl_read_complete(mms->mms_hd, mms->in_buffer+8, 4);
272 273 274 275
            if(read_result != 4) {
                av_log(NULL, AV_LOG_ERROR,
                       "Reading command packet length failed: %d (%s)\n",
                       read_result,
276
                       read_result < 0 ? strerror(AVUNERROR(read_result)) :
277
                           "The server closed the connection");
278
                return read_result < 0 ? read_result : AVERROR(EIO);
279
            }
280

281
            length_remaining= AV_RL32(mms->in_buffer+8) + 4;
282
            av_log(NULL, AV_LOG_TRACE, "Length remaining is %d\n", length_remaining);
283 284 285 286
            // read the rest of the packet.
            if (length_remaining < 0
                || length_remaining > sizeof(mms->in_buffer) - 12) {
                av_log(NULL, AV_LOG_ERROR,
287
                       "Incoming packet length %d exceeds bufsize %"SIZE_SPECIFIER"\n",
288 289 290
                       length_remaining, sizeof(mms->in_buffer) - 12);
                return AVERROR_INVALIDDATA;
            }
291
            read_result = ffurl_read_complete(mms->mms_hd, mms->in_buffer + 12,
292 293 294 295 296
                                            length_remaining) ;
            if (read_result != length_remaining) {
                av_log(NULL, AV_LOG_ERROR,
                       "Reading pkt data (length=%d) failed: %d (%s)\n",
                       length_remaining, read_result,
297
                       read_result < 0 ? strerror(AVUNERROR(read_result)) :
298
                           "The server closed the connection");
299
                return read_result < 0 ? read_result : AVERROR(EIO);
300 301
            }
            packet_type= AV_RL16(mms->in_buffer+36);
302
            if (read_result >= 44 && (hr = AV_RL32(mms->in_buffer + 40))) {
303
                av_log(NULL, AV_LOG_ERROR,
304
                       "Server sent a message with packet type 0x%x and error status code 0x%08x\n", packet_type, hr);
305
                return AVERROR(EINVAL);
306 307 308 309 310 311 312 313 314 315
            }
        } else {
            int length_remaining;
            int packet_id_type;
            int tmp;

            // note we cache the first 8 bytes,
            // then fill up the buffer with the others
            tmp                       = AV_RL16(mms->in_buffer + 6);
            length_remaining          = (tmp - 8) & 0xffff;
316
            mmst->incoming_packet_seq = AV_RL32(mms->in_buffer);
317
            packet_id_type            = mms->in_buffer[4];
318
            mmst->incoming_flags      = mms->in_buffer[5];
319 320 321 322

            if (length_remaining < 0
                || length_remaining > sizeof(mms->in_buffer) - 8) {
                av_log(NULL, AV_LOG_ERROR,
323
                       "Data length %d is invalid or too large (max=%"SIZE_SPECIFIER")\n",
324 325 326 327 328
                       length_remaining, sizeof(mms->in_buffer));
                return AVERROR_INVALIDDATA;
            }
            mms->remaining_in_len    = length_remaining;
            mms->read_in_ptr         = mms->in_buffer;
329
            read_result= ffurl_read_complete(mms->mms_hd, mms->in_buffer, length_remaining);
330 331 332 333
            if(read_result != length_remaining) {
                av_log(NULL, AV_LOG_ERROR,
                       "Failed to read packet data of size %d: %d (%s)\n",
                       length_remaining, read_result,
334
                       read_result < 0 ? strerror(AVUNERROR(read_result)) :
335
                           "The server closed the connection");
336
                return read_result < 0 ? read_result : AVERROR(EIO);
337 338
            }

339
            // if we successfully read everything.
340
            if(packet_id_type == mmst->header_packet_id) {
341
                int err;
342 343 344
                packet_type = SC_PKT_ASF_HEADER;
                // Store the asf header
                if(!mms->header_parsed) {
345 346
                    if ((err = av_reallocp(&mms->asf_header,
                                           mms->asf_header_size +
347 348
                                           mms->remaining_in_len)) < 0) {
                        mms->asf_header_size = 0;
349
                        return err;
350
                    }
351 352 353 354 355
                    memcpy(mms->asf_header + mms->asf_header_size,
                           mms->read_in_ptr, mms->remaining_in_len);
                    mms->asf_header_size += mms->remaining_in_len;
                }
                // 0x04 means asf header is sent in multiple packets.
356
                if (mmst->incoming_flags == 0x04)
357
                    continue;
358
            } else if(packet_id_type == mmst->packet_id) {
359 360
                packet_type = SC_PKT_ASF_MEDIA;
            } else {
361
                av_log(NULL, AV_LOG_TRACE, "packet id type %d is old.", packet_id_type);
362 363
                continue;
            }
364 365 366 367
        }

        // preprocess some packet type
        if(packet_type == SC_PKT_KEEPALIVE) {
368
            send_keepalive_packet(mmst);
369 370
            continue;
        } else if(packet_type == SC_PKT_STREAM_CHANGING) {
371
            handle_packet_stream_changing_type(mmst);
372 373 374 375
        } else if(packet_type == SC_PKT_ASF_MEDIA) {
            pad_media_packet(mms);
        }
        return packet_type;
376 377 378
    }
}

379 380
static int mms_safe_send_recv(MMSTContext *mmst,
                              int (*send_fun)(MMSTContext *mmst),
381 382 383 384
                              const MMSSCPacketType expect_type)
{
    MMSSCPacketType type;
    if(send_fun) {
385
        int ret = send_fun(mmst);
386
        if (ret < 0) {
387
            av_log(NULL, AV_LOG_TRACE, "Send Packet error before expecting recv packet %d\n", expect_type);
388 389 390 391
            return ret;
        }
    }

392
    if ((type = get_tcp_server_response(mmst)) != expect_type) {
393 394 395 396
        av_log(NULL, AV_LOG_ERROR,
               "Corrupt stream (unexpected packet type 0x%x, expected 0x%x)\n",
               type, expect_type);
        return AVERROR_INVALIDDATA;
397 398 399 400 401
    } else {
        return 0;
    }
}

402
static int send_media_header_request(MMSTContext *mmst)
403
{
404 405
    MMSContext *mms = &mmst->mms;
    start_command_packet(mmst, CS_PKT_MEDIA_HEADER_REQUEST);
406 407 408 409 410 411 412 413 414 415 416 417 418 419
    insert_command_prefixes(mms, 1, 0);
    bytestream_put_le32(&mms->write_out_ptr, 0);
    bytestream_put_le32(&mms->write_out_ptr, 0x00800000);
    bytestream_put_le32(&mms->write_out_ptr, 0xffffffff);
    bytestream_put_le32(&mms->write_out_ptr, 0);
    bytestream_put_le32(&mms->write_out_ptr, 0);
    bytestream_put_le32(&mms->write_out_ptr, 0);

    // the media preroll value in milliseconds?
    bytestream_put_le32(&mms->write_out_ptr, 0);
    bytestream_put_le32(&mms->write_out_ptr, 0x40AC2000);
    bytestream_put_le32(&mms->write_out_ptr, 2);
    bytestream_put_le32(&mms->write_out_ptr, 0);

420
    return send_command_packet(mmst);
421 422 423
}

/** Send the initial handshake. */
424
static int send_startup_packet(MMSTContext *mmst)
425 426
{
    char data_string[256];
427
    int ret;
428
    MMSContext *mms = &mmst->mms;
429
    // SubscriberName is defined in MS specification linked below.
430
    // The GUID value can be any valid value.
431 432 433 434
    // http://download.microsoft.com/
    // download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/%5BMS-WMSP%5D.pdf
    snprintf(data_string, sizeof(data_string),
            "NSPlayer/7.0.0.1956; {%s}; Host: %s",
435
            "7E667F5D-A661-495E-A512-F55686DDA178", mmst->host);
436

437
    start_command_packet(mmst, CS_PKT_INITIAL);
438 439
    insert_command_prefixes(mms, 0, 0x0004000b);
    bytestream_put_le32(&mms->write_out_ptr, 0x0003001c);
440 441
    if ((ret = mms_put_utf16(mms, data_string)) < 0)
        return ret;
442
    return send_command_packet(mmst);
443 444 445
}

/** Send MMST stream selection command based on the AVStream->discard values. */
446
static int send_stream_selection_request(MMSTContext *mmst)
447 448
{
    int i;
449
    MMSContext *mms = &mmst->mms;
450
    //  send the streams we want back...
451
    start_command_packet(mmst, CS_PKT_STREAM_ID_REQUEST);
452 453 454 455 456 457
    bytestream_put_le32(&mms->write_out_ptr, mms->stream_num);         // stream nums
    for(i= 0; i<mms->stream_num; i++) {
        bytestream_put_le16(&mms->write_out_ptr, 0xffff);              // flags
        bytestream_put_le16(&mms->write_out_ptr, mms->streams[i].id);  // stream id
        bytestream_put_le16(&mms->write_out_ptr, 0);                   // selection
    }
458
    return send_command_packet(mmst);
459 460
}

461
static int send_close_packet(MMSTContext *mmst)
462
{
463 464
    start_command_packet(mmst, CS_PKT_STREAM_CLOSE);
    insert_command_prefixes(&mmst->mms, 1, 1);
465

466
    return send_command_packet(mmst);
467 468 469 470 471
}

/** Close the MMSH/MMST connection */
static int mms_close(URLContext *h)
{
472 473
    MMSTContext *mmst = (MMSTContext *)h->priv_data;
    MMSContext *mms   = &mmst->mms;
474
    if(mms->mms_hd) {
475
        send_close_packet(mmst);
476
        ffurl_close(mms->mms_hd);
477 478 479
    }

    /* free all separately allocated pointers in mms */
480 481
    av_freep(&mms->streams);
    av_freep(&mms->asf_header);
482 483 484 485

    return 0;
}

486
static int send_media_packet_request(MMSTContext *mmst)
487
{
488 489
    MMSContext *mms = &mmst->mms;
    start_command_packet(mmst, CS_PKT_START_FROM_PKT_ID);
490 491 492 493 494 495 496 497 498
    insert_command_prefixes(mms, 1, 0x0001FFFF);
    bytestream_put_le64(&mms->write_out_ptr, 0);          // seek timestamp
    bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); // unknown
    bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); // packet offset
    bytestream_put_byte(&mms->write_out_ptr, 0xff);       // max stream time limit
    bytestream_put_byte(&mms->write_out_ptr, 0xff);       // max stream time limit
    bytestream_put_byte(&mms->write_out_ptr, 0xff);       // max stream time limit
    bytestream_put_byte(&mms->write_out_ptr, 0x00);       // stream time limit flag

499 500 501
    mmst->packet_id++;                                     // new packet_id
    bytestream_put_le32(&mms->write_out_ptr, mmst->packet_id);
    return send_command_packet(mmst);
502 503 504 505 506 507 508 509 510
}


static void clear_stream_buffers(MMSContext *mms)
{
    mms->remaining_in_len = 0;
    mms->read_in_ptr      = mms->in_buffer;
}

511 512
static int mms_open(URLContext *h, const char *uri, int flags)
{
513
    MMSTContext *mmst = h->priv_data;
514 515 516 517 518
    MMSContext *mms;
    int port, err;
    char tcpname[256];

    h->is_streamed = 1;
519
    mms = &mmst->mms;
520 521

    // only for MMS over TCP, so set proto = NULL
522
    av_url_split(NULL, 0, NULL, 0,
523 524
            mmst->host, sizeof(mmst->host), &port, mmst->path,
            sizeof(mmst->path), uri);
525 526

    if(port<0)
527
        port = 1755; // default MMS protocol port
528 529

    // establish tcp connection.
530
    ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, mmst->host, port, NULL);
531 532
    err = ffurl_open_whitelist(&mms->mms_hd, tcpname, AVIO_FLAG_READ_WRITE,
                               &h->interrupt_callback, NULL,
533
                               h->protocol_whitelist, h->protocol_blacklist, h);
534 535 536
    if (err)
        goto fail;

537 538 539
    mmst->packet_id        = 3;          // default, initial value.
    mmst->header_packet_id = 2;          // default, initial value.
    err = mms_safe_send_recv(mmst, send_startup_packet, SC_PKT_CLIENT_ACCEPTED);
540 541
    if (err)
        goto fail;
542
    err = mms_safe_send_recv(mmst, send_time_test_data, SC_PKT_TIMING_TEST_REPLY);
543 544
    if (err)
        goto fail;
545
    err = mms_safe_send_recv(mmst, send_protocol_select, SC_PKT_PROTOCOL_ACCEPTED);
546 547
    if (err)
        goto fail;
548
    err = mms_safe_send_recv(mmst, send_media_file_request, SC_PKT_MEDIA_FILE_DETAILS);
549 550
    if (err)
        goto fail;
551
    err = mms_safe_send_recv(mmst, send_media_header_request, SC_PKT_HEADER_REQUEST_ACCEPTED);
552 553
    if (err)
        goto fail;
554
    err = mms_safe_send_recv(mmst, NULL, SC_PKT_ASF_HEADER);
555 556
    if (err)
        goto fail;
557 558 559
    if((mmst->incoming_flags != 0X08) && (mmst->incoming_flags != 0X0C)) {
        av_log(NULL, AV_LOG_ERROR,
               "The server does not support MMST (try MMSH or RTSP)\n");
560
        err = AVERROR(EINVAL);
561
        goto fail;
562
    }
563
    err = ff_mms_asf_header_parser(mms);
564
    if (err) {
565
        av_log(NULL, AV_LOG_TRACE, "asf header parsed failed!\n");
566 567 568 569 570 571 572
        goto fail;
    }
    mms->header_parsed = 1;

    if (!mms->asf_packet_len || !mms->stream_num)
        goto fail;

573
    clear_stream_buffers(mms);
574
    err = mms_safe_send_recv(mmst, send_stream_selection_request, SC_PKT_STREAM_ID_ACCEPTED);
575 576 577
    if (err)
        goto fail;
    // send media packet request
578
    err = mms_safe_send_recv(mmst, send_media_packet_request, SC_PKT_MEDIA_PKT_FOLLOWS);
579 580 581
    if (err) {
        goto fail;
    }
582
    av_log(NULL, AV_LOG_TRACE, "Leaving open (success)\n");
583 584 585
    return 0;
fail:
    mms_close(h);
586
    av_log(NULL, AV_LOG_TRACE, "Leaving open (failure: %d)\n", err);
587 588 589 590 591 592 593
    return err;
}

/** Read ASF data through the protocol. */
static int mms_read(URLContext *h, uint8_t *buf, int size)
{
    /* TODO: see tcp.c:tcp_read() about a possible timeout scheme */
594 595
    MMSTContext *mmst = h->priv_data;
    MMSContext *mms   = &mmst->mms;
596
    int result = 0;
597

598 599 600
    do {
        if(mms->asf_header_read_size < mms->asf_header_size) {
            /* Read from ASF header buffer */
601
            result = ff_mms_read_header(mms, buf, size);
602 603 604
        } else if(mms->remaining_in_len) {
            /* Read remaining packet data to buffer.
             * the result can not be zero because remaining_in_len is positive.*/
605
            result = ff_mms_read_data(mms, buf, size);
606 607
        } else {
            /* Read from network */
608
            int err = mms_safe_send_recv(mmst, NULL, SC_PKT_ASF_MEDIA);
609 610 611 612 613
            if (err == 0) {
                if(mms->remaining_in_len>mms->asf_packet_len) {
                    av_log(NULL, AV_LOG_ERROR,
                           "Incoming pktlen %d is larger than ASF pktsize %d\n",
                           mms->remaining_in_len, mms->asf_packet_len);
614
                    result= AVERROR(EIO);
615 616
                } else {
                    // copy the data to the packet buffer.
617
                    result = ff_mms_read_data(mms, buf, size);
618
                    if (result == 0) {
619
                        av_log(NULL, AV_LOG_TRACE, "Read ASF media packet size is zero!\n");
620 621 622 623
                        break;
                    }
                }
            } else {
624
                av_log(NULL, AV_LOG_TRACE, "read packet error!\n");
625 626 627 628 629
                break;
            }
        }
    } while(!result); // only return one packet.
    return result;
630 631
}

632
const URLProtocol ff_mmst_protocol = {
633 634 635 636
    .name           = "mmst",
    .url_open       = mms_open,
    .url_read       = mms_read,
    .url_close      = mms_close,
637
    .priv_data_size = sizeof(MMSTContext),
638
    .flags          = URL_PROTOCOL_FLAG_NETWORK,
639
};