1
mirror of https://code.videolan.org/videolan/vlc synced 2024-10-07 03:56:28 +02:00

RTMP bugfix and reusability enhacenment

Signed-off-by: Ilkka Ollakka <ileoo@videolan.org>
This commit is contained in:
Miguel Angel Cabrera Moya 2008-05-05 23:28:39 +02:00 committed by Ilkka Ollakka
parent fb19b264f8
commit aa5fe86aed
3 changed files with 1151 additions and 594 deletions

View File

@ -82,77 +82,87 @@ static int Open( vlc_object_t *p_this )
int length_path, length_media_name;
int i;
/*DOWN:
p_access->info.i_update = 0;
p_access->info.i_size = 0;
p_access->info.i_pos = 0;
p_access->info.b_eof = false;
p_access->info.i_title = 0;
p_access->info.i_seekpoint = 0;
p_access->pf_read = Read;
p_access->pf_block = Block;
p_access->pf_control = Control;
p_access->pf_seek = Seek;
do
{
p_access->p_sys = (access_sys_t *) malloc( sizeof( access_sys_t ) );
if( !p_access->p_sys )
return VLC_ENOMEM;
} while(0);
p_sys = p_access->p_sys;
memset( p_sys, 0, sizeof( access_sys_t ) );
*/
STANDARD_READ_ACCESS_INIT
/* Parse URI - remove spaces */
p = psz = strdup( p_access->psz_path );
while( (p = strchr( p, ' ' )) != NULL )
*p = '+';
vlc_UrlParse( &p_sys->url, psz, 0 );
free( psz );
if( !p_access->psz_access ||
strncmp( p_access->psz_access, "rtmp", 4 ))
{
msg_Warn( p_access, "invalid protocol" );
vlc_UrlClean( &p_sys->url );
free( p_sys );
return VLC_EGENERIC;
}
if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
{
msg_Warn( p_access, "invalid host" );
vlc_UrlClean( &p_sys->url );
free( p_sys );
return VLC_EGENERIC;
}
if( p_sys->url.i_port <= 0 )
p_sys->url.i_port = 1935;
if ( p_sys->url.psz_path == NULL ) {
msg_Warn( p_access, "invalid path" );
vlc_UrlClean( &p_sys->url );
free( p_sys );
return VLC_EGENERIC;
}
length_path = strlen( p_sys->url.psz_path );
length_media_name = strlen( strrchr( p_sys->url.psz_path, '/' ) ) - 1;
p_sys->psz_application = strndup( p_sys->url.psz_path + 1, length_path - length_media_name - 2 );
p_sys->psz_media = strdup( p_sys->url.psz_path + ( length_path - length_media_name ) );
msg_Dbg( p_access, "rtmp: host='%s' port=%d path='%s'",
p_sys->url.psz_host, p_sys->url.i_port, p_sys->url.psz_path );
if( p_sys->url.psz_username && *p_sys->url.psz_username )
{
msg_Dbg( p_access, " user='%s', pwd='%s'",
p_sys->url.psz_username, p_sys->url.psz_password );
}
p_sys->p_thread =
vlc_object_create( p_access, sizeof( rtmp_control_thread_t ) );
if( !p_sys->p_thread )
{
msg_Err( p_access, "out of memory" );
vlc_UrlClean( &p_sys->url );
free( p_sys );
return VLC_EGENERIC;
return VLC_ENOMEM;
}
vlc_object_attach( p_sys->p_thread, p_access );
/* Parse URI - remove spaces */
p = psz = strdup( p_access->psz_path );
while( (p = strchr( p, ' ' )) != NULL )
*p = '+';
vlc_UrlParse( &p_sys->p_thread->url, psz, 0 );
free( psz );
if( p_sys->p_thread->url.psz_host == NULL
|| *p_sys->p_thread->url.psz_host == '\0' )
{
msg_Warn( p_access, "invalid host" );
goto error;
}
vlc_object_attach( p_sys->p_thread, p_access );
if( p_sys->p_thread->url.i_port <= 0 )
p_sys->p_thread->url.i_port = 1935;
if( p_sys->p_thread->url.psz_path == NULL )
{
msg_Warn( p_access, "invalid path" );
goto error;
}
length_path = strlen( p_sys->p_thread->url.psz_path );
length_media_name = strlen( strrchr( p_sys->p_thread->url.psz_path, '/' ) ) - 1;
p_sys->p_thread->psz_application = strndup( p_sys->p_thread->url.psz_path + 1, length_path - length_media_name - 2 );
p_sys->p_thread->psz_media = strdup( p_sys->p_thread->url.psz_path + ( length_path - length_media_name ) );
msg_Dbg( p_access, "rtmp: host='%s' port=%d path='%s'",
p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port, p_sys->p_thread->url.psz_path );
if( p_sys->p_thread->url.psz_username && *p_sys->p_thread->url.psz_username )
{
msg_Dbg( p_access, " user='%s', pwd='%s'",
p_sys->p_thread->url.psz_username, p_sys->p_thread->url.psz_password );
}
/* Initialize thread variables */
p_sys->p_thread->b_die = 0;
p_sys->p_thread->b_error= 0;
p_sys->p_thread->p_fifo_media = block_FifoNew();
p_sys->p_thread->p_fifo_input = block_FifoNew();
p_sys->p_thread->p_empty_blocks = block_FifoNew();
p_sys->p_thread->has_audio = 0;
p_sys->p_thread->has_video = 0;
p_sys->p_thread->metadata_received = 0;
p_sys->p_thread->first_media_packet = 1;
p_sys->p_thread->flv_tag_previous_tag_size = 0x00000000; /* FLV_TAG_FIRST_PREVIOUS_TAG_SIZE */
p_sys->p_thread->chunk_size_recv = 128; /* RTMP_DEFAULT_CHUNK_SIZE */
p_sys->p_thread->chunk_size_send = 128; /* RTMP_DEFAULT_CHUNK_SIZE */
for(i = 0; i < 64; i++)
{
memset( &p_sys->p_thread->rtmp_headers_recv[i], 0, sizeof( rtmp_packet_t ) );
@ -167,62 +177,54 @@ static int Open( vlc_object_t *p_this )
p_sys->p_thread->rtmp_headers_send[i].body = NULL;
}
p_sys->p_thread->p_base_object = p_this;
vlc_cond_init( p_sys->p_thread, &p_sys->p_thread->wait );
vlc_mutex_init( &p_sys->p_thread->lock );
p_sys->p_thread->result_connect = 1;
p_sys->p_thread->result_play = 1;
p_sys->p_thread->result_stop = 0;
/* Open connection */
p_sys->fd = net_ConnectTCP( p_access, p_sys->url.psz_host, p_sys->url.i_port );
p_sys->p_thread->fd = p_sys->fd;
if( p_sys->fd == -1 )
p_sys->p_thread->fd = net_ConnectTCP( p_access, p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port );
if( p_sys->p_thread->fd == -1 )
{
int *p_fd_listen;
msg_Warn( p_access, "cannot connect to %s:%d", p_sys->url.psz_host, p_sys->url.i_port );
msg_Warn( p_access, "cannot connect to %s:%d", p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port );
msg_Dbg( p_access, "switching to passive mode" );
p_sys->active = 0;
p_fd_listen = net_ListenTCP( p_access, p_sys->url.psz_host, p_sys->url.i_port );
p_fd_listen = net_ListenTCP( p_access, p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port );
if( p_fd_listen == NULL )
{
msg_Warn( p_access, "cannot listen to %s port %i", p_sys->url.psz_host, p_sys->url.i_port );
vlc_UrlClean( &p_sys->url );
net_Close( p_sys-> fd );
free( p_sys );
return VLC_EGENERIC;
msg_Err( p_access, "cannot listen to %s port %i", p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port );
goto error2;
}
p_sys->fd = net_Accept( p_access, p_fd_listen, -1 );
p_sys->p_thread->fd = net_Accept( p_access, p_fd_listen, -1 );
net_ListenClose( p_fd_listen );
if( rtmp_handshake_passive( p_this ) < 0 )
if( rtmp_handshake_passive( p_this, p_sys->p_thread->fd ) < 0 )
{
msg_Err( p_access, "Passive handshake failed");
vlc_UrlClean( &p_sys->url );
net_Close( p_sys-> fd );
free( p_sys );
return VLC_EGENERIC;
msg_Err( p_access, "handshake passive failed");
goto error2;
}
p_sys->p_thread->result_publish = 1;
}
else
{
msg_Dbg( p_access, "using active connection");
p_sys->active = 1;
if( rtmp_handshake_active( p_this ) < 0 )
if( rtmp_handshake_active( p_this, p_sys->p_thread->fd ) < 0 )
{
msg_Err( p_access, "Active handshake failed");
vlc_UrlClean( &p_sys->url );
net_Close( p_sys-> fd );
free( p_sys );
return VLC_EGENERIC;
msg_Err( p_access, "handshake active failed");
goto error2;
}
p_sys->p_thread->result_publish = 0;
@ -232,22 +234,15 @@ static int Open( vlc_object_t *p_this )
VLC_THREAD_PRIORITY_INPUT, false ) )
{
msg_Err( p_access, "cannot spawn rtmp control thread" );
vlc_UrlClean( &p_sys->url );
net_Close( p_sys-> fd );
free( p_sys );
return VLC_EGENERIC;
goto error2;
}
if( p_sys->active )
if( p_sys->active )
{
msg_Dbg( p_access, "Activation active connection");
if( rtmp_connect_active( p_this ) < 0)
if( rtmp_connect_active( p_sys->p_thread ) < 0 )
{
msg_Err( p_access, "Active connection failed");
vlc_UrlClean( &p_sys->url );
net_Close( p_sys-> fd );
free( p_sys );
return VLC_EGENERIC;
msg_Err( p_access, "connect active failed");
goto error2;
}
}
@ -255,19 +250,27 @@ static int Open( vlc_object_t *p_this )
p_access->p_sys->flv_packet = NULL;
p_access->p_sys->read_packet = 1;
msg_Dbg( p_access, "waiting for buffer to fill");
/* Wait until enough data is received for extracting metadata */
while( block_FifoCount( p_access->p_sys->p_thread->p_fifo_media ) < 10 )
{
msg_Dbg( p_access, "waiting for buffer to fill");
msleep(1000);
continue;
}
/* Update default_pts to a suitable value for rtmp access */
var_Create( p_access, "rtmp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
return VLC_SUCCESS;
error2:
vlc_cond_destroy( &p_sys->p_thread->wait );
vlc_mutex_destroy( &p_sys->p_thread->lock );
free( p_sys->p_thread->psz_application );
free( p_sys->p_thread->psz_media );
net_Close( p_sys->p_thread->fd );
error:
vlc_object_detach( p_sys->p_thread );
vlc_object_release( p_sys->p_thread );
vlc_UrlClean( &p_sys->p_thread->url );
free( p_sys );
return VLC_EGENERIC;
}
/*****************************************************************************
@ -277,47 +280,40 @@ static void Close( vlc_object_t * p_this )
{
access_t *p_access = (access_t *) p_this;
access_sys_t *p_sys = p_access->p_sys;
int i;
msg_Warn(p_access, "Close");
/* p_sys->p_thread->b_die = true;*/
vlc_object_kill( p_sys->p_thread );
block_FifoWake( p_sys->p_thread->p_fifo_media );
block_FifoWake( p_sys->p_thread->p_fifo_input );
block_FifoWake( p_sys->p_thread->p_empty_blocks );
/*
for( i = 0; i < 5; i++ )
{
block_t *p_dummy = block_New( p_access, 256 );
p_dummy->i_dts = 0;
p_dummy->i_pts = 0;
p_dummy->i_length = 0;
memset( p_dummy->p_buffer, 0, p_dummy->i_buffer );
block_FifoPut( p_sys->p_thread->p_fifo_media, p_dummy );
}
for( i = 0; i < 5; i++ )
{
block_t *p_dummy = block_New( p_access, 256 );
p_dummy->i_dts = 0;
p_dummy->i_pts = 0;
p_dummy->i_length = 0;
memset( p_dummy->p_buffer, 0, p_dummy->i_buffer );
block_FifoPut( p_sys->p_thread->p_empty_blocks, p_dummy );
}*/
vlc_thread_join( p_sys->p_thread );
vlc_cond_destroy( &p_sys->p_thread->wait );
vlc_mutex_destroy( &p_sys->p_thread->lock );
block_FifoRelease( p_sys->p_thread->p_fifo_media );
block_FifoRelease( p_sys->p_thread->p_fifo_input );
block_FifoRelease( p_sys->p_thread->p_empty_blocks );
net_Close( p_sys->fd );
for( i = 0; i < 64; i++ ) /* RTMP_HEADER_STREAM_INDEX_MASK */
{
if( p_sys->p_thread->rtmp_headers_recv[i].body != NULL )
{
free( p_sys->p_thread->rtmp_headers_recv[i].body->body );
free( p_sys->p_thread->rtmp_headers_recv[i].body );
}
}
net_Close( p_sys->p_thread->fd );
var_Destroy( p_access, "rtmp-caching" );
vlc_object_detach( p_sys->p_thread );
vlc_object_release( p_sys->p_thread );
vlc_UrlClean( &p_sys->url );
free( p_sys->psz_application );
free( p_sys->psz_media );
vlc_UrlClean( &p_sys->p_thread->url );
free( p_sys->p_thread->psz_application );
free( p_sys->p_thread->psz_media );
free( p_sys );
}
@ -327,35 +323,39 @@ static void Close( vlc_object_t * p_this )
static int Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
{
access_sys_t *p_sys = p_access->p_sys;
rtmp_packet_t *rtmp_packet;
uint8_t *tmp_buffer;
ssize_t i_ret;
int i_len_tmp;
if( p_sys->fd < 0 )
{
p_access->info.b_eof = true;
return 0;
}
i_len_tmp = 0;
while( i_len_tmp < i_len )
{
if( p_sys->p_thread->result_stop || p_access->info.b_eof || p_access->b_die )
{
p_access->info.b_eof = true;
return 0;
}
if( p_sys->read_packet )
{
if( !p_sys->p_thread->metadata_received )
{
/* Wait until enough data is received for extracting metadata */
if( block_FifoCount( p_sys->p_thread->p_fifo_input ) < 10 )
{
msleep(100000);
continue;
}
p_sys->flv_packet = flv_get_metadata( p_access );
p_sys->p_thread->metadata_received = 1;
}
else
{
if( p_sys->active && block_FifoCount( p_sys->p_thread->p_fifo_media ) == 0 )
{
p_access->info.b_eof = true;
break;
}
p_sys->flv_packet = block_FifoGet( p_sys->p_thread->p_fifo_media );
p_sys->flv_packet = block_FifoGet( p_sys->p_thread->p_fifo_input );
if( p_sys->flv_packet == NULL )
continue; /* Forced wake-up */
}
@ -400,12 +400,44 @@ p_buffer[i+8], p_buffer[i+9], p_buffer[i+10], p_buffer[i+11], p_buffer[i+12], p_
/* Send publish onStatus event only once */
p_sys->p_thread->result_publish = 0;
rtmp_send_publish_start( p_access );
rtmp_packet = rtmp_build_publish_start( p_sys->p_thread );
tmp_buffer = rtmp_encode_packet( p_sys->p_thread, rtmp_packet );
i_ret = net_Write( p_sys->p_thread, p_sys->p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded );
if( i_ret != rtmp_packet->length_encoded )
{
free( rtmp_packet->body->body );
free( rtmp_packet->body );
free( rtmp_packet );
free( tmp_buffer );
msg_Err( p_access, "failed send publish start" );
}
free( rtmp_packet->body->body );
free( rtmp_packet->body );
free( rtmp_packet );
free( tmp_buffer );
}
p_access->info.i_pos += i_len_tmp;
rtmp_send_bytes_read( p_access, p_access->info.i_pos );
rtmp_packet = rtmp_build_bytes_read( p_sys->p_thread, p_access->info.i_pos );
tmp_buffer = rtmp_encode_packet( p_sys->p_thread, rtmp_packet );
i_ret = net_Write( p_sys->p_thread, p_sys->p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded );
if( i_ret != rtmp_packet->length_encoded )
{
free( rtmp_packet->body->body );
free( rtmp_packet->body );
free( rtmp_packet );
free( tmp_buffer );
msg_Err( p_access, "failed send bytes read" );
}
free( rtmp_packet->body->body );
free( rtmp_packet->body );
free( rtmp_packet );
free( tmp_buffer );
}
return i_len_tmp;
@ -436,37 +468,37 @@ static int Seek( access_t *p_access, int64_t i_pos )
*****************************************************************************/
static int Control( access_t *p_access, int i_query, va_list args )
{
bool *pb_bool;
int *pi_int;
int64_t *pi_64;
bool *pb_bool;
int *pi_int;
int64_t *pi_64;
switch( i_query )
{
/* */
case ACCESS_CAN_SEEK:
case ACCESS_CAN_FASTSEEK:
pb_bool = (bool*)va_arg( args, bool* );
pb_bool = (bool*) va_arg( args, bool* );
*pb_bool = false; /* TODO */
break;
case ACCESS_CAN_PAUSE:
pb_bool = (bool*)va_arg( args, bool* );
pb_bool = (bool*) va_arg( args, bool* );
*pb_bool = false; /* TODO */
break;
case ACCESS_CAN_CONTROL_PACE:
pb_bool = (bool*)va_arg( args, bool* );
pb_bool = (bool*) va_arg( args, bool* );
*pb_bool = true;
break;
/* */
case ACCESS_GET_MTU:
pi_int = (int*)va_arg( args, int * );
pi_int = (int*) va_arg( args, int * );
*pi_int = 0;
break;
case ACCESS_GET_PTS_DELAY:
pi_64 = (int64_t*)va_arg( args, int64_t * );
pi_64 = (int64_t*) va_arg( args, int64_t * );
*pi_64 = var_GetInteger( p_access, "rtmp-caching" ) * INT64_C(1000);
break;
@ -503,13 +535,18 @@ static void ThreadControl( vlc_object_t *p_this )
while( !p_thread->b_die )
{
rtmp_packet = rtmp_read_net_packet( p_thread );
if( rtmp_packet != NULL )
{
if( rtmp_packet->content_type < 0x01 /* RTMP_CONTENT_TYPE_CHUNK_SIZE */
|| rtmp_packet->content_type > 0x14 ) /* RTMP_CONTENT_TYPE_INVOKE */
{
free( rtmp_packet->body->body );
free( rtmp_packet->body );
free( rtmp_packet );
msg_Warn( p_thread, "unknown content type received" );
}
else
p_thread->rtmp_handler[rtmp_packet->content_type]( p_thread, rtmp_packet );
}
@ -524,6 +561,9 @@ static void ThreadControl( vlc_object_t *p_this )
}
p_thread->b_die = 1;
((access_t *) p_thread->p_base_object)->info.b_eof = true;
block_FifoWake( p_thread->p_fifo_input );
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@
typedef struct rtmp_packet_t rtmp_packet_t;
typedef struct rtmp_body_t rtmp_body_t;
typedef struct rtmp_control_thread_t rtmp_control_thread_t;
typedef void (*rtmp_handler_t)( rtmp_control_thread_t *rtmp_control_thread, rtmp_packet_t *rtmp_packet );
typedef void (*rtmp_handler_t)( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet );
struct rtmp_packet_t
{
@ -54,51 +54,61 @@ struct rtmp_control_thread_t
int fd;
block_fifo_t *p_fifo_media;
vlc_url_t url;
char *psz_application;
char *psz_media;
block_fifo_t *p_fifo_input;
block_fifo_t *p_empty_blocks;
vlc_mutex_t lock;
vlc_cond_t wait;
int result_connect;
int result_publish;
int result_play;
int result_publish;
int result_stop;
double stream_client;
double stream_server;
double stream_client_id;
double stream_server_id;
char *psz_publish;
/* Rebuild FLV variables */
/* Rebuild FLV variables (access) */
int has_audio;
int has_video;
int metadata_received;
uint8_t metadata_stereo;
uint8_t metadata_samplesize;
uint8_t metadata_samplerate;
uint32_t metadata_samplerate;
uint8_t metadata_audiocodecid;
uint8_t metadata_videocodecid;
uint8_t metadata_frametype;
int first_media_packet;
uint32_t flv_tag_previous_tag_size;
/* Vars for rebuilding FLV (access_output) */
rtmp_body_t *flv_body;
uint8_t flv_content_type;
uint32_t flv_length_body;
uint32_t flv_timestamp;
/* vars for channel state */
uint32_t chunk_size_recv;
uint32_t chunk_size_send;
rtmp_packet_t rtmp_headers_recv[64]; /* RTMP_HEADER_STREAM_MAX */
rtmp_packet_t rtmp_headers_send[64];
rtmp_handler_t rtmp_handler[21]; /* index by RTMP_CONTENT_TYPE */
/* Pointer to base module object (later needs to casted) */
void *p_base_object;
};
struct access_sys_t
{
int active;
int fd;
vlc_url_t url;
char *psz_application;
char *psz_media;
/* vars for reading from fifo */
block_t *flv_packet;
int read_packet;
@ -110,17 +120,27 @@ struct access_sys_t
/*****************************************************************************
* RTMP header:
******************************************************************************/
int rtmp_handshake_passive( vlc_object_t *p_this );
int rtmp_handshake_active( vlc_object_t *p_this );
int rtmp_connect_active( vlc_object_t *p_this );
int rtmp_handshake_active( vlc_object_t *p_this, int fd );
int rtmp_handshake_passive( vlc_object_t *p_this, int fd );
int rtmp_connect_active( rtmp_control_thread_t *p_thread );
int rtmp_connect_passive( rtmp_control_thread_t *p_thread );
//int rtmp_seek( access_t *p_access, int64_t i_pos ); TODO
int rtmp_send_bytes_read( access_t *p_access, uint32_t reply );
int rtmp_send_publish_start( access_t *p_access );
//
rtmp_packet_t *rtmp_build_bytes_read( rtmp_control_thread_t *p_thread, uint32_t reply );
rtmp_packet_t *rtmp_build_publish_start( rtmp_control_thread_t *p_thread );
rtmp_packet_t *rtmp_build_flv_over_rtmp( rtmp_control_thread_t *p_thread, block_t *p_buffer );
rtmp_packet_t *rtmp_read_net_packet( rtmp_control_thread_t *p_thread );
uint8_t *rtmp_encode_packet( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet );
void rtmp_init_handler( rtmp_handler_t *rtmp_handler );
/*****************************************************************************
* FLV header:
******************************************************************************/
block_t *flv_get_metadata( access_t *p_access );
block_t *flv_insert_header( access_t *p_access, block_t *first_packet );
/*****************************************************************************
* RTMP body header:
******************************************************************************/
rtmp_body_t *rtmp_body_new( int length_buffer );
void rtmp_body_reset( rtmp_body_t * );