1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-25 00:02:13 +00:00
mpv/stream/asf_streaming.c

858 lines
26 KiB
C
Raw Normal View History

/*
* This file is part of MPlayer.
*
* MPlayer 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.
*
* MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <libavutil/intreadwrite.h>
#include "config.h"
#include "core/mp_msg.h"
#include "core/options.h"
#if HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#include "url.h"
#include "http.h"
#include "demux/asf.h"
#include "stream.h"
#include "demux/demux.h"
#include "asf_mmst_streaming.h"
#include "network.h"
#include "tcp.h"
#include "demux/asfguid.h"
extern int network_bandwidth;
static int asf_http_streaming_start(stream_t *stream, int *demuxer_type);
static int asf_read_wrapper(int fd, void *buffer, int len, streaming_ctrl_t *stream_ctrl) {
uint8_t *buf = buffer;
while (len > 0) {
int got = nop_streaming_read(fd, buf, len, stream_ctrl);
if (got <= 0) {
mp_tmsg(MSGT_NETWORK, MSGL_ERR, "Error while reading network stream.\n");
return got;
}
buf += got;
len -= got;
}
return 1;
}
// We can try several protocol for asf streaming
// * First we can try TCP, but if there is a proxy for
// internet connection, the TCP connection will not get
// through
// * Then we can try HTTP.
//
// Note: Using WMP sequence MMST and then HTTP.
static int asf_streaming_start( stream_t *stream, int *demuxer_type) {
char *proto = stream->streaming_ctrl->url->protocol;
int fd = -1;
int port = stream->streaming_ctrl->url->port;
//Is protocol mms or mmst?
if (!strcasecmp(proto, "mp_mmst") || !strcasecmp(proto, "mp_mms"))
{
mp_msg(MSGT_NETWORK,MSGL_V,"Trying ASF/TCP...\n");
fd = asf_mmst_streaming_start( stream );
stream->streaming_ctrl->url->port = port;
if( fd>-1 ) return fd;
mp_msg(MSGT_NETWORK,MSGL_V," ===> ASF/TCP failed\n");
if( fd==-2 ) return -1;
}
//Is protocol http, http_proxy, or mms?
if (!strcasecmp(proto, "mp_http_proxy") || !strcasecmp(proto, "mp_http") ||
!strcasecmp(proto, "mp_mms") || !strcasecmp(proto, "mp_mmsh") ||
!strcasecmp(proto, "mp_mmshttp"))
{
mp_msg(MSGT_NETWORK,MSGL_V,"Trying ASF/HTTP...\n");
fd = asf_http_streaming_start( stream, demuxer_type );
stream->streaming_ctrl->url->port = port;
if( fd>-1 ) return fd;
mp_msg(MSGT_NETWORK,MSGL_V," ===> ASF/HTTP failed\n");
if( fd==-2 ) return -1;
}
//everything failed
return -1;
}
static int asf_streaming(ASF_stream_chunck_t *stream_chunck, int *drop_packet ) {
/*
printf("ASF stream chunck size=%d\n", stream_chunck->size);
printf("length: %d\n", length );
printf("0x%02X\n", stream_chunck->type );
*/
if( drop_packet!=NULL ) *drop_packet = 0;
if( stream_chunck->size<8 ) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Ahhhh, stream_chunck size is too small: %d\n", stream_chunck->size);
return -1;
}
if( stream_chunck->size!=stream_chunck->size_confirm ) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"size_confirm mismatch!: %d %d\n", stream_chunck->size, stream_chunck->size_confirm);
return -1;
}
/*
printf(" type: 0x%02X\n", stream_chunck->type );
printf(" size: %d (0x%02X)\n", stream_chunck->size, stream_chunck->size );
printf(" sequence_number: 0x%04X\n", stream_chunck->sequence_number );
printf(" unknown: 0x%02X\n", stream_chunck->unknown );
printf(" size_confirm: 0x%02X\n", stream_chunck->size_confirm );
*/
switch(stream_chunck->type) {
case ASF_STREAMING_CLEAR: // $C Clear ASF configuration
mp_msg(MSGT_NETWORK,MSGL_V,"=====> Clearing ASF stream configuration!\n");
if( drop_packet!=NULL ) *drop_packet = 1;
return stream_chunck->size;
break;
case ASF_STREAMING_DATA: // $D Data follows
// printf("=====> Data follows\n");
break;
case ASF_STREAMING_END_TRANS: // $E Transfer complete
mp_msg(MSGT_NETWORK,MSGL_V,"=====> Transfer complete\n");
if( drop_packet!=NULL ) *drop_packet = 1;
return stream_chunck->size;
break;
case ASF_STREAMING_HEADER: // $H ASF header chunk follows
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF header chunk follows\n");
break;
default:
mp_msg(MSGT_NETWORK,MSGL_V,"=====> Unknown stream type 0x%x\n", stream_chunck->type );
}
return stream_chunck->size+4;
}
static void close_s(stream_t *stream) {
closesocket(stream->fd);
stream->fd=-1;
}
static int max_idx(int s_count, int *s_rates, int bound) {
int i, best = -1, rate = -1;
for (i = 0; i < s_count; i++) {
if (s_rates[i] > rate && s_rates[i] <= bound) {
rate = s_rates[i];
best = i;
}
}
return best;
}
static int asf_streaming_parse_header(stream_t *s, int fd) {
streaming_ctrl_t* streaming_ctrl = s->streaming_ctrl;
ASF_stream_chunck_t chunk;
asf_http_streaming_ctrl_t* asf_ctrl = streaming_ctrl->data;
char* buffer=NULL, *chunk_buffer=NULL;
int i,r,size,pos = 0;
int start;
int buffer_size = 0;
int chunk_size2read = 0;
int bw = streaming_ctrl->bandwidth;
int *v_rates = NULL, *a_rates = NULL;
int v_rate = 0, a_rate = 0, a_idx = -1, v_idx = -1;
if(asf_ctrl == NULL) return -1;
// The ASF header can be in several network chunks. For example if the content description
// is big, the ASF header will be split in 2 network chunk.
// So we need to retrieve all the chunk before starting to parse the header.
do {
if (asf_read_wrapper(fd, &chunk, sizeof(ASF_stream_chunck_t), streaming_ctrl) <= 0)
return -1;
// Endian handling of the stream chunk
le2me_ASF_stream_chunck_t(&chunk);
size = asf_streaming( &chunk, &r) - sizeof(ASF_stream_chunck_t);
if(r) mp_tmsg(MSGT_NETWORK,MSGL_WARN,"Warning: drop header ????\n");
if(size < 0){
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error while parsing chunk header\n");
return -1;
}
if (chunk.type != ASF_STREAMING_HEADER) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Didn't get a header as first chunk !!!!\n");
return -1;
}
// audit: do not overflow buffer_size
if (size > SIZE_MAX - buffer_size) return -1;
buffer = malloc(size+buffer_size);
if(buffer == NULL) {
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Error: Can't allocate %d bytes buffer.\n",size+buffer_size);
return -1;
}
if( chunk_buffer!=NULL ) {
memcpy( buffer, chunk_buffer, buffer_size );
free( chunk_buffer );
}
chunk_buffer = buffer;
buffer += buffer_size;
buffer_size += size;
if (asf_read_wrapper(fd, buffer, size, streaming_ctrl) <= 0)
return -1;
if( chunk_size2read==0 ) {
ASF_header_t *asfh = (ASF_header_t *)buffer;
if(size < (int)sizeof(ASF_header_t)) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error: Chunk is too small.\n");
return -1;
} else mp_msg(MSGT_NETWORK,MSGL_DBG2,"Got chunk\n");
chunk_size2read = AV_RL64(&asfh->objh.size);
mp_msg(MSGT_NETWORK,MSGL_DBG2,"Size 2 read=%d\n", chunk_size2read);
}
} while( buffer_size<chunk_size2read);
buffer = chunk_buffer;
size = buffer_size;
start = sizeof(ASF_header_t);
pos = find_asf_guid(buffer, asf_file_header_guid, start, size);
if (pos >= 0) {
ASF_file_header_t *fileh = (ASF_file_header_t *) &buffer[pos];
pos += sizeof(ASF_file_header_t);
if (pos > size) goto len_err_out;
/*
if(fileh.packetsize != fileh.packetsize2) {
printf("Error packetsize check don't match\n");
return -1;
}
*/
asf_ctrl->packet_size = AV_RL32(&fileh->max_packet_size);
// before playing.
// preroll: time in ms to bufferize before playing
unsigned int preroll = (unsigned int)(((double)fileh->preroll/1000.0)*((double)fileh->max_bitrate/8.0));
// buffer in KBytes, *5 assuming the prefill is 20% of the buffer.
s->cache_size = preroll / 1024 * 5;
}
pos = start;
while ((pos = find_asf_guid(buffer, asf_stream_header_guid, pos, size)) >= 0)
{
ASF_stream_header_t *streamh = (ASF_stream_header_t *)&buffer[pos];
pos += sizeof(ASF_stream_header_t);
if (pos > size) goto len_err_out;
switch(ASF_LOAD_GUID_PREFIX(streamh->type)) {
case 0xF8699E40 : // audio stream
if(asf_ctrl->audio_streams == NULL){
asf_ctrl->audio_streams = malloc(sizeof(int));
asf_ctrl->n_audio = 1;
} else {
asf_ctrl->n_audio++;
asf_ctrl->audio_streams = realloc(asf_ctrl->audio_streams,
asf_ctrl->n_audio*sizeof(int));
}
asf_ctrl->audio_streams[asf_ctrl->n_audio-1] = AV_RL16(&streamh->stream_no);
break;
case 0xBC19EFC0 : // video stream
if(asf_ctrl->video_streams == NULL){
asf_ctrl->video_streams = malloc(sizeof(int));
asf_ctrl->n_video = 1;
} else {
asf_ctrl->n_video++;
asf_ctrl->video_streams = realloc(asf_ctrl->video_streams,
asf_ctrl->n_video*sizeof(int));
}
asf_ctrl->video_streams[asf_ctrl->n_video-1] = AV_RL16(&streamh->stream_no);
break;
}
}
// always allocate to avoid lots of ifs later
v_rates = calloc(asf_ctrl->n_video, sizeof(int));
a_rates = calloc(asf_ctrl->n_audio, sizeof(int));
pos = find_asf_guid(buffer, asf_stream_group_guid, start, size);
if (pos >= 0) {
// stream bitrate properties object
int stream_count;
char *ptr = &buffer[pos];
char *end = &buffer[size];
mp_msg(MSGT_NETWORK, MSGL_V, "Stream bitrate properties object\n");
if (ptr + 2 > end) goto len_err_out;
stream_count = AV_RL16(ptr);
ptr += 2;
mp_msg(MSGT_NETWORK, MSGL_V, " stream count=[0x%x][%u]\n",
stream_count, stream_count );
for( i=0 ; i<stream_count ; i++ ) {
uint32_t rate;
int id;
int j;
if (ptr + 6 > end) goto len_err_out;
id = AV_RL16(ptr);
ptr += 2;
rate = AV_RL32(ptr);
ptr += 4;
mp_msg(MSGT_NETWORK, MSGL_V,
" stream id=[0x%x][%u]\n", id, id);
mp_msg(MSGT_NETWORK, MSGL_V,
" max bitrate=[0x%x][%u]\n", rate, rate);
for (j = 0; j < asf_ctrl->n_video; j++) {
if (id == asf_ctrl->video_streams[j]) {
mp_msg(MSGT_NETWORK, MSGL_V, " is video stream\n");
v_rates[j] = rate;
break;
}
}
for (j = 0; j < asf_ctrl->n_audio; j++) {
if (id == asf_ctrl->audio_streams[j]) {
mp_msg(MSGT_NETWORK, MSGL_V, " is audio stream\n");
a_rates[j] = rate;
break;
}
}
}
}
free(buffer);
// automatic stream selection based on bandwidth
if (bw == 0) bw = INT_MAX;
mp_msg(MSGT_NETWORK, MSGL_V, "Max bandwidth set to %d\n", bw);
if (asf_ctrl->n_audio) {
// find lowest-bitrate audio stream
a_rate = a_rates[0];
a_idx = 0;
for (i = 0; i < asf_ctrl->n_audio; i++) {
if (a_rates[i] < a_rate) {
a_rate = a_rates[i];
a_idx = i;
}
}
if (max_idx(asf_ctrl->n_video, v_rates, bw - a_rate) < 0) {
// both audio and video are not possible, try video only next
a_idx = -1;
a_rate = 0;
}
}
// find best video stream
v_idx = max_idx(asf_ctrl->n_video, v_rates, bw - a_rate);
if (v_idx >= 0)
v_rate = v_rates[v_idx];
// find best audio stream
a_idx = max_idx(asf_ctrl->n_audio, a_rates, bw - v_rate);
free(v_rates);
free(a_rates);
if (a_idx < 0 && v_idx < 0) {
mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Bandwidth too small, file cannot be played!\n");
return -1;
}
if (*streaming_ctrl->audio_id_ptr > 0)
// a audio stream was forced
asf_ctrl->audio_id = *streaming_ctrl->audio_id_ptr;
else if (a_idx >= 0)
asf_ctrl->audio_id = asf_ctrl->audio_streams[a_idx];
else if (asf_ctrl->n_audio) {
mp_tmsg(MSGT_NETWORK, MSGL_WARN, "Bandwidth too small, deselected audio stream.\n");
*streaming_ctrl->audio_id_ptr = -2;
}
if (*streaming_ctrl->video_id_ptr > 0)
// a video stream was forced
asf_ctrl->video_id = *streaming_ctrl->video_id_ptr;
else if (v_idx >= 0)
asf_ctrl->video_id = asf_ctrl->video_streams[v_idx];
else if (asf_ctrl->n_video) {
mp_tmsg(MSGT_NETWORK, MSGL_WARN, "Bandwidth too small, deselected video stream.\n");
*streaming_ctrl->video_id_ptr = -2;
}
return 1;
len_err_out:
mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Invalid length in ASF header!\n");
free(buffer);
free(v_rates);
free(a_rates);
return -1;
}
static int asf_http_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *streaming_ctrl ) {
static ASF_stream_chunck_t chunk;
int read,chunk_size = 0;
static int rest = 0, drop_chunk = 0, waiting = 0;
asf_http_streaming_ctrl_t *asf_http_ctrl = (asf_http_streaming_ctrl_t*)streaming_ctrl->data;
while(1) {
if (rest == 0 && waiting == 0) {
if (asf_read_wrapper(fd, &chunk, sizeof(ASF_stream_chunck_t), streaming_ctrl) <= 0)
return -1;
// Endian handling of the stream chunk
le2me_ASF_stream_chunck_t(&chunk);
chunk_size = asf_streaming( &chunk, &drop_chunk );
if(chunk_size < 0) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error while parsing chunk header\n");
return -1;
}
chunk_size -= sizeof(ASF_stream_chunck_t);
if(chunk.type != ASF_STREAMING_HEADER && (!drop_chunk)) {
if (asf_http_ctrl->packet_size < chunk_size) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error: chunk_size > packet_size\n");
return -1;
}
waiting = asf_http_ctrl->packet_size;
} else {
waiting = chunk_size;
}
} else if (rest){
chunk_size = rest;
rest = 0;
}
read = 0;
if ( waiting >= chunk_size) {
if (chunk_size > size){
rest = chunk_size - size;
chunk_size = size;
}
if (asf_read_wrapper(fd, buffer, chunk_size, streaming_ctrl) <= 0)
return -1;
read = chunk_size;
waiting -= read;
if (drop_chunk) continue;
}
if (rest == 0 && waiting > 0 && size-read > 0) {
int s = FFMIN(waiting,size-read);
memset(buffer+read,0,s);
waiting -= s;
read += s;
}
break;
}
return read;
}
static int asf_http_streaming_seek( int fd, int64_t pos, streaming_ctrl_t *streaming_ctrl ) {
return -1;
// to shut up gcc warning
fd++;
pos++;
streaming_ctrl=NULL;
}
static int asf_header_check( HTTP_header_t *http_hdr ) {
ASF_obj_header_t *objh;
if( http_hdr==NULL ) return -1;
if( http_hdr->body==NULL || http_hdr->body_size<sizeof(ASF_obj_header_t) ) return -1;
objh = (ASF_obj_header_t*)http_hdr->body;
if( ASF_LOAD_GUID_PREFIX(objh->guid)==0x75B22630 ) return 0;
return -1;
}
static int asf_http_streaming_type(char *content_type, char *features, HTTP_header_t *http_hdr ) {
if( content_type==NULL ) return ASF_Unknown_e;
if( !strcasecmp(content_type, "application/octet-stream") ||
!strcasecmp(content_type, "application/vnd.ms.wms-hdr.asfv1") || // New in Corona, first request
!strcasecmp(content_type, "application/x-mms-framed") || // New in Corana, second request
!strcasecmp(content_type, "video/x-ms-wmv") ||
!strcasecmp(content_type, "video/x-ms-asf")) {
if( strstr(features, "broadcast") ) {
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Live stream\n");
return ASF_Live_e;
} else {
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Prerecorded\n");
return ASF_Prerecorded_e;
}
} else {
// Ok in a perfect world, web servers should be well configured
// so we could used mime type to know the stream type,
// but guess what? All of them are not well configured.
// So we have to check for an asf header :(, but it works :p
if( http_hdr->body_size>sizeof(ASF_obj_header_t) ) {
if( asf_header_check( http_hdr )==0 ) {
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Plain text\n");
return ASF_PlainText_e;
} else if( (!strcasecmp(content_type, "text/html")) ) {
mp_msg(MSGT_NETWORK,MSGL_V,"=====> HTML, MPlayer is not a browser...yet!\n");
return ASF_Unknown_e;
} else {
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Redirector\n");
return ASF_Redirector_e;
}
} else {
if( (!strcasecmp(content_type, "audio/x-ms-wax")) ||
(!strcasecmp(content_type, "audio/x-ms-wma")) ||
(!strcasecmp(content_type, "video/x-ms-asf")) ||
(!strcasecmp(content_type, "video/x-ms-afs")) ||
(!strcasecmp(content_type, "video/x-ms-wmv")) ||
(!strcasecmp(content_type, "video/x-ms-wma")) ) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"=====> ASF Redirector\n");
return ASF_Redirector_e;
} else if( !strcasecmp(content_type, "text/plain") ) {
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Plain text\n");
return ASF_PlainText_e;
} else {
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF unknown content-type: %s\n", content_type );
return ASF_Unknown_e;
}
}
}
return ASF_Unknown_e;
}
static HTTP_header_t *asf_http_request(streaming_ctrl_t *streaming_ctrl) {
HTTP_header_t *http_hdr;
URL_t *url = NULL;
URL_t *server_url = NULL;
asf_http_streaming_ctrl_t *asf_http_ctrl;
char str[250];
char *ptr;
int i, enable;
int offset_hi=0, offset_lo=0, length=0;
int asf_nb_stream=0, stream_id;
// Sanity check
if( streaming_ctrl==NULL ) return NULL;
url = streaming_ctrl->url;
asf_http_ctrl = (asf_http_streaming_ctrl_t*)streaming_ctrl->data;
if( url==NULL || asf_http_ctrl==NULL ) return NULL;
// Common header for all requests.
http_hdr = http_new_header();
http_set_field( http_hdr, "Accept: */*" );
http_set_field( http_hdr, "User-Agent: NSPlayer/4.1.0.3856" );
http_add_basic_authentication( http_hdr, url->username, url->password );
// Check if we are using a proxy
if( !strcasecmp( url->protocol, "mp_http_proxy" ) ) {
server_url = url_new( (url->file)+1 );
if( server_url==NULL ) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"invalid proxy URL\n");
http_free( http_hdr );
return NULL;
}
http_set_uri( http_hdr, server_url->url );
sprintf( str, "Host: %.220s:%d", server_url->hostname, server_url->port );
url_free( server_url );
} else {
http_set_uri( http_hdr, url->file );
sprintf( str, "Host: %.220s:%d", url->hostname, url->port );
}
http_set_field( http_hdr, str );
http_set_field( http_hdr, "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}" );
sprintf(str,
"Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=%u",
offset_hi, offset_lo, asf_http_ctrl->request, length );
http_set_field( http_hdr, str );
switch( asf_http_ctrl->streaming_type ) {
case ASF_Live_e:
case ASF_Prerecorded_e:
http_set_field( http_hdr, "Pragma: xPlayStrm=1" );
ptr = str;
ptr += sprintf( ptr, "Pragma: stream-switch-entry=");
if(asf_http_ctrl->n_audio > 0) {
for( i=0; i<asf_http_ctrl->n_audio ; i++ ) {
stream_id = asf_http_ctrl->audio_streams[i];
if(stream_id == asf_http_ctrl->audio_id) {
enable = 0;
} else {
enable = 2;
continue;
}
asf_nb_stream++;
ptr += sprintf(ptr, "ffff:%x:%d ", stream_id, enable);
}
}
if(asf_http_ctrl->n_video > 0) {
for( i=0; i<asf_http_ctrl->n_video ; i++ ) {
stream_id = asf_http_ctrl->video_streams[i];
if(stream_id == asf_http_ctrl->video_id) {
enable = 0;
} else {
enable = 2;
continue;
}
asf_nb_stream++;
ptr += sprintf(ptr, "ffff:%x:%d ", stream_id, enable);
}
}
http_set_field( http_hdr, str );
sprintf( str, "Pragma: stream-switch-count=%d", asf_nb_stream );
http_set_field( http_hdr, str );
break;
case ASF_Redirector_e:
break;
case ASF_Unknown_e:
// First request goes here.
break;
default:
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"unknown ASF stream type\n");
}
http_set_field( http_hdr, "Connection: Close" );
http_build_request( http_hdr );
return http_hdr;
}
static int asf_http_parse_response(asf_http_streaming_ctrl_t *asf_http_ctrl, HTTP_header_t *http_hdr ) {
char *content_type, *pragma;
char features[64] = "\0";
size_t len;
if( http_response_parse(http_hdr)<0 ) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Failed to parse HTTP response.\n");
return -1;
}
switch( http_hdr->status_code ) {
case 200:
break;
case 401: // Authentication required
return ASF_Authenticate_e;
default:
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Server returned %d:%s\n", http_hdr->status_code, http_hdr->reason_phrase);
return -1;
}
content_type = http_get_field( http_hdr, "Content-Type");
//printf("Content-Type: [%s]\n", content_type);
pragma = http_get_field( http_hdr, "Pragma");
while( pragma!=NULL ) {
char *comma_ptr=NULL;
char *end;
//printf("Pragma: [%s]\n", pragma );
// The pragma line can get severals attributes
// separeted with a comma ','.
do {
if( !strncasecmp( pragma, "features=", 9) ) {
pragma += 9;
end = strstr( pragma, "," );
if( end==NULL ) {
len = strlen(pragma);
} else {
len = (unsigned int)(end-pragma);
}
if(len > sizeof(features) - 1) {
2011-04-28 07:36:00 +00:00
mp_tmsg(MSGT_NETWORK,MSGL_WARN,"ASF HTTP PARSE WARNING : Pragma %s cut from %zd bytes to %zd\n",pragma,len,sizeof(features) - 1);
len = sizeof(features) - 1;
}
strncpy( features, pragma, len );
features[len]='\0';
break;
}
comma_ptr = strstr( pragma, "," );
if( comma_ptr!=NULL ) {
pragma = comma_ptr+1;
if( pragma[0]==' ' ) pragma++;
}
} while( comma_ptr!=NULL );
pragma = http_get_next_field( http_hdr );
}
asf_http_ctrl->streaming_type = asf_http_streaming_type( content_type, features, http_hdr );
return 0;
}
static int asf_http_streaming_start( stream_t *stream, int *demuxer_type ) {
HTTP_header_t *http_hdr=NULL;
URL_t *url = stream->streaming_ctrl->url;
asf_http_streaming_ctrl_t *asf_http_ctrl;
char buffer[BUFFER_SIZE];
int i, ret;
int fd = stream->fd;
int done;
int auth_retry = 0;
asf_http_ctrl = malloc(sizeof(asf_http_streaming_ctrl_t));
if( asf_http_ctrl==NULL ) {
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
return -1;
}
asf_http_ctrl->streaming_type = ASF_Unknown_e;
asf_http_ctrl->request = 1;
asf_http_ctrl->audio_streams = asf_http_ctrl->video_streams = NULL;
asf_http_ctrl->n_audio = asf_http_ctrl->n_video = 0;
stream->streaming_ctrl->data = (void*)asf_http_ctrl;
do {
done = 1;
if( fd>0 ) closesocket( fd );
if( !strcasecmp( url->protocol, "mp_http_proxy" ) ) {
if( url->port==0 ) url->port = 8080;
} else {
if( url->port==0 ) url->port = 80;
}
fd = connect2Server( url->hostname, url->port, 1);
if( fd<0 ) return fd;
http_hdr = asf_http_request( stream->streaming_ctrl );
mp_msg(MSGT_NETWORK,MSGL_DBG2,"Request [%s]\n", http_hdr->buffer );
for(i=0; i < (int)http_hdr->buffer_size ; ) {
int r = send( fd, http_hdr->buffer+i, http_hdr->buffer_size-i, DEFAULT_SEND_FLAGS );
if(r <0) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"socket write error: %s\n",strerror(errno));
goto err_out;
}
i += r;
}
http_free( http_hdr );
http_hdr = http_new_header();
do {
i = recv( fd, buffer, BUFFER_SIZE, 0 );
//printf("read: %d\n", i );
if( i<=0 ) {
perror("read");
goto err_out;
}
http_response_append( http_hdr, buffer, i );
} while( !http_is_header_entire( http_hdr ) );
if( mp_msg_test(MSGT_NETWORK,MSGL_V) ) {
http_hdr->buffer[http_hdr->buffer_size]='\0';
mp_msg(MSGT_NETWORK,MSGL_DBG2,"Response [%s]\n", http_hdr->buffer );
}
ret = asf_http_parse_response(asf_http_ctrl, http_hdr);
if( ret<0 ) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Failed to parse header.\n");
goto err_out;
}
switch( asf_http_ctrl->streaming_type ) {
case ASF_Live_e:
case ASF_Prerecorded_e:
case ASF_PlainText_e:
if( http_hdr->body_size>0 ) {
if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
goto err_out;
}
}
if( asf_http_ctrl->request==1 ) {
if( asf_http_ctrl->streaming_type!=ASF_PlainText_e ) {
// First request, we only got the ASF header.
ret = asf_streaming_parse_header(stream,fd);
if(ret < 0) goto err_out;
if(asf_http_ctrl->n_audio == 0 && asf_http_ctrl->n_video == 0) {
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"No stream found.\n");
goto err_out;
}
asf_http_ctrl->request++;
done = 0;
} else {
done = 1;
}
}
break;
case ASF_Redirector_e:
if( http_hdr->body_size>0 ) {
if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
goto err_out;
}
}
*demuxer_type = DEMUXER_TYPE_PLAYLIST;
done = 1;
break;
case ASF_Authenticate_e:
if( http_authenticate( http_hdr, url, &auth_retry)<0 ) return -1;
asf_http_ctrl->streaming_type = ASF_Unknown_e;
done = 0;
break;
case ASF_Unknown_e:
default:
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"unknown ASF streaming type\n");
goto err_out;
}
// Check if we got a redirect.
} while(!done);
stream->fd = fd;
if( asf_http_ctrl->streaming_type==ASF_PlainText_e || asf_http_ctrl->streaming_type==ASF_Redirector_e ) {
stream->streaming_ctrl->streaming_read = nop_streaming_read;
stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
} else {
stream->streaming_ctrl->streaming_read = asf_http_streaming_read;
stream->streaming_ctrl->streaming_seek = asf_http_streaming_seek;
stream->streaming = true;
}
stream->streaming_ctrl->status = streaming_playing_e;
stream->close = close_s;
http_free( http_hdr );
return 0;
err_out:
if (fd > 0)
closesocket(fd);
stream->fd = -1;
http_free(http_hdr);
return -1;
}
static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
stream->streaming_ctrl = streaming_ctrl_new();
if( stream->streaming_ctrl==NULL ) {
return STREAM_ERROR;
}
stream->streaming_ctrl->audio_id_ptr = &stream->opts->audio_id;
stream->streaming_ctrl->video_id_ptr = &stream->opts->video_id;
stream->streaming_ctrl->bandwidth = network_bandwidth;
Fix potential bugs and issues, general cleanups Most of these are reimar fixing issues found by Coverity static analyzer, and possibly some more cleanup commits independent from this. Since these commits are rather noisy, squash them all together. Try to make code a bit clearer. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35294 b3059339-0415-0410-9bf9-f77b7e298cf2 Conflicts: audio/out/ao_alsa.c Check the correct variable for NULL. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35323 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove pointless unreachable code (the loop condition already checks the 0xff case). git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35325 b3059339-0415-0410-9bf9-f77b7e298cf2 Fix typo that might have caused reading beyond the string end. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35326 b3059339-0415-0410-9bf9-f77b7e298cf2 Do not needlessly use "long" types. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35331 b3059339-0415-0410-9bf9-f77b7e298cf2 Use AV_RB32 to avoid sign extension issues and validate offset before using it. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35332 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove nonsense casts. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35343 b3059339-0415-0410-9bf9-f77b7e298cf2 Fix crash in case sh_audio allocation failed. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35348 b3059339-0415-0410-9bf9-f77b7e298cf2 Fix potential NULL dereference. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35351 b3059339-0415-0410-9bf9-f77b7e298cf2 Conflicts: libmpcodecs/ad_ffmpeg.c Note: Slightly modified. Fix malloc failure check to check the correct variable. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35353 b3059339-0415-0410-9bf9-f77b7e298cf2 Avoid code duplication and pointless casts. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35363 b3059339-0415-0410-9bf9-f77b7e298cf2 Conflicts: stream/tv.c Error out if an invalid channel list name was specified instead of continuing and reading outside array bounds all over the place. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35364 b3059339-0415-0410-9bf9-f77b7e298cf2 Conflicts: stream/tv.c Make array "static const". git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35365 b3059339-0415-0410-9bf9-f77b7e298cf2 Properly free resources even when encountering many parse errors. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35367 b3059339-0415-0410-9bf9-f77b7e298cf2 Conflicts: parser-cfg.c Avoid leaks in error handling. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35380 b3059339-0415-0410-9bf9-f77b7e298cf2 Do not do sign comparisons on "char" type which can be both signed or unsigned. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35381 b3059339-0415-0410-9bf9-f77b7e298cf2 Free cookies file data after parsing it. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35382 b3059339-0415-0410-9bf9-f77b7e298cf2 http_set_field only makes a copy of the string, so we still need to free it. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35383 b3059339-0415-0410-9bf9-f77b7e298cf2 check4proxies does not modify input URL, so mark it const. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35390 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove proxy "support" from stream_rtp and stream_upd, trying to use a http proxy for UDP connections makes no sense. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35394 b3059339-0415-0410-9bf9-f77b7e298cf2 Conflicts: stream/stream_rtp.c stream/stream_udp.c Add url_new_with_proxy function to reduce code duplication and memleaks. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35395 b3059339-0415-0410-9bf9-f77b7e298cf2 Conflicts: stream/pnm.c stream/stream_live555.c stream/stream_nemesi.c stream/stream_rtsp.c Fix off-by-one errors in file descriptor validity checks. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35402 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove pointless cast. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35403 b3059339-0415-0410-9bf9-f77b7e298cf2 Abort when opening the file failed instead of calling "write" with an invalid descriptor. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35404 b3059339-0415-0410-9bf9-f77b7e298cf2 Remove pointless local variable. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@35411 b3059339-0415-0410-9bf9-f77b7e298cf2 Conflicts: stream/http.c
2012-10-30 17:28:34 +00:00
stream->streaming_ctrl->url = url_new_with_proxy(stream->url);
mp_tmsg(MSGT_OPEN, MSGL_INFO, "STREAM_ASF, URL: %s\n", stream->url);
if((!strncmp(stream->url, "mp_http", 4)) && (*file_format!=DEMUXER_TYPE_ASF && *file_format!=DEMUXER_TYPE_UNKNOWN)) {
streaming_ctrl_free(stream->streaming_ctrl);
stream->streaming_ctrl = NULL;
return STREAM_UNSUPPORTED;
}
if(asf_streaming_start(stream, file_format) < 0) {
mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed, exiting.\n");
streaming_ctrl_free(stream->streaming_ctrl);
stream->streaming_ctrl = NULL;
return STREAM_UNSUPPORTED;
}
if (*file_format != DEMUXER_TYPE_PLAYLIST)
*file_format = DEMUXER_TYPE_ASF;
stream->type = STREAMTYPE_STREAM;
return STREAM_OK;
}
const stream_info_t stream_info_asf = {
"mms and mms over http streaming",
"null",
"Bertrand, Reimar Doeffinger, Albeu",
"originally based on work by Majormms (is that code still there?)",
open_s,
{"mp_mms", "mp_mmst", "mp_http", "mp_http_proxy", "mp_mmsh", "mp_mmshttp", NULL},
NULL,
0 // Urls are an option string
};