VLC调用Live555源码解析
以前在看live555的源码和例子的时,发现live555的例子都是回调,这样我们根本无法判断命令是否发送成功,也无法判断发送是否超时;网上搜索,也没有看到有用的资料;于是就分析了VLC的live555.cpp文件,对该问题恍然大悟,于是记下vlc live555的Rtsp连接建立过程,供大家参考,我们可以从vlc怎么调用live555.cpp中看到vlc 是如何结合live555的,这样对我们调用live555写客户端等等有很大的帮助!
源码见:
http://blog.csdn.net/smilestone_322/article/details/17504909
1) 连接RTSP服务器
/*****************************************************************************
* Connect: connects to the RTSP server to setup the session DESCRIBE
*****************************************************************************/
static int Connect( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
Authenticator authenticator;
char *psz_user = NULL;
char *psz_pwd = NULL;
char *psz_url = NULL;
int i_http_port = 0;
int i_ret = VLC_SUCCESS;
//获取超时的时间
const int i_timeout = var_InheritInteger( p_demux, "ipv4-timeout" );
/* Get the user name and password */
//获取RTSP的URL的用户名和密码;
if( p_sys->url.psz_username || p_sys->url.psz_password )
{
/* Create the URL by stripping away the username/password part */
if( p_sys->url.i_port == 0 )
p_sys->url.i_port = 554;
if( asprintf( &psz_url, "rtsp://%s:%d%s",
strempty( p_sys->url.psz_host ),
p_sys->url.i_port,
strempty( p_sys->url.psz_path ) ) == -1 )
return VLC_ENOMEM;
psz_user = strdup( strempty( p_sys->url.psz_username ) );
psz_pwd = strdup( strempty( p_sys->url.psz_password ) );
}
else
{
if( asprintf( &psz_url, "rtsp://%s", p_sys->psz_path ) == -1 )
return VLC_ENOMEM;
psz_user = var_InheritString( p_demux, "rtsp-user" );
psz_pwd = var_InheritString( p_demux, "rtsp-pwd" );
}
createnew:
if( !vlc_object_alive (p_demux) )
{
i_ret = VLC_EGENERIC;
goto bailout;
}
//判断是否走http隧道
if( var_CreateGetBool( p_demux, "rtsp-http" ) )
i_http_port = var_InheritInteger( p_demux, "rtsp-http-port" );
//创建一个RTSPClient客户端, RTSPClientVlc继承自live555的RTSPClient
p_sys->rtsp = new RTSPClientVlc( *p_sys->env, psz_url,
var_InheritInteger( p_demux, "verbose" ) > 1 ? 1 : 0,
"LibVLC/"VERSION, i_http_port, p_sys );
if( !p_sys->rtsp )
{
msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
p_sys->env->getResultMsg() );
i_ret = VLC_EGENERIC;
goto bailout;
}
/* Kasenna enables KeepAlive by analysing the User-Agent string.
* Appending _KA to the string should be enough to enable this feature,
* however, there is a bug where the _KA doesn't get parsed from the
* default User-Agent as created by VLC/Live555 code. This is probably due
* to spaces in the string or the string being too long. Here we override
* the default string with a more compact version.
*/
if( var_InheritBool( p_demux, "rtsp-kasenna" ))
{
p_sys->rtsp->setUserAgentString( "VLC_MEDIA_PLAYER_KA" );
}
describe:
authenticator.setUsernameAndPassword( psz_user, psz_pwd );
//往服务器发送sendOptionsCommand请求,continueAfterOPTIONS和live555的例子一样,是一个回调,在发送完sendOptionsCommand请求,收到它的应答后调用该回调函数,在continueAfterOPTIONS函数中发送Describe请求;
p_sys->rtsp->sendOptionsCommand( &continueAfterOPTIONS, &authenticator );
// wait_Live555_response 用来判断发送命令是否超时,相当重要,Live555给的例子中都没有这个,这样我们是不好判断我们发送的命令是否失败,比如发送命令超时了,程序就没有响应了,都不能给个提示,VLC使用该函数解决了Live555的这个问题;
if( !wait_Live555_response( p_demux, i_timeout ) )
{
int i_code = p_sys->i_live555_ret;
if( i_code == 401 )
{
msg_Dbg( p_demux, "authentication failed" );
free( psz_user );
free( psz_pwd );
dialog_Login( p_demux, &psz_user, &psz_pwd,
_("RTSP authentication"), "%s",
_("Please enter a valid login name and a password.") );
if( psz_user != NULL && psz_pwd != NULL )
{
//鉴权失败,转到describe再次发送Opinion请求
msg_Dbg( p_demux, "retrying with user=%s", psz_user );
goto describe;
}
}
else if( i_code > 0 && i_code != 404 && !var_GetBool( p_demux, "rtsp-http" ) )
{
/* Perhaps a firewall is being annoying. Try HTTP tunneling mode */
//失败,转http 隧道模式
msg_Dbg( p_demux, "we will now try HTTP tunneling mode" );
var_SetBool( p_demux, "rtsp-http", true );
if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
p_sys->rtsp = NULL;
goto createnew;
}
else
{
// i_code=0表示连接超时
if( i_code == 0 )
msg_Dbg( p_demux, "connection timeout" );
else
{
//连接失败
msg_Dbg( p_demux, "connection error %d", i_code );
if( i_code == 403 )
dialog_Fatal( p_demux, _("RTSP connection failed"),
_("Access to the stream is denied by the server configuration.") );
}
if( p_sys->rtsp ) RTSPClient::close( p_sys->rtsp );
p_sys->rtsp = NULL;
}
i_ret = VLC_EGENERIC;
}
bailout:
/* malloc-ated copy */
free( psz_url );
free( psz_user );
free( psz_pwd );
return i_ret;
}
其中:
static void continueAfterOPTIONS( RTSPClient* client, int result_code,
char* result_string )
{
RTSPClientVlc *client_vlc = static_cast<RTSPClientVlc *> (client);
demux_sys_t *p_sys = client_vlc->p_sys;
p_sys->b_get_param =
// If OPTIONS fails, assume GET_PARAMETER is not supported but
// still continue on with the stream. Some servers (foscam)
// return 501/not implemented for OPTIONS.
result_code == 0
&& result_string != NULL
&& strstr( result_string, "GET_PARAMETER" ) != NULL;
client->sendDescribeCommand( continueAfterDESCRIBE );
delete[] result_string;
}
在continueAfterOPTIONS函数中,调用sendDescribeCommand函数发送Describe命令;continueAfterDESCRIBE为sendDescribeCommand命令发送后调用的回调函数;
continueAfterDESCRIBE函数如下:
static void continueAfterDESCRIBE( RTSPClient* client, int result_code,
char* result_string )
{
RTSPClientVlc *client_vlc = static_cast<RTSPClientVlc *> ( client );
demux_sys_t *p_sys = client_vlc->p_sys;
p_sys->i_live555_ret = result_code;
if ( result_code == 0 )
{
char* sdpDescription = result_string;
free( p_sys->p_sdp );
p_sys->p_sdp = NULL;
if( sdpDescription )
{
p_sys->p_sdp = strdup( sdpDescription );
p_sys->b_error = false;
}
}
else
p_sys->b_error = true;
delete[] result_string;
p_sys->event_rtsp = 1;
}
回调函数中的result_code和result_string还是挺重要的,可以从其中判断发送的命令是否错误;
判断超时的函数如下:
/* return true if the RTSP command succeeded */
static bool wait_Live555_response( demux_t *p_demux, int i_timeout = 0 /* ms */ )
{
TaskToken task;
demux_sys_t * p_sys = p_demux->p_sys;
p_sys->event_rtsp = 0;
if( i_timeout > 0 )
{
/* Create a task that will be called if we wait more than timeout ms */
task = p_sys->scheduler->scheduleDelayedTask( i_timeout*1000,
TaskInterruptRTSP,
p_demux );
}
p_sys->event_rtsp = 0;
p_sys->b_error = true;
p_sys->i_live555_ret = 0;
//
p_sys->scheduler->doEventLoop( &p_sys->event_rtsp );
//here, if b_error is true and i_live555_ret = 0 we didn't receive a response
if( i_timeout > 0 )
{
/* remove the task */
p_sys->scheduler->unscheduleDelayedTask( task );
}
return !p_sys->b_error;
}
如果超时:那么调用TaskInterruptRTSP函数;当超时时,在TaskInterruptRTSP将event_rtsp置为非0;这样doEventLoop里面的while循环会顺利退出;如果没有超时,在continueAfterDESCRIBE函数中也将event_rtsp置为非0;DoEventLoop的线程也会顺利退出,这样在根据p_sys->b_error;判断是否成功还是失败;
*****************************************************************************/
static void TaskInterruptRTSP( void *p_private )
{
demux_t *p_demux = (demux_t*)p_private;
/* Avoid lock */
p_demux->p_sys->event_rtsp = 0xff;
}
2) 发送Setup建立会话;
/*****************************************************************************
* SessionsSetup: prepares the subsessions and does the SETUP
*****************************************************************************/
static int SessionsSetup( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
MediaSubsessionIterator *iter = NULL;
MediaSubsession *sub = NULL;
bool b_rtsp_tcp;
int i_client_port;
int i_return = VLC_SUCCESS;
unsigned int i_receive_buffer = 0;
int i_frame_buffer = DEFAULT_FRAME_BUFFER_SIZE;
unsigned const thresh = 200000; /* RTP reorder threshold .2 second (default .1) */
b_rtsp_tcp = var_CreateGetBool( p_demux, "rtsp-tcp" ) ||
var_GetBool( p_demux, "rtsp-http" );
i_client_port = var_InheritInteger( p_demux, "rtp-client-port" );
/* Create the session from the SDP */
if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
{
msg_Err( p_demux, "Could not create the RTSP Session: %s",
p_sys->env->getResultMsg() );
return VLC_EGENERIC;
}
/* Initialise each media subsession */
iter = new MediaSubsessionIterator( *p_sys->ms );
while( ( sub = iter->next() ) != NULL )
{
Boolean bInit;
live_track_t *tk;
if( !vlc_object_alive (p_demux) )
{
delete iter;
return VLC_EGENERIC;
}
/* Value taken from mplayer */
if( !strcmp( sub->mediumName(), "audio" ) )
i_receive_buffer = 100000;
else if( !strcmp( sub->mediumName(), "video" ) )
{
int i_var_buf_size = var_InheritInteger( p_demux, "rtsp-frame-buffer-size" );
if( i_var_buf_size > 0 )
i_frame_buffer = i_var_buf_size;
i_receive_buffer = 2000000;
}
else if( !strcmp( sub->mediumName(), "text" ) )
;
else continue;
if( i_client_port != -1 )
{
sub->setClientPortNum( i_client_port );
i_client_port += 2;
}
if( strcasestr( sub->codecName(), "REAL" ) )
{
msg_Info( p_demux, "real codec detected, using real-RTSP instead" );
p_sys->b_real = true; /* This is a problem, we'll handle it later */
continue;
}
if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
bInit = sub->initiate( 0 );
else
bInit = sub->initiate();
if( !bInit )
{
msg_Warn( p_demux, "RTP subsession '%s/%s' failed (%s)",
sub->mediumName(), sub->codecName(),
p_sys->env->getResultMsg() );
}
else
{
if( sub->rtpSource() != NULL )
{
int fd = sub->rtpSource()->RTPgs()->socketNum();
/* Increase the buffer size */
if( i_receive_buffer > 0 )
increaseReceiveBufferTo( *p_sys->env, fd, i_receive_buffer );
/* Increase the RTP reorder timebuffer just a bit */
sub->rtpSource()->setPacketReorderingThresholdTime(thresh);
}
msg_Dbg( p_demux, "RTP subsession '%s/%s'", sub->mediumName(),
sub->codecName() );
/* Issue the SETUP */
if( p_sys->rtsp )
{
//发送Setup命令,建立会话;default_live555_callback为sendSetupCommand函数的回调函数;其主要作用是判断SetUp命令是否发送成功,置为DoEvent的线程标志,使得线程顺利退出,从而wait_Live555_response函数能够顺利退出;
p_sys->rtsp->sendSetupCommand( *sub, default_live555_callback, False,
toBool( b_rtsp_tcp ),
toBool( p_sys->b_force_mcast && !b_rtsp_tcp ) );
//判断Setup命令是否发送成功
if( !wait_Live555_response( p_demux ) )
{
/* if we get an unsupported transport error, toggle TCP
* use and try again */
if( p_sys->i_live555_ret == 461 )
p_sys->rtsp->sendSetupCommand( *sub, default_live555_callback, False,
!toBool( b_rtsp_tcp ), False );
if( p_sys->i_live555_ret != 461 || !wait_Live555_response( p_demux ) )
{
msg_Err( p_demux, "SETUP of'%s/%s' failed %s",
sub->mediumName(), sub->codecName(),
p_sys->env->getResultMsg() );
continue;
}
else
{
var_SetBool( p_demux, "rtsp-tcp", true );
b_rtsp_tcp = true;
}
}
}
/* Check if we will receive data from this subsession for
* this track */
if( sub->readSource() == NULL ) continue;
if( !p_sys->b_multicast )
{
/* We need different rollover behaviour for multicast */
p_sys->b_multicast = IsMulticastAddress( sub->connectionEndpointAddress() );
}
tk = (live_track_t*)malloc( sizeof( live_track_t ) );
if( !tk )
{
delete iter;
return VLC_ENOMEM;
}
tk->p_demux = p_demux;
tk->sub = sub;
tk->p_es = NULL;
tk->b_quicktime = false;
tk->b_asf = false;
tk->p_asf_block = NULL;
tk->b_muxed = false;
tk->b_discard_trunc = false;
tk->p_out_muxed = NULL;
tk->waiting = 0;
tk->b_rtcp_sync = false;
tk->i_pts = VLC_TS_INVALID;
tk->f_npt = 0.;
tk->b_selected = true;
tk->i_buffer = i_frame_buffer;
tk->p_buffer = (uint8_t *)malloc( i_frame_buffer );
if( !tk->p_buffer )
{
free( tk );
delete iter;
return VLC_ENOMEM;
}
/* Value taken from mplayer */
if( !strcmp( sub->mediumName(), "audio" ) )
{
es_format_Init( &tk->fmt, AUDIO_ES, VLC_FOURCC('u','n','d','f') );
tk->fmt.audio.i_channels = sub->numChannels();
tk->fmt.audio.i_rate = sub->rtpTimestampFrequency();
if( !strcmp( sub->codecName(), "MPA" ) ||
!strcmp( sub->codecName(), "MPA-ROBUST" ) ||
!strcmp( sub->codecName(), "X-MP3-DRAFT-00" ) )
{
tk->fmt.i_codec = VLC_CODEC_MPGA;
tk->fmt.audio.i_rate = 0;
}
else if( !strcmp( sub->codecName(), "AC3" ) )
{
tk->fmt.i_codec = VLC_CODEC_A52;
tk->fmt.audio.i_rate = 0;
}
else if( !strcmp( sub->codecName(), "L16" ) )
{
tk->fmt.i_codec = VLC_CODEC_S16B;
tk->fmt.audio.i_bitspersample = 16;
}
else if( !strcmp( sub->codecName(), "L20" ) )
{
tk->fmt.i_codec = VLC_CODEC_S20B;
tk->fmt.audio.i_bitspersample = 20;
}
else if( !strcmp( sub->codecName(), "L24" ) )
{
tk->fmt.i_codec = VLC_CODEC_S24B;
tk->fmt.audio.i_bitspersample = 24;
}
else if( !strcmp( sub->codecName(), "L8" ) )
{
tk->fmt.i_codec = VLC_CODEC_U8;
tk->fmt.audio.i_bitspersample = 8;
}
else if( !strcmp( sub->codecName(), "DAT12" ) )
{
tk->fmt.i_codec = VLC_CODEC_DAT12;
tk->fmt.audio.i_bitspersample = 12;
}
else if( !strcmp( sub->codecName(), "PCMU" ) )
{
tk->fmt.i_codec = VLC_CODEC_MULAW;
tk->fmt.audio.i_bitspersample = 8;
}
else if( !strcmp( sub->codecName(), "PCMA" ) )
{
tk->fmt.i_codec = VLC_CODEC_ALAW;
tk->fmt.audio.i_bitspersample = 8;
}
else if( !strncmp( sub->codecName(), "G726", 4 ) )
{
tk->fmt.i_codec = VLC_CODEC_ADPCM_G726;
tk->fmt.audio.i_rate = 8000;
tk->fmt.audio.i_channels = 1;
if( !strcmp( sub->codecName()+5, "40" ) )
tk->fmt.i_bitrate = 40000;
else if( !strcmp( sub->codecName()+5, "32" ) )
tk->fmt.i_bitrate = 32000;
else if( !strcmp( sub->codecName()+5, "24" ) )
tk->fmt.i_bitrate = 24000;
else if( !strcmp( sub->codecName()+5, "16" ) )
tk->fmt.i_bitrate = 16000;
}
else if( !strcmp( sub->codecName(), "AMR" ) )
{
tk->fmt.i_codec = VLC_CODEC_AMR_NB;
}
else if( !strcmp( sub->codecName(), "AMR-WB" ) )
{
tk->fmt.i_codec = VLC_CODEC_AMR_WB;
}
else if( !strcmp( sub->codecName(), "MP4A-LATM" ) )
{
unsigned int i_extra;
uint8_t *p_extra;
tk->fmt.i_codec = VLC_CODEC_MP4A;
if( ( p_extra = parseStreamMuxConfigStr( sub->fmtp_config(),
i_extra ) ) )
{
tk->fmt.i_extra = i_extra;
tk->fmt.p_extra = xmalloc( i_extra );
memcpy( tk->fmt.p_extra, p_extra, i_extra );
delete[] p_extra;
}
/* Because the "faad" decoder does not handle the LATM
* data length field at the start of each returned LATM
* frame, tell the RTP source to omit. */
((MPEG4LATMAudioRTPSource*)sub->rtpSource())->omitLATMDataLengthField();
}
else if( !strcmp( sub->codecName(), "MPEG4-GENERIC" ) )
{
unsigned int i_extra;
uint8_t *p_extra;
tk->fmt.i_codec = VLC_CODEC_MP4A;
if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
i_extra ) ) )
{
tk->fmt.i_extra = i_extra;
tk->fmt.p_extra = xmalloc( i_extra );
memcpy( tk->fmt.p_extra, p_extra, i_extra );
delete[] p_extra;
}
}
else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
{
tk->b_asf = true;
if( p_sys->p_out_asf == NULL )
p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
p_demux->out );
}
else if( !strcmp( sub->codecName(), "X-QT" ) ||
!strcmp( sub->codecName(), "X-QUICKTIME" ) )
{
tk->b_quicktime = true;
}
else if( !strcmp( sub->codecName(), "SPEEX" ) )
{
tk->fmt.i_codec = VLC_FOURCC( 's', 'p', 'x', 'r' );
if ( tk->fmt.audio.i_rate == 0 )
{
msg_Warn( p_demux,"Using 8kHz as default sample rate." );
tk->fmt.audio.i_rate = 8000;
}
}
else if( !strcmp( sub->codecName(), "VORBIS" ) )
{
tk->fmt.i_codec = VLC_CODEC_VORBIS;
unsigned int i_extra;
unsigned char *p_extra;
if( ( p_extra=parseVorbisConfigStr( sub->fmtp_config(),
i_extra ) ) )
{
tk->fmt.i_extra = i_extra;
tk->fmt.p_extra = p_extra;
}
else
msg_Warn( p_demux,"Missing or unsupported vorbis header." );
}
}
else if( !strcmp( sub->mediumName(), "video" ) )
{
es_format_Init( &tk->fmt, VIDEO_ES, VLC_FOURCC('u','n','d','f') );
if( !strcmp( sub->codecName(), "MPV" ) )
{
tk->fmt.i_codec = VLC_CODEC_MPGV;
tk->fmt.b_packetized = false;
}
else if( !strcmp( sub->codecName(), "H263" ) ||
!strcmp( sub->codecName(), "H263-1998" ) ||
!strcmp( sub->codecName(), "H263-2000" ) )
{
tk->fmt.i_codec = VLC_CODEC_H263;
}
else if( !strcmp( sub->codecName(), "H261" ) )
{
tk->fmt.i_codec = VLC_CODEC_H261;
}
else if( !strcmp( sub->codecName(), "H264" ) )
{
unsigned int i_extra = 0;
uint8_t *p_extra = NULL;
tk->fmt.i_codec = VLC_CODEC_H264;
tk->fmt.b_packetized = false;
if((p_extra=parseH264ConfigStr( sub->fmtp_spropparametersets(),
i_extra ) ) )
{
tk->fmt.i_extra = i_extra;
tk->fmt.p_extra = xmalloc( i_extra );
memcpy( tk->fmt.p_extra, p_extra, i_extra );
delete[] p_extra;
}
}
else if( !strcmp( sub->codecName(), "JPEG" ) )
{
tk->fmt.i_codec = VLC_CODEC_MJPG;
}
else if( !strcmp( sub->codecName(), "MP4V-ES" ) )
{
unsigned int i_extra;
uint8_t *p_extra;
tk->fmt.i_codec = VLC_CODEC_MP4V;
if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
i_extra ) ) )
{
tk->fmt.i_extra = i_extra;
tk->fmt.p_extra = xmalloc( i_extra );
memcpy( tk->fmt.p_extra, p_extra, i_extra );
delete[] p_extra;
}
}
else if( !strcmp( sub->codecName(), "X-QT" ) ||
!strcmp( sub->codecName(), "X-QUICKTIME" ) ||
!strcmp( sub->codecName(), "X-QDM" ) ||
!strcmp( sub->codecName(), "X-SV3V-ES" ) ||
!strcmp( sub->codecName(), "X-SORENSONVIDEO" ) )
{
tk->b_quicktime = true;
}
else if( !strcmp( sub->codecName(), "MP2T" ) )
{
tk->b_muxed = true;
tk->p_out_muxed = stream_DemuxNew( p_demux, "ts", p_demux->out );
}
else if( !strcmp( sub->codecName(), "MP2P" ) ||
!strcmp( sub->codecName(), "MP1S" ) )
{
tk->b_muxed = true;
tk->p_out_muxed = stream_DemuxNew( p_demux, "ps",
p_demux->out );
}
else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
{
tk->b_asf = true;
if( p_sys->p_out_asf == NULL )
p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
p_demux->out );;
}
else if( !strcmp( sub->codecName(), "DV" ) )
{
tk->b_muxed = true;
tk->b_discard_trunc = true;
tk->p_out_muxed = stream_DemuxNew( p_demux, "rawdv",
p_demux->out );
}
else if( !strcmp( sub->codecName(), "VP8" ) )
{
tk->fmt.i_codec = VLC_CODEC_VP8;
}
}
else if( !strcmp( sub->mediumName(), "text" ) )
{
es_format_Init( &tk->fmt, SPU_ES, VLC_FOURCC('u','n','d','f') );
if( !strcmp( sub->codecName(), "T140" ) )
{
tk->fmt.i_codec = VLC_CODEC_ITU_T140;
}
}
if( !tk->b_quicktime && !tk->b_muxed && !tk->b_asf )
{
tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
}
if( sub->rtcpInstance() != NULL )
{
sub->rtcpInstance()->setByeHandler( StreamClose, tk );
}
if( tk->p_es || tk->b_quicktime || ( tk->b_muxed && tk->p_out_muxed ) ||
( tk->b_asf && p_sys->p_out_asf ) )
{
TAB_APPEND_CAST( (live_track_t **), p_sys->i_track, p_sys->track, tk );
}
else
{
/* BUG ??? */
msg_Err( p_demux, "unusable RTSP track. this should not happen" );
es_format_Clean( &tk->fmt );
free( tk );
}
}
}
delete iter;
if( p_sys->i_track <= 0 ) i_return = VLC_EGENERIC;
/* Retrieve the starttime if possible */
p_sys->f_npt_start = p_sys->ms->playStartTime();
/* Retrieve the duration if possible */
p_sys->f_npt_length = p_sys->ms->playEndTime();
/* */
msg_Dbg( p_demux, "setup start: %f stop:%f", p_sys->f_npt_start, p_sys->f_npt_length );
/* */
p_sys->b_no_data = true;
p_sys->i_no_data_ti = 0;
return i_return;
}
其中default_live555_callback函数定义如下,该函数也是挺重要的:
static void default_live555_callback( RTSPClient* client, int result_code, char* result_string )
{
RTSPClientVlc *client_vlc = static_cast<RTSPClientVlc *> ( client );
demux_sys_t *p_sys = client_vlc->p_sys;
delete []result_string;
p_sys->i_live555_ret = result_code;
p_sys->b_error = p_sys->i_live555_ret != 0;
p_sys->event_rtsp = 1;
}
3) 发送Play请求;
static int Play( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
if( p_sys->rtsp )
{
/* The PLAY */
//发送sendPlayCommand命令,default_live555_callback为其回调函数
p_sys->rtsp->sendPlayCommand( *p_sys->ms, default_live555_callback, p_sys->f_npt_start, -1, 1 );
//判断Play命令是否发送超时
if( !wait_Live555_response(p_demux) )
{
msg_Err( p_demux, "RTSP PLAY failed %s", p_sys->env->getResultMsg() );
return VLC_EGENERIC;
}
/* Retrieve the timeout value and set up a timeout prevention thread */
p_sys->i_timeout = p_sys->rtsp->sessionTimeoutParameter();
if( p_sys->i_timeout <= 0 )
p_sys->i_timeout = 60; /* default value from RFC2326 */
/* start timeout-thread only if GET_PARAMETER is supported by the server */
/* or start it if wmserver dialect, since they don't report that GET_PARAMETER is supported correctly */
if( !p_sys->p_timeout && ( p_sys->b_get_param || var_InheritBool( p_demux, "rtsp-wmserver" ) ) )
{
msg_Dbg( p_demux, "We have a timeout of %d seconds", p_sys->i_timeout );
p_sys->p_timeout = (timeout_thread_t *)malloc( sizeof(timeout_thread_t) );
if( p_sys->p_timeout )
{
memset( p_sys->p_timeout, 0, sizeof(timeout_thread_t) );
p_sys->p_timeout->p_sys = p_demux->p_sys; /* lol, object recursion :D */
if( vlc_clone( &p_sys->p_timeout->handle, TimeoutPrevention,
p_sys->p_timeout, VLC_THREAD_PRIORITY_LOW ) )
{
msg_Err( p_demux, "cannot spawn liveMedia timeout thread" );
free( p_sys->p_timeout );
p_sys->p_timeout = NULL;
}
else
msg_Dbg( p_demux, "spawned timeout thread" );
}
else
msg_Err( p_demux, "cannot spawn liveMedia timeout thread" );
}
}
p_sys->i_pcr = 0;
/* Retrieve the starttime if possible */
p_sys->f_npt_start = p_sys->ms->playStartTime();
if( p_sys->ms->playEndTime() > 0 )
p_sys->f_npt_length = p_sys->ms->playEndTime();
msg_Dbg( p_demux, "play start: %f stop:%f", p_sys->f_npt_start, p_sys->f_npt_length );
return VLC_SUCCESS;
}