diff options
-rw-r--r-- | src/input/input_mms.c | 65 | ||||
-rw-r--r-- | src/input/mms.c | 324 |
2 files changed, 303 insertions, 86 deletions
diff --git a/src/input/input_mms.c b/src/input/input_mms.c index 87cc05b4b..77beec4d3 100644 --- a/src/input/input_mms.c +++ b/src/input/input_mms.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: input_mms.c,v 1.26 2002/11/20 11:57:43 mroi Exp $ + * $Id: input_mms.c,v 1.27 2002/12/12 23:48:02 tmattern Exp $ * * mms input plugin based on work from major mms */ @@ -55,12 +55,13 @@ extern int errno; #define FNDELAY O_NDELAY #endif -typedef struct { - - input_class_t input_class; +/* network bandwidth */ +const uint32_t mms_bandwidths[]={14400,19200,28800,33600,34430,57600, + 115200,262200,393216,524300,1544000,10485800}; - xine_t *xine; -} mms_input_class_t; +const char * mms_bandwidth_strs[]={"14.4 Kbps", "19.2 Kbps", "28.8 Kbps", "33.6 Kbps", + "34.4 Kbps", "57.6 Kbps", "115.2 Kbps","262.2 Kbps", + "393.2 Kbps","524.3 Kbps", "1.5 Mbps", "10.5 Mbps", NULL}; typedef struct { input_plugin_t input_plugin; @@ -75,8 +76,18 @@ typedef struct { char scratch[1025]; + int bandwidth; + } mms_input_plugin_t; +typedef struct { + + input_class_t input_class; + + mms_input_plugin_t *ip; + + xine_t *xine; +} mms_input_class_t; static off_t mms_plugin_read (input_plugin_t *this_gen, char *buf, off_t len) { @@ -249,14 +260,31 @@ static int mms_plugin_get_optional_data (input_plugin_t *this_gen, return INPUT_OPTIONAL_UNSUPPORTED; } +void bandwidth_changed_cb (void *this_gen, xine_cfg_entry_t *entry) { + mms_input_class_t *class = (mms_input_class_t*) this_gen; + +#ifdef LOG + printf ("input_mms: bandwidth_changed_cb %d\n", entry->num_value); +#endif + if(!class) + return; + + if(class->ip && ((entry->num_value >= 0) && (entry->num_value <= 11))) { + mms_input_plugin_t *this = class->ip; + + this->bandwidth = mms_bandwidths[entry->num_value]; + } +} + static input_plugin_t *open_plugin (input_class_t *cls_gen, xine_stream_t *stream, const char *data) { - /* mms_input_class_t *cls = (mms_input_class_t *) cls_gen; */ + mms_input_class_t *cls = (mms_input_class_t *) cls_gen; mms_input_plugin_t *this; mms_t *mms; char *mrl = strdup(data); - + xine_cfg_entry_t bandwidth_entry; + #ifdef LOG printf ("input_mms: trying to open '%s'\n", mrl); #endif @@ -266,15 +294,23 @@ static input_plugin_t *open_plugin (input_class_t *cls_gen, xine_stream_t *strea return NULL; } - mms = mms_connect (stream, mrl); + this = (mms_input_plugin_t *) malloc (sizeof (mms_input_plugin_t)); + cls->ip = this; + + if (xine_config_lookup_entry (stream->xine, "input.mms_network_bandwidth", + &bandwidth_entry)) { + bandwidth_changed_cb(cls, &bandwidth_entry); + } + mms = mms_connect (stream, mrl, this->bandwidth); if (!mms) { free (mrl); return NULL; } - this = (mms_input_plugin_t *) malloc (sizeof (mms_input_plugin_t)); - + + + this->mms = mms; this->mrl = mrl; this->curpos = 0; @@ -291,6 +327,7 @@ static input_plugin_t *open_plugin (input_class_t *cls_gen, xine_stream_t *strea this->input_plugin.dispose = mms_plugin_dispose; this->input_plugin.get_optional_data = mms_plugin_get_optional_data; this->input_plugin.input_class = cls_gen; + return &this->input_plugin; } @@ -320,6 +357,7 @@ static void *init_class (xine_t *xine, void *data) { this = (mms_input_class_t *) xine_xmalloc (sizeof (mms_input_class_t)); this->xine = xine; + this->ip = NULL; this->input_class.open_plugin = open_plugin; this->input_class.get_identifier = mms_class_get_identifier; @@ -329,6 +367,11 @@ static void *init_class (xine_t *xine, void *data) { this->input_class.dispose = mms_class_dispose; this->input_class.eject_media = NULL; + xine->config->register_enum(xine->config, "input.mms_network_bandwidth", 10, + mms_bandwidth_strs, + "Network bandwidth", + NULL, 0, bandwidth_changed_cb, (void*) this); + return this; } diff --git a/src/input/mms.c b/src/input/mms.c index be4e19bf2..2efc62a01 100644 --- a/src/input/mms.c +++ b/src/input/mms.c @@ -17,7 +17,7 @@ * 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.c,v 1.15 2002/12/03 23:30:39 tmattern Exp $ + * $Id: mms.c,v 1.16 2002/12/12 23:48:03 tmattern Exp $ * * based on work from major mms * utility functions to handle communication with an mms server @@ -46,6 +46,7 @@ #include "bswap.h" #include "mms.h" +#include "../demuxers/asfheader.h" /* #define LOG @@ -62,6 +63,7 @@ #define CMD_HEADER_LEN 48 #define CMD_BODY_LEN 1024 + struct mms_s { int s; @@ -84,14 +86,20 @@ struct mms_s { int buf_read; uint8_t asf_header[8192]; - int asf_header_len; - int asf_header_read; + uint32_t asf_header_len; + uint32_t asf_header_read; int seq_num; int num_stream_ids; - int stream_ids[20]; + int stream_ids[ASF_MAX_NUM_STREAMS]; + int stream_types[ASF_MAX_NUM_STREAMS]; int packet_length; uint32_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; }; /* network/socket utility functions */ @@ -187,17 +195,79 @@ static int host_connect(const char *host, int port) { 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[this->scmd_len ] = value & 0xff; + this->scmd[this->scmd_len + 1] = (value >> 8) & 0xff; + this->scmd[this->scmd_len + 2] = (value >> 16) & 0xff; + this->scmd[this->scmd_len + 3] = (value >> 24) & 0xff; this->scmd_len += 4; } +static uint32_t get_64 (uint8_t *buffer, int offset) { + + uint64_t ret; + + ret = ((uint64_t)buffer[offset]) | + ((uint64_t)buffer[offset + 1] << 8) | + ((uint64_t)buffer[offset + 2] << 16) | + ((uint64_t)buffer[offset + 2] << 24) | + ((uint64_t)buffer[offset + 2] << 32) | + ((uint64_t)buffer[offset + 2] << 40) | + ((uint64_t)buffer[offset + 2] << 48) | + ((uint64_t)buffer[offset + 2] << 56); + + return ret; +} + +static uint32_t get_32 (uint8_t *buffer, int offset) { + + uint32_t ret; + + ret = buffer[offset] | + buffer[offset + 1] << 8 | + buffer[offset + 2] << 16 | + buffer[offset + 3] << 24 ; + + return ret; +} + +static uint16_t get_16 (unsigned char *buffer, int offset) { + + uint16_t ret; + + ret = buffer[offset] | + buffer[offset + 1] << 8; + + return ret; +} + +static int get_guid (unsigned char *buffer, int offset) { + int i; + GUID g; + + g.v1 = get_32(buffer, offset); + g.v2 = get_16(buffer, offset + 4); + g.v3 = get_16(buffer, offset + 6); + for(i = 0; i < 8; i++) { + g.v4[i] = buffer[offset + 8 + i]; + } + + for (i = 1; i < GUID_END; i++) { + if (!memcmp(&g, &guids[i].guid, sizeof(GUID))) { +#ifdef LOG + printf ("libmms: GUID: %s\n", guids[i].name); +#endif + return i; + } + } + + printf ("libmms: 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.v1, g.v2, g.v3, + g.v4[0], g.v4[1], g.v4[2], g.v4[3], g.v4[4], g.v4[5], g.v4[6], g.v4[7]); + return GUID_ERROR; +} + static int send_data (int s, char *buf, int len) { int total, timeout; @@ -223,18 +293,6 @@ static int send_data (int s, char *buf, int len) { 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 int send_command (mms_t *this, int command, uint32_t switches, uint32_t extra, int length) { int len8; @@ -511,71 +569,104 @@ static void interp_header (mms_t *this) { i = 30; while (i < this->asf_header_len) { - uint64_t guid_1, guid_2, length; + int guid; + uint64_t 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); + guid = get_guid(this->asf_header, i); + i += 16; + + length = get_64(this->asf_header, i); 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; + switch (guid) { + case GUID_ASF_FILE_PROPERTIES: + + 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 ("guid found: %016llx%016llx\n", guid_1, guid_2); + printf ("libmms: file object, packet length = %d (%d)\n", + this->packet_length, get_32(this->asf_header, i + 96 - 24)); #endif + break; + + case GUID_ASF_STREAM_PROPERTIES: + { + uint16_t stream_id; + int type; + + 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: + 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; + } + + stream_id = get_16(this->asf_header, i + 48); - 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)\n", - this->packet_length, get_32(this->asf_header, i + 96 - 24)); + printf ("libmms: stream object, stream id: %d\n", stream_id); #endif + this->stream_types[stream_id] = type; + this->stream_ids[this->num_stream_ids] = stream_id; + this->num_stream_ids++; + + } + break; - - } else if ((guid_1 == 0x6553200cc000e68e) && (guid_2 == 0x11cfa9b7b7dc0791)) { - - int stream_id = this->asf_header[i + 48] | this->asf_header[i + 49] << 8; + case GUID_ASF_STREAM_BITRATE_PROPERTIES: + { + uint16_t streams = get_16(this->asf_header, i); + uint16_t stream_id; + int j; #ifdef LOG - printf ("stream object, stream id: %d\n", stream_id); + printf ("libmms: stream bitrate properties\n"); #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 ("libmms: streams %d\n", streams); +#endif + for(j = 0; j < streams; j++) { + stream_id = get_16(this->asf_header, i + 2 + j * 6); +#ifdef LOG + printf ("libmms: stream id %d\n", stream_id); +#endif + this->bitrates[stream_id] = get_32(this->asf_header, i + 4 + j * 6); + this->bitrates_pos[stream_id] = i + 4 + j * 6; + printf ("libmms: stream id %d, bitrate %d\n", stream_id, + this->bitrates[stream_id]); + } + } + break; + + default: +#ifdef LOG + printf ("libmms: unknown object\n"); +#endif + break; } #ifdef LOG - printf ("length : %lld\n", length); + printf ("libmms: length : %lld\n", length); #endif - i += length - 24; - + if (length > 24) { + i += length - 24; + } } } @@ -693,7 +784,7 @@ static void report_progress (xine_stream_t *stream, int p) { xine_event_send (stream, &event); } -mms_t *mms_connect (xine_stream_t *stream, const char *url_) { +mms_t *mms_connect (xine_stream_t *stream, const char *url_, int bandwidth) { mms_t *this; char *url = NULL; char *url1 = NULL; @@ -702,6 +793,13 @@ mms_t *mms_connect (xine_stream_t *stream, const char *url_) { char *host = NULL; int port; int i, s; + 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; if (!url_) return NULL; @@ -734,7 +832,9 @@ mms_t *mms_connect (xine_stream_t *stream, const char *url_) { this->packet_length = 0; this->buf_size = 0; this->buf_read = 0; - + this->has_audio = 0; + this->has_video = 0; + #ifdef LOG printf ("libmms: url=%s\nlibmms: host=%s\nlibmms: " "path=%s\nlibmms: file=%s\n", url, host, path, file); @@ -807,19 +907,93 @@ mms_t *mms_connect (xine_stream_t *stream, const char *url_) { report_progress (stream, 50); /* 0x33 */ + /* 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 (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; + } +#ifdef LOG + printf("libmms: bandwitdh %d, left %d\n", bandwidth, bandwitdh_left); +#endif + 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 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; + } + } + } + + printf("libmms: audio stream %d, video stream %d\n", audio_stream, video_stream); + 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 ] = 0x00; + 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 { + printf("libmms: 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 */ + 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 (this, 0x33, this->num_stream_ids, 0xFFFF | this->stream_ids[0] << 16, - (this->num_stream_ids-1)*6+2)) + this->num_stream_ids * 6 + 2)) goto fail; if (!get_answer (this)) |