Appendice A - Implementazione dello schema di
migrazione coordinato con la rete
Di seguito si riportano le modifiche apportate al codice sorgente C del software VLC
(versione 0.7.2). In particolare sono stati modificati 2 file:
• /modules/access/http.c: implementa il modulo di accesso alla rete mediante il protocollo
HTTP. Su questo file è stata implementato il trigger della migrazione basato sul time-out
lettura socket (vedi funzione Read), nonchè la migrazione stessa (vedi funzione
Migrazione). Tra le altre modifiche, sono stati inseriti nuovi campi nel menù grafico
dell’applicazione, le quali possono essere impostate in preferences
modules/access/access_http.
• /src/input/input.c: funziona da interfaccia tra il modulo di accesso alla rete e il decoder
MPEG. Su questo file è stato implementato il trigger basato sull’underflowing del buffer
del decoder, basato sull’algoritmo di pagina 45
File /modules/access/http
.
c
/***************************************************************************** * http.c: HTTP input module ***************************************************************************** * Copyright (C) 2001-2004 VideoLAN * $Id: http.c 7522 2004-04-27 16:35:15Z sam $ ** Authors: Laurent Aimar <fenrir@via.ecp.fr> * 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. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include <stdlib.h> #include <vlc/vlc.h> #include <vlc/input.h> #include "vlc_playlist.h" #include "network.h"
/***************************************************************************** * Module descriptor
*****************************************************************************/ static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
#define PROXY_TEXT N_("HTTP proxy") #define PROXY_LONGTEXT N_( \
"You can specify an HTTP proxy to use. It must be of the form " \
"http://myproxy.mydomain:myport/. If none is specified, the HTTP_PROXY " \ "environment variable will be tried." )
#define BKPSRV_TEXT N_("Backup Server ")
#define BKPSRV_LONGTEXT N_("You must specify the URL of backup server") #define TIMEOUT_TEXT N_("Socket reading timeout value in ms")
#define TIMEOUT_LONGTEXT N_( \
"Allows you to modify the default socket reading timeout value for http
streams. This " \
"value should be set in millisecond units." ) #define CACHING_TEXT N_("Caching value in ms") #define CACHING_LONGTEXT N_( \
"Allows you to modify the default caching value for http streams. This " \ "value should be set in millisecond units." )
#define USER_TEXT N_("HTTP user name")
#define USER_LONGTEXT N_("Allows you to modify the user name that will " \ "be used for the connection (Basic authentication only).")
#define PASS_TEXT N_("HTTP password")
#define PASS_LONGTEXT N_("Allows you to modify the password that will be " \ "used for the connection.")
#define RB_TEXT N_("HTTP user RB")
#define RB_LONGTEXT N_("Allows you to modify the user RB that will be " \ "used for the connection.")
vlc_module_begin();
set_description( _("HTTP input") ); set_capability( "access", 0 );
add_string( "http-proxy", NULL, NULL, PROXY_TEXT, PROXY_LONGTEXT, VLC_FALSE );
add_string( "http-user", NULL, NULL, USER_TEXT, USER_LONGTEXT, VLC_FALSE ); add_string( "http-pwd", NULL , NULL, PASS_TEXT, PASS_LONGTEXT, VLC_FALSE ); add_string( "http-user-RB", COPYRIGHT_MESSAGE , NULL, RB_TEXT,
RB_LONGTEXT, VLC_FALSE );
// Le impostazioni che appaiono qui possono essere impostate a tempo di // esecuzione nel menù grafico dell’applicativo VLC, seguendo il percorso // preferences modules/access/access_http
// Inserisce nel menu il campo per l’URL di backup
add_string( "Backup Server", NULL, NULL, BKPSRV_TEXT, BKPSRV_LONGTEXT, VLC_FALSE );
// Inserisce nel menu campo per stabilire time-out lettura socket // E’ possibile stabilire valore di default in msec (500)
add_integer( "socket-timeout", 500, NULL,
TIMEOUT_TEXT, TIMEOUT_LONGTEXT, VLC_TRUE );
// Inserisce nel menu campo per stabilire lunghezza temporale del buffer
add_integer( "http-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
add_shortcut( "http" ); add_shortcut( "http4" );
add_shortcut( "http6" );
set_callbacks( Open, Close ); vlc_module_end(); /***************************************************************************** * Local prototypes *****************************************************************************/ struct access_sys_t { int fd; /* From uri */ vlc_url_t url; char *psz_user; char *psz_passwd; char *psz_user_RB; /* Proxy */ vlc_bool_t b_proxy; vlc_url_t proxy;
// Allocazione variabili per la migrazione //
vlc_bool_t migrate; // Booleano che stabilisce se la migrazione può avere luogo vlc_url_t bkp_srv; // Struttura che definisce l’URL backup
mtime_t timeout; // Definisce lunghezza del timeout
float startup; // /* */ int i_code; char *psz_protocol; int i_version; char *psz_mime; char *psz_location; vlc_bool_t b_chunked; int64_t i_chunk; int64_t i_tell; int64_t i_size; };
static void Seek( input_thread_t *, off_t );
static ssize_t Read( input_thread_t *, byte_t *, size_t ); static void ParseURL( access_sys_t *, char *psz_url );
static int Connect( input_thread_t *, vlc_bool_t *, off_t *, off_t ); static char *b64_encode( unsigned char *src );
static vlc_bool_t Migrazione ( input_thread_t * );
/***************************************************************************** Open: questa funzione viene richiamata dalla funzione Inithread del modulo input allo scopo di inizializzare il modulo di accesso alla rete
*****************************************************************************/ static int Open ( vlc_object_t *p_this )
{
input_thread_t *p_input = (input_thread_t*)p_this; access_sys_t *p_sys;
vlc_value_t val;
/* Create private struct */
p_sys = p_input->p_access_data = malloc( sizeof( access_sys_t ) ); memset( p_sys, 0, sizeof( access_sys_t ) );
p_sys->fd = -1;
p_sys->b_proxy = VLC_FALSE; p_sys->i_version = 1; p_sys->psz_mime = NULL;
p_sys->psz_location = NULL;
p_sys->psz_user_RB = NULL;
/* First set ipv4/ipv6 */
var_Create( p_input, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_input, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); if( *p_input->psz_access ) { **** OMISSIS **** } val.b_bool = VLC_TRUE;
var_Set( p_input, "ipv4", val ); val.b_bool = VLC_FALSE;
var_Set( p_input, "ipv6", val );
/* Definisce struttura del server primario*/ ParseURL( p_sys, p_input->psz_name);
if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' ) {
msg_Warn( p_input, "invalid host" ); goto error; } if( p_sys->url.i_port <= 0 ) { p_sys->url.i_port = 80; }
/** Definizione variabili per la migrazione **/
var_Create( p_input, "Backup Server", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); var_Get( p_input, "Backup Server", &val );
// Controlla se nel menù è stato inserito URL backup
if( val.psz_string && *val.psz_string ) {
// Se è presente inizializza struttura bck_srv che definisce URL backup
vlc_UrlParse( &p_sys->bck_srv, val.psz_string, 0 );
if( p_sys->bkp_srv.psz_host == NULL || *p_sys->bkp_srv.psz_host == '\0' ) { msg_Err( p_input, "Controller Server not valid" );
p_sys->migrate = VLC_FALSE; }
else{
p_sys->migrate = VLC_TRUE; // Abilita la migrazione
p_sys->startup = 0.05; // Posizione relativa dello streaming prima // della quale non può avvenire migrazione
// Imposta time-out in lettura con valore impostato nel menù
var_Create( p_input, "socket-timeout", VLC_VAR_INTEGER, VLC_VAR_DOINHERIT );
var_Get( p_input, "socket-timeout", &val ); p_sys->timeout = val.i_int * 1000;
if( p_sys->bkp_srv.i_port <= 0 ) p_sys->bkp_srv.i_port = 80;
msg_Dbg(p_input,"Backup Server: Server %s Port %d",p_sys->ctrl_srv.psz_host,p_sys->ctrl_srv.i_port);
} }
if( !p_sys->psz_user || *p_sys->psz_user == '\0' ) {
**** OMISSIS **** }
/* Do user RB */ **** OMISSIS **** /* Check proxy */ **** OMISSIS ****
/* Connessione al server primario*/
if( Connect( p_input, &p_input->stream.b_seekable,
&p_input->stream.p_selected_area->i_size, 0 ) ) {
**** OMISSIS **** }
if( ( p_sys->i_code == 301 || p_sys->i_code == 302 || p_sys->i_code == 303 || p_sys->i_code == 307 ) && p_sys->psz_location && *p_sys->psz_location ) {
**** OMISSIS **** }
/* Finish to set up p_input */
**** OMISSIS ****
/* Imposta lunghezza del playout buffer*/
var_Create( p_input, "http-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Get( p_input, "http-caching", &val );
p_input->i_pts_delay = val.i_int * 1000; return VLC_SUCCESS; error: **** OMISSIS **** } /***************************************************************************** * Read: questa funzione viene richiamata periodicamente dalla funzione runthread del modulo input per leggere I dati dal socket di rete ed inviarli al buffer del decoder
*****************************************************************************/ static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len ) {
access_sys_t *p_sys = p_input->p_access_data; int i_read;
if( p_sys->fd < 0 ) {
return -1; }
if( p_sys->i_size > 0 && i_len + p_sys->i_tell > p_sys->i_size ) { **** OMISSIS **** } if( p_sys->b_chunked ) { **** OMISSIS **** }
// Controlla se modulo input ha chiesto la migrazione per l’underflow del // buffer al decoder if (p_input->b_startmigrate) { Migrazione(p_input); } Lettura:
// Legge dati dal buffer. Se non c’è alcun dato nel bufferaspetto un tempo // massimo pari a timeout. I_read è il numero di byte letti dal socket. i_read = net_ReadNonBlock( p_input, p_sys->fd, p_buffer, i_len, timeout);
// Ha letto dati dal socket li elabora poi li invia al decoder buffer if( i_read > 0 )
{
**** OMISSIS **** }
// Nessun dato letto dal socket, cioè si è esaurito il timeout: viene fatta // intervenire la migrazione
if (i_read == 0) {
msg_Info(p_input,"No data read from socket"); p_input->b_startmigrate = VLC_TRUE;
if (Migrazione (p_input)) goto Lettura; // Se la migrazione ha successo // ritenta nuova lettura
} return i_read; } /***************************************************************************** * Migrazione: viene chiamata dalla funzione Read qualora debba intervenire la migrazione del server
*****************************************************************************/ static vlc_bool_t Migrazione( input_thread_t * p_input)
{
access_sys_t *p_sys = p_input->p_access_data;
// Primo byte dello streaming non ancora ricevuto. In caso di migrazione richiede // al nuovo server di iniziare il servizio da questo punto
off_t i_pos = p_sys->i_tell;
// Evita la migrazione in caso lo streaming sia all’inizio
float pos = (float)p_sys->i_tell / (float)p_sys->i_size ; if (pos < p_sys->startup)
{
msg_Info(p_input,"Stream relative position: %f",pos); p_input->b_startmigrate = VLC_FALSE;
return VLC_FALSE; }
// Inizia la migrazione
msg_Dbg(p_input,"Stream relative position: %f. Starting migration",pos); msg_Dbg( p_input, "Closing network socket to Primary Server...");
net_Close( p_sys->fd ); p_sys->fd = -1;
msg_Dbg( p_input, "Network socket to primary server closed");
// Sostituisce la struttura del server primario con il secondario
p_sys->url = p_sys->bkpsrv;
msg_Dbg( p_input, "Connecting to Backup Server...");
// Si connette con il server di backup chiedendo di iniziare il servizio da // dove si è interrotto il primario
if( Connect( p_input, &p_input->stream.b_seekable,
&p_input->stream.p_selected_area->i_size, i_pos ) )
msg_Err( p_input, "Connection to Backup Server Failed" );
return VLC_FALSE; } msg_Dbg(p_input,"Migration completed"); p_input->b_startmigrate = VLC_FALSE; return VLC_TRUE; }
File /src/input/input
.
c
/***************************************************************************** * input.c: input thread* Read a stream, demultiplex and parse it before sending it to * decoders.
***************************************************************************** * Copyright (C) 1998-2004 VideoLAN
* $Id: input.c 7611 2004-05-06 23:14:23Z hartman $ *
* 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. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include <stdlib.h> #include <vlc/vlc.h> #include <vlc/input.h> #include <vlc/decoder.h> #include <vlc/vout.h> #ifdef HAVE_SYS_TIMES_H # include <sys/times.h> #endif
#include "stream_output.h" #include "vlc_interface.h" #include "codecs.h" #include "vlc_meta.h" #include "../../modules/demux/util/sub.h" /***************************************************************************** * Local prototypes *****************************************************************************/ struct input_thread_sys_t { **** OMISSIS **** };
static int RunThread ( input_thread_t *p_input ); static int InitThread ( input_thread_t *p_input ); static void ErrorThread( input_thread_t *p_input ); static void EndThread ( input_thread_t *p_input );
static void ParseOption( input_thread_t *p_input, const char *psz_option ); static void DecodeUrl ( char * );
/***************************************************************************** * Callbacks
*****************************************************************************/ /***************************************************************************** * Inithread: Inizializza il thread che si occupa della lettura dei dati dalla rete per inviarli al buffer del decoder. Richiama la funzione Open del modullo http.c per inizializzare l’accesso alla rete.
*****************************************************************************/ static int InitThread( input_thread_t *p_input )
{
**** OMISSIS **** }
/***************************************************************************** * RunThread: prima chiama la funzione InitThread pr inizializzre il thread. Quindi esegue loop principale. Ad ogni ciclo chiama la funzione Read del modulo http.c per leggere dati dal socket ed inviarli al buffer del decoder.
*****************************************************************************/ static int RunThread( input_thread_t *p_input )
{
vlc_value_t val;
mtime_t i_update_next = -1;
/* Signal right now, otherwise we'll get stuck in a peek */ vlc_thread_ready( p_input );
if( InitThread( p_input ) ) { **** OMISSIS **** } /* initialization is complete */ **** OMISSIS ****
float bufdimean = 0; // Lunghezza media del buffer del decoder ptrdiff_t bufdim; // Lunghezza corrente del buffer del decoder
float k = 0.05; // Parametro k (equazione 1)
int down = 0; // conta quante volte consecutive bufdim < bufdimean
int N = 4; // Parametro N (condizione 2)
// Ciclo principale
while( !p_input->b_die && !p_input->b_error && !p_input->b_eof ) {
unsigned int i, i_count; p_input->c_loops++;
vlc_mutex_lock( &p_input->stream.stream_lock ); if( p_input->stream.p_new_program )
{
**** OMISSIS **** }
if( p_input->stream.p_new_area ) {
**** OMISSIS **** }
if( p_input->stream.p_selected_area->i_seek != NO_SEEK ) {
**** OMISSIS **** }
if( p_input->stream.p_removed_es ) {
**** OMISSIS **** }
if( p_input->stream.p_newly_selected_es ) {
**** OMISSIS **** }
if( p_input->stream.b_new_mute != MUTE_NO_CHANGE ) {
**** OMISSIS **** }
vlc_mutex_unlock( &p_input->stream.stream_lock ); /* Read and demultiplex some data. */
// Richiama funzione Read di http.c per leggere dati da socket
i_count = p_input->pf_demux( p_input );
// Rileva attuale dimensione del buffer
bufdim = p_input->p_last_data - p_input->p_current_data;
// In caso di migrazione reinizializza il valore di bufdimean
if (p_input->b_startmigrate) bufdimean = bufdim;
// Altrimenti aggiorna stima della lunghezza media (equazione 1)
else bufdimean = k * bufdim + (1-k)*bufdimean;
// Verifica condizione 2: se è verificata incrementa contatore
if (bufdim < 0.3*bufdimean) down++; // Altrimenti azzera il contatore
else down = 0;
// Verifica se la condizione 2 si è presentata per N volte consecutive
if (down > N) {
// Richiede migrazione per underflowing del buffer
msg_Info(p_input,"Buffer Underflowing"); p_input->b_startmigrate = VLC_TRUE; bufdimean = 0; } if( i_count == 0 ) { **** OMISSIS **** }
else if( i_count < 0 ) {
p_input->b_error = 1; }
if( !p_input->b_error && !p_input->b_eof && i_update_next < mdate() ) { **** OMISSIS **** } } if( p_input->b_error || p_input->b_eof ) { ErrorThread( p_input ); } EndThread( p_input ); return 0; }