Commit 5813fcd8 authored by Laurent Aimar's avatar Laurent Aimar

* modules/demux/asf/.cvsignore : put the good one.

 * modules/access/mms : add MMS (Microsoft Media Streaming) access (Support
only TCP, I will try to add UDP and HTTP as well).
 There are some problems with streams selections and we cannot seek, but
anyway it seems to work. (Usefull for some radio web)
 * other: enable mms access by default.
parent c0e8ae07
......@@ -582,7 +582,7 @@ PLUGINS="${PLUGINS} wav araw"
dnl
dnl Network modules
dnl
NETWORK_MODULES="access_udp access_http access_rtp ipv4"
NETWORK_MODULES="access_udp access_http access_rtp ipv4 access_mms"
dnl
dnl Accelerated modules
......
......@@ -4,6 +4,7 @@ EXTRA_DIST = \
access/dvd/Modules.am \
access/dvdplay/Modules.am \
access/dvdread/Modules.am \
access/mms/Modules.am \
access/satellite/Modules.am \
access/v4l/Modules.am \
access/vcd/Modules.am \
......
SOURCES_access_mms = modules/access/mms/mms.c
noinst_HEADERS += modules/access/mms/mms.h \
modules/access/mms/var_buffer.h \
modules/access/mms/asf.h
/*****************************************************************************
* asf.h: MMS access plug-in
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: asf.h,v 1.1 2002/11/12 00:54:40 fenrir Exp $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program 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.
*
* 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, USA.
*****************************************************************************/
/****************************************************************************
* XXX:
* Definitions and data duplicated from asf demuxers but I want access
* and demux plugins to be independant
*
****************************************************************************/
typedef struct guid_s
{
u32 v1; /* le */
u16 v2; /* le */
u16 v3; /* le */
u8 v4[8];
} guid_t;
static inline int CmpGuid( const guid_t *p_guid1, const guid_t *p_guid2 )
{
return( ( p_guid1->v1 == p_guid2->v1 &&
p_guid1->v2 == p_guid2->v2 &&
p_guid1->v3 == p_guid2->v3 &&
p_guid1->v4[0] == p_guid2->v4[0] &&
p_guid1->v4[1] == p_guid2->v4[1] &&
p_guid1->v4[2] == p_guid2->v4[2] &&
p_guid1->v4[3] == p_guid2->v4[3] &&
p_guid1->v4[4] == p_guid2->v4[4] &&
p_guid1->v4[5] == p_guid2->v4[5] &&
p_guid1->v4[6] == p_guid2->v4[6] &&
p_guid1->v4[7] == p_guid2->v4[7] ) ? 1 : 0 );
}
static inline void GenerateGuid( guid_t *p_guid )
{
int i;
srand( mdate() & 0xffffffff );
/* FIXME should be generated using random data */
p_guid->v1 = 0xbabac001;
p_guid->v2 = ( (u64)rand() << 16 ) / RAND_MAX;
p_guid->v3 = ( (u64)rand() << 16 ) / RAND_MAX;
for( i = 0; i < 8; i++ )
{
p_guid->v4[i] = ( (u64)rand() * 256 ) / RAND_MAX;
}
}
#define GUID_FMT "%8.8x-%4.4x-%4.4x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x"
#define GUID_PRINT( guid ) \
(guid).v1, \
(guid).v2, \
(guid).v3, \
(guid).v4[0],(guid).v4[1],(guid).v4[2],(guid).v4[3], \
(guid).v4[4],(guid).v4[5],(guid).v4[6],(guid).v4[7]
static const guid_t asf_object_header_guid =
{
0x75B22630,
0x668E,
0x11CF,
{ 0xA6,0xD9, 0x00,0xAA,0x00,0x62,0xCE,0x6C }
};
static const guid_t asf_object_stream_properties_guid =
{
0xB7DC0791,
0xA9B7,
0x11CF,
{ 0x8E,0xE6, 0x00,0xC0,0x0C,0x20,0x53,0x65 }
};
static const guid_t asf_object_stream_type_audio =
{
0xF8699E40,
0x5B4D,
0x11CF,
{ 0xA8,0xFD, 0x00,0x80,0x5F,0x5C,0x44,0x2B }
};
static const guid_t asf_object_stream_type_video =
{
0xbc19efc0,
0x5B4D,
0x11CF,
{ 0xA8,0xFD, 0x00,0x80,0x5F,0x5C,0x44,0x2B }
};
static const guid_t asf_object_bitrate_properties_guid =
{
0x7BF875CE,
0x468D,
0x11D1,
{ 0x8D,0x82,0x00,0x60,0x97,0xC9,0xA2,0xB2 }
};
static const guid_t asf_object_bitrate_mutual_exclusion_guid =
{
0xD6E229DC,
0x35DA,
0x11D1,
{ 0x90,0x34,0x00,0xA0,0xC9,0x03,0x49,0xBE }
};
/*****************************************************************************
* mms.c: MMS access plug-in
*****************************************************************************
* Copyright (C) 2001, 2002 VideoLAN
* $Id: mms.c,v 1.1 2002/11/12 00:54:40 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program 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.
*
* 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, USA.
*****************************************************************************/
/*
* TODO:
* - clean code, break huge code block
* - fix memory leak
* - begin udp support...
*/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 )
# include <io.h>
#endif
#ifdef WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
# ifndef IN_MULTICAST
# define IN_MULTICAST(a) IN_CLASSD(a)
# endif
#else
# include <sys/socket.h>
#endif
#include "network.h"
#include "asf.h"
#include "var_buffer.h"
#include "mms.h"
/****************************************************************************
* NOTES:
* MMSProtocole documentation found at http://get.to/sdp
****************************************************************************/
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int Read ( input_thread_t * p_input, byte_t * p_buffer,
size_t i_len );
static void Seek ( input_thread_t *, off_t );
static int SetProgram ( input_thread_t *, pgrm_descriptor_t * );
static int MMSOpen( input_thread_t *, url_t *, int, char * );
static int MMSStart ( input_thread_t *, uint32_t );
static int MMSStop ( input_thread_t *p_input ); // Not used
static int MMSClose ( input_thread_t * );
static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 );
static int mms_CommandSend( input_thread_t *, int, uint32_t, uint32_t, uint8_t *, int );
static int mms_HeaderMediaRead( input_thread_t *, int );
static int mms_ReceivePacket( input_thread_t * );
static void mms_ParseURL( url_t *p_url, char *psz_url );
/*
* XXX DON'T FREE MY MEMORY !!! XXX
* non mais :P
*/
#define INPUT_FDNETWORKCLOSE( p_input ) \
{ \
void *__p_access = p_input->p_access_data; \
input_socket_t *__p_socket = malloc( sizeof( input_socket_t ) ); \
memcpy( __p_socket, __p_access, sizeof( input_socket_t ) ); \
p_input->p_access_data = (void*)__p_socket; \
input_FDNetworkClose( p_input ); \
p_input->p_access_data = __p_access; \
}
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("MMS access module") );
set_capability( "access", 0 );
add_shortcut( "mms" );
add_shortcut( "mmsu" );
add_shortcut( "mmst" );
set_callbacks( Open, Close );
vlc_module_end();
#define BUF_SIZE 200000
static int Open( vlc_object_t *p_this )
{
access_t *p_access;
int i_proto;
char *psz_network;
int i_status;
input_thread_t *p_input = (input_thread_t*)p_this;
/* *** allocate p_access_data *** */
p_input->p_access_data =
(void*)p_access = malloc( sizeof( access_t ) );
memset( p_access, 0, sizeof( access_t ) );
p_access->p_cmd = malloc( BUF_SIZE );
/* *** Parse URL and get server addr/port and path *** */
mms_ParseURL( &p_access->url, p_input->psz_name );
if( p_access->url.psz_server_addr == NULL ||
!( *p_access->url.psz_server_addr ) )
{
FREE( p_access->url.psz_private );
FREE( p_access->p_cmd );
msg_Err( p_input, "invalid server name" );
return( -1 );
}
if( p_access->url.i_server_port == 0 )
{
p_access->url.i_server_port = 1755; // default port
}
/* *** connect to this server *** */
/* 1: look at requested protocol (udp/tcp) */
i_proto = MMS_PROTO_AUTO;
if( *p_input->psz_access )
{
if( !strncmp( p_input->psz_access, "mmsu", 4 ) )
{
i_proto = MMS_PROTO_UDP;
}
else if( !strncmp( p_input->psz_access, "mmst", 4 ) )
{
i_proto = MMS_PROTO_TCP;
}
}
/* 2: look at ip version ipv4/ipv6 */
psz_network = "";
if( config_GetInt( p_input, "ipv4" ) )
{
psz_network = "ipv4";
}
else if( config_GetInt( p_input, "ipv6" ) )
{
psz_network = "ipv6";
}
/* 3: connect */
if( i_proto == MMS_PROTO_AUTO )
{ // first try with TCP
i_status =
MMSOpen( p_input, &p_access->url, MMS_PROTO_TCP, psz_network );
if( i_status < 0 )
{ // then with UDP
i_status =
MMSOpen( p_input, &p_access->url, MMS_PROTO_UDP, psz_network );
}
}
else
{
i_status =
MMSOpen( p_input, &p_access->url, i_proto, psz_network );
}
if( i_status < 0 )
{
// all sockets are closed
msg_Err( p_input, "cannot connect to server" );
FREE( p_access->url.psz_private );
FREE( p_access->p_cmd );
return( -1 );
}
msg_Dbg( p_input, "connected to %s", p_access->url.psz_server_addr );
// all sockets are open
/* *** set exported functions *** */
p_input->pf_read = Read;
p_input->pf_seek = Seek;
p_input->pf_set_program = SetProgram;
p_input->pf_set_area = NULL;
p_input->p_private = NULL; // XXX ??
/* *** finished to set some variable *** */
vlc_mutex_lock( &p_input->stream.stream_lock );
/* those data could be different for UDP/TCP */
p_input->stream.b_pace_control = 0;
p_input->stream.p_selected_area->i_tell = 0;
if( p_access->i_packet_count <= 0 )
{
p_input->stream.b_seekable = 0;
p_input->stream.p_selected_area->i_size = 0;
}
else
{
p_input->stream.b_seekable = 0;
p_input->stream.p_selected_area->i_size =
p_access->i_header +
p_access->i_packet_count * p_access->i_packet_length;
}
p_input->stream.i_method = INPUT_METHOD_NETWORK;
vlc_mutex_unlock( &p_input->stream.stream_lock );
/* *** Start stream *** */
if( MMSStart( p_input, 0xffffffff ) < 0 )
{
msg_Err( p_input, "cannot start stream" );
MMSClose( p_input );
FREE( p_access->url.psz_private );
FREE( p_access->p_cmd );
return( -1 );
}
return( 0 );
}
/*****************************************************************************
* Close: free unused data structures
*****************************************************************************/
static void Close( vlc_object_t *p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
access_t *p_access = (access_t*)p_input->p_access_data;
/* close connection with server */
MMSClose( p_input );
/* free memory */
FREE( p_access->url.psz_private );
FREE( p_access->p_cmd );
}
/*****************************************************************************
* SetProgram: do nothing
*****************************************************************************/
static int SetProgram( input_thread_t * p_input,
pgrm_descriptor_t * p_program )
{
return( 0 );
}
/*****************************************************************************
* Seek: try to go at the right place
*****************************************************************************/
static void Seek( input_thread_t * p_input, off_t i_pos )
{
/*
* FIXME
* Don't work
* Probably some bad or missing command
*
*
*/
#if 0
access_t *p_access = (access_t*)p_input->p_access_data;
uint32_t i_packet;
uint32_t i_offset;
if( i_pos < 0 )
{
return;
}
msg_Dbg( p_input, "seeking to %lld, header size:%d", i_pos, p_access->i_header );
if( i_pos < p_access->i_header)
{
if( p_access->i_pos < p_access->i_header )
{
/* no need to restart stream, it was already one
* or no stream was yet read */
p_access->i_pos = i_pos;
return;
}
else
{
i_packet = 0xffffffff;
i_offset = 0;
}
}
else
{
i_packet = ( i_pos - p_access->i_header ) / p_access->i_packet_length;
i_offset = ( i_pos - p_access->i_header ) % p_access->i_packet_length;
}
MMSStop( p_input );
MMSStart( p_input, i_packet );
p_access->i_media_used += i_offset;
p_access->i_pos = i_pos;
#endif
}
static int Read ( input_thread_t * p_input, byte_t * p_buffer,
size_t i_len )
{
access_t *p_access = (access_t*)p_input->p_access_data;
size_t i_data;
size_t i_copy;
i_data = 0;
/* *** send header if needed ** */
if( p_access->i_pos < p_access->i_header )
{
i_copy = __MIN( i_len, p_access->i_header - p_access->i_pos );
if( i_copy > 0 )
{
memcpy( p_buffer,
p_access->p_header + p_access->i_pos,
i_copy );
}
i_data += i_copy;
}
/* *** now send data if needed *** */
while( i_data < i_len )
{
if( p_access->i_media_used < p_access->i_media )
{
i_copy = __MIN( i_len - i_data ,
p_access->i_media - p_access->i_media_used );
memcpy( p_buffer + i_data,
p_access->p_media + p_access->i_media_used,
i_copy );
i_data += i_copy;
p_access->i_media_used += i_copy;
}
else if( p_access->p_media != NULL &&
p_access->i_media_used < p_access->i_packet_length )
{
i_copy = __MIN( i_len - i_data,
p_access->i_packet_length - p_access->i_media_used);
memset( p_buffer + i_data, 0, i_copy );
i_data += i_copy;
p_access->i_media_used += i_copy;
}
else
{
if( mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA ) < 0 );
{
p_access->i_pos += i_data;
return( i_data );
}
}
}
p_access->i_pos += i_data;
return( i_data );
}
static void asf_HeaderParse( mms_stream_t stream[128],
uint8_t *p_header, int i_header )
{
var_buffer_t buffer;
guid_t guid;
uint64_t i_size;
int i;
for( i = 0; i < 128; i++ )
{
stream[i].i_cat = MMS_STREAM_UNKNOWN;
}
var_buffer_initread( &buffer, p_header, i_header );
var_buffer_getguid( &buffer, &guid );
if( !CmpGuid( &guid, &asf_object_header_guid ) )
{
// XXX Error
}
var_buffer_getmemory( &buffer, NULL, 30 - 16 );
for( ;; )
{
if( var_buffer_readempty( &buffer ) )
{
return;
}
var_buffer_getguid( &buffer, &guid );
i_size = var_buffer_get64( &buffer );
if( CmpGuid( &guid, &asf_object_stream_properties_guid ) )
{
int i_stream_id;
guid_t stream_type;
// msg_Dbg( p_input, "found stream_properties" );
var_buffer_getguid( &buffer, &stream_type );
var_buffer_getmemory( &buffer, NULL, 32 );
i_stream_id = var_buffer_get8( &buffer ) & 0x7f;
var_buffer_getmemory( &buffer, NULL, i_size - 24 - 32 - 16 - 1 );
if( CmpGuid( &stream_type, &asf_object_stream_type_video ) )
{
// msg_Dbg( p_input, "video stream[%d] found", i_stream_id );
stream[i_stream_id].i_cat = MMS_STREAM_VIDEO;
}
else if( CmpGuid( &stream_type, &asf_object_stream_type_audio ) )
{
// msg_Dbg( p_input, "audio stream[%d] found", i_stream_id );
stream[i_stream_id].i_cat = MMS_STREAM_AUDIO;
}
else
{
// msg_Dbg( p_input, "unknown stream[%d] found", i_stream_id );
stream[i_stream_id].i_cat = MMS_STREAM_UNKNOWN;
}
}
else if ( CmpGuid( &guid, &asf_object_bitrate_properties_guid ) )
{
int i_count;
uint8_t i_stream_id;
i_count = var_buffer_get16( &buffer );
i_size -= 2;
while( i_count > 0 )
{
i_stream_id = var_buffer_get16( &buffer )&0x7f;
stream[i_stream_id].i_bitrate = var_buffer_get32( &buffer );
i_count--;
i_size -= 6;
}
var_buffer_getmemory( &buffer, NULL, i_size - 24 );
}
else
{
// skip unknown guid
var_buffer_getmemory( &buffer, NULL, i_size - 24 );
}
}
}
/****************************************************************************
* MMSOpen : Open a connection with the server over mmst or mmsu(not yet)
****************************************************************************/
static int MMSOpen( input_thread_t *p_input,
url_t *p_url,
int i_proto,
char *psz_network ) /* "", "ipv4", "ipv6" */
{
module_t *p_network;
access_t *p_access = (access_t*)p_input->p_access_data;
network_socket_t socket_desc;
int b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
var_buffer_t buffer;
char tmp[4096];
uint16_t *p;