/***************************************************************************** * Run: interface thread *****************************************************************************/ static void Run( intf_thread_t *p_intf ) { #define MAX_MSG_LENGTH (2 * sizeof(int64_t)) vlc_bool_t b_master = config_GetInt( p_intf, "netsync-master" ); char *psz_master = NULL; char p_data[MAX_MSG_LENGTH]; int i_socket; if( !b_master ) { psz_master = config_GetPsz( p_intf, "netsync-master-ip" ); if( psz_master == NULL ) { msg_Err( p_intf, "master address not specified" ); return; } } i_socket = net_OpenUDP( p_intf, NULL, b_master ? NETSYNC_PORT_MASTER : NETSYNC_PORT_SLAVE, b_master ? NULL : psz_master, b_master ? 0 : NETSYNC_PORT_MASTER ); if( psz_master ) free( psz_master ); if( i_socket < 0 ) { msg_Err( p_intf, "failed opening UDP socket." ); return; } /* High priority thread */ vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_INPUT ); while( !p_intf->b_die ) { struct timeval timeout; fd_set fds_r; /* Update the input */ if( p_intf->p_sys->p_input == NULL ) { p_intf->p_sys->p_input = (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE ); } else if( p_intf->p_sys->p_input->b_dead ) { vlc_object_release( p_intf->p_sys->p_input ); p_intf->p_sys->p_input = NULL; } if( p_intf->p_sys->p_input == NULL ) { /* Wait a bit */ msleep( INTF_IDLE_SLEEP ); continue; } /* * We now have an input */ /* Initialize file descriptor set and timeout (0.5s) */ FD_ZERO( &fds_r ); FD_SET( i_socket, &fds_r ); timeout.tv_sec = 0; timeout.tv_usec = 500000; if( b_master ) { struct sockaddr_storage from; mtime_t i_date, i_clockref, i_master_clockref; int i_struct_size, i_read, i_ret; /* Don't block */ i_ret = select( i_socket + 1, &fds_r, 0, 0, &timeout ); if( i_ret == 0 ) continue; if( i_ret < 0 ) { /* Wait a bit */ msleep( INTF_IDLE_SLEEP ); continue; } /* We received something */ i_struct_size = sizeof( from ); i_read = recvfrom( i_socket, p_data, MAX_MSG_LENGTH, 0, (struct sockaddr*)&from, &i_struct_size ); i_clockref = ntoh64(*(int64_t *)p_data); i_date = mdate(); *(int64_t *)p_data = hton64( i_date ); i_master_clockref = GetClockRef( p_intf, i_clockref ); *(((int64_t *)p_data)+1) = hton64( i_master_clockref ); /* Reply to the sender */ sendto( i_socket, p_data, 2 * sizeof(int64_t), 0, (struct sockaddr *)&from, i_struct_size ); #if 0 msg_Dbg( p_intf, "Master clockref: "I64Fd" -> "I64Fd", from %s " "(date: "I64Fd")", i_clockref, i_master_clockref, from.ss_family == AF_INET ? inet_ntoa(((struct sockaddr_in *)&from)->sin_addr) : "non-IPv4", i_date ); #endif } else { mtime_t i_send_date, i_receive_date, i_master_date, i_diff_date; mtime_t i_master_clockref, i_client_clockref, i_drift; mtime_t i_clockref = 0; int i_sent, i_read, i_ret; /* Send clock request to the master */ *(int64_t *)p_data = hton64( i_clockref ); i_send_date = mdate(); i_sent = send( i_socket, p_data, sizeof(int64_t), 0 ); if( i_sent <= 0 ) { /* Wait a bit */ msleep( INTF_IDLE_SLEEP ); continue; } /* Don't block */ i_ret = select(i_socket + 1, &fds_r, 0, 0, &timeout); if( i_ret == 0 ) continue; if( i_ret < 0 ) { /* Wait a bit */ msleep( INTF_IDLE_SLEEP ); continue; } i_receive_date = mdate(); i_read = recv( i_socket, p_data, MAX_MSG_LENGTH, 0 ); if( i_read <= 0 ) { /* Wait a bit */ msleep( INTF_IDLE_SLEEP ); continue; } i_master_date = ntoh64(*(int64_t *)p_data); i_master_clockref = ntoh64(*(((int64_t *)p_data)+1)); i_diff_date = i_receive_date - ((i_receive_date - i_send_date) / 2 + i_master_date); i_client_clockref = i_drift = 0; if( p_intf->p_sys->p_input && i_master_clockref ) { i_client_clockref = GetClockRef( p_intf, i_clockref ); i_drift = i_client_clockref - i_master_clockref - i_diff_date; /* Update our clock to match the master's one */ if( i_client_clockref ) p_intf->p_sys->p_input->i_pts_delay -= i_drift; } #if 0 msg_Dbg( p_intf, "Slave clockref: "I64Fd" -> "I64Fd" -> "I64Fd", " "clock diff: "I64Fd" drift: "I64Fd, i_clockref, i_master_clockref, i_client_clockref, i_diff_date, i_drift ); #endif /* Wait a bit */ msleep( INTF_IDLE_SLEEP ); } } if( p_intf->p_sys->p_input ) vlc_object_release( p_intf->p_sys->p_input ); net_Close( i_socket ); }
/* Add a SAP announce */ static int announce_SAPAnnounceAdd( sap_handler_t *p_sap, session_descriptor_t *p_session, announce_method_t *p_method ) { int i; char *psz_type = "application/sdp"; int i_header_size; char *psz_head; vlc_bool_t b_found = VLC_FALSE; sap_session_t *p_sap_session; mtime_t i_hash; vlc_mutex_lock( &p_sap->object_lock ); /* If needed, build the SDP */ if( !p_session->psz_sdp ) { if ( SDPGenerate( p_sap, p_session ) != VLC_SUCCESS ) { vlc_mutex_unlock( &p_sap->object_lock ); return VLC_EGENERIC; } } if( !p_method->psz_address ) { if( p_method->i_ip_version == 6 ) { char sz_scope; if( p_method->psz_ipv6_scope != NULL ) { sz_scope = *p_method->psz_ipv6_scope; } else { sz_scope = DEFAULT_IPV6_SCOPE; } p_method->psz_address = (char*)malloc( 30*sizeof(char )); sprintf( p_method->psz_address, "%s%c%s", SAP_IPV6_ADDR_1, sz_scope, SAP_IPV6_ADDR_2 ); } else { /* IPv4 */ p_method->psz_address = (char*)malloc( 15*sizeof(char) ); snprintf(p_method->psz_address, 15, SAP_IPV4_ADDR ); } } msg_Dbg( p_sap, "using SAP address: %s",p_method->psz_address); /* XXX: Check for dupes */ p_sap_session = (sap_session_t*)malloc(sizeof(sap_session_t)); p_sap_session->psz_sdp = strdup( p_session->psz_sdp ); p_sap_session->i_last = 0; /* Add the address to the buffer */ for( i = 0; i< p_sap->i_addresses; i++) { if( !strcmp( p_method->psz_address, p_sap->pp_addresses[i]->psz_address ) ) { p_sap_session->p_address = p_sap->pp_addresses[i]; b_found = VLC_TRUE; break; } } if( b_found == VLC_FALSE ) { sap_address_t *p_address = (sap_address_t *) malloc( sizeof(sap_address_t) ); if( !p_address ) { msg_Err( p_sap, "out of memory" ); return VLC_ENOMEM; } p_address->psz_address = strdup( p_method->psz_address ); p_address->i_ip_version = p_method->i_ip_version; p_address->i_port = 9875; p_address->i_wfd = net_OpenUDP( p_sap, "", 0, p_address->psz_address, p_address->i_port ); if( p_sap->b_control == VLC_TRUE ) { p_address->i_rfd = net_OpenUDP( p_sap, p_method->psz_address, p_address->i_port, "", 0 ); p_address->i_buff = 0; p_address->b_enabled = VLC_TRUE; p_address->b_ready = VLC_FALSE; p_address->i_limit = 10000; /* 10000 bps */ p_address->t1 = 0; } else { p_address->b_enabled = VLC_TRUE; p_address->b_ready = VLC_TRUE; p_address->i_interval = config_GetInt( p_sap,"sap-interval"); } if( p_address->i_wfd == -1 || (p_address->i_rfd == -1 && p_sap->b_control ) ) { msg_Warn( p_sap, "disabling address" ); p_address->b_enabled = VLC_FALSE; } INSERT_ELEM( p_sap->pp_addresses, p_sap->i_addresses, p_sap->i_addresses, p_address ); p_sap_session->p_address = p_address; } /* Build the SAP Headers */ i_header_size = ( p_method->i_ip_version == 6 ? 20 : 8 ) + strlen( psz_type ) + 1; psz_head = (char *) malloc( i_header_size * sizeof( char ) ); if( ! psz_head ) { msg_Err( p_sap, "out of memory" ); return VLC_ENOMEM; } psz_head[0] = 0x20; /* Means SAPv1, IPv4, not encrypted, not compressed */ psz_head[1] = 0x00; /* No authentification length */ i_hash = mdate(); psz_head[2] = (i_hash & 0xFF00) >> 8; /* Msg id hash */ psz_head[3] = (i_hash & 0xFF); /* Msg id hash 2 */ if( p_method->i_ip_version == 6 ) { /* in_addr_t ip_server = inet_addr( ip ); */ psz_head[0] |= 0x10; /* Set IPv6 */ psz_head[4] = 0x01; /* Source IP FIXME: we should get the real address */ psz_head[5] = 0x02; /* idem */ psz_head[6] = 0x03; /* idem */ psz_head[7] = 0x04; /* idem */ psz_head[8] = 0x01; /* Source IP FIXME: we should get the real address */ psz_head[9] = 0x02; /* idem */ psz_head[10] = 0x03; /* idem */ psz_head[11] = 0x04; /* idem */ psz_head[12] = 0x01; /* Source IP FIXME: we should get the real address */ psz_head[13] = 0x02; /* idem */ psz_head[14] = 0x03; /* idem */ psz_head[15] = 0x04; /* idem */ psz_head[16] = 0x01; /* Source IP FIXME: we should get the real address */ psz_head[17] = 0x02; /* idem */ psz_head[18] = 0x03; /* idem */ psz_head[19] = 0x04; /* idem */ strncpy( psz_head + 20, psz_type, 15 ); } else { /* in_addr_t ip_server = inet_addr( ip) */ /* Source IP FIXME: we should get the real address */ psz_head[4] = 0x01; /* ip_server */ psz_head[5] = 0x02; /* ip_server>>8 */ psz_head[6] = 0x03; /* ip_server>>16 */ psz_head[7] = 0x04; /* ip_server>>24 */ strncpy( psz_head + 8, psz_type, 15 ); } psz_head[ i_header_size-1 ] = '\0'; p_sap_session->i_length = i_header_size + strlen( p_sap_session->psz_sdp); p_sap_session->psz_data = (char *)malloc( sizeof(char)* p_sap_session->i_length ); /* Build the final message */ memcpy( p_sap_session->psz_data, psz_head, i_header_size ); memcpy( p_sap_session->psz_data+i_header_size, p_sap_session->psz_sdp, strlen( p_sap_session->psz_sdp) ); free( psz_head ); /* Enqueue the announce */ INSERT_ELEM( p_sap->pp_sessions, p_sap->i_sessions, p_sap->i_sessions, p_sap_session ); msg_Dbg( p_sap,"Addresses: %i Sessions: %i", p_sap->i_addresses,p_sap->i_sessions); /* Remember the SAP session for later deletion */ p_session->p_sap = p_sap_session; vlc_mutex_unlock( &p_sap->object_lock ); return VLC_SUCCESS; }
/***************************************************************************** * Open: *****************************************************************************/ static int Open( vlc_object_t *p_this ) { sout_stream_t *p_stream = (sout_stream_t*)p_this; sout_stream_sys_t *p_sys; vlc_value_t val; char *psz_files, *psz_sizes; int i_height = 0, i_width = 0; p_sys = malloc( sizeof(sout_stream_sys_t) ); memset( p_sys, 0, sizeof(sout_stream_sys_t) ); p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next ); if( !p_sys->p_out ) { msg_Err( p_stream, "cannot create chain" ); free( p_sys ); return VLC_EGENERIC; } sout_CfgParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg ); var_Get( p_stream, SOUT_CFG_PREFIX "files", &val ); psz_files = val.psz_string; var_Get( p_stream, SOUT_CFG_PREFIX "sizes", &val ); psz_sizes = val.psz_string; p_sys->i_nb_pictures = 0; while ( psz_files && *psz_files ) { char * psz_file = psz_files; char * psz_size = psz_sizes; while ( *psz_files && *psz_files != ':' ) psz_files++; if ( *psz_files == ':' ) *psz_files++ = '\0'; if ( *psz_sizes ) { while ( *psz_sizes && *psz_sizes != ':' ) psz_sizes++; if ( *psz_sizes == ':' ) *psz_sizes++ = '\0'; if ( sscanf( psz_size, "%dx%d", &i_width, &i_height ) != 2 ) { msg_Err( p_stream, "bad size %s for file %s", psz_size, psz_file ); free( p_sys ); return VLC_EGENERIC; } } if ( UnpackFromFile( p_stream, psz_file, i_width, i_height, &p_sys->p_pictures[p_sys->i_nb_pictures] ) < 0 ) { free( p_sys ); return VLC_EGENERIC; } p_sys->i_nb_pictures++; } var_Get( p_stream, SOUT_CFG_PREFIX "aspect-ratio", &val ); if ( val.psz_string ) { char *psz_parser = strchr( val.psz_string, ':' ); if( psz_parser ) { *psz_parser++ = '\0'; p_sys->i_aspect = atoi( val.psz_string ) * VOUT_ASPECT_FACTOR / atoi( psz_parser ); } else { msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string ); p_sys->i_aspect = 4 * VOUT_ASPECT_FACTOR / 3; } free( val.psz_string ); } else { p_sys->i_aspect = 4 * VOUT_ASPECT_FACTOR / 3; } var_Get( p_stream, SOUT_CFG_PREFIX "port", &val ); p_sys->i_fd = net_OpenUDP( p_stream, NULL, val.i_int, NULL, 0 ); if ( p_sys->i_fd < 0 ) { free( p_sys ); return VLC_EGENERIC; } var_Get( p_stream, SOUT_CFG_PREFIX "command", &val ); p_sys->i_cmd = val.i_int; p_sys->i_old_cmd = 0; var_Get( p_stream, SOUT_CFG_PREFIX "gop", &val ); p_sys->i_gop = val.i_int; var_Get( p_stream, SOUT_CFG_PREFIX "qscale", &val ); p_sys->i_qscale = val.i_int; var_Get( p_stream, SOUT_CFG_PREFIX "mute-audio", &val ); p_sys->b_audio = val.b_bool; p_stream->pf_add = Add; p_stream->pf_del = Del; p_stream->pf_send = Send; p_stream->p_sys = p_sys; avcodec_init(); avcodec_register_all(); return VLC_SUCCESS; }