Mercurial > audlegacy
changeset 1407:b94847e68f33 trunk
[svn] Newer libmms, from their CVS. Needs to be hooked up properly as it wants more arguments to its functions.
author | chainsaw |
---|---|
date | Sat, 15 Jul 2006 16:39:37 -0700 |
parents | f40d0496fda5 |
children | 8accd819fa5e |
files | ChangeLog Plugins/Input/wma/libffwma/mms.c Plugins/Input/wma/libffwma/mms.h Plugins/Input/wma/libffwma/mmsio.h |
diffstat | 4 files changed, 1468 insertions(+), 611 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Sat Jul 15 16:05:21 2006 -0700 +++ b/ChangeLog Sat Jul 15 16:39:37 2006 -0700 @@ -1,3 +1,11 @@ +2006-07-15 23:05:21 +0000 Tony Vroon <chainsaw@gentoo.org> + revision [1726] + Volume fix (using a lookup table) from upstream CVS. + + Changes: Modified: + +16 -0 trunk/Plugins/Input/adplug/core/amd.cpp + + 2006-07-14 21:07:10 +0000 Giacomo Lozito <james@develia.org> revision [1724] - libconsole: added an option to ignore length information from spc tags
--- a/Plugins/Input/wma/libffwma/mms.c Sat Jul 15 16:05:21 2006 -0700 +++ b/Plugins/Input/wma/libffwma/mms.c Sat Jul 15 16:39:37 2006 -0700 @@ -1,14 +1,14 @@ -/* - * Copyright (C) 2000-2001 major mms +/* + * Copyright (C) 2002-2004 the xine project * - * This file is part of libmms + * This file is part of LibMMS, an MMS protocol handling library. * - * libmms is free software; you can redistribute it and/or modify + * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the ree Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * libmms is distributed in the hope that it will be useful, + * xine 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 General Public License for more details. @@ -17,18 +17,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * utility functions to handle communication with an mms server + * $Id: mms.c,v 1.21 2006/07/13 12:32:20 shawarma Exp $ + * + * MMS over TCP protocol + * based on work from major mms + * utility functions to handle communication with an mms server + * + * TODO: + * error messages + * enable seeking ! */ #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include <glib.h> - #include <unistd.h> #include <stdio.h> -#include <assert.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> @@ -38,776 +43,1509 @@ #include <fcntl.h> #include <errno.h> #include <stdlib.h> +#include <time.h> -#include "avcodec.h" +#if defined(HAVE_ICONV) && defined(HAVE_LANGINFO_CODESET) +#define USE_ICONV +#include <iconv.h> +#include <locale.h> +#include <langinfo.h> +#endif + +/********** logging **********/ +#define LOG_MODULE "mms" +#define LOG_VERBOSE +#ifdef DEBUG +# define lprintf printf +#else +# define lprintf(x...) +#endif + + #include "bswap.h" #include "mms.h" +#include "asfheader.h" +#include "uri.h" -#define LOG /* * mms specific types */ -#define MMS_PORT 1755 +#define MMST_PORT 1755 #define BUF_SIZE 102400 -#define CMD_HEADER_LEN 48 +#define CMD_HEADER_LEN 40 +#define CMD_PREFIX_LEN 8 #define CMD_BODY_LEN 1024 +#define ASF_HEADER_LEN 8192 + + +#define MMS_PACKET_ERR 0 +#define MMS_PACKET_COMMAND 1 +#define MMS_PACKET_ASF_HEADER 2 +#define MMS_PACKET_ASF_PACKET 3 + +#define ASF_HEADER_PACKET_ID_TYPE 2 +#define ASF_MEDIA_PACKET_ID_TYPE 4 + + +typedef struct mms_buffer_s mms_buffer_t; +struct mms_buffer_s { + uint8_t *buffer; + int pos; +}; + +typedef struct mms_packet_header_s mms_packet_header_t; +struct mms_packet_header_s { + uint32_t packet_len; + uint8_t flags; + uint8_t packet_id_type; + uint32_t packet_seq; +}; + + struct mms_s { + /* FIXME: de-xine-ification */ + void *custom_data; + int s; - + + /* url parsing */ + char *url; + char *proto; char *host; - char *path; - char *file; - char *url; + int port; + char *user; + char *password; + char *uri; /* command to send */ - char scmd[CMD_HEADER_LEN+CMD_BODY_LEN]; + char scmd[CMD_HEADER_LEN + CMD_BODY_LEN]; char *scmd_body; /* pointer to &scmd[CMD_HEADER_LEN] */ int scmd_len; /* num bytes written in header */ - + char str[1024]; /* scratch buffer to built strings */ - + /* receive buffer */ - char buf[BUF_SIZE]; + uint8_t buf[BUF_SIZE]; int buf_size; int buf_read; - - uint8_t asf_header[8192]; - int asf_header_len; - int asf_header_read; + + uint8_t asf_header[ASF_HEADER_LEN]; + uint32_t asf_header_len; + uint32_t asf_header_read; int seq_num; int num_stream_ids; - int stream_ids[20]; - int packet_length; - + int stream_ids[ASF_MAX_NUM_STREAMS]; + int stream_types[ASF_MAX_NUM_STREAMS]; + off_t start_packet_seq; /* for live streams != 0, need to keep it around */ + int need_discont; /* whether we need to set start_packet_seq */ + uint32_t asf_packet_len; + uint64_t file_len; + char guid[37]; + uint32_t bitrates[ASF_MAX_NUM_STREAMS]; + uint32_t bitrates_pos[ASF_MAX_NUM_STREAMS]; + int bandwidth; + + int has_audio; + int has_video; + int live_flag; + off_t current_pos; + int eos; }; -/* network/socket utility functions */ - -static int host_connect_attempt(struct in_addr ia, int port) { +static int fallback_io_select(void *data, int socket, int state, int timeout_msec) +{ + fd_set set; + struct timeval tv = { timeout_msec / 1000, (timeout_msec % 1000) * 1000}; + FD_ZERO(&set); + FD_SET(socket, &set); + return select(1, (state == MMS_IO_READ_READY) ? &set : NULL, + (state == MMS_IO_WRITE_READY) ? &set : NULL, NULL, &tv); +} - int s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - struct sockaddr_in sin; +static off_t fallback_io_read(void *data, int socket, char *buf, off_t num) +{ + off_t len = 0, ret; +/* lprintf("%d\n", fallback_io_select(data, socket, MMS_IO_READ_READY, 1000)); */ + errno = 0; + while (len < num) + { + ret = (off_t)read(socket, buf + len, num - len); + if(ret == 0) + break; /* EOF */ + if(ret < 0) + switch(errno) + { + case EAGAIN: + lprintf("len == %lld\n", (long long int) len); + break; + default: + lprintf("len == %lld\n", (long long int) len); + perror(NULL); + /* if already read something, return it, we will fail next time */ + return len ? len : ret; + } + len += ret; + } + lprintf("ret len == %lld\nnum == %lld\n", (long long int) len, (long long int) num); + return len; +} + +static off_t fallback_io_write(void *data, int socket, char *buf, off_t num) +{ + return (off_t)write(socket, buf, num); +} + +static int fallback_io_tcp_connect(void *data, const char *host, int port) +{ - if (s==-1) { - printf ("libmms: socket(): %s\n", strerror(errno)); + struct hostent *h; + int i, s; + + h = gethostbyname(host); + if (h == NULL) { +/* fprintf(stderr, "unable to resolve host: %s\n", host); */ + return -1; + } + + s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) { +/* fprintf(stderr, "failed to create socket: %s", strerror(errno)); */ + return -1; + } + + if (fcntl (s, F_SETFL, fcntl (s, F_GETFL) & ~O_NONBLOCK) == -1) { +/* _x_message(stream, XINE_MSG_CONNECTION_REFUSED, "can't put socket in non-blocking mode", strerror(errno), NULL); */ return -1; } - sin.sin_family = AF_INET; - sin.sin_addr = ia; - sin.sin_port = htons(port); - - if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 - && errno != EINPROGRESS) { - printf ("libmms: connect(): %s\n", strerror(errno)); - close(s); - return -1; - } - - return s; -} - -static int host_connect(const char *host, int port) { + for (i = 0; h->h_addr_list[i]; i++) { + struct in_addr ia; + struct sockaddr_in sin; + + memcpy (&ia, h->h_addr_list[i], 4); + sin.sin_family = AF_INET; + sin.sin_addr = ia; + sin.sin_port = htons(port); + + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) ==-1 && errno != EINPROGRESS) { - struct hostent *h; - int i, s; - - h=gethostbyname(host); - if (h==NULL) { - printf ("libmms: unable to resolve '%s'.\n", host); - return -1; + /* FIXME: de-xine-ification */ +/* _x_message(stream, XINE_MSG_CONNECTION_REFUSED, strerror(errno), NULL); */ + close(s); + continue; + } + + return s; } - - for (i=0; h->h_addr_list[i]; i++) { - struct in_addr ia; - memcpy (&ia, h->h_addr_list[i],4); - s = host_connect_attempt(ia, port); - if(s != -1) - return s; - } - printf ("libmms: unable to connect to '%s'.\n", host); return -1; } -static void put_32 (mms_t *this, uint32_t value) { + +static mms_io_t fallback_io = + { + &fallback_io_select, + NULL, + &fallback_io_read, + NULL, + &fallback_io_write, + NULL, + &fallback_io_tcp_connect, + NULL, + }; + +static mms_io_t default_io = { + &fallback_io_select, + NULL, + &fallback_io_read, + NULL, + &fallback_io_write, + NULL, + &fallback_io_tcp_connect, + NULL, + }; + + +#define io_read(io, args...) ((io) ? (io)->read(io->read_data , ## args) : default_io.read(NULL , ## args)) +#define io_write(io, args...) ((io) ? (io)->write(io->write_data , ## args) : default_io.write(NULL , ## args)) +#define io_select(io, args...) ((io) ? (io)->select(io->select_data , ## args) : default_io.select(NULL , ## args)) +#define io_connect(io, args...) ((io) ? (io)->connect(io->connect_data , ## args) : default_io.connect(NULL , ## args)) + +const mms_io_t* mms_get_default_io_impl() +{ + return &default_io; +} - this->scmd[this->scmd_len ] = value % 256; - value = value >> 8; - this->scmd[this->scmd_len+1] = value % 256 ; - value = value >> 8; - this->scmd[this->scmd_len+2] = value % 256 ; - value = value >> 8; - this->scmd[this->scmd_len+3] = value % 256 ; +void mms_set_default_io_impl(const mms_io_t *io) +{ + if(io->select) + { + default_io.select = io->select; + default_io.select_data = io->select_data; + } else + { + default_io.select = fallback_io.select; + default_io.select_data = fallback_io.select_data; + } + if(io->read) + { + default_io.read = io->read; + default_io.read_data = io->read_data; + } else + { + default_io.read = fallback_io.read; + default_io.read_data = fallback_io.read_data; + } + if(io->write) + { + default_io.write = io->write; + default_io.write_data = io->write_data; + } else + { + default_io.write = fallback_io.write; + default_io.write_data = fallback_io.write_data; + } + if(io->connect) + { + default_io.connect = io->connect; + default_io.connect_data = io->connect_data; + } else + { + default_io.connect = fallback_io.connect; + default_io.connect_data = fallback_io.connect_data; + } +} - this->scmd_len += 4; +static void mms_buffer_init (mms_buffer_t *mms_buffer, uint8_t *buffer) { + mms_buffer->buffer = buffer; + mms_buffer->pos = 0; +} + +static void mms_buffer_put_8 (mms_buffer_t *mms_buffer, uint8_t value) { + + mms_buffer->buffer[mms_buffer->pos] = value & 0xff; + + mms_buffer->pos += 1; } -static int send_data (int s, char *buf, int len) { - int total; +#if 0 +static void mms_buffer_put_16 (mms_buffer_t *mms_buffer, uint16_t value) { + + mms_buffer->buffer[mms_buffer->pos] = value & 0xff; + mms_buffer->buffer[mms_buffer->pos + 1] = (value >> 8) & 0xff; + + mms_buffer->pos += 2; +} +#endif - total=0; - while (total<len){ - int n; +static void mms_buffer_put_32 (mms_buffer_t *mms_buffer, uint32_t value) { + + mms_buffer->buffer[mms_buffer->pos] = value & 0xff; + mms_buffer->buffer[mms_buffer->pos + 1] = (value >> 8) & 0xff; + mms_buffer->buffer[mms_buffer->pos + 2] = (value >> 16) & 0xff; + mms_buffer->buffer[mms_buffer->pos + 3] = (value >> 24) & 0xff; + + mms_buffer->pos += 4; +} - n = write (s, &buf[total], len-total); - if (n > 0) - total += n; - else if (n<0 && errno!=EAGAIN) - return total; +static int get_guid (unsigned char *buffer, int offset) { + int i; + GUID g; + + g.Data1 = LE_32(buffer + offset); + g.Data2 = LE_16(buffer + offset + 4); + g.Data3 = LE_16(buffer + offset + 6); + for(i = 0; i < 8; i++) { + g.Data4[i] = buffer[offset + 8 + i]; } - return total; + + for (i = 1; i < GUID_END; i++) { + if (!memcmp(&g, &guids[i].guid, sizeof(GUID))) { + lprintf("GUID: %s\n", guids[i].name); + return i; + } + } + + lprintf("unknown GUID: 0x%x, 0x%x, 0x%x, " + "{ 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx }\n", + g.Data1, g.Data2, g.Data3, + g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], + g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]); + + return GUID_ERROR; } -static uint32_t get_32 (char *cmd, int offset) { - uint32_t ret; +static void print_command (char *data, int len) { - ret = cmd[offset] ; - ret |= cmd[offset+1]<<8 ; - ret |= cmd[offset+2]<<16 ; - ret |= cmd[offset+3]<<24 ; +#ifdef DEBUG + int i; + int dir = LE_32 (data + 36) >> 16; + int comm = LE_32 (data + 36) & 0xFFFF; - return ret; -} + lprintf ("----------------------------------------------\n"); + if (dir == 3) { + lprintf ("send command 0x%02x, %d bytes\n", comm, len); + } else { + lprintf ("receive command 0x%02x, %d bytes\n", comm, len); + } + lprintf (" start sequence %08x\n", LE_32 (data + 0)); + lprintf (" command id %08x\n", LE_32 (data + 4)); + lprintf (" length %8x \n", LE_32 (data + 8)); + lprintf (" protocol %08x\n", LE_32 (data + 12)); + lprintf (" len8 %8x \n", LE_32 (data + 16)); + lprintf (" sequence # %08x\n", LE_32 (data + 20)); + lprintf (" len8 (II) %8x \n", LE_32 (data + 32)); + lprintf (" dir | comm %08x\n", LE_32 (data + 36)); + if (len >= 4) + lprintf (" prefix1 %08x\n", LE_32 (data + 40)); + if (len >= 8) + lprintf (" prefix2 %08x\n", LE_32 (data + 44)); -static void send_command (mms_t *this, int command, uint32_t switches, - uint32_t extra, int length) { - - int len8; - int i; + for (i = (CMD_HEADER_LEN + CMD_PREFIX_LEN); i < (CMD_HEADER_LEN + CMD_PREFIX_LEN + len); i += 1) { + unsigned char c = data[i]; + + if ((c >= 32) && (c < 128)) + lprintf ("%c", c); + else + lprintf (" %02x ", c); + + } + if (len > CMD_HEADER_LEN) + lprintf ("\n"); + lprintf ("----------------------------------------------\n"); +#endif +} - len8 = (length + (length%8)) / 8; + + +static int send_command (mms_io_t *io, mms_t *this, int command, + uint32_t prefix1, uint32_t prefix2, + int length) { + int len8; + off_t n; + mms_buffer_t command_buffer; + + len8 = (length + 7) / 8; this->scmd_len = 0; - put_32 (this, 0x00000001); /* start sequence */ - put_32 (this, 0xB00BFACE); /* #-)) */ - put_32 (this, length + 32); - put_32 (this, 0x20534d4d); /* protocol type "MMS " */ - put_32 (this, len8 + 4); - put_32 (this, this->seq_num); this->seq_num++; - put_32 (this, 0x0); /* unknown */ - put_32 (this, 0x0); - put_32 (this, len8+2); - put_32 (this, 0x00030000 | command); /* dir | command */ - put_32 (this, switches); - put_32 (this, extra); + mms_buffer_init(&command_buffer, this->scmd); + mms_buffer_put_32 (&command_buffer, 0x00000001); /* start sequence */ + mms_buffer_put_32 (&command_buffer, 0xB00BFACE); /* #-)) */ + mms_buffer_put_32 (&command_buffer, len8 * 8 + 32); + mms_buffer_put_32 (&command_buffer, 0x20534d4d); /* protocol type "MMS " */ + mms_buffer_put_32 (&command_buffer, len8 + 4); + mms_buffer_put_32 (&command_buffer, this->seq_num); + this->seq_num++; + mms_buffer_put_32 (&command_buffer, 0x0); /* timestamp */ + mms_buffer_put_32 (&command_buffer, 0x0); + mms_buffer_put_32 (&command_buffer, len8 + 2); + mms_buffer_put_32 (&command_buffer, 0x00030000 | command); /* dir | command */ + /* end of the 40 byte command header */ + + mms_buffer_put_32 (&command_buffer, prefix1); + mms_buffer_put_32 (&command_buffer, prefix2); + + if (length & 7) + memset(this->scmd + length + CMD_HEADER_LEN + CMD_PREFIX_LEN, 0, 8 - (length & 7)); + + n = io_write(io, this->s, this->scmd, len8 * 8 + CMD_HEADER_LEN + CMD_PREFIX_LEN); + if (n != (len8 * 8 + CMD_HEADER_LEN + CMD_PREFIX_LEN)) { + return 0; + } + + print_command (this->scmd, length); + + return 1; +} + +#ifdef USE_ICONV +static iconv_t string_utf16_open() { + return iconv_open("UTF-16LE", nl_langinfo(CODESET)); +} - /* memcpy (&cmd->buf[48], data, length); */ +static void string_utf16_close(iconv_t url_conv) { + if (url_conv != (iconv_t)-1) { + iconv_close(url_conv); + } +} + +static void string_utf16(iconv_t url_conv, char *dest, char *src, int len) { + memset(dest, 0, 2 * len); + + if (url_conv == (iconv_t)-1) { + int i; - if (send_data (this->s, this->scmd, length+48) != (length+48)) { - printf ("libmms: send error\n"); + for (i = 0; i < len; i++) { + dest[i * 2] = src[i]; + dest[i * 2 + 1] = 0; + } + dest[i * 2] = 0; + dest[i * 2 + 1] = 0; + } + else { + size_t len1, len2; + char *ip, *op; + + len1 = len; len2 = 1000; + ip = src; op = dest; + iconv(url_conv, &ip, &len1, &op, &len2); + } +} + +#else +static void string_utf16(int unused, char *dest, char *src, int len) { + int i; + + memset (dest, 0, 2 * len); + + for (i = 0; i < len; i++) { + dest[i * 2] = src[i]; + dest[i * 2 + 1] = 0; } -#ifdef LOG - printf ("\nlibmms: ***************************************************\ncommand sent, %d bytes\n", length+48); + dest[i * 2] = 0; + dest[i * 2 + 1] = 0; +} +#endif + - printf ("start sequence %08x\n", get_32 (this->scmd, 0)); - printf ("command id %08x\n", get_32 (this->scmd, 4)); - printf ("length %8x \n", get_32 (this->scmd, 8)); - printf ("len8 %8x \n", get_32 (this->scmd, 16)); - printf ("sequence # %08x\n", get_32 (this->scmd, 20)); - printf ("len8 (II) %8x \n", get_32 (this->scmd, 32)); - printf ("dir | comm %08x\n", get_32 (this->scmd, 36)); - printf ("switches %08x\n", get_32 (this->scmd, 40)); +/* + * return packet type + */ +static int get_packet_header (mms_io_t *io, mms_t *this, mms_packet_header_t *header) { + size_t len; + int packet_type; - printf ("ascii contents>"); - for (i=48; i<(length+48); i+=2) { - unsigned char c = this->scmd[i]; + header->packet_len = 0; + header->packet_seq = 0; + header->flags = 0; + header->packet_id_type = 0; + len = io_read(io, this->s, this->buf, 8); + if (len != 8) + goto error; - if ((c>=32) && (c<=128)) - printf ("%c", c); - else - printf ("."); + if (LE_32(this->buf + 4) == 0xb00bface) { + /* command packet */ + header->flags = this->buf[3]; + len = io_read(io, this->s, this->buf + 8, 4); + if (len != 4) + goto error; + + header->packet_len = LE_32(this->buf + 8) + 4; + if (header->packet_len > BUF_SIZE - 12) { + header->packet_len = 0; + goto error; + } + lprintf("mms command\n"); + packet_type = MMS_PACKET_COMMAND; + } else { + header->packet_seq = LE_32(this->buf); + header->packet_id_type = this->buf[4]; + header->flags = this->buf[5]; + header->packet_len = (LE_16(this->buf + 6) - 8) & 0xffff; + if (header->packet_id_type == ASF_HEADER_PACKET_ID_TYPE) { + lprintf("asf header\n"); + packet_type = MMS_PACKET_ASF_HEADER; + } else { + lprintf("asf packet\n"); + packet_type = MMS_PACKET_ASF_PACKET; + } } - printf ("\n"); - - printf ("libmms: complete hexdump of package follows:\n"); - for (i=0; i<(length+48); i++) { - unsigned char c = this->scmd[i]; + + return packet_type; + +error: + lprintf("read error, len=%d\n", len); + perror("Could not read packet header"); + return MMS_PACKET_ERR; +} - printf ("%02x", c); + +static int get_packet_command (mms_io_t *io, mms_t *this, uint32_t packet_len) { + - if ((i % 16) == 15) - printf ("\nlibmms: "); + int command = 0; + size_t len; + + /* always enter this loop */ + lprintf("packet_len: %d bytes\n", packet_len); + + len = io_read(io, this->s, this->buf + 12, packet_len) ; + if (len != packet_len) { + return 0; + } - if ((i % 2) == 1) - printf (" "); + print_command (this->buf, len); + + /* check protocol type ("MMS ") */ + if (LE_32(this->buf + 12) != 0x20534D4D) { + lprintf("unknown protocol type: %c%c%c%c (0x%08X)\n", + this->buf[12], this->buf[13], this->buf[14], this->buf[15], + LE_32(this->buf + 12)); + return 0; + } - } - printf ("\n"); -#endif + command = LE_32 (this->buf + 36) & 0xFFFF; + lprintf("command = 0x%2x\n", command); + + return command; } -static void string_utf16(char *dest, char *src, int len) { - int i; - - memset (dest, 0, 1000); +static int get_answer (mms_io_t *io, mms_t *this) { + int command = 0; + mms_packet_header_t header; - for (i=0; i<len; i++) { - dest[i*2] = src[i]; - dest[i*2+1] = 0; + switch (get_packet_header (io, this, &header)) { + case MMS_PACKET_ERR: + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: failed to read mms packet header\n"); + break; + case MMS_PACKET_COMMAND: + command = get_packet_command (io, this, header.packet_len); + + if (command == 0x1b) { + + if (!send_command (io, this, 0x1b, 0, 0, 0)) { + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: failed to send command\n"); + return 0; + } + /* FIXME: limit recursion */ + command = get_answer (io, this); + } + break; + case MMS_PACKET_ASF_HEADER: + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: unexpected asf header packet\n"); + break; + case MMS_PACKET_ASF_PACKET: + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " +"libmms: unexpected asf packet\n"); + break; } - - dest[i*2] = 0; - dest[i*2+1] = 0; + + return command; } -static void print_answer (char *data, int len) { + +static int get_asf_header (mms_io_t *io, mms_t *this) { + + off_t len; + int stop = 0; + + this->asf_header_read = 0; + this->asf_header_len = 0; + + while (!stop) { + mms_packet_header_t header; + int command; -#ifdef LOG + switch (get_packet_header (io, this, &header)) { + case MMS_PACKET_ERR: + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: failed to read mms packet header\n"); + return 0; + break; + case MMS_PACKET_COMMAND: + command = get_packet_command (io, this, header.packet_len); + + if (command == 0x1b) { + + if (!send_command (io, this, 0x1b, 0, 0, 0)) { + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: failed to send command\n"); + return 0; + } + command = get_answer (io, this); + } else { + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: unexpected command packet\n"); + } + break; + case MMS_PACKET_ASF_HEADER: + case MMS_PACKET_ASF_PACKET: + if (header.packet_len + this->asf_header_len > ASF_HEADER_LEN) { + lprintf( "***LOG:*** -- " + "libmms: asf packet too large\n"); + return 0; + } + len = io_read(io, this->s, + this->asf_header + this->asf_header_len, header.packet_len); + if (len != header.packet_len) { + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: get_asf_header failed\n"); + return 0; + } + this->asf_header_len += header.packet_len; + lprintf("header flags: %d\n", header.flags); + if ((header.flags == 0X08) || (header.flags == 0X0C)) + stop = 1; + break; + } + } + lprintf ("get header packet succ\n"); + return 1; +} + +static void interp_asf_header (mms_t *this) { + int i; - printf ("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\nanswer received, %d bytes\n", len); + this->asf_packet_len = 0; + this->num_stream_ids = 0; + /* + * parse header + */ + + i = 30; + while (i < this->asf_header_len) { + + int guid; + uint64_t length; + + guid = get_guid(this->asf_header, i); + i += 16; + + length = LE_64(this->asf_header + i); + i += 8; + + switch (guid) { + + case GUID_ASF_FILE_PROPERTIES: - printf ("start sequence %08x\n", get_32 (data, 0)); - printf ("command id %08x\n", get_32 (data, 4)); - printf ("length %8x \n", get_32 (data, 8)); - printf ("len8 %8x \n", get_32 (data, 16)); - printf ("sequence # %08x\n", get_32 (data, 20)); - printf ("len8 (II) %8x \n", get_32 (data, 32)); - printf ("dir | comm %08x\n", get_32 (data, 36)); - printf ("switches %08x\n", get_32 (data, 40)); + this->asf_packet_len = LE_32(this->asf_header + i + 92 - 24); + if (this->asf_packet_len > BUF_SIZE) { + this->asf_packet_len = 0; + lprintf( "***LOG:*** -- " + "libmms: asf packet len too large\n"); + break; + } + this->file_len = LE_64(this->asf_header + i + 40 - 24); + lprintf ("file object, packet length = %d (%d)\n", + this->asf_packet_len, LE_32(this->asf_header + i + 96 - 24)); + break; - for (i=48; i<len; i+=2) { - unsigned char c = data[i]; + case GUID_ASF_STREAM_PROPERTIES: + { + uint16_t flags; + uint16_t stream_id; + int type; + int encrypted; + + guid = get_guid(this->asf_header, i); + switch (guid) { + case GUID_ASF_AUDIO_MEDIA: + type = ASF_STREAM_TYPE_AUDIO; + this->has_audio = 1; + break; - if ((c>=32) && (c<128)) - printf ("%c", c); - else - printf (" %02x ", c); - - } - printf ("\n"); -#endif + case GUID_ASF_VIDEO_MEDIA: + case GUID_ASF_JFIF_MEDIA: + case GUID_ASF_DEGRADABLE_JPEG_MEDIA: + type = ASF_STREAM_TYPE_VIDEO; + this->has_video = 1; + break; + + case GUID_ASF_COMMAND_MEDIA: + type = ASF_STREAM_TYPE_CONTROL; + break; + + default: + type = ASF_STREAM_TYPE_UNKNOWN; + } -} + flags = LE_16(this->asf_header + i + 48); + stream_id = flags & 0x7F; + encrypted = flags >> 15; + + lprintf ("stream object, stream id: %d, type: %d, encrypted: %d\n", + stream_id, type, encrypted); -static void get_answer (mms_t *this) { + if (this->num_stream_ids < ASF_MAX_NUM_STREAMS && stream_id < ASF_MAX_NUM_STREAMS) { + this->stream_types[stream_id] = type; + this->stream_ids[this->num_stream_ids] = stream_id; + this->num_stream_ids++; + } else { + lprintf ("too many streams, skipping\n"); + } + } + break; - int command = 0x1b; + case GUID_ASF_STREAM_BITRATE_PROPERTIES: + { + uint16_t streams = LE_16(this->asf_header + i); + uint16_t stream_id; + int j; - while (command == 0x1b) { - int len; + lprintf ("stream bitrate properties\n"); + lprintf ("streams %d\n", streams); - len = read (this->s, this->buf, BUF_SIZE) ; - if (!len) { - printf ("\nalert! eof\n"); - return; + for(j = 0; j < streams; j++) { + stream_id = LE_16(this->asf_header + i + 2 + j * 6); + lprintf ("stream id %d\n", stream_id); + this->bitrates[stream_id] = LE_32(this->asf_header + i + 4 + j * 6); + this->bitrates_pos[stream_id] = i + 4 + j * 6; + lprintf ("stream id %d, bitrate %d\n", stream_id, + this->bitrates[stream_id]); + } + } + break; + + default: + lprintf ("unknown object\n"); + break; } - print_answer (this->buf, len); + lprintf ("length : %lld\n", length); - command = get_32 (this->buf, 36) & 0xFFFF; - - if (command == 0x1b) - send_command (this, 0x1b, 0, 0, 0); + if (length > 24) { + i += length - 24; + } } } -static int receive (int s, char *buf, size_t count) { - - ssize_t len, total; - - total = 0; - - while (total < count) { - - len = read (s, &buf[total], count-total); - - if (len<0) { - perror ("read error:"); - return 0; - } - - total += len; - -#ifdef LOG - if (len != 0) { - printf ("[%d/%d]", total, count); - fflush (stdout); - } -#endif - - } - - return 1; +const static char *const mmst_proto_s[] = { "mms", "mmst", NULL }; -} - -static void get_header (mms_t *this) { - - char pre_header[8]; - int i; - - this->asf_header_len = 0; - - while (1) { +static int mmst_valid_proto (char *proto) { + int i = 0; - if (!receive (this->s, pre_header, 8)) { - printf ("libmms: pre-header read failed\n"); - return ; - } - -#ifdef LOG - for (i=0; i<8; i++) - printf ("libmms: pre_header[%d] = %02x (%d)\n", - i, pre_header[i], pre_header[i]); -#endif - - if (pre_header[4] == 0x02) { - - int packet_len; - - packet_len = (pre_header[7] << 8 | pre_header[6]) - 8; + lprintf("mmst_valid_proto\n"); -#ifdef LOG - printf ("libmms: asf header packet detected, len=%d\n", - packet_len); -#endif - - if (!receive (this->s, (char*)&this->asf_header[this->asf_header_len], packet_len)) { - printf ("libmms: header data read failed\n"); - return; - } - - this->asf_header_len += packet_len; - - if ( (this->asf_header[this->asf_header_len-1] == 1) - && (this->asf_header[this->asf_header_len-2]==1)) { - - printf ("libmms: get header packet finished\n"); - - return; - - } - - } else { - - int packet_len, command; + if (!proto) + return 0; - if (!receive (this->s, (char *) &packet_len, 4)) { - printf ("packet_len read failed\n"); - return; - } - - packet_len = get_32 ((char *)&packet_len, 0) + 4; - -#ifdef LOG - printf ("command packet detected, len=%d\n", - packet_len); -#endif - - if (!receive (this->s, this->buf, packet_len)) { - printf ("command data read failed\n"); - return ; - } - - command = get_32 (this->buf, 24) & 0xFFFF; - -#ifdef LOG - printf ("command: %02x\n", command); -#endif - - if (command == 0x1b) - send_command (this, 0x1b, 0, 0, 0); - + while(mmst_proto_s[i]) { + if (!strcasecmp(proto, mmst_proto_s[i])) { + return 1; } - - printf ("mms: get header packet succ\n"); + i++; } + return 0; } -static void interp_header (mms_t *this) { - - int i; - - this->packet_length = 0; - - /* - * parse header - */ +/* FIXME: de-xine-ification */ - i = 30; - while (i<this->asf_header_len) { - - uint64_t guid_1, guid_2, length; +/* static void report_progress (void *data, int p) { - guid_2 = (uint64_t)this->asf_header[i] | ((uint64_t)this->asf_header[i+1]<<8) - | ((uint64_t)this->asf_header[i+2]<<16) | ((uint64_t)this->asf_header[i+3]<<24) - | ((uint64_t)this->asf_header[i+4]<<32) | ((uint64_t)this->asf_header[i+5]<<40) - | ((uint64_t)this->asf_header[i+6]<<48) | ((uint64_t)this->asf_header[i+7]<<56); - i += 8; + xine_event_t event; + xine_progress_data_t prg; - guid_1 = (uint64_t)this->asf_header[i] | ((uint64_t)this->asf_header[i+1]<<8) - | ((uint64_t)this->asf_header[i+2]<<16) | ((uint64_t)this->asf_header[i+3]<<24) - | ((uint64_t)this->asf_header[i+4]<<32) | ((uint64_t)this->asf_header[i+5]<<40) - | ((uint64_t)this->asf_header[i+6]<<48) | ((uint64_t)this->asf_header[i+7]<<56); - i += 8; - -#ifdef LOG - printf ("guid found: %016llx%016llx\n", guid_1, guid_2); -#endif - - length = (uint64_t)this->asf_header[i] | ((uint64_t)this->asf_header[i+1]<<8) - | ((uint64_t)this->asf_header[i+2]<<16) | ((uint64_t)this->asf_header[i+3]<<24) - | ((uint64_t)this->asf_header[i+4]<<32) | ((uint64_t)this->asf_header[i+5]<<40) - | ((uint64_t)this->asf_header[i+6]<<48) | ((uint64_t)this->asf_header[i+7]<<56); - - i += 8; - - if ( (guid_1 == 0x6cce6200aa00d9a6) && (guid_2 == 0x11cf668e75b22630) ) { - printf ("header object\n"); - } else if ((guid_1 == 0x6cce6200aa00d9a6) && (guid_2 == 0x11cf668e75b22636)) { - printf ("data object\n"); - } else if ((guid_1 == 0x6553200cc000e48e) && (guid_2 == 0x11cfa9478cabdca1)) { - - this->packet_length = get_32((char*)this->asf_header, i+92-24); - -#ifdef LOG - printf ("file object, packet length = %d (%d)\n", - this->packet_length, get_32((char*)this->asf_header, i+96-24)); -#endif + prg.description = _("Connecting MMS server (over tcp)..."); + prg.percent = p; + + event.type = XINE_EVENT_PROGRESS; + event.data = &prg; + event.data_length = sizeof (xine_progress_data_t); + + xine_event_send (stream, &event); +} */ - } else if ((guid_1 == 0x6553200cc000e68e) && (guid_2 == 0x11cfa9b7b7dc0791)) { - - int stream_id = this->asf_header[i+48] | this->asf_header[i+49] << 8; - -#ifdef LOG - printf ("stream object, stream id: %d\n", stream_id); -#endif - - this->stream_ids[this->num_stream_ids] = stream_id; - this->num_stream_ids++; - - - /* - } else if ((guid_1 == 0x) && (guid_2 == 0x)) { - printf ("??? object\n"); - */ - } else { - printf ("unknown object\n"); - } - -#ifdef LOG - printf ("length : %lld\n", length); -#endif - - i += length-24; - - } -} - - -mms_t *mms_connect (const char *url_) { - - mms_t *this; - char *url, *host, *hostend; - char *path, *file; - int hostlen, s, len, i; - - /* parse url*/ - - if (strncasecmp (url_, "mms://",6)) { - printf ("libmms: invalid url >%s< (should be mms:// - style)\n", url_); - return NULL; - } - - url = strdup (url_); - - /* extract hostname */ - - hostend = strchr(&url[6],'/'); - if (!hostend) { - printf ("libmms: invalid url >%s<, failed to find hostend\n", url); - return NULL; - } - hostlen = hostend - url - 6; - host = av_malloc (hostlen+1); - strncpy (host, &url[6], hostlen); - host[hostlen]=0; - - /* extract path and file */ - - path = url+hostlen+7; - file = strrchr (url, '/'); +/* + * returns 1 on error + */ +static int mms_tcp_connect(mms_io_t *io, mms_t *this) { + int progress, res; + + if (!this->port) this->port = MMST_PORT; /* * try to connect */ - - s = host_connect (host, MMS_PORT); - if (s == -1) { - printf ("libmms: failed to connect\n"); - free (host); - free (url); - return NULL; + lprintf("try to connect to %s on port %d \n", this->host, this->port); + this->s = io_connect(io, this->host, this->port); + if (this->s == -1) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "failed to connect '%s'\n", this->host); + return 1; } - this = (mms_t*) av_malloc (sizeof (mms_t)); + /* connection timeout 15s */ + progress = 0; + do { + /*FIXME: de-xine-ification */ +/* report_progress(this->stream, progress); */ + res = io_select(io, this->s, MMS_IO_WRITE_READY, 500); + progress += 1; + } while ((res == MMS_IO_STATUS_TIMEOUT) && (progress < 30)); + if (res != MMS_IO_STATUS_READY) { + return 1; + } + lprintf ("connected\n"); + return 0; +} + +static void mms_gen_guid(char guid[]) { + static char digit[16] = "0123456789ABCDEF"; + int i = 0; + + srand(time(NULL)); + for (i = 0; i < 36; i++) { + guid[i] = digit[(int) ((16.0*rand())/(RAND_MAX+1.0))]; + } + guid[8] = '-'; guid[13] = '-'; guid[18] = '-'; guid[23] = '-'; + guid[36] = '\0'; +} + +/* + * return 0 on error + */ +int static mms_choose_best_streams(mms_io_t *io, mms_t *this) { + int i; + int video_stream = 0; + int audio_stream = 0; + int max_arate = 0; + int min_vrate = 0; + int min_bw_left = 0; + int stream_id; + int bandwitdh_left; + int res; + + /* command 0x33 */ + /* choose the best quality for the audio stream */ + /* i've never seen more than one audio stream */ + lprintf("num_stream_ids=%d\n", this->num_stream_ids); + for (i = 0; i < this->num_stream_ids; i++) { + stream_id = this->stream_ids[i]; + switch (this->stream_types[stream_id]) { + case ASF_STREAM_TYPE_AUDIO: + if (this->bitrates[stream_id] > max_arate) { + audio_stream = stream_id; + max_arate = this->bitrates[stream_id]; + } + break; + default: + break; + } + } + + /* choose a video stream adapted to the user bandwidth */ + bandwitdh_left = this->bandwidth - max_arate; + if (bandwitdh_left < 0) { + bandwitdh_left = 0; + } + lprintf("bandwitdh %d, left %d\n", this->bandwidth, bandwitdh_left); + + min_bw_left = bandwitdh_left; + for (i = 0; i < this->num_stream_ids; i++) { + stream_id = this->stream_ids[i]; + switch (this->stream_types[stream_id]) { + case ASF_STREAM_TYPE_VIDEO: + if (((bandwitdh_left - this->bitrates[stream_id]) < min_bw_left) && + (bandwitdh_left >= this->bitrates[stream_id])) { + video_stream = stream_id; + min_bw_left = bandwitdh_left - this->bitrates[stream_id]; + } + break; + default: + break; + } + } - this->url = url; - this->host = host; - this->path = path; - this->file = file; - this->s = s; + /* choose the lower bitrate of */ + if (!video_stream && this->has_video) { + for (i = 0; i < this->num_stream_ids; i++) { + stream_id = this->stream_ids[i]; + switch (this->stream_types[stream_id]) { + case ASF_STREAM_TYPE_VIDEO: + if ((this->bitrates[stream_id] < min_vrate) || + (!min_vrate)) { + video_stream = stream_id; + min_vrate = this->bitrates[stream_id]; + } + break; + default: + break; + } + } + } + + lprintf("selected streams: audio %d, video %d\n", audio_stream, video_stream); + lprintf("disabling other streams\n"); + memset (this->scmd_body, 0, 40); + for (i = 1; i < this->num_stream_ids; i++) { + this->scmd_body [ (i - 1) * 6 + 2 ] = 0xFF; + this->scmd_body [ (i - 1) * 6 + 3 ] = 0xFF; + this->scmd_body [ (i - 1) * 6 + 4 ] = this->stream_ids[i] ; + this->scmd_body [ (i - 1) * 6 + 5 ] = this->stream_ids[i] >> 8; + if ((this->stream_ids[i] == audio_stream) || + (this->stream_ids[i] == video_stream)) { + this->scmd_body [ (i - 1) * 6 + 6 ] = 0x00; + this->scmd_body [ (i - 1) * 6 + 7 ] = 0x00; + } else { + lprintf("disabling stream %d\n", this->stream_ids[i]); + this->scmd_body [ (i - 1) * 6 + 6 ] = 0x02; + this->scmd_body [ (i - 1) * 6 + 7 ] = 0x00; + + /* forces the asf demuxer to not choose this stream */ + if (this->bitrates_pos[this->stream_ids[i]]) { + this->asf_header[this->bitrates_pos[this->stream_ids[i]]] = 0; + this->asf_header[this->bitrates_pos[this->stream_ids[i]] + 1] = 0; + this->asf_header[this->bitrates_pos[this->stream_ids[i]] + 2] = 0; + this->asf_header[this->bitrates_pos[this->stream_ids[i]] + 3] = 0; + } + } + } + + if (!send_command (io, this, 0x33, this->num_stream_ids, + 0xFFFF | this->stream_ids[0] << 16, + this->num_stream_ids * 6 + 2)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: mms_choose_best_streams failed\n"); + return 0; + } + + if ((res = get_answer (io, this)) != 0x21) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: unexpected response: %02x (0x21)\n", res); + } + + return 1; +} + +/* + * TODO: error messages + * network timing request + */ +/* FIXME: got somewhat broken during xine_stream_t->(void*) conversion */ +mms_t *mms_connect (mms_io_t *io, void *data, const char *url, int bandwidth) { +#ifdef USE_ICONV + iconv_t url_conv; +#else + int url_conv = 0; +#endif + mms_t *this; + int res; + GURI *uri; + + if (!url) + return NULL; + + /* FIXME: needs proper error-signalling work */ + this = (mms_t*) malloc (sizeof (mms_t)); + + this->custom_data = data; + this->url = strdup (url); + this->s = -1; this->seq_num = 0; - this->scmd_body = &this->scmd[CMD_HEADER_LEN]; + this->scmd_body = this->scmd + CMD_HEADER_LEN + CMD_PREFIX_LEN; this->asf_header_len = 0; this->asf_header_read = 0; this->num_stream_ids = 0; - this->packet_length = 0; + this->asf_packet_len = 0; + this->start_packet_seq= 0; + this->need_discont = 1; this->buf_size = 0; this->buf_read = 0; + this->has_audio = 0; + this->has_video = 0; + this->bandwidth = bandwidth; + this->current_pos = 0; + this->eos = 0; + /* FIXME de-xine-ification */ +/* report_progress (stream, 0); */ + + uri = gnet_uri_new(this->url); + if(!uri) { + lprintf ("invalid url\n"); + goto fail; + } + this->proto = uri->scheme; + this->user = uri->user; + this->host = uri->hostname; + this->port = uri->port; + this->password = uri->passwd; + this->uri = uri->path; + + if (!mmst_valid_proto(this->proto)) { + lprintf ("unsupported protocol\n"); + goto fail; + } + + if (mms_tcp_connect(io, this)) { + goto fail; + } + /* FIXME de-xine-ification */ +/* report_progress (stream, 30); */ + +#ifdef USE_ICONV + url_conv = string_utf16_open(); +#endif /* * let the negotiations begin... */ - /* cmd1 */ + /* command 0x1 */ + lprintf("send command 0x01\n"); + mms_gen_guid(this->guid); + sprintf (this->str, "\x1c\x03NSPlayer/7.0.0.1956; {%s}; Host: %s", + this->guid, this->host); + string_utf16 (url_conv, this->scmd_body, this->str, strlen(this->str) + 2); + + if (!send_command (io, this, 1, 0, 0x0004000b, strlen(this->str) * 2 + 8)) { + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: failed to send command 0x01\n"); + goto fail; + } - sprintf (this->str, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", - this->host); - string_utf16 (this->scmd_body, this->str, strlen(this->str)+2); + if ((res = get_answer (io, this)) != 0x01) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: unexpected response: %02x (0x01)\n", res); + lprintf("answer: %d\n", res); + goto fail; + } + + /* FIXME de-xine-ification */ +/* report_progress (stream, 40); */ - send_command (this, 1, 0, 0x0004000b, strlen(this->str) * 2+8); + /* TODO: insert network timing request here */ + /* command 0x2 */ + lprintf("send command 0x02\n"); + string_utf16 (url_conv, &this->scmd_body[8], "\002\000\\\\192.168.0.129\\TCP\\1037\0000", 28); + memset (this->scmd_body, 0, 8); + if (!send_command (io, this, 2, 0, 0, 28 * 2 + 8)) { + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: failed to send command 0x02\n"); + + goto fail; + } - len = read (this->s, this->buf, BUF_SIZE) ; - if (len>0) - print_answer (this->buf, len); - else { - printf ("libmms: read failed: %s\n", strerror(errno)); - + switch (res = get_answer (io, this)) { + case 0x02: + /* protocol accepted */ + break; + case 0x03: + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: protocol failed\n"); + goto fail; + break; + default: + lprintf("unexpected response: %02x (0x02 or 0x03)\n", res); + goto fail; } - /* cmd2 */ - - string_utf16 (&this->scmd_body[8], "\002\000\\\\192.168.0.129\\TCP\\1037\0000", - 28); - memset (this->scmd_body, 0, 8); - send_command (this, 2, 0, 0, 28*2+8); - - len = read (this->s, this->buf, BUF_SIZE) ; - if (len) - print_answer (this->buf, len); - - /* 0x5 */ + /* FIXME de-xine-ification */ +/* report_progress (stream, 50); */ - string_utf16 (&this->scmd_body[8], path, strlen(path)); - memset (this->scmd_body, 0, 8); - send_command (this, 5, 0, 0, strlen(path)*2+12); - - get_answer (this); + /* command 0x5 */ + { + mms_buffer_t command_buffer; + + lprintf("send command 0x05\n"); + mms_buffer_init(&command_buffer, this->scmd_body); + mms_buffer_put_32 (&command_buffer, 0x00000000); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0x00000000); /* ?? */ + string_utf16 (url_conv, this->scmd_body + command_buffer.pos, this->uri, strlen(this->uri)); + if (!send_command (io, this, 5, 1, 0xffffffff, strlen(this->uri) * 2 + 12)) + goto fail; + } + + switch (res = get_answer (io, this)) { + case 0x06: + { + int xx, yy; + /* no authentication required */ + + /* Warning: sdp is not right here */ + xx = this->buf[62]; + yy = this->buf[63]; + this->live_flag = ((xx == 0) && ((yy & 0xf) == 2)); + lprintf("live: live_flag=%d, xx=%d, yy=%d\n", this->live_flag, xx, yy); + } + break; + case 0x1A: + /* authentication request, not yet supported */ + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: authentication request, not yet supported\n"); + goto fail; + break; + default: + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: unexpected response: %02x (0x06 or 0x1A)\n", res); + goto fail; + } - /* 0x15 */ + /* FIXME de-xine-ification */ +/* report_progress (stream, 60); */ - memset (this->scmd_body, 0, 40); - this->scmd_body[32] = 2; - - send_command (this, 0x15, 1, 0, 40); + /* command 0x15 */ + lprintf("send command 0x15\n"); + { + mms_buffer_t command_buffer; + mms_buffer_init(&command_buffer, this->scmd_body); + mms_buffer_put_32 (&command_buffer, 0x00000000); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0x00800000); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0x00000000); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0x00000000); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0x00000000); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0x00000000); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0x40AC2000); /* ?? */ + mms_buffer_put_32 (&command_buffer, ASF_HEADER_PACKET_ID_TYPE); /* Header Packet ID type */ + mms_buffer_put_32 (&command_buffer, 0x00000000); /* ?? */ + if (!send_command (io, this, 0x15, 1, 0, command_buffer.pos)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: failed to send command 0x15\n"); + goto fail; + } + } + + if ((res = get_answer (io, this)) != 0x11) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: unexpected response: %02x (0x11)\n", res); + goto fail; + } this->num_stream_ids = 0; - get_header (this); - interp_header (this); - - /* 0x33 */ - - memset (this->scmd_body, 0, 40); + if (!get_asf_header (io, this)) + goto fail; - for (i=1; i<this->num_stream_ids; i++) { - this->scmd_body [ (i-1) * 6 + 2 ] = 0xFF; - this->scmd_body [ (i-1) * 6 + 3 ] = 0xFF; - this->scmd_body [ (i-1) * 6 + 4 ] = this->stream_ids[i]; - this->scmd_body [ (i-1) * 6 + 5 ] = 0x00; - } - - send_command (this, 0x33, this->num_stream_ids, - 0xFFFF | this->stream_ids[0] << 16, - (this->num_stream_ids-1)*6+2); - - get_answer (this); - - /* 0x07 */ + interp_asf_header (this); - memset (this->scmd_body, 0, 40); - - for (i=8; i<16; i++) - this->scmd_body[i] = 0xFF; - - this->scmd_body[20] = 0x04; - - send_command (this, 0x07, 1, - 0xFFFF | this->stream_ids[0] << 16, - 24); + /* FIXME de-xine-ification */ +/* report_progress (stream, 70); */ - return this; -} - -static int get_media_packet (mms_t *this) { - - char pre_header[8]; - int i; - - if (!receive (this->s, pre_header, 8)) { - printf ("pre-header read failed\n"); - return 0; + if (!mms_choose_best_streams(io, this)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: mms_choose_best_streams failed"); + goto fail; } -#ifdef LOG - for (i=0; i<8; i++) - printf ("pre_header[%d] = %02x (%d)\n", - i, pre_header[i], pre_header[i]); -#endif - - if (pre_header[4] == 0x04) { - - int packet_len; - - packet_len = (pre_header[7] << 8 | pre_header[6]) - 8; - -#ifdef LOG - printf ("asf media packet detected, len=%d\n", - packet_len); -#endif - - if (!receive (this->s, this->buf, packet_len)) { - printf ("media data read failed\n"); - return 0; - } - - /* implicit padding (with "random" data)*/ - this->buf_size = this->packet_length; - - } else { - - int packet_len, command; - - if (!receive (this->s, (char *)&packet_len, 4)) { - printf ("packet_len read failed\n"); - return 0; - } + /* FIXME de-xine-ification */ +/* report_progress (stream, 80); */ - packet_len = get_32 ((char *)&packet_len, 0) + 4; - -#ifdef LOG - printf ("command packet detected, len=%d\n", - packet_len); -#endif - - if (!receive (this->s, this->buf, packet_len)) { - printf ("command data read failed\n"); - return 0; - } - - if ( (pre_header[7] != 0xb0) || (pre_header[6] != 0x0b) - || (pre_header[5] != 0xfa) || (pre_header[4] != 0xce) ) { - - printf ("missing signature\n"); - exit (1); - - } - - command = get_32 (this->buf, 24) & 0xFFFF; - -#ifdef LOG - printf ("command: %02x\n", command); -#endif - - if (command == 0x1b) - send_command (this, 0x1b, 0, 0, 0); - else if (command == 0x1e) { - - printf ("libmms: everything done. Thank you for downloading a media file containing proprietary and patentend technology.\n"); - - return 0; - } else if (command != 0x05) { - printf ("unknown command %02x\n", command); - exit (1); + /* command 0x07 */ + { + mms_buffer_t command_buffer; + mms_buffer_init(&command_buffer, this->scmd_body); + mms_buffer_put_32 (&command_buffer, 0x00000000); /* 64 byte float timestamp */ + mms_buffer_put_32 (&command_buffer, 0x00000000); + mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* first packet sequence */ + mms_buffer_put_8 (&command_buffer, 0xFF); /* max stream time limit (3 bytes) */ + mms_buffer_put_8 (&command_buffer, 0xFF); + mms_buffer_put_8 (&command_buffer, 0xFF); + mms_buffer_put_8 (&command_buffer, 0x00); /* stream time limit flag */ + mms_buffer_put_32 (&command_buffer, ASF_MEDIA_PACKET_ID_TYPE); /* asf media packet id type */ + if (!send_command (io, this, 0x07, 1, 0x0001FFFF, command_buffer.pos)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: failed to send command 0x07\n"); + goto fail; } } -#ifdef LOG - printf ("get media packet succ\n"); +/* report_progress (stream, 100); */ + +#ifdef USE_ICONV + string_utf16_close(url_conv); #endif + lprintf("mms_connect: passed\n" ); + + return this; + +fail: + if (this->s != -1) + close (this->s); + if (this->url) + free(this->url); + if (this->proto) + free(this->proto); + if (this->host) + free(this->host); + if (this->user) + free(this->user); + if (this->password) + free(this->password); + if (this->uri) + free(this->uri); + + free (this); + return NULL; +} + +static int get_media_packet (mms_io_t *io, mms_t *this) { + mms_packet_header_t header; + off_t len; + + switch (get_packet_header (io, this, &header)) { + case MMS_PACKET_ERR: + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: failed to read mms packet header\n"); + return 0; + break; + + case MMS_PACKET_COMMAND: + { + int command; + command = get_packet_command (io, this, header.packet_len); + + switch (command) { + case 0x1e: + { + uint32_t error_code; + + /* Warning: sdp is incomplete. Do not stop if error_code==1 */ + error_code = LE_32(this->buf + CMD_HEADER_LEN); + lprintf ("End of the current stream. Continue=%d\n", error_code); + + if (error_code == 0) { + this->eos = 1; + return 0; + } + + } + break; + + case 0x20: + { + lprintf ("new stream.\n"); + /* asf header */ + if (!get_asf_header (io, this)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "failed to read new ASF header\n"); + return 0; + } + + interp_asf_header (this); + + if (!mms_choose_best_streams(io, this)) + return 0; + + /* send command 0x07 */ + /* TODO: ugly */ + /* command 0x07 */ + { + mms_buffer_t command_buffer; + mms_buffer_init(&command_buffer, this->scmd_body); + mms_buffer_put_32 (&command_buffer, 0x00000000); /* 64 byte float timestamp */ + mms_buffer_put_32 (&command_buffer, 0x00000000); + mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* ?? */ + mms_buffer_put_32 (&command_buffer, 0xFFFFFFFF); /* first packet sequence */ + mms_buffer_put_8 (&command_buffer, 0xFF); /* max stream time limit (3 bytes) */ + mms_buffer_put_8 (&command_buffer, 0xFF); + mms_buffer_put_8 (&command_buffer, 0xFF); + mms_buffer_put_8 (&command_buffer, 0x00); /* stream time limit flag */ + mms_buffer_put_32 (&command_buffer, ASF_MEDIA_PACKET_ID_TYPE); /* asf media packet id type */ + if (!send_command (io, this, 0x07, 1, 0x0001FFFF, command_buffer.pos)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: failed to send command 0x07\n"); + return 0; + } + } + this->current_pos = 0; + } + break; + + case 0x1b: + { + if (!send_command (io, this, 0x1b, 0, 0, 0)) { + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: failed to send command\n"); + return 0; + } + } + break; + + case 0x05: + break; + + default: + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "unexpected mms command %02x\n", command); + } + this->buf_size = 0; + } + break; + + case MMS_PACKET_ASF_HEADER: + /* FIXME: de-xine-ification */ + lprintf( "***LOG:*** -- " + "libmms: unexpected asf header packet\n"); + this->buf_size = 0; + break; + + case MMS_PACKET_ASF_PACKET: + { + /* media packet */ + + /* FIXME: probably needs some more sophisticated logic, but + until we do seeking, this should work */ + if(this->need_discont) + { + this->need_discont = 0; + this->start_packet_seq = header.packet_seq; + } + + lprintf ("asf media packet detected, packet_len=%d, packet_seq=%d\n", + header.packet_len, header.packet_seq); + if (header.packet_len > this->asf_packet_len) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: invalid asf packet len: %d bytes\n", header.packet_len); + return 0; + } + + /* simulate a seek */ + this->current_pos = (off_t)this->asf_header_len + + ((off_t)header.packet_seq - this->start_packet_seq) * (off_t)this->asf_packet_len; + + len = io_read(io, this->s, this->buf, header.packet_len); + if (len != header.packet_len) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: read failed\n"); + return 0; + } + + /* explicit padding with 0 */ + lprintf("padding: %d bytes\n", this->asf_packet_len - header.packet_len); + { + char *base = (char *)(this->buf); + char *start = base + header.packet_len; + char *end = start + this->asf_packet_len - header.packet_len; + if ((start > base) && (start < (base+BUF_SIZE-1)) && + (start < end) && (end < (base+BUF_SIZE-1))) { + memset(this->buf + header.packet_len, 0, this->asf_packet_len - header.packet_len); + } + if (this->asf_packet_len > BUF_SIZE) { + this->buf_size = BUF_SIZE; + } else { + this->buf_size = this->asf_packet_len; + } + } + } + break; + } + + lprintf ("get media packet succ\n"); + return 1; } -int mms_read (mms_t *this, char *data, int len) { + +int mms_peek_header (mms_t *this, char *data, int maxsize) { + + int len; + len = (this->asf_header_len < maxsize) ? this->asf_header_len : maxsize; + + memcpy(data, this->asf_header, len); + return len; +} + +int mms_read (mms_io_t *io, mms_t *this, char *data, int len) { int total; total = 0; - - while (total < len) { - -#ifdef LOG - printf ("libmms: read, got %d / %d bytes\n", total, len); -#endif + while (total < len && !this->eos) { if (this->asf_header_read < this->asf_header_len) { - - int n, bytes_left ; + int n, bytes_left; bytes_left = this->asf_header_len - this->asf_header_read ; - if (len<bytes_left) - n = len; + if ((len - total) < bytes_left) + n = len-total; else - n = bytes_left; + n = bytes_left; memcpy (&data[total], &this->asf_header[this->asf_header_read], n); this->asf_header_read += n; total += n; + this->current_pos += n; } else { - int n, bytes_left ; + int n, bytes_left; bytes_left = this->buf_size - this->buf_read; - - while (!bytes_left) { - - this->buf_read = 0; + if (bytes_left == 0) { + this->buf_size = this->buf_read = 0; + if (!get_media_packet (io, this)) { + /* FIXME: de-xine-ification */ + lprintf ( "***LOG:*** -- " + "libmms: get_media_packet failed\n"); + return total; + } + bytes_left = this->buf_size; + } - if (!get_media_packet (this)) { - printf ("libmms: get_media_packet failed\n"); - return total; - } - bytes_left = this->buf_size - this->buf_read; - } - - - if (len<bytes_left) - n = len; + if ((len - total) < bytes_left) + n = len - total; else - n = bytes_left; + n = bytes_left; memcpy (&data[total], &this->buf[this->buf_read], n); this->buf_read += n; total += n; + this->current_pos += n; } } - return total; } + void mms_close (mms_t *this) { - if (this->s >= 0) { - close(this->s); - } + if (this->s != -1) + close (this->s); + if (this->url) + free(this->url); + if (this->proto) + free(this->proto); + if (this->host) + free(this->host); + if (this->user) + free(this->user); + if (this->password) + free(this->password); + if (this->uri) + free(this->uri); - free (this->host); - free (this->url); free (this); } + +uint32_t mms_get_length (mms_t *this) { + return this->file_len; +} + +off_t mms_get_current_pos (mms_t *this) { + return this->current_pos; +}
--- a/Plugins/Input/wma/libffwma/mms.h Sat Jul 15 16:05:21 2006 -0700 +++ b/Plugins/Input/wma/libffwma/mms.h Sat Jul 15 16:39:37 2006 -0700 @@ -1,34 +1,65 @@ -/* - * Copyright (C) 2000-2001 major mms - * - * This file is part of libmms - * - * xine-mms is free software; you can redistribute it and/or modify +/* + * Copyright (C) 2002-2003 the xine project + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * xine-mms is distributed in the hope that it will be useful, + * + * xine 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 General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * + * $Id: mms.h,v 1.11 2006/06/06 09:49:15 shawarma Exp $ + * * libmms public header */ + +/* TODO/dexineification: + * + functions needed: + * - _x_io_*() + * - xine_malloc() [?] + * - xine_fast_memcpy() [?] + */ + #ifndef HAVE_MMS_H #define HAVE_MMS_H +#include <inttypes.h> +#include <stdio.h> +#include <sys/types.h> + +/* #include "xine_internal.h" */ + +#include "bswap.h" +#include "mmsio.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + typedef struct mms_s mms_t; -mms_t *mms_connect (const char *url); +mms_t* mms_connect (mms_io_t *io, void *data, const char *url, int bandwidth); + +int mms_read (mms_io_t *io, mms_t *instance, char *data, int len); +uint32_t mms_get_length (mms_t *instance); +void mms_close (mms_t *instance); -int mms_read (mms_t *this, char *data, int len); +int mms_peek_header (mms_t *instance, char *data, int maxsize); + +off_t mms_get_current_pos (mms_t *instance); -void mms_close (mms_t *this); +#ifdef __cplusplus +} +#endif /* __cplusplus */ #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/wma/libffwma/mmsio.h Sat Jul 15 16:39:37 2006 -0700 @@ -0,0 +1,80 @@ +#ifndef __MMS_IO_H__ +#define __MMS_IO_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef off_t (*mms_io_write_func)(void *data, int socket, char *buf, off_t num); +typedef off_t (*mms_io_read_func)(void *data, int socket, char *buf, off_t num); + +/* select states */ +#define MMS_IO_READ_READY 1 +#define MMS_IO_WRITE_READY 2 + +enum + { + MMS_IO_STATUS_READY, /* IO can be safely performed */ + MMS_IO_STATUS_ERROR, /* There was IO error */ + MMS_IO_STATUS_ABORTED, /* IO command was (somehow) + aborted. This is not error, but invalidates IO for further operations*/ + MMS_IO_STATUS_TIMEOUT /* Timeout was exceeded */ + }; + +/* + * Waits for a file descriptor/socket to change status. + * + * users can use this handler to provide their own implementations, + * for example abortable ones + * + * params : + * data whatever parameter may be needed by implementation + * fd file/socket descriptor + * state MMS_IO_READ_READY, MMS_IO_WRITE_READY + * timeout_sec timeout in seconds + * + * + * return value : + * MMS_IO_READY the file descriptor is ready for cmd + * MMS_IO_ERROR an i/o error occured + * MMS_IO_ABORTED command aborted + * MMS_IO_TIMEOUT the file descriptor is not ready after timeout_msec milliseconds + * every other return value is interpreted same as MMS_IO_ABORTED + */ +typedef int (*mms_io_select_func)(void *data, int fd, int state, int timeout_msec); + +/* + * open a tcp connection + * + * params : + * stream needed for reporting errors but may be NULL + * host address of target + * port port on target + * + * returns a socket descriptor or -1 if an error occured + */ +typedef int (*mms_io_tcp_connect_func)(void *data, const char *host, int port); + +typedef struct +{ + mms_io_select_func select; + void *select_data; + mms_io_read_func read; + void *read_data; + mms_io_write_func write; + void *write_data; + mms_io_tcp_connect_func connect; + void *connect_data; +} mms_io_t; + +/* set default IO implementation, it will be used in absence of specific IO + parameter. Structure is referenced, not copied, must remain valid for entire + usage period. Passing NULL reverts to default, POSIX based implementation */ +void mms_set_default_io_impl(const mms_io_t *io); +const mms_io_t* mms_get_default_io_impl(); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MMS_IO_H__ */