From 1ced1a01cb0977720ca507297be2dad19c0bbb29 Mon Sep 17 00:00:00 2001 From: Guenter Bartsch Date: Sun, 17 Mar 2002 22:23:36 +0000 Subject: adding (slightly updated) mms input plugin CVS patchset: 1582 CVS date: 2002/03/17 22:23:36 --- src/input/Makefile.am | 7 +- src/input/asxparser.c | 160 ++++++++++ src/input/input_mms.c | 350 ++++++++++++++++++++++ src/input/mms.c | 815 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/input/mms.h | 39 +++ 5 files changed, 1369 insertions(+), 2 deletions(-) create mode 100644 src/input/asxparser.c create mode 100644 src/input/input_mms.c create mode 100644 src/input/mms.c create mode 100644 src/input/mms.h diff --git a/src/input/Makefile.am b/src/input/Makefile.am index b5ef1728f..0a47ab942 100644 --- a/src/input/Makefile.am +++ b/src/input/Makefile.am @@ -22,7 +22,7 @@ endif lib_LTLIBRARIES = xineplug_inp_file.la $(in_dvd) $(in_vcd) $(in_cda) \ xineplug_inp_stdin_fifo.la xineplug_inp_net.la \ - xineplug_inp_rtp.la xineplug_inp_http.la + xineplug_inp_rtp.la xineplug_inp_http.la xineplug_inp_mms.la xineplug_inp_file_la_SOURCES = input_file.c xineplug_inp_file_la_LDFLAGS = -avoid-version -module @@ -33,6 +33,9 @@ xineplug_inp_dvd_la_LDFLAGS = -avoid-version -module xineplug_inp_net_la_SOURCES = input_net.c strict_scr.c xineplug_inp_net_la_LDFLAGS = -avoid-version -module +xineplug_inp_mms_la_SOURCES = input_mms.c strict_scr.c mms.c asxparser.c +xineplug_inp_mms_la_LDFLAGS = -avoid-version -module + xineplug_inp_vcd_la_SOURCES = input_vcd.c xineplug_inp_vcd_la_LDFLAGS = -avoid-version -module @@ -49,7 +52,7 @@ xineplug_inp_cda_la_SOURCES = input_cda.c xineplug_inp_cda_la_LDFLAGS = -avoid-version -module include_HEADERS = input_plugin.h -noinst_HEADERS = dvd_udf.h read_cache.h strict_scr.h +noinst_HEADERS = dvd_udf.h read_cache.h strict_scr.h mms.h EXTRA_DIST = input_dvd.c dvd_udf.c input_vcd.c read_cache.c diff --git a/src/input/asxparser.c b/src/input/asxparser.c new file mode 100644 index 000000000..e2d3195bd --- /dev/null +++ b/src/input/asxparser.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void asx_find_entries(char* buff, char** fname, + int *IsNotFinished); + +char *strupr(char *string); + +void first_request(char* buff, char* file,int *len) { + + char *ptr; + + bzero(buff,*len); + ptr=buff; + ptr+=sprintf(ptr,"GET %s HTTP/1.0\r\n",file); + ptr+=sprintf(ptr,"Accept: */*\r\n"); + ptr+=sprintf(ptr,"User-Agent: xine/0.9.8\r\n"); + ptr+=sprintf(ptr,"Host: %s\r\n", "192.168.0.129"); + ptr+=sprintf(ptr,"Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=1,max-duration=0\r\n"); + ptr+=sprintf(ptr,"Pragma: xClientGUID=%s\r\n", "{33715801-BAB3-9D85-24E9-03B90328270A}"); + ptr+=sprintf(ptr,"Connection: Close\r\n\r\n"); + *len =(int)ptr-(int)buff; +} + +int asx_parse (char* mrl, char** rname) { + + char* ptr=NULL; + char buff[1024]; + int res; + int is_not_finished=0; + char *url, *host, *hostend; + char *path, *file; + int hostlen, s ,l; + + if (strncasecmp (mrl, "mmshttp://", 10)) { + return -1; + } + + ptr=strstr(mrl,"//"); + if(!ptr) + return 1; + + l=ptr-mrl+2; + url = strdup (mrl); + + /* extract hostname */ + + hostend = strchr(&url[l],'/'); + if (!hostend) { + printf ("asxparser: invalid url >%s<, failed to find hostend \n", url); + free(url); + return 1; + } + hostlen = hostend - url - l; + host = malloc (hostlen+1); + strncpy (host, &url[l], hostlen); + host[hostlen]=0; + + /* extract path and file */ + + path = url+hostlen+l+1; + file = strrchr (url, '/'); + + /* + * try to connect + */ + + printf("asxparser host=%s \n",host); + printf("asxparser file=%s \n",hostend); + s = host_connect (host, 80); + if (s == -1) { + printf ("asxparser: failed to connect\n"); + free (host); + free (url); + return 1; + } + printf("asxparser: connect passed \n"); + + l=sizeof(buff); + first_request(buff, hostend, &l); + write(s,buff,l); + + res=read(s,buff, sizeof(buff)); + printf("asxparser: answer1=%s %d byte received \n",buff,res); + if(!strstr(buff,"mms://")) { + l=sizeof(buff); + first_request(buff, hostend, &l); + write(s,buff,l); + res=read(s,buff, sizeof(buff)); + printf("asxparser: answer2=%s %d byte received\n",buff,res); + } + close(s); + + free(url); + free(host); + + if(res<1) + return 1; + + printf ("asxparser: finding entries...\n"); + + asx_find_entries(buff,rname,&is_not_finished); + + return 1; + +} + +void asx_find_entries (char* buff, char** fname, + int *is_not_finished ) { + int res; + char *ptr; + char *uptr; + char* ptre; + + /*no */ + uptr=strdup(buff); + uptr=strupr(uptr); + + if (!strstr(uptr,"ASX VERSION")){ + free(uptr); + return ; + } + free(uptr); + + ptr=(char*)strstr(buff, "mms://"); + if(!ptr) + return ; + + ptre=(char*)strstr(ptr, "\""); + if (!ptre) + return ; + + res=(int)(ptre-ptr); + if(res<=0) + return ; + (*fname)=(char*)malloc(res+2); + memcpy(*fname,ptr,res); + (*fname)[res]=0; + + printf("asxparser path is %s \n", *fname); + return ; +} + +char *strupr(char *string) { + char *s; + + if (string){ + for (s = string; *s; ++s) + *s = toupper(*s); + } + return string; +} + + diff --git a/src/input/input_mms.c b/src/input/input_mms.c new file mode 100644 index 000000000..421508906 --- /dev/null +++ b/src/input/input_mms.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2000-2002 major mms + * + * This file is part of xine-mms + * + * xine-mms 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, + * 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 + * + * input plugin for mms network streams + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bswap.h" + +#include "xine_internal.h" +#include "xineutils.h" +#include "input_plugin.h" + +#include "mms.h" +#include "strict_scr.h" + +/* +#define LOG +*/ + +extern int errno; + +#if !defined(NDELAY) && defined(O_NDELAY) +#define FNDELAY O_NDELAY +#endif + + +typedef struct { + input_plugin_t input_plugin; + + xine_t *xine; + config_values_t *config; + + mms_t *mms; + + char *mrl; + + off_t curpos; + + int buffering; + + strictscr_t *scr; + + char scratch[1025]; + +} mms_input_plugin_t; + + +static int mms_plugin_open (input_plugin_t *this_gen, char *mrl) { + + char* nmrl=NULL; + mms_input_plugin_t *this = (mms_input_plugin_t *) this_gen; + + + asx_parse(mrl,&nmrl); + + if(!nmrl) + nmrl=mrl; + + printf("mms_plugin_open: using mrl <%s> \n", nmrl); + if (strncasecmp (nmrl, "mms://",6)) + return 0; + + this->mrl = strdup(nmrl); /* FIXME: small memory leak */ + + this->mms = mms_connect (nmrl); + + if (!this->mms) + return 0; + + this->curpos = 0; + this->buffering = 0; + + /* register our scr plugin */ + + this->scr->scr.start (&this->scr->scr, this->xine->metronom->get_current_time (this->xine->metronom)); + this->xine->metronom->register_scr (this->xine->metronom, &this->scr->scr); + + return 1; +} + +#define LOW_WATER_MARK 50 +#define HIGH_WATER_MARK 100 + +static off_t mms_plugin_read (input_plugin_t *this_gen, + char *buf, off_t len) { + mms_input_plugin_t *this = (mms_input_plugin_t *) this_gen; + off_t n; + int fifo_fill; + +#ifdef LOG + printf ("mms_plugin_read: %lld bytes ...\n", + len); +#endif + + fifo_fill = this->xine->video_fifo->size(this->xine->video_fifo); + + if (fifo_fillxine->metronom->set_speed (this->xine->metronom, SPEED_PAUSE); + this->buffering = 1; + this->scr->adjustable = 0; + printf ("input_mms: buffering (%d/%d)...\n", fifo_fill, LOW_WATER_MARK); + + } else if ( (fifo_fill>HIGH_WATER_MARK) && (this->buffering)) { + this->xine->metronom->set_speed (this->xine->metronom, SPEED_NORMAL); + this->buffering = 0; + this->scr->adjustable = 1; + printf ("input_mms: buffering...done\n"); + } + + n = mms_read (this->mms, buf, len); + this->curpos += n; + + return n; +} + +static buf_element_t *mms_plugin_read_block (input_plugin_t *this_gen, + fifo_buffer_t *fifo, off_t todo) { + /*mms_input_plugin_t *this = (mms_input_plugin_t *) this_gen; */ + buf_element_t *buf = fifo->buffer_pool_alloc (fifo); + int total_bytes; + +#ifdef LOG + printf ("mms_plugin_read_block: %lld bytes...\n", + todo); +#endif + + buf->content = buf->mem; + buf->type = BUF_DEMUX_BLOCK; + + total_bytes = mms_plugin_read (this_gen, buf->content, todo); + + if (total_bytes != todo) { + buf->free_buffer (buf); + return NULL; + } + + buf->size = total_bytes; + + return buf; +} + +static off_t mms_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) { + + mms_input_plugin_t *this = (mms_input_plugin_t *) this_gen; + + off_t dest = this->curpos; + +#ifdef LOG + printf ("mms_plugin_seek: %lld offset, %d origin...\n", + offset, origin); +#endif + + switch (origin) { + case SEEK_SET: + dest = offset; + break; + case SEEK_CUR: + dest = this->curpos + offset; + break; + case SEEK_END: + printf ("input_mms: SEEK_END not implemented!\n"); + return this->curpos; + default: + printf ("input_mms: unknown origin in seek!\n"); + return this->curpos; + } + + if (this->curpos > dest) { + printf ("input_mms: cannot seek back!\n"); + return this->curpos; + } + + while (this->curposcurpos; + + if (diff>1024) + diff = 1024; + + n = mms_read (this->mms, this->scratch, diff); + this->curpos += n; + if (ncurpos; + } + + return this->curpos; +} + +static off_t mms_plugin_get_length (input_plugin_t *this_gen) { + + mms_input_plugin_t *this = (mms_input_plugin_t *) this_gen; + + off_t length; + + if (!this->mms) + return 0; + + length = mms_get_length (this->mms); + +#ifdef LOG + printf ("input_mms: length is %lld\n", length); +#endif + + return length; + +} + +static uint32_t mms_plugin_get_capabilities (input_plugin_t *this_gen) { + + return INPUT_CAP_NOCAP; +} + +static uint32_t mms_plugin_get_blocksize (input_plugin_t *this_gen) { + + return 0; +; +} + +static off_t mms_plugin_get_current_pos (input_plugin_t *this_gen){ + mms_input_plugin_t *this = (mms_input_plugin_t *) this_gen; + + /* + printf ("current pos is %lld\n", this->curpos); + */ + + return this->curpos; +} + +static int mms_plugin_eject_media (input_plugin_t *this_gen) { + return 1; +} + + +static void mms_plugin_close (input_plugin_t *this_gen) { + mms_input_plugin_t *this = (mms_input_plugin_t *) this_gen; + + if (this->mms) { + mms_close (this->mms); + this->mms = NULL; + } + + this->xine->metronom->unregister_scr (this->xine->metronom, &this->scr->scr); +} + +static void mms_plugin_stop (input_plugin_t *this_gen) { + + mms_plugin_close(this_gen); +} + +static char *mms_plugin_get_description (input_plugin_t *this_gen) { + return "mms input plugin"; +} + +static char *mms_plugin_get_identifier (input_plugin_t *this_gen) { + return "MMS"; +} + +static char* mms_plugin_get_mrl (input_plugin_t *this_gen) { + mms_input_plugin_t *this = (mms_input_plugin_t *) this_gen; + + return this->mrl; +} + +static int mms_plugin_get_optional_data (input_plugin_t *this_gen, + void *data, int data_type) { + + return INPUT_OPTIONAL_UNSUPPORTED; +} + + +input_plugin_t *init_input_plugin (int iface, xine_t *xine) { + + mms_input_plugin_t *this; + config_values_t *config; + + if (iface != 5) { + printf ("mms input plugin doesn't support plugin API version %d.\n" + "PLUGIN DISABLED.\n" + "This means there's a version mismatch between xine and this input" + "plugin.\nInstalling current input plugins should help.\n", + iface); + return NULL; + } + + this = (mms_input_plugin_t *) malloc (sizeof (mms_input_plugin_t)); + config = xine->config; + this->xine = xine; + + this->input_plugin.interface_version = INPUT_PLUGIN_IFACE_VERSION; + this->input_plugin.get_capabilities = mms_plugin_get_capabilities; + this->input_plugin.open = mms_plugin_open; + this->input_plugin.read = mms_plugin_read; + this->input_plugin.read_block = mms_plugin_read_block; + this->input_plugin.seek = mms_plugin_seek; + this->input_plugin.get_current_pos = mms_plugin_get_current_pos; + this->input_plugin.get_length = mms_plugin_get_length; + this->input_plugin.get_blocksize = mms_plugin_get_blocksize; + this->input_plugin.get_dir = NULL; + this->input_plugin.eject_media = mms_plugin_eject_media; + this->input_plugin.get_mrl = mms_plugin_get_mrl; + this->input_plugin.close = mms_plugin_close; + this->input_plugin.stop = mms_plugin_stop; + this->input_plugin.get_description = mms_plugin_get_description; + this->input_plugin.get_identifier = mms_plugin_get_identifier; + this->input_plugin.get_autoplay_list = NULL; + this->input_plugin.get_optional_data = mms_plugin_get_optional_data; + this->input_plugin.is_branch_possible= NULL; + + this->mrl = NULL; + this->config = config; + this->curpos = 0; + this->buffering = 0; + + this->scr = strictscr_init (); + + return &this->input_plugin; +} diff --git a/src/input/mms.c b/src/input/mms.c new file mode 100644 index 000000000..2f99e7127 --- /dev/null +++ b/src/input/mms.c @@ -0,0 +1,815 @@ +/* + * Copyright (C) 2000-2001 major mms + * + * This file is part of libmms + * + * libmms 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. + * + * libmms 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 + * + * utility functions to handle communication with an mms server + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bswap.h" +#include "mms.h" + +/* +#define LOG +*/ + +/* + * mms specific types + */ + +#define MMS_PORT 1755 + +#define BUF_SIZE 102400 + +#define CMD_HEADER_LEN 48 +#define CMD_BODY_LEN 1024 + +struct mms_s { + + int s; + + char *host; + char *path; + char *file; + char *url; + + /* command to send */ + 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]; + int buf_size; + int buf_read; + + uint8_t asf_header[8192]; + int asf_header_len; + int asf_header_read; + int seq_num; + int num_stream_ids; + int stream_ids[20]; + int packet_length; + uint32_t file_length; +}; + +/* network/socket utility functions */ + +static int host_connect_attempt(struct in_addr ia, int port) { + + int s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + struct sockaddr_in sin; + + if (s==-1) { + printf ("libmms: socket(): %s\n", strerror(errno)); + 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; +} + +int host_connect(const char *host, int port) { + + struct hostent *h; + int i, s; + + h=gethostbyname(host); + if (h==NULL) { + printf ("libmms: unable to resolve '%s'.\n", host); + return -1; + } + + 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) { + + 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 ; + + this->scmd_len += 4; +} + +static int send_data (int s, char *buf, int len) { + int total; + + total=0; + while (total 0) + total += n; + else if (n<0 && errno!=EAGAIN) + return total; + } + return total; +} + +static uint32_t get_32 (unsigned char *cmd, int offset) { + + uint32_t ret; + + ret = cmd[offset] ; + ret |= cmd[offset+1]<<8 ; + ret |= cmd[offset+2]<<16 ; + ret |= cmd[offset+3]<<24 ; + + return ret; +} + +static void send_command (mms_t *this, int command, uint32_t switches, + uint32_t extra, int length) { + + int len8; + + len8 = (length + (length%8)) / 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); + + /* memcpy (&cmd->buf[48], data, length); */ + + if (send_data (this->s, this->scmd, length+48) != (length+48)) { + printf ("libmms: send error\n"); + } + +#ifdef LOG + printf ("\nlibmms: ***************************************************\ncommand sent, %d bytes\n", length+48); + + 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)); + + printf ("ascii contents>"); + for (i=48; i<(length+48); i+=2) { + unsigned char c = this->scmd[i]; + + if ((c>=32) && (c<=128)) + printf ("%c", c); + else + printf ("."); + } + printf ("\n"); + + printf ("libmms: complete hexdump of package follows:\n"); + for (i=0; i<(length+48); i++) { + unsigned char c = this->scmd[i]; + + printf ("%02x", c); + + if ((i % 16) == 15) + printf ("\nlibmms: "); + + if ((i % 2) == 1) + printf (" "); + + } + printf ("\n"); +#endif +} + +static void string_utf16(char *dest, char *src, int len) { + int i; + + memset (dest, 0, 1000); + + for (i=0; i=32) && (c<128)) + printf ("%c", c); + else + printf (" %02x ", c); + + } + printf ("\n"); +#endif + +} + +static void get_answer (mms_t *this) { + + int command = 0x1b; + + while (command == 0x1b) { + int len; + + len = read (this->s, this->buf, BUF_SIZE) ; + if (!len) { + printf ("\nalert! eof\n"); + return; + } + + print_answer (this->buf, len); + + command = get_32 (this->buf, 36) & 0xFFFF; + + if (command == 0x1b) + send_command (this, 0x1b, 0, 0, 0); + } +} + +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; + +} + +static void get_header (mms_t *this) { + + unsigned char pre_header[8]; + + this->asf_header_len = 0; + + while (1) { + + 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; + +#ifdef LOG + printf ("libmms: asf header packet detected, len=%d\n", + packet_len); +#endif + + if (!receive (this->s, &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 (!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); + + } + + printf ("mms: get header packet succ\n"); + } +} + +static void interp_header (mms_t *this) { + + int i; + + this->packet_length = 0; + + /* + * parse header + */ + + i = 30; + while (iasf_header_len) { + + uint64_t guid_1, guid_2, length; + + 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; + + 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(this->asf_header, i+92-24); + this->file_length = get_32(this->asf_header, i+40-24); + +#ifdef LOG + printf ("file object, packet length = %d (%d), file_length=%d\n", + this->packet_length, get_32(this->asf_header, i+96-24), this->file_length); +#endif + + + } 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 (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 = malloc (hostlen+1); + strncpy (host, &url[6], hostlen); + host[hostlen]=0; + + /* extract path and file */ + + path = url+hostlen+7; + file = strrchr (url, '/'); + + /* + * try to connect + */ + + s = host_connect (host, MMS_PORT); + if (s == -1) { + printf ("libmms: failed to connect\n"); + free (host); + free (url); + return NULL; + } + + this = (mms_t*) malloc (sizeof (mms_t)); + + this->url = url; + this->host = host; + this->path = path; + this->file = file; + this->s = s; + this->seq_num = 0; + this->scmd_body = &this->scmd[CMD_HEADER_LEN]; + this->asf_header_len = 0; + this->asf_header_read = 0; + this->num_stream_ids = 0; + this->packet_length = 0; + this->buf_size = 0; + this->buf_read = 0; + + /* + * let the negotiations begin... + */ + + /* cmd1 */ + + 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); + + send_command (this, 1, 0, 0x0004000b, strlen(this->str) * 2+8); + + 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)); + + } + + /* 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 */ + + 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); + + /* 0x15 */ + + memset (this->scmd_body, 0, 40); + this->scmd_body[32] = 2; + + send_command (this, 0x15, 1, 0, 40); + + this->num_stream_ids = 0; + + get_header (this); + interp_header (this); + + /* 0x33 */ + + memset (this->scmd_body, 0, 40); + + for (i=1; inum_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 */ + + 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); + + return this; +} + +static int get_media_packet (mms_t *this) { + + unsigned char pre_header[8]; + + if (!receive (this->s, pre_header, 8)) { + printf ("pre-header read failed\n"); + return 0; + } + +#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; + } + + 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); + } + } + +#ifdef LOG + printf ("get media packet succ\n"); +#endif + + return 1; +} + +int mms_read (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 + + if (this->asf_header_read < this->asf_header_len) { + + int n, bytes_left ; + + bytes_left = this->asf_header_len - this->asf_header_read ; + + if (lenasf_header[this->asf_header_read], n); + + this->asf_header_read += n; + total += n; + } else { + + int n, bytes_left ; + + bytes_left = this->buf_size - this->buf_read; + + while (!bytes_left) { + + this->buf_read = 0; + + if (!get_media_packet (this)) { + printf ("libmms: get_media_packet failed\n"); + return total; + } + bytes_left = this->buf_size - this->buf_read; + } + + + if (lenbuf[this->buf_read], n); + + this->buf_read += n; + total += n; + } + } + + return total; +} + +void mms_close (mms_t *this) { + + if (this->s >= 0) { + close(this->s); + } + + free (this->host); + free (this->url); + free (this); +} + + +uint32_t mms_get_length (mms_t *this) { + return this->file_length; +} diff --git a/src/input/mms.h b/src/input/mms.h new file mode 100644 index 000000000..65ba0b532 --- /dev/null +++ b/src/input/mms.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2000-2002 major mms + * + * This file is part of libmms + * + * xine-mms 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, + * 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 + * + * libmms public header + */ +#ifndef HAVE_MMS_H +#define HAVE_MMS_H + +#include + +typedef struct mms_s mms_t; + +mms_t *mms_connect (char *url); + +int mms_read (mms_t *this, char *data, int len); + +void mms_close (mms_t *this); +int asx_parse (char* fname, char** rname); + +uint32_t mms_get_length (mms_t *this); + +#endif + -- cgit v1.2.3