[FFmpeg-soc] [soc]: r5656 - mms/mmst.c
spyfeng
subversion at mplayerhq.hu
Sat Mar 13 13:04:37 CET 2010
Author: spyfeng
Date: Sat Mar 13 13:04:37 2010
New Revision: 5656
Log:
change file format from dos file version to unix version.
Modified:
mms/mmst.c
Modified: mms/mmst.c
==============================================================================
--- mms/mmst.c Sat Mar 13 13:02:15 2010 (r5655)
+++ mms/mmst.c Sat Mar 13 13:04:37 2010 (r5656)
@@ -1,1291 +1,1291 @@
-/*
- * 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 "libavutil/avstring.h"
-#include "network.h"
-#include "asf.h"
-
-#define DEBUG
-#define MMS_DEBUG_LEVEL 2
-#define MMS_MAXIMUM_PACKET_LENGTH 512
-#define MMS_KILO 1024
-#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 {
- 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
-
- /** 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.
-
- /** 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.
- int asf_packet_len;
- /*@}*/
-
- int pause_resume_seq; ///< Last packet returned by mms_read. Useful for resuming pause.
- char location[4096];
- int stream_num;
-} 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 err;
-
- close_connection(mms);
-
- snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", mms->host, mms->port);
- err = url_open(&mms->mms_hd, tcpname, URL_RDWR);
- 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/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);
-
- // 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 */
-static 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;
-}
-
-static int read_bytes(MMSContext *mms, uint8_t *buffer, int length_to_read)
-{
- int len= 0;
-
- while(len<length_to_read)
- {
- int read_result= url_read(mms->mms_hd, buffer+len, length_to_read-len);
- if(read_result==0)
- {
- break;
- }
- else if(read_result<0)
- {
- // if we read something, we want to return up to that portion; if we didn't error out.
- if(!len)
- len= -1;
- break;
- } else {
- len+= read_result;
- }
- }
-
- return len;
-}
-
-/** 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= read_bytes(mms, 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= read_bytes(mms, 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 = read_bytes(mms, 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= read_bytes(mms, 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
- 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) {
- 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);
-
- 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(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;
-
- 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) {
- 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_packet_len) {
- int padding_size = mms->asf_packet_len - 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",
- "7E667F5D-A661-495E-A512-F55686DDA178", mms->host);
-
- 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...
-
- err = send_command_packet(mms);
- ff_mms_set_state(mms, AWAITING_SC_PACKET_CLIENT_ACCEPTED);
- return err;
-}
-
-static void print_guid(const ff_asf_guid *g)
-{
- int i;
- av_log(NULL, AV_LOG_DEBUG, "{");
- for(i=0;i<16;i++)
- av_log(NULL, AV_LOG_DEBUG, " 0x%02x,", (*g)[i]);
- av_log(NULL, AV_LOG_DEBUG, "}\n");
-}
-
-static int asf_header_parser(MMSContext *mms)
-{
- uint8_t *p = mms->asf_header, *end = mms->asf_header + mms->asf_header_size;
- mms->stream_num = 0;
-
- if (mms->asf_header_size < 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))) {
- /* read packet size */
- if (end - p > sizeof(ff_asf_guid) * 2 + 64) {
- mms->asf_packet_len = AV_RL32(p + sizeof(ff_asf_guid) * 2 + 64);
- }
- } else if (!memcmp(p, ff_asf_stream_header, sizeof(ff_asf_guid))) {
- mms->stream_num++;
- }
- if (chunksize > end - p)
- return -1;
- p += chunksize;
- } while (end - p >= sizeof(ff_asf_guid) + 8);
-
- return -1;
-}
-
-/** 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;
-
- asf_header_parser(mms);
- mms->header_parsed = 1;
-
- return 0;
-}
-
-/** Clear all buffers of partial and old packets after a seek or other discontinuity */
-static void clear_stream_buffers(MMSContext *mms)
-{
- mms->media_packet_buffer_length = 0;
- mms->media_packet_read_ptr = mms->media_packet_incoming_buffer;
-}
-
-/** 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->stream_num); // stream nums.
- for(ii= 0; ii<mms->stream_num; ii++) {
- put_le16(&mms->outgoing_packet_data, 0xffff); // flags
- put_le16(&mms->outgoing_packet_data, ii +1); // stream id
- put_le16(&mms->outgoing_packet_data, 0); // 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_packet_len;
-
- /* 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;
-
- 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_packet_len) {
- fprintf(stderr, "Incoming packet larger than the asf packet size stated (%d>%d)\n", mms->media_packet_buffer_length, mms->asf_packet_len);
-// 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);
-
- if(mms->port<0)
- mms->port = DEFAULT_MMS_PORT;
-
- /* 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, sizeof(mms->location));
-
- 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_packet_len;
-
- 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,
-};
+/*
+ * 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 "libavutil/avstring.h"
+#include "network.h"
+#include "asf.h"
+
+#define DEBUG
+#define MMS_DEBUG_LEVEL 2
+#define MMS_MAXIMUM_PACKET_LENGTH 512
+#define MMS_KILO 1024
+#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 {
+ 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
+
+ /** 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.
+
+ /** 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.
+ int asf_packet_len;
+ /*@}*/
+
+ int pause_resume_seq; ///< Last packet returned by mms_read. Useful for resuming pause.
+ char location[4096];
+ int stream_num;
+} 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 err;
+
+ close_connection(mms);
+
+ snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", mms->host, mms->port);
+ err = url_open(&mms->mms_hd, tcpname, URL_RDWR);
+ 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/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);
+
+ // 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 */
+static 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;
+}
+
+static int read_bytes(MMSContext *mms, uint8_t *buffer, int length_to_read)
+{
+ int len= 0;
+
+ while(len<length_to_read)
+ {
+ int read_result= url_read(mms->mms_hd, buffer+len, length_to_read-len);
+ if(read_result==0)
+ {
+ break;
+ }
+ else if(read_result<0)
+ {
+ // if we read something, we want to return up to that portion; if we didn't error out.
+ if(!len)
+ len= -1;
+ break;
+ } else {
+ len+= read_result;
+ }
+ }
+
+ return len;
+}
+
+/** 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= read_bytes(mms, 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= read_bytes(mms, 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 = read_bytes(mms, 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= read_bytes(mms, 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
+ 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) {
+ 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);
+
+ 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(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;
+
+ 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) {
+ 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_packet_len) {
+ int padding_size = mms->asf_packet_len - 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",
+ "7E667F5D-A661-495E-A512-F55686DDA178", mms->host);
+
+ 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...
+
+ err = send_command_packet(mms);
+ ff_mms_set_state(mms, AWAITING_SC_PACKET_CLIENT_ACCEPTED);
+ return err;
+}
+
+static void print_guid(const ff_asf_guid *g)
+{
+ int i;
+ av_log(NULL, AV_LOG_DEBUG, "{");
+ for(i=0;i<16;i++)
+ av_log(NULL, AV_LOG_DEBUG, " 0x%02x,", (*g)[i]);
+ av_log(NULL, AV_LOG_DEBUG, "}\n");
+}
+
+static int asf_header_parser(MMSContext *mms)
+{
+ uint8_t *p = mms->asf_header, *end = mms->asf_header + mms->asf_header_size;
+ mms->stream_num = 0;
+
+ if (mms->asf_header_size < 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))) {
+ /* read packet size */
+ if (end - p > sizeof(ff_asf_guid) * 2 + 64) {
+ mms->asf_packet_len = AV_RL32(p + sizeof(ff_asf_guid) * 2 + 64);
+ }
+ } else if (!memcmp(p, ff_asf_stream_header, sizeof(ff_asf_guid))) {
+ mms->stream_num++;
+ }
+ if (chunksize > end - p)
+ return -1;
+ p += chunksize;
+ } while (end - p >= sizeof(ff_asf_guid) + 8);
+
+ return -1;
+}
+
+/** 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;
+
+ asf_header_parser(mms);
+ mms->header_parsed = 1;
+
+ return 0;
+}
+
+/** Clear all buffers of partial and old packets after a seek or other discontinuity */
+static void clear_stream_buffers(MMSContext *mms)
+{
+ mms->media_packet_buffer_length = 0;
+ mms->media_packet_read_ptr = mms->media_packet_incoming_buffer;
+}
+
+/** 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->stream_num); // stream nums.
+ for(ii= 0; ii<mms->stream_num; ii++) {
+ put_le16(&mms->outgoing_packet_data, 0xffff); // flags
+ put_le16(&mms->outgoing_packet_data, ii +1); // stream id
+ put_le16(&mms->outgoing_packet_data, 0); // 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_packet_len;
+
+ /* 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;
+
+ 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_packet_len) {
+ fprintf(stderr, "Incoming packet larger than the asf packet size stated (%d>%d)\n", mms->media_packet_buffer_length, mms->asf_packet_len);
+// 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);
+
+ if(mms->port<0)
+ mms->port = DEFAULT_MMS_PORT;
+
+ /* 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, sizeof(mms->location));
+
+ 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_packet_len;
+
+ 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