[FFmpeg-soc] [soc]: r5635 - mms/mmst.c

spyfeng subversion at mplayerhq.hu
Thu Mar 4 14:04:02 CET 2010


Author: spyfeng
Date: Thu Mar  4 14:04:01 2010
New Revision: 5635

Log:
add mmst.c as primary version to be modified.

Added:
   mms/mmst.c

Added: mms/mmst.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ mms/mmst.c	Thu Mar  4 14:04:01 2010	(r5635)
@@ -0,0 +1,1305 @@
+/*
+ * MMS protocol over TCP
+ * Copyright (c) 2006,2007 Ryan Martell
+ * Copyright (c) 2007 Björn Axelsson
+ * 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
+ */
+#include "avformat.h"
+#include "libavutil/intreadwrite.h"
+#include "network.h"
+#include "asf.h"
+
+#define MMS_DEBUG_LEVEL 2
+#define MMS_MAXIMUM_PACKET_LENGTH 512
+#define MMS_KILO                  1024
+#define MMS_URL_SIZE          4096
+#define DEFAULT_MMS_PORT      1755
+
+/** State machine states. */
+typedef enum {
+    AWAITING_SC_PACKET_CLIENT_ACCEPTED= 0,
+    AWAITING_SC_PACKET_TIMING_TEST_REPLY_TYPE,
+    AWAITING_CS_PACKET_PROTOCOL_ACCEPTANCE,
+    AWAITING_PASSWORD_QUERY_OR_MEDIA_FILE,
+    AWAITING_PACKET_HEADER_REQUEST_ACCEPTED_TYPE,
+    AWAITING_STREAM_ID_ACCEPTANCE,
+    AWAITING_STREAM_START_PACKET,
+    AWAITING_ASF_HEADER,
+    ASF_HEADER_DONE,
+    AWAITING_PAUSE_ACKNOWLEDGE,
+    AWAITING_HTTP_PAUSE_CONTROL_ACKNOWLEDGE,
+    STREAMING,
+    STREAM_DONE,
+    STATE_ERROR,
+    STREAM_PAUSED,
+    USER_CANCELLED
+} MMSState;
+
+/** Client to server packet types. */
+typedef enum {
+    CS_PACKET_INITIAL_TYPE= 0x01,
+    CS_PACKET_PROTOCOL_SELECT_TYPE= 0x02,
+    CS_PACKET_MEDIA_FILE_REQUEST_TYPE= 0x05,
+    CS_PACKET_START_FROM_PACKET_ID_TYPE= 0x07,
+    CS_PACKET_STREAM_PAUSE_TYPE= 0x09, // tcp left open, but data stopped.
+    CS_PACKET_STREAM_CLOSE_TYPE= 0x0d,
+    CS_PACKET_MEDIA_HEADER_REQUEST_TYPE= 0x15,
+    CS_PACKET_TIMING_DATA_REQUEST_TYPE= 0x18,
+    CS_PACKET_USER_PASSWORD_TYPE= 0x1a,
+    CS_PACKET_KEEPALIVE_TYPE= 0x1b,
+    CS_PACKET_STREAM_ID_REQUEST_TYPE= 0x33,
+} MMSCSPacketType;
+
+/** Server to client packet types. */
+typedef enum {
+    /** Control packets. */
+    /*@{*/
+    SC_PACKET_CLIENT_ACCEPTED= 0x01,
+    SC_PACKET_PROTOCOL_ACCEPTED_TYPE= 0x02,
+    SC_PACKET_PROTOCOL_FAILED_TYPE= 0x03,
+    SC_PACKET_MEDIA_PACKET_FOLLOWS_TYPE= 0x05,
+    SC_PACKET_MEDIA_FILE_DETAILS_TYPE= 0x06,
+    SC_PACKET_HEADER_REQUEST_ACCEPTED_TYPE= 0x11,
+    SC_PACKET_TIMING_TEST_REPLY_TYPE= 0x15,
+    SC_PACKET_PASSWORD_REQUIRED_TYPE= 0x1a,
+    SC_PACKET_KEEPALIVE_TYPE= 0x1b,
+    SC_PACKET_STREAM_STOPPED_TYPE= 0x1e, // mmst, mmsh
+    SC_PACKET_STREAM_CHANGING_TYPE= 0x20,
+    SC_PACKET_STREAM_ID_ACCEPTED_TYPE= 0x21,
+    /*@}*/
+
+    /** Pseudo packets. */
+    /*@{*/
+    SC_PACKET_TYPE_CANCEL = -1, // mmst
+    SC_PACKET_TYPE_NO_DATA = -2, // mmst
+    SC_PACKET_HTTP_CONTROL_ACKNOWLEDGE = -3, // mmsh
+    /*@}*/
+
+    /** Data packets. */
+    /*@{*/
+    SC_PACKET_ASF_HEADER_TYPE= 0x81, // mmst, mmsh
+    SC_PACKET_ASF_MEDIA_TYPE= 0x82, // mmst, mmsh
+    /*@}*/
+} MMSSCPacketType;
+
+#if (MMS_DEBUG_LEVEL>0)
+static const char *state_names[]= {
+    "AWAITING_SC_PACKET_CLIENT_ACCEPTED",
+    "AWAITING_SC_PACKET_TIMING_TEST_REPLY_TYPE",
+    "AWAITING_CS_PACKET_PROTOCOL_ACCEPTANCE",
+    "AWAITING_PASSWORD_QUERY_OR_MEDIA_FILE",
+    "AWAITING_PACKET_HEADER_REQUEST_ACCEPTED_TYPE",
+    "AWAITING_STREAM_ID_ACCEPTANCE",
+    "AWAITING_STREAM_START_PACKET",
+    "AWAITING_ASF_HEADER",
+    "ASF_HEADER_DONE",
+    "AWAITING_PAUSE_ACKNOWLEDGE",
+    "AWAITING_HTTP_PAUSE_CONTROL_ACKNOWLEDGE",
+    "STREAMING",
+    "STREAM_DONE",
+    "STATE_ERROR",
+    "STREAM_PAUSED",
+    "USER_CANCELLED"
+};
+#endif
+
+typedef struct {
+    char local_guid[37]; ///< My randomly generated GUID.
+    uint32_t local_ip_address; ///< Not ipv6 compatible, but neither is the protocol (sent, but not correct).
+    int local_port; ///< My local port (sent but not correct).
+    int sequence_number; ///< Outgoing packet sequence number.
+    MMSState state; ///< Packet state machine current state.
+    char path[256]; ///< Path of the resource being asked for.
+    char host[128]; ///< Host of the resources.
+    int port; ///< Port of the resource.
+
+    URLContext *mms_hd; ///< TCP connection handle
+    ByteIOContext incoming_io_buffer; ///< Incoming data on the socket
+
+    /** Buffer for outgoing packets. */
+    /*@{*/
+    ByteIOContext outgoing_packet_data; ///< Outgoing packet stream
+    uint8_t outgoing_packet_buffer[MMS_MAXIMUM_PACKET_LENGTH]; ///< Outgoing packet data
+    /*@}*/
+
+    /** Buffer for incoming control packets. */
+    /*@{*/
+    uint8_t incoming_buffer[8*MMS_KILO]; ///< Incoming buffer location.
+    int incoming_buffer_length; ///< Incoming buffer length.
+    /*@}*/
+
+    /** Buffer for incoming media/header packets. */
+    /*@{*/
+    uint8_t media_packet_incoming_buffer[8*MMS_KILO]; ///< Either a header or media packet.
+    uint8_t *media_packet_read_ptr; ///< Pointer for partial reads.
+    int media_packet_buffer_length; ///< Buffer length.
+    int media_packet_seek_offset;   ///< Additional offset into packet from seek.
+    /*@}*/
+
+    int incoming_packet_seq; ///< Incoming packet sequence number.
+    int incoming_flags; ///< Incoming packet flags.
+
+    int packet_id; ///< Identifier for packets in the current stream, incremented on stops, etc.
+    unsigned int header_packet_id; ///< The header packet id (default is 2, can be reset)
+
+    int seekable; ///< This tells you if the stream is seekable.
+
+    int http_client_id; ///< HTTP's client id.
+    int http_play_rate; ///< Rate of playback (1 for normal, 5 or -5 for ffwd/rewind)
+
+    /** Internal handling of the ASF header */
+    /*@{*/
+    uint8_t *asf_header; ///< Stored ASF header, for seeking into it and internal parsing.
+    int asf_header_size; ///< Size of stored ASF header.
+    int asf_header_read_pos; ///< Current read position in header. See read_packet().
+    int header_parsed; ///< The header has been received and parsed.
+    AVFormatContext private_av_format_ctx; ///< Private parsed header data (generic).
+    ASFContext      asf_context;           ///< Private parsed header data (ASF-specific).
+    AVFormatContext *av_format_ctx; ///< Optional external format context (for stream selection).
+    /*@}*/
+
+    int pause_resume_seq; ///< Last packet returned by mms_read. Useful for resuming pause.
+    // new added on 2010.2.21
+    char location[MMS_URL_SIZE];
+} MMSContext;
+
+/** Perform state transition. */
+static void ff_mms_set_state(MMSContext *mms, int new_state)
+{
+    /* Can't exit error state */
+    if(mms->state==STATE_ERROR) {
+#if (MMS_DEBUG_LEVEL>0)
+        fprintf(stderr, "Trying to set state to %s from %s!\n", state_names[new_state], state_names[mms->state]);
+#endif
+        return;
+    }
+
+#if (MMS_DEBUG_LEVEL>0)
+    fprintf(stderr, "Set state to %s (%d) from %s (%d)!\n", state_names[new_state], new_state, state_names[mms->state], mms->state);
+#endif
+    if(mms->state==new_state && new_state==USER_CANCELLED) {
+        ff_mms_set_state(mms, STATE_ERROR);
+        return;
+    }
+
+    mms->state= new_state;
+}
+
+/** Close the remote connection. */
+static void close_connection(MMSContext *mms)
+{
+    av_freep(&mms->incoming_io_buffer.buffer);
+    url_close(mms->mms_hd);
+}
+
+/** Open or reopen (http) the remote connection. */
+static int ff_mms_open_connection(MMSContext *mms)
+{
+    char tcpname[256];
+    int flags, err;
+
+    close_connection(mms);
+
+    snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", mms->host, mms->port);
+    err = url_open(&mms->mms_hd, tcpname, URL_RDWR);
+    if(err == 0) {
+        /* open the incoming and outgoing connections; you can't open a single one with read/write, because it only has one buffer, not two. */
+        /* you can't use url_fdopen if the flags of the mms_hd have a WR component, because it will screw up (returning data that is uninitialized) */
+        flags = mms->mms_hd->flags;
+        mms->mms_hd->flags = URL_RDONLY;
+        err = url_fdopen(&mms->incoming_io_buffer, mms->mms_hd);
+        mms->mms_hd->flags = flags;
+        if(err) {
+            // should have a url_fdclose()
+            url_close(mms->mms_hd);
+        }
+    }
+
+    return err;
+}
+
+/** Create MMST command packet header */
+static void start_command_packet(MMSContext *mms, MMSCSPacketType packet_type)
+{
+    ByteIOContext *context= &mms->outgoing_packet_data;
+
+    url_fseek(context, 0, SEEK_SET); // start at the beginning...
+    put_le32(context, 1); // start sequence?
+    put_le32(context, 0xb00bface);
+    put_le32(context, 0); // Length of command until the end of all data  Value is in bytes and starts from after the protocol type bytes
+    put_byte(context, 'M'); put_byte(context, 'M'); put_byte(context, 'S'); put_byte(context, ' ');
+    put_le32(context, 0);
+    put_le32(context, mms->sequence_number++);
+    put_le64(context, 0); // timestmamp
+    put_le32(context, 0);
+    put_le16(context, packet_type);
+    put_le16(context, 3); // direction- to server
+}
+
+/** Add prefixes to MMST command packet. */
+static void insert_command_prefixes(MMSContext *mms,
+        uint32_t prefix1, uint32_t prefix2)
+{
+    ByteIOContext *context= &mms->outgoing_packet_data;
+
+    put_le32(context, prefix1); // first prefix
+    put_le32(context, prefix2); // second prefix
+}
+
+/** Write a utf-16 string in little-endian order.
+ * @note This is NOT the same as static int ascii_to_wc (ByteIOContext *pb, uint8_t *b), because ascii_to_wc is big endian, and this is little endian.
+ */
+static void put_le_utf16(ByteIOContext *pb, char *utf8)
+{
+    int val;
+
+    while(*utf8) {
+        GET_UTF8(val, *utf8++, break;); // goto's suck, but i want to make sure it's terminated.
+        put_le16(pb, val);
+    }
+
+    put_le16(pb, 0x00);
+
+    return;
+}
+
+/** Send a prepared MMST command packet. */
+static int send_command_packet(MMSContext *mms)
+{
+    ByteIOContext *context= &mms->outgoing_packet_data;
+    int exact_length= url_ftell(context);
+    int first_length= exact_length - 16;
+//    int len8= (first_length+7)/8;
+    int len8= first_length/8;
+    int write_result;
+
+    // first adjust the header fields (the lengths)...
+    url_fseek(context, 8, SEEK_SET);
+    put_le32(context, first_length);
+    url_fseek(context, 16, SEEK_SET);
+    put_le32(context, len8);
+    url_fseek(context, 32, SEEK_SET);
+    put_le32(context, len8-2);
+
+    // seek back to the end (may not be necessary...)
+    url_fseek(context, exact_length, SEEK_SET);
+
+    // print it out...
+    //    print_command(context->buffer, exact_length);
+
+    // write it out...
+    write_result= url_write(mms->mms_hd, context->buffer, exact_length);
+    if(write_result != exact_length) {
+#if (MMS_DEBUG_LEVEL>0)
+        fprintf(stderr, "url_write returned: %d != %d\n", write_result, exact_length);
+#endif
+
+        ff_mms_set_state(mms, STATE_ERROR);
+        return AVERROR_IO;
+    }
+
+    return 0;
+}
+
+
+/** Log unexpected incoming packet */
+void log_packet_in_wrong_state(MMSContext *mms, MMSSCPacketType packet_type)
+{
+#if (MMS_DEBUG_LEVEL>0)
+    if(packet_type>=0) {
+        fprintf(stderr, "Got a packet 0x%02x in the wrong state: %s (%d)!\n", packet_type, state_names[mms->state], mms->state);
+    } else {
+        fprintf(stderr, "Got a pseudo-packet %d in the wrong state: %s (%d)!\n", packet_type, state_names[mms->state], mms->state);
+    }
+#endif
+}
+
+static int send_protocol_select(MMSContext *mms)
+{
+    char data_string[256];
+    int err= 0;
+
+    // send the timing request packet...
+    start_command_packet(mms, CS_PACKET_PROTOCOL_SELECT_TYPE);
+    insert_command_prefixes(mms, 0, 0);
+    put_le32(&mms->outgoing_packet_data, 0);  // timestamp?
+    put_le32(&mms->outgoing_packet_data, 0);  // timestamp?
+    put_le32(&mms->outgoing_packet_data, 2);
+    snprintf(data_string, sizeof(data_string), "\\\\%d.%d.%d.%d\\%s\\%d",
+            (mms->local_ip_address>>24)&0xff,
+            (mms->local_ip_address>>16)&0xff,
+            (mms->local_ip_address>>8)&0xff,
+            mms->local_ip_address&0xff,
+            "TCP", // or UDP
+            mms->local_port);
+    put_le_utf16(&mms->outgoing_packet_data, data_string);
+    put_le16(&mms->outgoing_packet_data, 0x30);
+
+    err = send_command_packet(mms);
+    ff_mms_set_state(mms, AWAITING_CS_PACKET_PROTOCOL_ACCEPTANCE);
+    return err;
+}
+
+static int send_media_file_request(MMSContext *mms)
+{
+    int err= 0;
+    start_command_packet(mms, CS_PACKET_MEDIA_FILE_REQUEST_TYPE);
+    insert_command_prefixes(mms, 1, 0xffffffff);
+    put_le32(&mms->outgoing_packet_data, 0);
+    put_le32(&mms->outgoing_packet_data, 0);
+    put_le_utf16(&mms->outgoing_packet_data, mms->path+1); // +1 because we skip the leading /
+    put_le32(&mms->outgoing_packet_data, 0); /* More zeroes */
+
+    err = send_command_packet(mms);
+    ff_mms_set_state(mms, AWAITING_PASSWORD_QUERY_OR_MEDIA_FILE);
+    return err;
+}
+
+/** Read incoming MMST media, header or command packet. */
+static MMSSCPacketType get_tcp_server_response(MMSContext *mms)
+{
+    // read the 8 byte header...
+    int read_result;
+    MMSSCPacketType packet_type= -1;
+    int done;
+
+    // use url_fdopen & url_fclose...
+    do {
+        done= 1; // assume we're going to get a valid packet.
+        if((read_result= get_buffer(&mms->incoming_io_buffer, mms->incoming_buffer, 8))==8) {
+            // check if we are a command packet...
+            if(AV_RL32(mms->incoming_buffer + 4)==0xb00bface) {
+                mms->incoming_flags= mms->incoming_buffer[3];
+                if((read_result= get_buffer(&mms->incoming_io_buffer, mms->incoming_buffer+8, 4)) == 4) {
+                    int length_remaining= AV_RL32(mms->incoming_buffer+8) + 4;
+
+#if (MMS_DEBUG_LEVEL>0)
+                    fprintf(stderr, "Length remaining is %d\n", length_remaining);
+#endif
+                    // FIXME? ** VERIFY LENGTH REMAINING HAS SPACE
+                    // read the rest of the packet....
+                    read_result = get_buffer(&mms->incoming_io_buffer, mms->incoming_buffer + 12, length_remaining) ;
+                    if (read_result == length_remaining) {
+                        // we have it all; get the stuff out of it.
+                        mms->incoming_buffer_length= length_remaining+12;
+
+                        // get the packet type...
+                        packet_type= AV_RL16(mms->incoming_buffer+36);
+
+                    } else {
+#if (MMS_DEBUG_LEVEL>0)
+                        // read error...
+                        fprintf(stderr, "3 read returned %d!\n", read_result);
+#endif
+                    }
+                } else {
+#if (MMS_DEBUG_LEVEL>0)
+                    // read error...
+                    fprintf(stderr, "2 read returned %d!\n", read_result);
+#endif
+                }
+            } else {
+                int length_remaining= (AV_RL16(mms->incoming_buffer + 6) - 8) & 0xffff;
+                uint8_t *dst= mms->media_packet_incoming_buffer;
+                int packet_id_type;
+
+                assert(mms->media_packet_buffer_length==0); // assert all has been consumed.
+
+                //** VERIFY LENGTH REMAINING HAS SPACE
+                // note we cache the first 8 bytes, then fill up the buffer with the others
+                mms->incoming_packet_seq        = AV_RL32(mms->incoming_buffer);
+                packet_id_type                  = mms->incoming_buffer[4]; // NOTE: THIS IS THE ONE I CAN CHANGE
+                mms->incoming_flags             = mms->incoming_buffer[5];
+                mms->media_packet_buffer_length = length_remaining;
+                mms->media_packet_read_ptr      = mms->media_packet_incoming_buffer;
+
+                if(mms->media_packet_buffer_length>=sizeof(mms->media_packet_incoming_buffer)) {
+                    fprintf(stderr, "Incoming Buffer Length exceeds buffer: %d>%d\n", mms->media_packet_buffer_length, (int) sizeof(mms->media_packet_incoming_buffer));
+                }
+                assert(mms->media_packet_buffer_length<sizeof(mms->media_packet_incoming_buffer));
+                read_result= get_buffer(&mms->incoming_io_buffer, dst, length_remaining);
+                if(read_result != length_remaining) {
+#if (MMS_DEBUG_LEVEL>0)
+                    fprintf(stderr, "read_bytes result: %d asking for %d\n", read_result, length_remaining);
+#endif
+                    break;
+                } else {
+                    // if we successfully read everything....
+                    if(packet_id_type == mms->header_packet_id) {
+                        // asf header
+                        // fprintf(stderr, "asf header: %d\n", mms->incoming_buffer_length);
+                        packet_type = SC_PACKET_ASF_HEADER_TYPE;
+
+                        // Store the asf header
+                        if(!mms->header_parsed) {
+                            mms->asf_header = av_realloc(mms->asf_header, mms->asf_header_size + mms->media_packet_buffer_length);
+                            memcpy(mms->asf_header + mms->asf_header_size, mms->media_packet_read_ptr, mms->media_packet_buffer_length);
+                            mms->asf_header_size += mms->media_packet_buffer_length;
+                        }
+                    } else if(packet_id_type == mms->packet_id) {
+                        //                    fprintf(stderr, "asf packet: %d\n", mms->incoming_buffer_length);
+                        packet_type = SC_PACKET_ASF_MEDIA_TYPE;
+                    } else {
+#if (MMS_DEBUG_LEVEL>0)
+                        fprintf(stderr, "packet id type %d which must be old, getting another one.", packet_id_type);
+#endif
+                        done= 0;
+                    }
+                }
+            }
+        } else {
+            // read error...
+            if(read_result<0) {
+#if (MMS_DEBUG_LEVEL>0)
+                fprintf(stderr, "Read error (or cancelled) returned %d!\n", read_result);
+#endif
+                packet_type = SC_PACKET_TYPE_CANCEL;
+            } else {// 0 is okay, no data received.
+#if (MMS_DEBUG_LEVEL>0)
+                fprintf(stderr, "Read result of zero?!\n");
+#endif
+                packet_type = SC_PACKET_TYPE_NO_DATA;
+            }
+            done = 1;
+        }
+    } while(!done);
+
+    return packet_type;
+}
+
+static void handle_packet_media_file_details(MMSContext *mms)
+{
+    ByteIOContext pkt;
+    uint16_t broadcast_flags;
+    int64_t total_file_length_in_seconds;
+    uint32_t total_length_in_seconds;
+    uint32_t packet_length;
+    uint32_t total_packet_count;
+    uint32_t highest_bit_rate;
+    uint32_t header_size;
+    uint32_t flags;
+    double duration;
+
+    // read these from the incoming buffer.. (48 is the packet header size)
+    init_put_byte(&pkt, mms->incoming_buffer+48, mms->incoming_buffer_length-48, 0, NULL, NULL, NULL, NULL);
+    flags= get_le32(&pkt); // flags?
+    if(flags==0xffffffff) {
+        // this is a permission denied event.
+#if (MMS_DEBUG_LEVEL>0)
+        fprintf(stderr, "Permission denied!\n");
+#endif
+        ff_mms_set_state(mms, STATE_ERROR);
+    } else {
+        get_le32(&pkt);
+        get_le32(&pkt);
+        get_le16(&pkt);
+        broadcast_flags= get_le16(&pkt);
+
+        total_file_length_in_seconds= get_le64(&pkt);
+        duration= av_int2dbl(total_file_length_in_seconds);
+        total_length_in_seconds= get_le32(&pkt);
+        get_le32(&pkt);
+        get_le32(&pkt);
+        get_le32(&pkt);
+        get_le32(&pkt);
+        packet_length= get_le32(&pkt);
+        total_packet_count= get_le32(&pkt);
+        get_le32(&pkt);
+        highest_bit_rate= get_le32(&pkt);
+        header_size= get_le32(&pkt);
+#if (MMS_DEBUG_LEVEL>0)
+        fprintf(stderr, "Broadcast flags: 0x%x\n", broadcast_flags); // 8000= allow index, 01= prerecorded, 02= live 42= presentation with script commands
+        fprintf(stderr, "File Time Point?: %lld double size: %d double value: %lf\n", total_file_length_in_seconds, (int) sizeof(double), duration);
+        fprintf(stderr, "Total in Seconds: %d\n", total_length_in_seconds);
+        fprintf(stderr, "Packet length: %d\n", packet_length);
+        fprintf(stderr, "Total Packet Count: %d\n", total_packet_count);
+        fprintf(stderr, "Highest Bit Rate: %d\n", highest_bit_rate);
+        fprintf(stderr, "Header Size: %d\n", header_size);
+        fprintf(stderr, "---- Done ----\n");
+#endif
+
+        /* Disable seeking in live broadcasts for now */
+        if(! (broadcast_flags & 0x0200))
+            mms->seekable = 1;
+    }
+}
+
+static int send_media_header_request(MMSContext *mms)
+{
+    int err= 0;
+    start_command_packet(mms, CS_PACKET_MEDIA_HEADER_REQUEST_TYPE);
+    insert_command_prefixes(mms, 1, 0);
+    put_le32(&mms->outgoing_packet_data, 0);
+    put_le32(&mms->outgoing_packet_data, 0x00800000);
+    put_le32(&mms->outgoing_packet_data, 0xffffffff);
+    put_le32(&mms->outgoing_packet_data, 0);
+    put_le32(&mms->outgoing_packet_data, 0);
+    put_le32(&mms->outgoing_packet_data, 0);
+
+    // the media preroll value in milliseconds?
+    put_le32(&mms->outgoing_packet_data, 0);
+    put_le32(&mms->outgoing_packet_data, 0x40AC2000);
+    put_le32(&mms->outgoing_packet_data, 2);
+    put_le32(&mms->outgoing_packet_data, 0);
+
+    err = send_command_packet(mms);
+    ff_mms_set_state(mms, AWAITING_PACKET_HEADER_REQUEST_ACCEPTED_TYPE);
+    return err;
+}
+
+static void handle_packet_stream_changing_type(MMSContext *mms)
+{
+    ByteIOContext pkt;
+#if (MMS_DEBUG_LEVEL>0)
+    fprintf(stderr, "Stream changing!\n");
+#endif
+
+    // read these from the incoming buffer.. (40 is the packet header size, without the prefixes)
+    init_put_byte(&pkt, mms->incoming_buffer+40, mms->incoming_buffer_length-40, 0, NULL, NULL, NULL, NULL);
+    get_le32(&pkt); // prefix 1
+    mms->header_packet_id= (get_le32(&pkt) & 0xff); // prefix 2
+
+    fprintf(stderr, "Changed header prefix to 0x%x", mms->header_packet_id);
+    // mms->asf_header_length= 0;
+
+    ff_mms_set_state(mms, AWAITING_ASF_HEADER); // this is going to hork our avstreams.
+}
+
+static int send_keepalive_packet(MMSContext *mms)
+{
+    // respond to a keepalive with a keepalive...
+    start_command_packet(mms, CS_PACKET_KEEPALIVE_TYPE);
+    insert_command_prefixes(mms, 1, 0x100FFFF);
+    return send_command_packet(mms);
+}
+
+/** Handling pf TCP-specific packets.
+ * @param packet_type incoming packet type to process.
+ * @return  0 if the packet_type wasn't handled by this function.
+ *          1 if it expected and handled.
+ *         -1 if it the packet was unexpected in the current state.
+ */
+static int tcp_packet_state_machine(MMSContext *mms, MMSSCPacketType packet_type)
+{
+    switch(packet_type) {
+    case SC_PACKET_CLIENT_ACCEPTED:
+#if 0
+        if(mms->state==AWAITING_SC_PACKET_CLIENT_ACCEPTED) {
+#if (MMS_DEBUG_LEVEL>0)
+            fprintf(stderr, "Transitioning from AWAITING_SC_PACKET_CLIENT_ACCEPTED to AWAITING_SC_PACKET_TIMING_TEST_REPLY_TYPE\n");
+#endif
+            // send the timing request packet...
+            start_command_packet(mms, CS_PACKET_TIMING_DATA_REQUEST_TYPE);
+            insert_command_prefixes(mms, 0xf0f0f0f1, 0x0004000b);
+            send_command_packet(mms);
+
+            ff_mms_set_state(mms, AWAITING_SC_PACKET_TIMING_TEST_REPLY_TYPE);
+        } else {
+            return -1;
+        }
+        break;
+#endif
+
+    case SC_PACKET_TIMING_TEST_REPLY_TYPE: // we may, or may not have timing tests.
+        if(mms->state==AWAITING_SC_PACKET_TIMING_TEST_REPLY_TYPE || mms->state==AWAITING_SC_PACKET_CLIENT_ACCEPTED) {
+            send_protocol_select(mms);
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_PROTOCOL_ACCEPTED_TYPE:
+        if(mms->state==AWAITING_CS_PACKET_PROTOCOL_ACCEPTANCE) {
+            send_media_file_request(mms);
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_PROTOCOL_FAILED_TYPE:
+        if(mms->state==AWAITING_CS_PACKET_PROTOCOL_ACCEPTANCE) {
+            // abort;
+#if (MMS_DEBUG_LEVEL>0)
+            fprintf(stderr, "Protocol failed\n");
+#endif
+            ff_mms_set_state(mms, STATE_ERROR);
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_PASSWORD_REQUIRED_TYPE:
+        if(mms->state==AWAITING_PASSWORD_QUERY_OR_MEDIA_FILE) {
+            // we don't support this right now.
+#if (MMS_DEBUG_LEVEL>0)
+            fprintf(stderr, "Password required\n");
+#endif
+            ff_mms_set_state(mms, STATE_ERROR);
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_MEDIA_FILE_DETAILS_TYPE:
+        if(mms->state==AWAITING_PASSWORD_QUERY_OR_MEDIA_FILE) {
+            handle_packet_media_file_details(mms);
+            if(mms->state != STATE_ERROR)
+                send_media_header_request(mms);
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_HEADER_REQUEST_ACCEPTED_TYPE:
+        if(mms->state==AWAITING_PACKET_HEADER_REQUEST_ACCEPTED_TYPE) {
+            // reset (in case we are doing this more than once)
+//                mms->asf_header_length= 0;
+
+            // wait for the header (follows immediately)
+            ff_mms_set_state(mms, AWAITING_ASF_HEADER);
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_STREAM_CHANGING_TYPE:
+        if(mms->state==STREAMING) {
+            handle_packet_stream_changing_type(mms);
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_STREAM_ID_ACCEPTED_TYPE:
+        if(mms->state==AWAITING_STREAM_ID_ACCEPTANCE) {
+#if (MMS_DEBUG_LEVEL>0)
+            fprintf(stderr, "Stream ID's accepted!\n");
+#endif
+            ff_mms_set_state(mms, STREAM_PAUSED); // only way to get out of this is to play...
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_MEDIA_PACKET_FOLLOWS_TYPE:
+        if(mms->state==AWAITING_STREAM_START_PACKET) {
+            // get the stream packets...
+            ff_mms_set_state(mms, STREAMING);
+        } else {
+            return -1;
+        }
+        break;
+
+    case SC_PACKET_KEEPALIVE_TYPE:
+        if(mms->state==STREAMING || mms->state==STREAM_PAUSED) {
+#if (MMS_DEBUG_LEVEL>0)
+            fprintf(stderr, "Got a Keepalive!\n");
+#endif
+            send_keepalive_packet(mms);
+        } else {
+            return -1;
+        }
+        break;
+
+    default:
+        return 0; // Not handled here
+    }
+
+    return 1; // Handled here
+}
+
+/** 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->media_packet_buffer_length<mms->asf_context.packet_obj_size) {
+        int padding_size = mms->asf_context.packet_obj_size - mms->media_packet_buffer_length;
+        //  fprintf(stderr, "Incoming packet smaller than the asf packet size stated (%d<%d) Padding.\n", mms->media_packet_buffer_length, mms->asf_context.packet_size);
+        memset(mms->media_packet_incoming_buffer+mms->media_packet_buffer_length, 0, padding_size);
+        mms->media_packet_buffer_length += padding_size;
+    }
+
+    if(mms->media_packet_seek_offset) {
+        mms->media_packet_buffer_length -= mms->media_packet_seek_offset;
+        mms->media_packet_read_ptr += mms->media_packet_seek_offset;
+        mms->media_packet_seek_offset = 0;
+    }
+}
+
+/** Single-step the packet-pumping state machine.
+ * @return The type of the last packet from the server.
+ */
+static MMSSCPacketType ff_mms_packet_state_machine(MMSContext *mms)
+{
+    MMSSCPacketType packet_type = get_tcp_server_response(mms);
+
+    /* First, try protocol-specific packet handling */
+    int ret = tcp_packet_state_machine(mms, packet_type);
+    if(ret != 0) {
+        if(ret == -1)
+            log_packet_in_wrong_state(mms, packet_type);
+        return packet_type;
+    }
+
+    /* Common packet handling */
+    switch(packet_type) {
+    case SC_PACKET_ASF_HEADER_TYPE:
+        if(mms->state==AWAITING_ASF_HEADER) {
+#if (MMS_DEBUG_LEVEL>0)
+            fprintf(stderr, "Got a SC_PACKET_ASF_HEADER: %d\n", mms->media_packet_buffer_length);
+#endif
+            if((mms->incoming_flags == 0X08) || (mms->incoming_flags == 0X0C))
+            {
+#if (MMS_DEBUG_LEVEL>0)
+                fprintf(stderr, "Got the full header!\n");
+#endif
+                ff_mms_set_state(mms, ASF_HEADER_DONE);
+            }
+        } else {
+            log_packet_in_wrong_state(mms, packet_type);
+        }
+        break;
+
+    case SC_PACKET_ASF_MEDIA_TYPE:
+        if(mms->state==STREAMING || mms->state==AWAITING_PAUSE_ACKNOWLEDGE || mms->state==AWAITING_HTTP_PAUSE_CONTROL_ACKNOWLEDGE) {
+//                fprintf(stderr, "Got a stream packet of length %d!\n", mms->incoming_buffer_length);
+            pad_media_packet(mms);
+        } else {
+            log_packet_in_wrong_state(mms, packet_type);
+        }
+        break;
+
+    case SC_PACKET_STREAM_STOPPED_TYPE:
+        if(mms->state==AWAITING_PAUSE_ACKNOWLEDGE) {
+#if (MMS_DEBUG_LEVEL>0)
+            fprintf(stderr, "Server echoed stream pause\n");
+#endif
+            ff_mms_set_state(mms, STREAM_PAUSED);
+        } else if(mms->state==STREAMING) {
+            /*
+            When echoing a start (from me):
+             receive command 0x1e, 48 bytes
+             start sequence 00000001
+             command id     b00bface
+             length               20
+             protocol       20534d4d
+             len8                  4
+             sequence #     00000006
+             len8  (II)            2
+             dir | comm     0004001e
+             prefix1        00000000
+             prefix2        ffff0100
+
+             When Ending on it's own:
+             receive command 0x1e, 48 bytes
+             start sequence 09000001
+             command id     b00bface
+             length               20
+             protocol       20534d4d
+             len8                  4
+             sequence #     00000006
+             len8  (II)            2
+             dir | comm     0004001e
+             prefix1        00000000
+             prefix2        00000004
+            */
+#if (MMS_DEBUG_LEVEL>0)
+            fprintf(stderr, "** Server hit end of stream (may be sending new header information)\n");
+#endif
+            // TODO: if this is a live stream, on the resumption of a pause, this happens, then it follows with a SC_PACKET_STREAM_CHANGING_TYPE
+            // otherwise it means this stream is done.
+            ff_mms_set_state(mms, STREAM_DONE);
+        } else {
+            log_packet_in_wrong_state(mms, packet_type);
+        }
+        break;
+
+    case SC_PACKET_TYPE_CANCEL:
+        fprintf(stderr, "Got a -1 packet type\n");
+        // user cancelled; let us out so it gets closed down...
+        ff_mms_set_state(mms, USER_CANCELLED);
+        break;
+
+    case SC_PACKET_TYPE_NO_DATA:
+#if (MMS_DEBUG_LEVEL>0)
+        fprintf(stderr, "Got no data (closed?)\n");
+#endif
+        ff_mms_set_state(mms, STREAM_DONE); //?
+        break;
+
+    case SC_PACKET_HTTP_CONTROL_ACKNOWLEDGE:
+        ff_mms_set_state(mms, AWAITING_PAUSE_ACKNOWLEDGE);
+        break;
+
+    default:
+        fprintf(stderr, "Unhandled packet type %d\n", packet_type);
+        break;
+    }
+
+    return packet_type;
+}
+
+/** Send the initial handshake. */
+static int send_startup_packet(MMSContext *mms)
+{
+    int err;
+    char data_string[256];
+
+    //snprintf(data_string, sizeof(data_string), "NSPlayer/7.0.0.1956; {%s}; Host: %s",
+    //        my_guid, mms->host);
+    snprintf(data_string, sizeof(data_string), "NSPlayer/7.0.0.1956; {%s}",
+            "7E667F5D-A661-495E-A512-F55686DDA178");
+
+    start_command_packet(mms, CS_PACKET_INITIAL_TYPE);
+    insert_command_prefixes(mms, 0, 0x0004000b);
+    put_le32(&mms->outgoing_packet_data, 0x0003001c);
+    put_le_utf16(&mms->outgoing_packet_data, data_string);
+//    put_le16(&mms->outgoing_packet_data, 0); // double unicode ended string...
+    put_le32(&mms->outgoing_packet_data, 0); // double unicode ended string...
+
+    err = send_command_packet(mms);
+    ff_mms_set_state(mms, AWAITING_SC_PACKET_CLIENT_ACCEPTED);
+    return err;
+}
+
+/** Read the whole mms header into a buffer of our own .*/
+static int read_mms_header(MMSContext *mms)
+{
+    if(mms->state != AWAITING_ASF_HEADER) {
+        fprintf(stderr, "cannot read header this state\n");
+        ff_mms_set_state(mms, STATE_ERROR);
+        return -1;
+    }
+
+    /* TODO: add timeout */
+    /* This will run until the header is stored */
+    while(mms->state != ASF_HEADER_DONE && mms->state != STATE_ERROR)
+        ff_mms_packet_state_machine(mms);
+
+    if(mms->state == STATE_ERROR)
+        return -1;
+
+    /* Parse the header */
+    init_put_byte(&mms->private_av_format_ctx.pb, mms->asf_header, mms->asf_header_size, 0, NULL, NULL, NULL, NULL);
+    mms->private_av_format_ctx.priv_data = &mms->asf_context;
+
+    if(asf_demuxer.read_header(&mms->private_av_format_ctx, NULL) < 0) {
+        fprintf(stderr, "read_header failed\n");
+        return -1;
+    }
+
+    mms->av_format_ctx = &mms->private_av_format_ctx; // Default
+    mms->header_parsed = 1;
+
+    return 0;
+}
+
+/** Clear all buffers of partial and old packets after a seek or other discontinuity */
+void clear_stream_buffers(MMSContext *mms)
+{
+    mms->incoming_io_buffer.buf_ptr = mms->incoming_io_buffer.buf_end;
+    mms->media_packet_buffer_length = 0;
+    mms->media_packet_read_ptr = mms->media_packet_incoming_buffer;
+}
+
+/** Convert from AVDISCARD_* values to MMS stream selection code for the stream. */
+static int ff_mms_stream_selection_code(AVStream *st)
+{
+    switch(st->discard) {
+    case AVDISCARD_NONE:
+    case AVDISCARD_DEFAULT:
+    default:
+        return 0; // 00 = stream at full frame rate;
+
+    case AVDISCARD_NONREF:
+    case AVDISCARD_BIDIR:
+    case AVDISCARD_NONKEY:
+        return 1; // 01 = only stream key frames (doesn't work that well)
+
+    case AVDISCARD_ALL:
+        return 2; // 02 = no stream, switch it off.
+    }
+}
+
+
+/** Send MMST stream selection command based on the AVStream->discard values. */
+static int send_stream_selection_request(MMSContext *mms)
+{
+    int ii;
+    int err;
+
+    //  send the streams we want back...
+    start_command_packet(mms, CS_PACKET_STREAM_ID_REQUEST_TYPE);
+    put_le32(&mms->outgoing_packet_data, mms->av_format_ctx->nb_streams);
+
+    for(ii= 0; ii<mms->av_format_ctx->nb_streams; ii++) {
+        AVStream *st= mms->av_format_ctx->streams[ii];
+
+        put_le16(&mms->outgoing_packet_data, 0xffff); // flags
+        put_le16(&mms->outgoing_packet_data, st->id); // stream id
+        put_le16(&mms->outgoing_packet_data, ff_mms_stream_selection_code(st)); // selection
+    }
+
+    put_le16(&mms->outgoing_packet_data, 0); /* Extra zeroes */
+
+    err = send_command_packet(mms);
+    ff_mms_set_state(mms, AWAITING_STREAM_ID_ACCEPTANCE);
+    return err;
+}
+
+/** Request a MMST stream from a timestamp or packet offset.
+ * @param rate Play rate. (Only 1 supported as for now)
+ * @param byte_offset Byte position to seek to. Set to -1 when seeking by timestamp.
+ *  The position is from the start of the media stream, i.e. not counting header size.
+ * @param timestamp Time point in ms. Set to 0 when seeking from packet offsets.
+ */
+static int request_streaming_from(MMSContext *mms,
+        int rate, int64_t byte_offset, int64_t timestamp)
+{
+    int32_t packet = -1;
+    int result;
+
+    if(byte_offset > 0)
+        packet = byte_offset / mms->asf_context.packet_obj_size;
+
+    /* Send a stream selection request if this is the first call to play */
+    if(mms->state == ASF_HEADER_DONE) {
+        result = send_stream_selection_request(mms);
+
+        if(result==0) {
+            while(mms->state != STREAM_PAUSED && mms->state != STATE_ERROR && mms->state != STREAM_DONE) {
+                ff_mms_packet_state_machine(mms);
+            }
+        }
+    }
+
+    if(mms->state==STREAM_PAUSED || mms->state == ASF_HEADER_DONE) {
+        timestamp= av_dbl2int((double)timestamp/1000.0); // is this needed?
+
+        start_command_packet(mms, CS_PACKET_START_FROM_PACKET_ID_TYPE);
+        insert_command_prefixes(mms, 1, mms->packet_id);
+        put_le64(&mms->outgoing_packet_data, timestamp); // seek timestamp
+        put_le32(&mms->outgoing_packet_data, 0xffffffff);  // unknown
+        put_le32(&mms->outgoing_packet_data, packet);    // packet offset
+        put_byte(&mms->outgoing_packet_data, 0xff); // max stream time limit
+        put_byte(&mms->outgoing_packet_data, 0xff); // max stream time limit
+        put_byte(&mms->outgoing_packet_data, 0xff); // max stream time limit
+        put_byte(&mms->outgoing_packet_data, 0x00); // stream time limit flag
+
+        mms->packet_id++; // new packet_id so we can separate new data from old data
+        put_le32(&mms->outgoing_packet_data, mms->packet_id);
+        send_command_packet(mms);
+
+        ff_mms_set_state(mms, AWAITING_STREAM_START_PACKET);
+        return 0;
+    } else {
+#if (MMS_DEBUG_LEVEL>0)
+//        fprintf(stderr, "Tried a read_play when the state was not stream paused (%s)\n", state_names[mms->state]);
+#endif
+        return -1;
+    }
+}
+
+/** Read at most one media packet (or a whole header). */
+static int read_mms_packet(MMSContext *mms, uint8_t *buf, int buf_size)
+{
+    int result = 0;
+    MMSSCPacketType packet_type;
+    int size_to_copy;
+
+//    fprintf(stderr, "mms_read_packet()\n");
+
+//    fprintf(stderr, "*** read packet %p needs %d bytes at %lld...\n", buf, buf_size, url_ftell(&mms->av_format_ctx->pb));
+    if(mms->state != STREAM_DONE && mms->state != STREAM_PAUSED && mms->state != STATE_ERROR) {
+        do {
+            if(mms->asf_header_read_pos < mms->asf_header_size) {
+                /* Read from ASF header buffer */
+                size_to_copy= FFMIN(buf_size, mms->asf_header_size - mms->asf_header_read_pos);
+                memcpy(buf, mms->asf_header + mms->asf_header_read_pos, size_to_copy);
+                mms->asf_header_read_pos += size_to_copy;
+                result += size_to_copy;
+#if (MMS_DEBUG_LEVEL > 0)
+                fprintf(stderr, "Copied %d bytes from stored header. left: %d\n", size_to_copy, mms->asf_header_size - mms->asf_header_read_pos);
+#endif
+            } else if(mms->media_packet_buffer_length) {
+                /* Read from media packet buffer */
+                size_to_copy = FFMIN(buf_size, mms->media_packet_buffer_length);
+                memcpy(buf, mms->media_packet_read_ptr, size_to_copy);
+                mms->media_packet_buffer_length -= size_to_copy;
+                mms->media_packet_read_ptr+= size_to_copy;
+                result += size_to_copy;
+                //fprintf(stderr, "Copied %d bytes from media_packet read pointer! (result: %d, left: %d)\n", size_to_copy, result, mms->media_packet_buffer_length);
+            } else {
+                /* Read from network */
+//               fprintf(stderr, "Calling state machine...\n");
+                packet_type= ff_mms_packet_state_machine(mms);
+//                fprintf(stderr, "Type: 0x%x\n", packet_type);
+                switch (packet_type) {
+                case SC_PACKET_ASF_MEDIA_TYPE:
+                    if(mms->media_packet_buffer_length>mms->asf_context.packet_obj_size) {
+                        fprintf(stderr, "Incoming packet larger than the asf packet size stated (%d>%d)\n", mms->media_packet_buffer_length, mms->asf_context.packet_obj_size);
+                        result= AVERROR_IO;
+                        break;
+                    }
+
+                    // copy the data to the packet buffer...
+                    size_to_copy= FFMIN(buf_size, mms->media_packet_buffer_length);
+                    memcpy(buf, mms->media_packet_read_ptr, size_to_copy);
+                    mms->media_packet_buffer_length -= size_to_copy;
+                    mms->media_packet_read_ptr += size_to_copy;
+                    result += size_to_copy;
+//fprintf(stderr, "Copied %d bytes (Media) from media_packet read pointer! (result: %d)\n", size_to_copy, result);
+                    break;
+                case SC_PACKET_ASF_HEADER_TYPE:
+                    // copy the data to the packet buffer...
+                    size_to_copy= FFMIN(buf_size, mms->media_packet_buffer_length);
+                    memcpy(buf, mms->media_packet_read_ptr, size_to_copy);
+                    mms->media_packet_buffer_length -= size_to_copy;
+                    mms->media_packet_read_ptr+= size_to_copy;
+                    result+= size_to_copy;
+//fprintf(stderr, "Copied %d bytes (header) from media_packet read pointer! (result: %d)\n", size_to_copy, result);
+                    break;
+                default:
+                    if(mms->state==STREAM_PAUSED) {
+                        result= 0;
+                    } else if(mms->state==STREAM_DONE || mms->state==USER_CANCELLED) {
+                        result=-1;
+                    } else if(mms->state==AWAITING_ASF_HEADER) {
+                        // we have reset the header; spin though the loop..
+//                        fprintf(stderr, "****-- Spinning the loop!\n");
+                        while(mms->state != STREAMING && mms->state != STATE_ERROR) {
+                            ff_mms_packet_state_machine(mms);
+                        }
+//                        fprintf(stderr, "****-- Done Spinning the loop!\n");
+                    } else {
+#if (MMS_DEBUG_LEVEL>0)
+                        fprintf(stderr, "Got a packet in odd state: %s Packet Type: 0x%x\n", state_names[mms->state], packet_type);
+#endif
+                    }
+                    break;
+                }
+            }
+        } while(result==0 && mms->state!=STREAM_PAUSED); // only return one packet...
+    } else {
+        if(mms->state==STREAM_PAUSED) {
+            result= 0;
+        } else {
+            result= -1;
+        }
+    }
+//    fprintf(stderr, "read packet %p needs %d bytes.  getting %d\n", buf, buf_size, result);
+
+    return result;
+}
+
+static int send_close_packet(MMSContext *mms)
+{
+    int err;
+    start_command_packet(mms, CS_PACKET_STREAM_CLOSE_TYPE);
+    insert_command_prefixes(mms, 1, 1);
+
+    err = send_command_packet(mms);
+    ff_mms_set_state(mms, AWAITING_PAUSE_ACKNOWLEDGE);
+    return err;
+}
+
+/** Close the MMSH/MMST connection */
+static int mms_close(URLContext *h)
+{
+    MMSContext *mms = (MMSContext *)h->priv_data;
+
+#if (MMS_DEBUG_LEVEL>0)
+    fprintf(stderr, "mms_close\n");
+#endif
+    if(mms->mms_hd) {
+        // send the close packet if we should...
+        if(mms->state != STATE_ERROR) {
+            send_close_packet(mms);
+        }
+
+        // need an url_fdclose()
+        close_connection(mms);
+    }
+#if (MMS_DEBUG_LEVEL>0)
+    fprintf(stderr, "done with mms_close\n");
+#endif
+
+    /* TODO: free all separately allocated pointers in mms */
+    av_free(mms->asf_header);
+    av_freep(&h->priv_data);
+
+    return 0;
+}
+
+static int mms_open_cnx(URLContext *h)
+{
+    MMSContext *mms = h->priv_data;
+
+    char authorization[64];
+    int err = AVERROR(EIO);
+
+    // only for MMS over TCP, so set proto = NULL
+    url_split(NULL, 0, authorization, sizeof(authorization), mms->host, sizeof(mms->host),
+              &mms->port, mms->path, sizeof(mms->path), mms->location);
+
+//    mms->protocol = &mmst_mmsprotocol;
+
+    if(mms->port<0)
+        mms->port = DEFAULT_MMS_PORT;
+//#if (MMS_DEBUG_LEVEL>0)
+//    fprintf(stderr, "Opening %s\n Auth: %s\n Host: %s\n Port: %d\n Path: %s\n", mms->location, authorization, mms->host, mms->port, mms->path);
+//#endif
+
+    /* the outgoing packet buffer */
+    init_put_byte(&mms->outgoing_packet_data, mms->outgoing_packet_buffer, sizeof(mms->outgoing_packet_buffer), 1, NULL, NULL, NULL, NULL);
+
+    /* open the tcp connexion */
+    if((err = ff_mms_open_connection(mms)))
+        goto fail;
+
+    // Fill in some parameters...
+    mms->local_ip_address = 0xc0a80081; // This should be the local IP address; how do I get this from the url_ stuff?  (nothing is apparent)
+    mms->local_port = 1037; // as above, this should be the port I am connected to; how do I get this frmo the url stuff? (Server doesn't really seem to care too much)
+    mms->packet_id = 3; // default, initial value. (3 will be incremented to 4 before first use)
+    mms->header_packet_id = 2; // default, initial value.
+
+    /* okay, now setup stuff.  working from unclear specifications is great! */
+    //mms->protocol->send_startup_message(mms); //TOBEDONE
+    send_startup_packet(mms);
+
+    // TODO: add timeout here... (mms_watchdog_reset() ?)
+    while(mms->state != AWAITING_ASF_HEADER &&
+            mms->state != STATE_ERROR &&
+            mms->state != STREAM_DONE) {
+        ff_mms_packet_state_machine(mms); // TOBEDONE
+    }
+
+    /* We store the header internally */
+    if(mms->state != STATE_ERROR)
+        read_mms_header(mms); // TOBEDONE use asf_read_header?
+
+    if(mms->state == STATE_ERROR) {
+        err = AVERROR(EIO);
+        goto fail;
+    }
+
+#if (MMS_DEBUG_LEVEL>0)
+    fprintf(stderr, "Leaving open (success)\n");
+#endif
+
+    return 0;
+fail:
+    mms_close(h);
+
+#if (MMS_DEBUG_LEVEL>0)
+    fprintf(stderr, "Leaving open (failure: %d)\n", err);
+#endif
+
+    return err;
+}
+
+static int mms_open(URLContext *h, const char *uri, int flags)
+{
+    MMSContext *mms;
+    int ret;
+
+    h->is_streamed = 1;
+    mms = av_malloc(sizeof(MMSContext));
+    if (!mms)
+        return AVERROR(ENOMEM);
+    memset(mms, 0, sizeof(MMSContext));
+    h->priv_data = mms;
+    av_strlcpy(mms->location, uri, MMS_URL_SIZE);
+
+    ret = mms_open_cnx(h);
+    return ret;
+}
+
+/** Like AVInputFormat::read_play().
+ * @see AVInputFormat::read_play()
+ */
+static int ff_mms_play(URLContext *h)
+{
+    MMSContext *mms = h->priv_data;
+    int64_t stream_offset = 0;
+
+    /* Early return if already playing. */
+    if(mms->state == STREAMING || mms->state == AWAITING_STREAM_START_PACKET)
+        return 0;
+
+#if (MMS_DEBUG_LEVEL>0)
+    fprintf(stderr, "MMS Play\n");
+#endif
+
+    /* If resuming from pause */
+    if(mms->state==STREAM_PAUSED)
+        stream_offset = (mms->pause_resume_seq+1) * mms->asf_context.packet_obj_size;
+
+    clear_stream_buffers(mms);
+
+    return request_streaming_from(mms, 1, stream_offset, 0);
+}
+
+/** 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 */
+    MMSContext *mms = h->priv_data;
+    int result = 0;
+
+    /* Since we read the header at open(), this shouldn't be possible */
+    assert(mms->header_parsed);
+
+    /* Automatically start playing if the app wants to read before it has called play()
+     * (helps with non-streaming aware apps) */
+    if(mms->state == ASF_HEADER_DONE && mms->asf_header_read_pos >= mms->asf_header_size) {
+        fprintf(stderr, "mms_read() before play(). Playing automatically.\n");
+        result = ff_mms_play(h);// TOBEDONE
+        if(result < 0)
+            return result;
+    }
+
+    /* We won't get any packets from the server if paused. Nothing else to do than
+     * to return. FIXME: return AVERROR(EAGAIN)? */
+    if(mms->state == STREAM_PAUSED) {
+        fprintf(stderr, "mms_read in STREAM_PAUSED\n");
+        return 0;
+    } else if(mms->state==STREAMING || mms->state==AWAITING_STREAM_START_PACKET || mms->state == ASF_HEADER_DONE) {
+        result = read_mms_packet(mms, buf, size);//TOBEDONE use asf_read_packet?
+
+        /* Note which packet we last returned. FIXME: doesn't handle partially read packets */
+        mms->pause_resume_seq = mms->incoming_packet_seq;
+    } else {
+#if (MMS_DEBUG_LEVEL>0)
+        fprintf(stderr, "mms_read: wrong state %s, returning AVERROR_IO!\n", state_names[mms->state]);
+#endif
+        result = AVERROR(EIO);
+    }
+
+    return result;
+}
+
+URLProtocol mmst_protocol = {
+    "mmst",
+    mms_open,
+    mms_read,
+    NULL, // write
+    NULL, // seek
+    mms_close,
+};


More information about the FFmpeg-soc mailing list