yading@11: /* yading@11: * MMS protocol over TCP yading@11: * Copyright (c) 2006,2007 Ryan Martell yading@11: * Copyright (c) 2007 Björn Axelsson yading@11: * Copyright (c) 2010 Zhentan Feng yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /* References yading@11: * MMS protocol specification: yading@11: * [1]http://msdn.microsoft.com/en-us/library/cc234711(PROT.10).aspx yading@11: * ASF specification. Revision 01.20.03. yading@11: * [2]http://msdn.microsoft.com/en-us/library/bb643323.aspx yading@11: */ yading@11: yading@11: #include "avformat.h" yading@11: #include "mms.h" yading@11: #include "internal.h" yading@11: #include "avio_internal.h" yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "libavcodec/bytestream.h" yading@11: #include "network.h" yading@11: #include "url.h" yading@11: yading@11: #define LOCAL_ADDRESS 0xc0a80081 // FIXME get and use correct local ip address. yading@11: #define LOCAL_PORT 1037 // as above. yading@11: /** Client to server packet types. */ yading@11: typedef enum { yading@11: CS_PKT_INITIAL = 0x01, yading@11: CS_PKT_PROTOCOL_SELECT = 0x02, yading@11: CS_PKT_MEDIA_FILE_REQUEST = 0x05, yading@11: CS_PKT_START_FROM_PKT_ID = 0x07, yading@11: CS_PKT_STREAM_PAUSE = 0x09, yading@11: CS_PKT_STREAM_CLOSE = 0x0d, yading@11: CS_PKT_MEDIA_HEADER_REQUEST = 0x15, yading@11: CS_PKT_TIMING_DATA_REQUEST = 0x18, yading@11: CS_PKT_USER_PASSWORD = 0x1a, yading@11: CS_PKT_KEEPALIVE = 0x1b, yading@11: CS_PKT_STREAM_ID_REQUEST = 0x33, yading@11: } MMSCSPacketType; yading@11: yading@11: /** Server to client packet types. */ yading@11: typedef enum { yading@11: /** Control packets. */ yading@11: /*@{*/ yading@11: SC_PKT_CLIENT_ACCEPTED = 0x01, yading@11: SC_PKT_PROTOCOL_ACCEPTED = 0x02, yading@11: SC_PKT_PROTOCOL_FAILED = 0x03, yading@11: SC_PKT_MEDIA_PKT_FOLLOWS = 0x05, yading@11: SC_PKT_MEDIA_FILE_DETAILS = 0x06, yading@11: SC_PKT_HEADER_REQUEST_ACCEPTED = 0x11, yading@11: SC_PKT_TIMING_TEST_REPLY = 0x15, yading@11: SC_PKT_PASSWORD_REQUIRED = 0x1a, yading@11: SC_PKT_KEEPALIVE = 0x1b, yading@11: SC_PKT_STREAM_STOPPED = 0x1e, yading@11: SC_PKT_STREAM_CHANGING = 0x20, yading@11: SC_PKT_STREAM_ID_ACCEPTED = 0x21, yading@11: /*@}*/ yading@11: yading@11: /** Pseudo packets. */ yading@11: /*@{*/ yading@11: SC_PKT_CANCEL = -1, yading@11: SC_PKT_NO_DATA = -2, yading@11: /*@}*/ yading@11: yading@11: /** Data packets. */ yading@11: /*@{*/ yading@11: SC_PKT_ASF_HEADER = 0x010000,// make it bigger than 0xFF in case of yading@11: SC_PKT_ASF_MEDIA = 0x010001,// receiving false data packets. yading@11: /*@}*/ yading@11: } MMSSCPacketType; yading@11: yading@11: typedef struct { yading@11: MMSContext mms; yading@11: int outgoing_packet_seq; ///< Outgoing packet sequence number. yading@11: char path[256]; ///< Path of the resource being asked for. yading@11: char host[128]; ///< Host of the resources. yading@11: int incoming_packet_seq; ///< Incoming packet sequence number. yading@11: int incoming_flags; ///< Incoming packet flags. yading@11: int packet_id; ///< Identifier for packets in the current stream. yading@11: unsigned int header_packet_id; ///< default is 2. yading@11: } MMSTContext; yading@11: yading@11: /** Create MMST command packet header */ yading@11: static void start_command_packet(MMSTContext *mmst, MMSCSPacketType packet_type) yading@11: { yading@11: MMSContext *mms = &mmst->mms; yading@11: mms->write_out_ptr = mms->out_buffer; yading@11: yading@11: bytestream_put_le32(&mms->write_out_ptr, 1); // start sequence yading@11: bytestream_put_le32(&mms->write_out_ptr, 0xb00bface); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); // Length starts from after the protocol type bytes yading@11: bytestream_put_le32(&mms->write_out_ptr, MKTAG('M','M','S',' ')); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: bytestream_put_le32(&mms->write_out_ptr, mmst->outgoing_packet_seq++); yading@11: bytestream_put_le64(&mms->write_out_ptr, 0); // timestamp yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: bytestream_put_le16(&mms->write_out_ptr, packet_type); yading@11: bytestream_put_le16(&mms->write_out_ptr, 3); // direction to server yading@11: } yading@11: yading@11: /** Add prefixes to MMST command packet. */ yading@11: static void insert_command_prefixes(MMSContext *mms, yading@11: uint32_t prefix1, uint32_t prefix2) yading@11: { yading@11: bytestream_put_le32(&mms->write_out_ptr, prefix1); // first prefix yading@11: bytestream_put_le32(&mms->write_out_ptr, prefix2); // second prefix yading@11: } yading@11: yading@11: /** Send a prepared MMST command packet. */ yading@11: static int send_command_packet(MMSTContext *mmst) yading@11: { yading@11: MMSContext *mms = &mmst->mms; yading@11: int len= mms->write_out_ptr - mms->out_buffer; yading@11: int exact_length = FFALIGN(len, 8); yading@11: int first_length= exact_length - 16; yading@11: int len8= first_length/8; yading@11: int write_result; yading@11: yading@11: // update packet length fields. yading@11: AV_WL32(mms->out_buffer + 8, first_length); yading@11: AV_WL32(mms->out_buffer + 16, len8); yading@11: AV_WL32(mms->out_buffer + 32, len8-2); yading@11: memset(mms->write_out_ptr, 0, exact_length - len); yading@11: yading@11: // write it out. yading@11: write_result= ffurl_write(mms->mms_hd, mms->out_buffer, exact_length); yading@11: if(write_result != exact_length) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Failed to write data of length %d: %d (%s)\n", yading@11: exact_length, write_result, yading@11: write_result < 0 ? strerror(AVUNERROR(write_result)) : yading@11: "The server closed the connection"); yading@11: return AVERROR(EIO); yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static void mms_put_utf16(MMSContext *mms, const uint8_t *src) yading@11: { yading@11: AVIOContext bic; yading@11: int size = mms->write_out_ptr - mms->out_buffer; yading@11: int len; yading@11: ffio_init_context(&bic, mms->write_out_ptr, yading@11: sizeof(mms->out_buffer) - size, 1, NULL, NULL, NULL, NULL); yading@11: yading@11: len = avio_put_str16le(&bic, src); yading@11: mms->write_out_ptr += len; yading@11: } yading@11: yading@11: static int send_time_test_data(MMSTContext *mmst) yading@11: { yading@11: start_command_packet(mmst, CS_PKT_TIMING_DATA_REQUEST); yading@11: insert_command_prefixes(&mmst->mms, 0x00f0f0f0, 0x0004000b); yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: static int send_protocol_select(MMSTContext *mmst) yading@11: { yading@11: char data_string[256]; yading@11: MMSContext *mms = &mmst->mms; yading@11: yading@11: start_command_packet(mmst, CS_PKT_PROTOCOL_SELECT); yading@11: insert_command_prefixes(mms, 0, 0xffffffff); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); // maxFunnelBytes yading@11: bytestream_put_le32(&mms->write_out_ptr, 0x00989680); // maxbitRate yading@11: bytestream_put_le32(&mms->write_out_ptr, 2); // funnelMode yading@11: snprintf(data_string, sizeof(data_string), "\\\\%d.%d.%d.%d\\%s\\%d", yading@11: (LOCAL_ADDRESS>>24)&0xff, yading@11: (LOCAL_ADDRESS>>16)&0xff, yading@11: (LOCAL_ADDRESS>>8)&0xff, yading@11: LOCAL_ADDRESS&0xff, yading@11: "TCP", // or UDP yading@11: LOCAL_PORT); yading@11: yading@11: mms_put_utf16(mms, data_string); yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: static int send_media_file_request(MMSTContext *mmst) yading@11: { yading@11: MMSContext *mms = &mmst->mms; yading@11: start_command_packet(mmst, CS_PKT_MEDIA_FILE_REQUEST); yading@11: insert_command_prefixes(mms, 1, 0xffffffff); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: mms_put_utf16(mms, mmst->path + 1); // +1 for skip "/" yading@11: yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: static void handle_packet_stream_changing_type(MMSTContext *mmst) yading@11: { yading@11: MMSContext *mms = &mmst->mms; yading@11: av_dlog(NULL, "Stream changing!\n"); yading@11: yading@11: // 40 is the packet header size, 7 is the prefix size. yading@11: mmst->header_packet_id= AV_RL32(mms->in_buffer + 40 + 7); yading@11: av_dlog(NULL, "Changed header prefix to 0x%x", mmst->header_packet_id); yading@11: } yading@11: yading@11: static int send_keepalive_packet(MMSTContext *mmst) yading@11: { yading@11: // respond to a keepalive with a keepalive... yading@11: start_command_packet(mmst, CS_PKT_KEEPALIVE); yading@11: insert_command_prefixes(&mmst->mms, 1, 0x100FFFF); yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: /** Pad media packets smaller than max_packet_size and/or adjust read position yading@11: * after a seek. */ yading@11: static void pad_media_packet(MMSContext *mms) yading@11: { yading@11: if(mms->remaining_in_lenasf_packet_len) { yading@11: int padding_size = mms->asf_packet_len - mms->remaining_in_len; yading@11: memset(mms->in_buffer + mms->remaining_in_len, 0, padding_size); yading@11: mms->remaining_in_len += padding_size; yading@11: } yading@11: } yading@11: yading@11: /** Read incoming MMST media, header or command packet. */ yading@11: static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) yading@11: { yading@11: int read_result; yading@11: MMSSCPacketType packet_type= -1; yading@11: MMSContext *mms = &mmst->mms; yading@11: for(;;) { yading@11: read_result = ffurl_read_complete(mms->mms_hd, mms->in_buffer, 8); yading@11: if (read_result != 8) { yading@11: if(read_result < 0) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Error reading packet header: %d (%s)\n", yading@11: read_result, strerror(AVUNERROR(read_result))); yading@11: packet_type = SC_PKT_CANCEL; yading@11: } else { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "The server closed the connection\n"); yading@11: packet_type = SC_PKT_NO_DATA; yading@11: } yading@11: return packet_type; yading@11: } yading@11: yading@11: // handle command packet. yading@11: if(AV_RL32(mms->in_buffer + 4)==0xb00bface) { yading@11: int length_remaining, hr; yading@11: yading@11: mmst->incoming_flags= mms->in_buffer[3]; yading@11: read_result= ffurl_read_complete(mms->mms_hd, mms->in_buffer+8, 4); yading@11: if(read_result != 4) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Reading command packet length failed: %d (%s)\n", yading@11: read_result, yading@11: read_result < 0 ? strerror(AVUNERROR(read_result)) : yading@11: "The server closed the connection"); yading@11: return read_result < 0 ? read_result : AVERROR(EIO); yading@11: } yading@11: yading@11: length_remaining= AV_RL32(mms->in_buffer+8) + 4; yading@11: av_dlog(NULL, "Length remaining is %d\n", length_remaining); yading@11: // read the rest of the packet. yading@11: if (length_remaining < 0 yading@11: || length_remaining > sizeof(mms->in_buffer) - 12) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Incoming packet length %d exceeds bufsize %zu\n", yading@11: length_remaining, sizeof(mms->in_buffer) - 12); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: read_result = ffurl_read_complete(mms->mms_hd, mms->in_buffer + 12, yading@11: length_remaining) ; yading@11: if (read_result != length_remaining) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Reading pkt data (length=%d) failed: %d (%s)\n", yading@11: length_remaining, read_result, yading@11: read_result < 0 ? strerror(AVUNERROR(read_result)) : yading@11: "The server closed the connection"); yading@11: return read_result < 0 ? read_result : AVERROR(EIO); yading@11: } yading@11: packet_type= AV_RL16(mms->in_buffer+36); yading@11: if (read_result >= 44 && (hr = AV_RL32(mms->in_buffer + 40))) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Server sent a message with packet type 0x%x and error status code 0x%08x\n", packet_type, hr); yading@11: return AVERROR(EINVAL); yading@11: } yading@11: } else { yading@11: int length_remaining; yading@11: int packet_id_type; yading@11: int tmp; yading@11: yading@11: // note we cache the first 8 bytes, yading@11: // then fill up the buffer with the others yading@11: tmp = AV_RL16(mms->in_buffer + 6); yading@11: length_remaining = (tmp - 8) & 0xffff; yading@11: mmst->incoming_packet_seq = AV_RL32(mms->in_buffer); yading@11: packet_id_type = mms->in_buffer[4]; yading@11: mmst->incoming_flags = mms->in_buffer[5]; yading@11: yading@11: if (length_remaining < 0 yading@11: || length_remaining > sizeof(mms->in_buffer) - 8) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Data length %d is invalid or too large (max=%zu)\n", yading@11: length_remaining, sizeof(mms->in_buffer)); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: mms->remaining_in_len = length_remaining; yading@11: mms->read_in_ptr = mms->in_buffer; yading@11: read_result= ffurl_read_complete(mms->mms_hd, mms->in_buffer, length_remaining); yading@11: if(read_result != length_remaining) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Failed to read packet data of size %d: %d (%s)\n", yading@11: length_remaining, read_result, yading@11: read_result < 0 ? strerror(AVUNERROR(read_result)) : yading@11: "The server closed the connection"); yading@11: return read_result < 0 ? read_result : AVERROR(EIO); yading@11: } yading@11: yading@11: // if we successfully read everything. yading@11: if(packet_id_type == mmst->header_packet_id) { yading@11: packet_type = SC_PKT_ASF_HEADER; yading@11: // Store the asf header yading@11: if(!mms->header_parsed) { yading@11: void *p = av_realloc(mms->asf_header, yading@11: mms->asf_header_size + mms->remaining_in_len); yading@11: if (!p) { yading@11: av_freep(&mms->asf_header); yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: mms->asf_header = p; yading@11: memcpy(mms->asf_header + mms->asf_header_size, yading@11: mms->read_in_ptr, mms->remaining_in_len); yading@11: mms->asf_header_size += mms->remaining_in_len; yading@11: } yading@11: // 0x04 means asf header is sent in multiple packets. yading@11: if (mmst->incoming_flags == 0x04) yading@11: continue; yading@11: } else if(packet_id_type == mmst->packet_id) { yading@11: packet_type = SC_PKT_ASF_MEDIA; yading@11: } else { yading@11: av_dlog(NULL, "packet id type %d is old.", packet_id_type); yading@11: continue; yading@11: } yading@11: } yading@11: yading@11: // preprocess some packet type yading@11: if(packet_type == SC_PKT_KEEPALIVE) { yading@11: send_keepalive_packet(mmst); yading@11: continue; yading@11: } else if(packet_type == SC_PKT_STREAM_CHANGING) { yading@11: handle_packet_stream_changing_type(mmst); yading@11: } else if(packet_type == SC_PKT_ASF_MEDIA) { yading@11: pad_media_packet(mms); yading@11: } yading@11: return packet_type; yading@11: } yading@11: } yading@11: yading@11: static int mms_safe_send_recv(MMSTContext *mmst, yading@11: int (*send_fun)(MMSTContext *mmst), yading@11: const MMSSCPacketType expect_type) yading@11: { yading@11: MMSSCPacketType type; yading@11: if(send_fun) { yading@11: int ret = send_fun(mmst); yading@11: if (ret < 0) { yading@11: av_dlog(NULL, "Send Packet error before expecting recv packet %d\n", expect_type); yading@11: return ret; yading@11: } yading@11: } yading@11: yading@11: if ((type = get_tcp_server_response(mmst)) != expect_type) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Corrupt stream (unexpected packet type 0x%x, expected 0x%x)\n", yading@11: type, expect_type); yading@11: return AVERROR_INVALIDDATA; yading@11: } else { yading@11: return 0; yading@11: } yading@11: } yading@11: yading@11: static int send_media_header_request(MMSTContext *mmst) yading@11: { yading@11: MMSContext *mms = &mmst->mms; yading@11: start_command_packet(mmst, CS_PKT_MEDIA_HEADER_REQUEST); yading@11: insert_command_prefixes(mms, 1, 0); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0x00800000); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: yading@11: // the media preroll value in milliseconds? yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0x40AC2000); yading@11: bytestream_put_le32(&mms->write_out_ptr, 2); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0); yading@11: yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: /** Send the initial handshake. */ yading@11: static int send_startup_packet(MMSTContext *mmst) yading@11: { yading@11: char data_string[256]; yading@11: MMSContext *mms = &mmst->mms; yading@11: // SubscriberName is defined in MS specification linked below. yading@11: // The guid value can be any valid value. yading@11: // http://download.microsoft.com/ yading@11: // download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/%5BMS-WMSP%5D.pdf yading@11: snprintf(data_string, sizeof(data_string), yading@11: "NSPlayer/7.0.0.1956; {%s}; Host: %s", yading@11: "7E667F5D-A661-495E-A512-F55686DDA178", mmst->host); yading@11: yading@11: start_command_packet(mmst, CS_PKT_INITIAL); yading@11: insert_command_prefixes(mms, 0, 0x0004000b); yading@11: bytestream_put_le32(&mms->write_out_ptr, 0x0003001c); yading@11: mms_put_utf16(mms, data_string); yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: /** Send MMST stream selection command based on the AVStream->discard values. */ yading@11: static int send_stream_selection_request(MMSTContext *mmst) yading@11: { yading@11: int i; yading@11: MMSContext *mms = &mmst->mms; yading@11: // send the streams we want back... yading@11: start_command_packet(mmst, CS_PKT_STREAM_ID_REQUEST); yading@11: bytestream_put_le32(&mms->write_out_ptr, mms->stream_num); // stream nums yading@11: for(i= 0; istream_num; i++) { yading@11: bytestream_put_le16(&mms->write_out_ptr, 0xffff); // flags yading@11: bytestream_put_le16(&mms->write_out_ptr, mms->streams[i].id); // stream id yading@11: bytestream_put_le16(&mms->write_out_ptr, 0); // selection yading@11: } yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: static int send_close_packet(MMSTContext *mmst) yading@11: { yading@11: start_command_packet(mmst, CS_PKT_STREAM_CLOSE); yading@11: insert_command_prefixes(&mmst->mms, 1, 1); yading@11: yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: /** Close the MMSH/MMST connection */ yading@11: static int mms_close(URLContext *h) yading@11: { yading@11: MMSTContext *mmst = (MMSTContext *)h->priv_data; yading@11: MMSContext *mms = &mmst->mms; yading@11: if(mms->mms_hd) { yading@11: send_close_packet(mmst); yading@11: ffurl_close(mms->mms_hd); yading@11: } yading@11: yading@11: /* free all separately allocated pointers in mms */ yading@11: av_free(mms->streams); yading@11: av_free(mms->asf_header); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int send_media_packet_request(MMSTContext *mmst) yading@11: { yading@11: MMSContext *mms = &mmst->mms; yading@11: start_command_packet(mmst, CS_PKT_START_FROM_PKT_ID); yading@11: insert_command_prefixes(mms, 1, 0x0001FFFF); yading@11: bytestream_put_le64(&mms->write_out_ptr, 0); // seek timestamp yading@11: bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); // unknown yading@11: bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); // packet offset yading@11: bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit yading@11: bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit yading@11: bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit yading@11: bytestream_put_byte(&mms->write_out_ptr, 0x00); // stream time limit flag yading@11: yading@11: mmst->packet_id++; // new packet_id yading@11: bytestream_put_le32(&mms->write_out_ptr, mmst->packet_id); yading@11: return send_command_packet(mmst); yading@11: } yading@11: yading@11: yading@11: static void clear_stream_buffers(MMSContext *mms) yading@11: { yading@11: mms->remaining_in_len = 0; yading@11: mms->read_in_ptr = mms->in_buffer; yading@11: } yading@11: yading@11: static int mms_open(URLContext *h, const char *uri, int flags) yading@11: { yading@11: MMSTContext *mmst = h->priv_data; yading@11: MMSContext *mms; yading@11: int port, err; yading@11: char tcpname[256]; yading@11: yading@11: h->is_streamed = 1; yading@11: mms = &mmst->mms; yading@11: yading@11: // only for MMS over TCP, so set proto = NULL yading@11: av_url_split(NULL, 0, NULL, 0, yading@11: mmst->host, sizeof(mmst->host), &port, mmst->path, yading@11: sizeof(mmst->path), uri); yading@11: yading@11: if(port<0) yading@11: port = 1755; // defaut mms protocol port yading@11: yading@11: // establish tcp connection. yading@11: ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, mmst->host, port, NULL); yading@11: err = ffurl_open(&mms->mms_hd, tcpname, AVIO_FLAG_READ_WRITE, yading@11: &h->interrupt_callback, NULL); yading@11: if (err) yading@11: goto fail; yading@11: yading@11: mmst->packet_id = 3; // default, initial value. yading@11: mmst->header_packet_id = 2; // default, initial value. yading@11: err = mms_safe_send_recv(mmst, send_startup_packet, SC_PKT_CLIENT_ACCEPTED); yading@11: if (err) yading@11: goto fail; yading@11: err = mms_safe_send_recv(mmst, send_time_test_data, SC_PKT_TIMING_TEST_REPLY); yading@11: if (err) yading@11: goto fail; yading@11: err = mms_safe_send_recv(mmst, send_protocol_select, SC_PKT_PROTOCOL_ACCEPTED); yading@11: if (err) yading@11: goto fail; yading@11: err = mms_safe_send_recv(mmst, send_media_file_request, SC_PKT_MEDIA_FILE_DETAILS); yading@11: if (err) yading@11: goto fail; yading@11: err = mms_safe_send_recv(mmst, send_media_header_request, SC_PKT_HEADER_REQUEST_ACCEPTED); yading@11: if (err) yading@11: goto fail; yading@11: err = mms_safe_send_recv(mmst, NULL, SC_PKT_ASF_HEADER); yading@11: if (err) yading@11: goto fail; yading@11: if((mmst->incoming_flags != 0X08) && (mmst->incoming_flags != 0X0C)) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "The server does not support MMST (try MMSH or RTSP)\n"); yading@11: err = AVERROR(EINVAL); yading@11: goto fail; yading@11: } yading@11: err = ff_mms_asf_header_parser(mms); yading@11: if (err) { yading@11: av_dlog(NULL, "asf header parsed failed!\n"); yading@11: goto fail; yading@11: } yading@11: mms->header_parsed = 1; yading@11: yading@11: if (!mms->asf_packet_len || !mms->stream_num) yading@11: goto fail; yading@11: yading@11: clear_stream_buffers(mms); yading@11: err = mms_safe_send_recv(mmst, send_stream_selection_request, SC_PKT_STREAM_ID_ACCEPTED); yading@11: if (err) yading@11: goto fail; yading@11: // send media packet request yading@11: err = mms_safe_send_recv(mmst, send_media_packet_request, SC_PKT_MEDIA_PKT_FOLLOWS); yading@11: if (err) { yading@11: goto fail; yading@11: } yading@11: av_dlog(NULL, "Leaving open (success)\n"); yading@11: return 0; yading@11: fail: yading@11: mms_close(h); yading@11: av_dlog(NULL, "Leaving open (failure: %d)\n", err); yading@11: return err; yading@11: } yading@11: yading@11: /** Read ASF data through the protocol. */ yading@11: static int mms_read(URLContext *h, uint8_t *buf, int size) yading@11: { yading@11: /* TODO: see tcp.c:tcp_read() about a possible timeout scheme */ yading@11: MMSTContext *mmst = h->priv_data; yading@11: MMSContext *mms = &mmst->mms; yading@11: int result = 0; yading@11: yading@11: do { yading@11: if(mms->asf_header_read_size < mms->asf_header_size) { yading@11: /* Read from ASF header buffer */ yading@11: result = ff_mms_read_header(mms, buf, size); yading@11: } else if(mms->remaining_in_len) { yading@11: /* Read remaining packet data to buffer. yading@11: * the result can not be zero because remaining_in_len is positive.*/ yading@11: result = ff_mms_read_data(mms, buf, size); yading@11: } else { yading@11: /* Read from network */ yading@11: int err = mms_safe_send_recv(mmst, NULL, SC_PKT_ASF_MEDIA); yading@11: if (err == 0) { yading@11: if(mms->remaining_in_len>mms->asf_packet_len) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Incoming pktlen %d is larger than ASF pktsize %d\n", yading@11: mms->remaining_in_len, mms->asf_packet_len); yading@11: result= AVERROR(EIO); yading@11: } else { yading@11: // copy the data to the packet buffer. yading@11: result = ff_mms_read_data(mms, buf, size); yading@11: if (result == 0) { yading@11: av_dlog(NULL, "Read ASF media packet size is zero!\n"); yading@11: break; yading@11: } yading@11: } yading@11: } else { yading@11: av_dlog(NULL, "read packet error!\n"); yading@11: break; yading@11: } yading@11: } yading@11: } while(!result); // only return one packet. yading@11: return result; yading@11: } yading@11: yading@11: URLProtocol ff_mmst_protocol = { yading@11: .name = "mmst", yading@11: .url_open = mms_open, yading@11: .url_read = mms_read, yading@11: .url_close = mms_close, yading@11: .priv_data_size = sizeof(MMSTContext), yading@11: .flags = URL_PROTOCOL_FLAG_NETWORK, yading@11: };