# HG changeset patch # User chainsaw # Date 1153008187 25200 # Node ID bf7fe0421354b9009bee70ca50b0b81ea5646a84 # Parent 93c0af9bb52132bb1c123bbc1de020b15bf7bdde [svn] Bolt on libmms versions of byteswap macros and bring yet more files in (mmsh.c & mms.h). diff -r 93c0af9bb521 -r bf7fe0421354 ChangeLog --- a/ChangeLog Sat Jul 15 16:55:29 2006 -0700 +++ b/ChangeLog Sat Jul 15 17:03:07 2006 -0700 @@ -1,3 +1,13 @@ +2006-07-15 23:55:29 +0000 Tony Vroon + revision [1734] + Add more files from libmms CVS. + + Changes: Modified: + +261 -0 trunk/Plugins/Input/wma/libffwma/asfheader.h + +857 -0 trunk/Plugins/Input/wma/libffwma/uri.c + +92 -0 trunk/Plugins/Input/wma/libffwma/uri.h + + 2006-07-15 23:52:52 +0000 William Pitcock revision [1732] - properly hook up mms diff -r 93c0af9bb521 -r bf7fe0421354 Plugins/Input/wma/libffwma/bswap.h --- a/Plugins/Input/wma/libffwma/bswap.h Sat Jul 15 16:55:29 2006 -0700 +++ b/Plugins/Input/wma/libffwma/bswap.h Sat Jul 15 17:03:07 2006 -0700 @@ -127,3 +127,47 @@ #endif #endif /* __BSWAP_H__ */ + +#ifndef BSWAP_H_INCLUDED +#define BSWAP_H_INCLUDED + +/* + * Copyright (C) 2004 Maciej Katafiasz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 + */ + + +/* Go cheap now, will rip out glib later. *Sigh* */ +#include + +/* NOTE: + * Now, to clear up confusion: LE_XX means "from LE to native, XX bits wide" + * I know it's not very clear naming (tell me about it, I + * misinterpreted in first version and caused bad nasty bug, *sigh*), + * but that's inherited code, will clean up as things go + * Oh, and one more thing -- they take *pointers*, not actual ints + */ + +#define LE_16(val) (GINT16_FROM_LE (*((u_int16_t*)(val)))) +#define BE_16(val) (GINT16_FROM_BE (*((u_int16_t*)(val)))) +#define LE_32(val) (GINT32_FROM_LE (*((u_int32_t*)(val)))) +#define BE_32(val) (GINT32_FROM_BE (*((u_int32_t*)(val)))) + +#define LE_64(val) (GINT64_FROM_LE (*((u_int64_t*)(val)))) +#define BE_64(val) (GINT64_FROM_BE (*((u_int64_t*)(val)))) + +#endif /* BSWAP_H_INCLUDED */ diff -r 93c0af9bb521 -r bf7fe0421354 Plugins/Input/wma/libffwma/mmsh.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/wma/libffwma/mmsh.c Sat Jul 15 17:03:07 2006 -0700 @@ -0,0 +1,1274 @@ +/* + * 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 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: mmsh.c,v 1.5 2006/07/13 12:32:20 shawarma Exp $ + * + * MMS over HTTP protocol + * written by Thibaut Mattern + * based on mms.c and specs from avifile + * (http://avifile.sourceforge.net/asf-1.0.htm) + * + * TODO: + * error messages + * http support cleanup, find a way to share code with input_http.c (http.h|c) + * http proxy support + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE "mmsh" +#define LOG_VERBOSE +#ifdef DEBUG +# define lprintf printf +#else +# define lprintf(x...) +#endif + +#include "bswap.h" +#include "mmsh.h" +#include "asfheader.h" +#include "uri.h" + +/* #define USERAGENT "User-Agent: NSPlayer/7.1.0.3055\r\n" */ +#define USERAGENT "User-Agent: NSPlayer/4.1.0.3856\r\n" +#define CLIENTGUID "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n" + + +#define MMSH_PORT 80 +#define MMSH_UNKNOWN 0 +#define MMSH_SEEKABLE 1 +#define MMSH_LIVE 2 + +#define CHUNK_HEADER_LENGTH 4 +#define EXT_HEADER_LENGTH 8 +#define CHUNK_TYPE_RESET 0x4324 +#define CHUNK_TYPE_DATA 0x4424 +#define CHUNK_TYPE_END 0x4524 +#define CHUNK_TYPE_ASF_HEADER 0x4824 +#define CHUNK_SIZE 65536 /* max chunk size */ +#define ASF_HEADER_SIZE 8192 /* max header size */ + +#define SCRATCH_SIZE 1024 + +static const char* mmsh_FirstRequest = + "GET %s HTTP/1.0\r\n" + "Accept: */*\r\n" + USERAGENT + "Host: %s:%d\r\n" + "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%u,max-duration=0\r\n" + CLIENTGUID + "Connection: Close\r\n\r\n"; + +static const char* mmsh_SeekableRequest = + "GET %s HTTP/1.0\r\n" + "Accept: */*\r\n" + USERAGENT + "Host: %s:%d\r\n" + "Pragma: no-cache,rate=1.000000,stream-time=%u,stream-offset=%u:%u,request-context=%u,max-duration=%u\r\n" + CLIENTGUID + "Pragma: xPlayStrm=1\r\n" + "Pragma: stream-switch-count=%d\r\n" + "Pragma: stream-switch-entry=%s\r\n" /* ffff:1:0 ffff:2:0 */ + "Connection: Close\r\n\r\n"; + +static const char* mmsh_LiveRequest = + "GET %s HTTP/1.0\r\n" + "Accept: */*\r\n" + USERAGENT + "Host: %s:%d\r\n" + "Pragma: no-cache,rate=1.000000,request-context=%u\r\n" + "Pragma: xPlayStrm=1\r\n" + CLIENTGUID + "Pragma: stream-switch-count=%d\r\n" + "Pragma: stream-switch-entry=%s\r\n" + "Connection: Close\r\n\r\n"; + + +#if 0 +/* Unused requests */ +static const char* mmsh_PostRequest = + "POST %s HTTP/1.0\r\n" + "Accept: */*\r\n" + USERAGENT + "Host: %s\r\n" + "Pragma: client-id=%u\r\n" +/* "Pragma: log-line=no-cache,rate=1.000000,stream-time=%u,stream-offset=%u:%u,request-context=2,max-duration=%u\r\n" */ + "Pragma: Content-Length: 0\r\n" + CLIENTGUID + "\r\n"; + +static const char* mmsh_RangeRequest = + "GET %s HTTP/1.0\r\n" + "Accept: */*\r\n" + USERAGENT + "Host: %s:%d\r\n" + "Range: bytes=%Lu-\r\n" + CLIENTGUID + "Connection: Close\r\n\r\n"; +#endif + + + +/* + * mmsh specific types + */ + + +struct mmsh_s { + + /* FIXME: de-xine-ification */ + void *custom_data; + + int s; + + /* url parsing */ + char *url; + char *proxy_url; + char *proto; + char *connect_host; + int connect_port; + char *http_host; + int http_port; + char *proxy_user; + char *proxy_password; + char *host_user; + char *host_password; + char *uri; + + char str[SCRATCH_SIZE]; /* scratch buffer to built strings */ + + int stream_type; /* seekable or broadcast */ + + /* receive buffer */ + + /* chunk */ + uint16_t chunk_type; + uint16_t chunk_length; + uint16_t chunk_seq_number; + uint8_t buf[CHUNK_SIZE]; + + int buf_size; + int buf_read; + + uint8_t asf_header[ASF_HEADER_SIZE]; + uint32_t asf_header_len; + uint32_t asf_header_read; + int seq_num; + int num_stream_ids; + int stream_ids[ASF_MAX_NUM_STREAMS]; + int stream_types[ASF_MAX_NUM_STREAMS]; + uint32_t packet_length; + int64_t file_length; + char guid[37]; + uint32_t bitrates[ASF_MAX_NUM_STREAMS]; + uint32_t bitrates_pos[ASF_MAX_NUM_STREAMS]; + + int has_audio; + int has_video; + + off_t current_pos; + int user_bandwidth; +}; + +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); +} + +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); + lprintf("read\n"); + 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) +{ + + 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; + } + + 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) { + + /* FIXME: de-xine-ification */ +/* _x_message(stream, XINE_MSG_CONNECTION_REFUSED, strerror(errno), NULL); */ + close(s); + continue; + } + + return s; + } + return -1; +} + + +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)) + +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]; + } + + 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 ("libmmsh: 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 int send_command (mms_io_t *io, mmsh_t *this, char *cmd) { + int length; + + lprintf ("send_command:\n%s\n", cmd); + + length = strlen(cmd); + if (io_write(io, this->s, cmd, length) != length) { + lprintf ("mmsh: send error.\n"); + return 0; + } + return 1; +} + +static int get_answer (mms_io_t *io, mmsh_t *this) { + + int done, len, linenum; + char *features; + + lprintf ("get_answer\n"); + + done = 0; len = 0; linenum = 0; + this->stream_type = MMSH_UNKNOWN; + + while (!done) { + + if (io_read(io, this->s, &(this->buf[len]), 1) != 1) { + lprintf ("mmsh: alart: end of stream\n"); + return 0; + } + + if (this->buf[len] == '\012') { + + this->buf[len] = '\0'; + len--; + + if ((len >= 0) && (this->buf[len] == '\015')) { + this->buf[len] = '\0'; + len--; + } + + linenum++; + + lprintf ("answer: >%s<\n", this->buf); + + if (linenum == 1) { + int httpver, httpsub, httpcode; + char httpstatus[51]; + + if (sscanf(this->buf, "HTTP/%d.%d %d %50[^\015\012]", &httpver, &httpsub, + &httpcode, httpstatus) != 4) { + lprintf ("mmsh: bad response format\n"); + return 0; + } + + if (httpcode >= 300 && httpcode < 400) { + lprintf ("mmsh: 3xx redirection not implemented: >%d %s<\n", httpcode, httpstatus); + return 0; + } + + if (httpcode < 200 || httpcode >= 300) { + lprintf ("mmsh: http status not 2xx: >%d %s<\n", httpcode, httpstatus); + return 0; + } + } else { + + if (!strncasecmp(this->buf, "Location: ", 10)) { + lprintf ("mmsh: Location redirection not implemented.\n"); + return 0; + } + + if (!strncasecmp(this->buf, "Pragma:", 7)) { + features = strstr(this->buf + 7, "features="); + if (features) { + if (strstr(features, "seekable")) { + lprintf("seekable stream\n"); + this->stream_type = MMSH_SEEKABLE; + } else { + if (strstr(features, "broadcast")) { + lprintf("live stream\n"); + this->stream_type = MMSH_LIVE; + } + } + } + } + } + + if (len == -1) { + done = 1; + } else { + len = 0; + } + } else { + len ++; + } + } + if (this->stream_type == MMSH_UNKNOWN) { + lprintf ("mmsh: unknown stream type\n"); + this->stream_type = MMSH_SEEKABLE; /* FIXME ? */ + } + return 1; +} + +static int get_chunk_header (mms_io_t *io, mmsh_t *this) { + uint8_t chunk_header[CHUNK_HEADER_LENGTH]; + uint8_t ext_header[EXT_HEADER_LENGTH]; + int read_len; + int ext_header_len; + + lprintf ("get_chunk_header\n"); + + /* read chunk header */ + read_len = io_read(io, this->s, chunk_header, CHUNK_HEADER_LENGTH); + if (read_len != CHUNK_HEADER_LENGTH) { + lprintf ("chunk header read failed, %d != %d\n", len, CHUNK_HEADER_LENGTH); + return 0; + } + this->chunk_type = LE_16 (&chunk_header[0]); + this->chunk_length = LE_16 (&chunk_header[2]); + + switch (this->chunk_type) { + case CHUNK_TYPE_DATA: + ext_header_len = 8; + break; + case CHUNK_TYPE_END: + ext_header_len = 4; + break; + case CHUNK_TYPE_ASF_HEADER: + ext_header_len = 8; + break; + case CHUNK_TYPE_RESET: + ext_header_len = 4; + break; + default: + ext_header_len = 0; + } + /* read extended header */ + if (ext_header_len > 0) { + read_len = io_read (io, this->s, ext_header, ext_header_len); + if (read_len != ext_header_len) { + lprintf ("extended header read failed. %d != %d\n", read_len, ext_header_len); + return 0; + } + } + /* display debug infos */ +#ifdef DEBUG + switch (this->chunk_type) { + case CHUNK_TYPE_DATA: + this->chunk_seq_number = LE_32 (&ext_header[0]); + lprintf ("chunk type: CHUNK_TYPE_DATA\n"); + lprintf ("chunk length: %d\n", this->chunk_length); + lprintf ("chunk seq: %d\n", this->chunk_seq_number); + lprintf ("unknown: %d\n", ext_header[4]); + lprintf ("mmsh seq: %d\n", ext_header[5]); + lprintf ("len2: %d\n", LE_16(&ext_header[6])); + break; + case CHUNK_TYPE_END: + this->chunk_seq_number = LE_32 (&ext_header[0]); + lprintf ("chunk type: CHUNK_TYPE_END\n"); + lprintf ("continue: %d\n", this->chunk_seq_number); + break; + case CHUNK_TYPE_ASF_HEADER: + lprintf ("chunk type: CHUNK_TYPE_ASF_HEADER\n"); + lprintf ("chunk length: %d\n", this->chunk_length); + lprintf ("unknown: %2X %2X %2X %2X %2X %2X\n", + ext_header[0], ext_header[1], ext_header[2], ext_header[3], + ext_header[4], ext_header[5]); + lprintf ("len2: %d\n", LE_16(&ext_header[6])); + break; + case CHUNK_TYPE_RESET: + lprintf ("chunk type: CHUNK_TYPE_RESET\n"); + lprintf ("chunk seq: %d\n", this->chunk_seq_number); + lprintf ("unknown: %2X %2X %2X %2X\n", + ext_header[0], ext_header[1], ext_header[2], ext_header[3]); + break; + default: + lprintf ("unknown chunk: %4X\n", this->chunk_type); + } +#endif + + this->chunk_length -= ext_header_len; + return 1; +} + +static int get_header (mms_io_t *io, mmsh_t *this) { + int len = 0; + + lprintf("get_header\n"); + + this->asf_header_len = 0; + this->asf_header_read = 0; + + /* read chunk */ + while (1) { + if (get_chunk_header(io, this)) { + if (this->chunk_type == CHUNK_TYPE_ASF_HEADER) { + if ((this->asf_header_len + this->chunk_length) > ASF_HEADER_SIZE) { + lprintf ("mmsh: the asf header exceed %d bytes\n", ASF_HEADER_SIZE); + return 0; + } else { + len = io_read(io, this->s, this->asf_header + this->asf_header_len, + this->chunk_length); + this->asf_header_len += len; + if (len != this->chunk_length) { + return 0; + } + } + } else { + break; + } + } else { + lprintf("get_chunk_header failed\n"); + return 0; + } + } + + if (this->chunk_type == CHUNK_TYPE_DATA) { + /* read the first data chunk */ + len = io_read (io, this->s, this->buf, this->chunk_length); + + if (len != this->chunk_length) { + return 0; + } else { + return 1; + } + } else { + /* unexpected packet type */ + return 0; + } +} + +static void interp_header (mms_io_t *io, mmsh_t *this) { + + int i; + + lprintf ("interp_header, header_len=%d\n", this->asf_header_len); + + this->packet_length = 0; + + /* + * parse asf header + */ + + i = 30; + while ((i + 24) < 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; + + if ((i + length) >= this->asf_header_len) return; + + switch (guid) { + + case GUID_ASF_FILE_PROPERTIES: + + this->packet_length = LE_32(this->asf_header + i + 92 - 24); + if (this->packet_length > CHUNK_SIZE) { + this->packet_length = 0; + break; + } + this->file_length = LE_64(this->asf_header + i + 40 - 24); + lprintf ("file object, packet length = %d (%d)\n", + this->packet_length, LE_32(this->asf_header + i + 96 - 24)); + break; + + 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; + + 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); + + this->stream_types[stream_id] = type; + this->stream_ids[this->num_stream_ids] = stream_id; + this->num_stream_ids++; + + } + break; + + case GUID_ASF_STREAM_BITRATE_PROPERTIES: + { + uint16_t streams = LE_16(this->asf_header + i); + uint16_t stream_id; + int j; + + lprintf ("stream bitrate properties\n"); + lprintf ("streams %d\n", streams); + + 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 ("mmsh: stream id %d, bitrate %d\n", stream_id, this->bitrates[stream_id]); + } + } + break; + + default: + lprintf ("unknown object\n"); + break; + } + + lprintf ("length : %lld\n", length); + + if (length > 24) { + i += length - 24; + } + } +} + +const static char *const mmsh_proto_s[] = { "mms", "mmsh", NULL }; + +static int mmsh_valid_proto (char *proto) { + int i = 0; + + lprintf("mmsh_valid_proto\n"); + + if (!proto) + return 0; + + while(mmsh_proto_s[i]) { + if (!strcasecmp(proto, mmsh_proto_s[i])) { + return 1; + } + i++; + } + return 0; +} + +/* + * returns 1 on error + */ +static int mmsh_tcp_connect(mms_io_t *io, mmsh_t *this) { + int progress, res; + + if (!this->connect_port) this->connect_port = MMSH_PORT; + + /* + * try to connect + */ + lprintf("try to connect to %s on port %d \n", this->connect_host, this->connect_port); + + this->s = io_connect (io, this->connect_host, this->connect_port); + + if (this->s == -1) { + lprintf ("mmsh: failed to connect '%s'\n", this->connect_host); + return 1; + } + + /* connection timeout 15s */ + progress = 0; + do { +// 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 int mmsh_connect_int (mms_io_t *io, mmsh_t *this, int bandwidth) { + int i; + int video_stream = -1; + int audio_stream = -1; + int max_arate = -1; + int min_vrate = -1; + int min_bw_left = 0; + int stream_id; + int bandwitdh_left; + char stream_selection[10 * ASF_MAX_NUM_STREAMS]; /* 10 chars per stream */ + int offset; + + /* + * let the negotiations begin... + */ + this->num_stream_ids = 0; + + /* first request */ + lprintf("first http request\n"); + + snprintf (this->str, SCRATCH_SIZE, mmsh_FirstRequest, this->uri, + this->http_host, this->http_port, 1); + + if (!send_command (io, this, this->str)) + goto fail; + + if (!get_answer (io, this)) + goto fail; + + + get_header(io, this); + interp_header(io, this); + + close(this->s); +// report_progress (stream, 20); + + + /* choose the best quality for the audio stream */ + /* i've never seen more than one audio stream */ + 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 ((audio_stream == -1) || (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 = bandwidth - max_arate; + if (bandwitdh_left < 0) { + bandwitdh_left = 0; + } + lprintf("bandwitdh %d, left %d\n", 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; + } + } + + /* choose the stream with the lower bitrate */ + if ((video_stream == -1) && 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 ((video_stream == -1) || + (this->bitrates[stream_id] < min_vrate) || + (!min_vrate)) { + video_stream = stream_id; + min_vrate = this->bitrates[stream_id]; + } + break; + default: + break; + } + } + } + + lprintf("audio stream %d, video stream %d\n", audio_stream, video_stream); + + /* second request */ + lprintf("second http request\n"); + + if (mmsh_tcp_connect(io, this)) { + goto fail; + } + + /* stream selection string */ + /* The same selection is done with mmst */ + /* 0 means selected */ + /* 2 means disabled */ + offset = 0; + for (i = 0; i < this->num_stream_ids; i++) { + int size; + if ((this->stream_ids[i] == audio_stream) || + (this->stream_ids[i] == video_stream)) { + size = snprintf(stream_selection + offset, sizeof(stream_selection) - offset, + "ffff:%d:0 ", this->stream_ids[i]); + } else { + lprintf ("disabling stream %d\n", this->stream_ids[i]); + size = snprintf(stream_selection + offset, sizeof(stream_selection) - offset, + "ffff:%d:2 ", this->stream_ids[i]); + } + if (size < 0) goto fail; + offset += size; + } + + switch (this->stream_type) { + case MMSH_SEEKABLE: + snprintf (this->str, SCRATCH_SIZE, mmsh_SeekableRequest, this->uri, + this->http_host, this->http_port, 0, 0, 0, 2, 0, + this->num_stream_ids, stream_selection); + break; + case MMSH_LIVE: + snprintf (this->str, SCRATCH_SIZE, mmsh_LiveRequest, this->uri, + this->http_host, this->http_port, 2, + this->num_stream_ids, stream_selection); + break; + } + + if (!send_command (io, this, this->str)) + goto fail; + + lprintf("before read \n"); + + if (!get_answer (io, this)) + goto fail; + + if (!get_header(io, this)) + goto fail; + interp_header(io, this); + + for (i = 0; i < this->num_stream_ids; i++) { + if ((this->stream_ids[i] != audio_stream) && + (this->stream_ids[i] != video_stream)) { + lprintf("disabling stream %d\n", this->stream_ids[i]); + + /* 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; + } + } + } + return 1; +fail: + return 0; +} + +mmsh_t *mmsh_connect (mms_io_t *io, void *data, const char *url, int bandwidth) { + mmsh_t *this; + GURI *uri = NULL; + GURI *proxy_uri = NULL; + char *proxy_env; + if (!url) + return NULL; + +// report_progress (stream, 0); + /* + * initializatoin is essential here. the fail: label depends + * on the various char * in our this structure to be + * NULL if they haven't been assigned yet. + */ + this = (mmsh_t*) malloc (sizeof (mmsh_t)); + this->url=NULL; + this->proxy_url = NULL; + this->proto = NULL; + this->connect_host = NULL; + this->http_host = NULL; + this->proxy_user = NULL; + this->proxy_password = NULL; + this->host_user = NULL; + this->host_password = NULL; + this->uri = NULL; + + this->custom_data = data; + this->url = strdup(url); + if ((proxy_env = getenv("http_proxy")) != NULL) + this->proxy_url = strdup(proxy_env); + else + this->proxy_url = NULL; + this->s = -1; + 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; + this->has_audio = 0; + this->has_video = 0; + this->current_pos = 0; + this->user_bandwidth = bandwidth; + +// report_progress (stream, 0); + + if (this->proxy_url) { + proxy_uri = gnet_uri_new(this->proxy_url); + if (!proxy_uri) { + lprintf("invalid proxy url\n"); + goto fail; + } + if (! proxy_uri->port ) { + proxy_uri->port = 3128; //default squid port + } + } + uri = gnet_uri_new(this->url); + if (!uri) { + lprintf ("invalid url\n"); + goto fail; + } + if (! uri->port ) { + //checked in tcp_connect, but it's better to initialize it here + uri->port = MMSH_PORT; + } + if (this->proxy_url) { + this->proto = (uri->scheme) ? strdup(uri->scheme) : NULL; + this->connect_host = (proxy_uri->hostname) ? strdup(proxy_uri->hostname) : NULL; + this->connect_port = proxy_uri->port; + this->http_host = (uri->scheme) ? strdup(uri->hostname) : NULL; + this->http_port = uri->port; + this->proxy_user = (proxy_uri->user) ? strdup(proxy_uri->user) : NULL; + this->proxy_password = (proxy_uri->passwd) ? strdup(proxy_uri->passwd) : NULL; + this->host_user = (uri->user) ? strdup(uri->user) : NULL; + this->host_password = (uri->passwd) ? strdup(uri->passwd) : NULL; + gnet_uri_set_scheme(uri,"http"); + char * uri_string = gnet_uri_get_string(uri); + this->uri = strdup(uri_string); + g_free(uri_string); + } else { + this->proto = (uri->scheme) ? strdup(uri->scheme) : NULL; + this->connect_host = (uri->hostname) ? strdup(uri->hostname) : NULL; + this->connect_port = uri->port; + this->http_host = (uri->hostname) ? strdup(uri->hostname) : NULL; + this->http_port = uri->port; + this->proxy_user = NULL; + this->proxy_password = NULL; + this->host_user =(uri->user) ? strdup(uri->user) : NULL; + this->host_password = (uri->passwd) ? strdup(uri->passwd) : NULL; + this->uri = (uri->path) ? strdup(uri->path) : NULL; + } + if (proxy_uri) { + gnet_uri_delete(proxy_uri); + proxy_uri = NULL; + } + if (uri) { + gnet_uri_delete(uri); + uri = NULL; + } + if (!mmsh_valid_proto(this->proto)) { + lprintf ("unsupported protocol\n"); + goto fail; + } + + if (mmsh_tcp_connect(io, this)) { + goto fail; + } +// report_progress (stream, 30); + + if (!mmsh_connect_int(io, this, this->user_bandwidth)) + goto fail; + +// report_progress (stream, 100); + + lprintf("mmsh_connect: passed\n" ); + + return this; + +fail: + lprintf("mmsh_connect: failed\n" ); + if (proxy_uri) + gnet_uri_delete(proxy_uri); + if (uri) + gnet_uri_delete(uri); + if (this->s != -1) + close(this->s); + if (this->url) + free(this->url); + if (this->proxy_url) + free(this->proxy_url); + if (this->proto) + free(this->proto); + if (this->connect_host) + free(this->connect_host); + if (this->http_host) + free(this->http_host); + if (this->proxy_user) + free(this->proxy_user); + if (this->proxy_password) + free(this->proxy_password); + if (this->host_user) + free(this->host_user); + if (this->host_password) + free(this->host_password); + if (this->uri) + free(this->uri); + + free(this); + + lprintf("mmsh_connect: failed return\n" ); + return NULL; +} + + +/* + * returned value: + * 0: error + * 1: data packet read + * 2: new header read + */ +static int get_media_packet (mms_io_t *io, mmsh_t *this) { + int len = 0; + + lprintf("get_media_packet: this->packet_length: %d\n", this->packet_length); + + if (get_chunk_header(io, this)) { + switch (this->chunk_type) { + case CHUNK_TYPE_END: + /* this->chunk_seq_number: + * 0: stop + * 1: a new stream follows + */ + if (this->chunk_seq_number == 0) + return 0; + + close (this->s); + + if (mmsh_tcp_connect (io, this)) + return 0; + + if (!mmsh_connect_int (io, this, this->user_bandwidth)) + return 0; + + /* mmsh_connect_int reads the first data packet */ + /* this->buf_size is set by mmsh_connect_int */ + return 2; + + case CHUNK_TYPE_DATA: + /* nothing to do */ + break; + + case CHUNK_TYPE_RESET: + /* next chunk is an ASF header */ + + if (this->chunk_length != 0) { + /* that's strange, don't know what to do */ + return 0; + } + if (!get_header (io, this)) + return 0; + interp_header(io, this); + this->buf_size = this->packet_length; + return 2; + + default: + lprintf ("mmsh: unexpected chunk type\n"); + return 0; + } + + len = io_read (io, this->s, this->buf, this->chunk_length); + + if (len == this->chunk_length) { + /* explicit padding with 0 */ + if (this->chunk_length > this->packet_length) { + lprintf ("mmsh: chunk_length(%d) > packet_length(%d)\n", + this->chunk_length, this->packet_length); + return 0; + } + + { + char *base = (char *)(this->buf); + char *start = base + this->chunk_length; + char *end = start + this->packet_length - this->chunk_length; + if ((start > base) && (start < (base+CHUNK_SIZE-1)) && + (start < end) && (end < (base+CHUNK_SIZE-1))) { + memset(start, 0, + this->packet_length - this->chunk_length); + } + if (this->packet_length > CHUNK_SIZE) { + this->buf_size = CHUNK_SIZE; + } else { + this->buf_size = this->packet_length; + } + } + return 1; + } else { + lprintf ("mmsh: read error, %d != %d\n", len, this->chunk_length); + return 0; + } + } else { + return 0; + } +} + +int mmsh_peek_header (mmsh_t *this, char *data, int maxsize) { + int len; + + lprintf("mmsh_peek_header\n"); + + len = (this->asf_header_len < maxsize) ? this->asf_header_len : maxsize; + + memcpy(data, this->asf_header, len); + return len; +} + +int mmsh_read (mms_io_t *io, mmsh_t *this, char *data, int len) { + int total; + + total = 0; + + lprintf ("mmsh_read: len: %d\n", len); + + while (total < len) { + + if (this->asf_header_read < this->asf_header_len) { + int n, bytes_left ; + + bytes_left = this->asf_header_len - this->asf_header_read ; + + if ((len-total) < bytes_left) + n = len-total; + else + 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 ; + + bytes_left = this->buf_size - this->buf_read; + + if (bytes_left == 0) { + int packet_type; + + this->buf_size=this ->buf_read = 0; + packet_type = get_media_packet (io, this); + + if (packet_type == 0) { + lprintf ("mmsh: get_media_packet failed\n"); + return total; + } else if (packet_type == 2) { + continue; + } + bytes_left = this->buf_size; + } + + if ((len-total) < bytes_left) + n = len-total; + else + 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 mmsh_close (mmsh_t *this) { + + lprintf("mmsh_close\n"); + + if (this->s != -1) + close(this->s); + if (this->url) + free(this->url); + if (this->proxy_url) + free(this->proxy_url); + if (this->proto) + free(this->proto); + if (this->connect_host) + free(this->connect_host); + if (this->http_host) + free(this->http_host); + if (this->proxy_user) + free(this->proxy_user); + if (this->proxy_password) + free(this->proxy_password); + if (this->host_user) + free(this->host_user); + if (this->host_password) + free(this->host_password); + if (this->uri) + free(this->uri); + if (this) + free (this); +} + + +uint32_t mmsh_get_length (mmsh_t *this) { + return this->file_length; +} + +off_t mmsh_get_current_pos (mmsh_t *this) { + return this->current_pos; +} diff -r 93c0af9bb521 -r bf7fe0421354 Plugins/Input/wma/libffwma/mmsh.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/wma/libffwma/mmsh.h Sat Jul 15 17:03:07 2006 -0700 @@ -0,0 +1,53 @@ +/* + * 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 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: mmsh.h,v 1.4 2006/06/06 09:52:28 shawarma Exp $ + * + * libmmsh public header + */ + +#ifndef HAVE_MMSH_H +#define HAVE_MMSH_H + +#include +#include +#include +#include "mmsio.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct mmsh_s mmsh_t; + +char* mmsh_connect_common(int *s ,int *port, char *url, char **host, char **path, char **file); +mmsh_t* mmsh_connect (mms_io_t *io, void *data, const char *url_, int bandwidth); + +int mmsh_read (mms_io_t *io, mmsh_t *instance, char *data, int len); +uint32_t mmsh_get_length (mmsh_t *instance); +off_t mmsh_get_current_pos (mmsh_t *instance); +void mmsh_close (mmsh_t *instance); + +int mmsh_peek_header (mmsh_t *instance, char *data, int maxsize); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif