#include <assert.h>
-#if defined( WIN32 )
+#if defined( _WIN32 )
# include <winsock2.h>
#endif
#include <GroupsockHelper.hh>
#include <liveMedia.hh>
#include <liveMedia_version.hh>
+#include <Base64.hh>
extern "C" {
#include "../access/mms/asf.h" /* Who said ugly ? */
#define PASS_TEXT N_("RTSP password")
#define PASS_LONGTEXT N_("Sets the password for the connection, " \
"if no username or password are set in the url.")
+#define FRAME_BUFFER_SIZE_TEXT N_("RTSP frame buffer size")
+#define FRAME_BUFFER_SIZE_LONGTEXT N_("RTSP start frame buffer size of the video " \
+ "track, can be increased in case of broken pictures due " \
+ "to too small buffer.")
+#define DEFAULT_FRAME_BUFFER_SIZE 100000
vlc_module_begin ()
set_description( N_("RTP/RTSP/SDP demuxer (using Live555)" ) )
change_safe()
add_password( "rtsp-pwd", NULL, PASS_TEXT,
PASS_LONGTEXT, true )
- change_safe()
+ add_integer( "rtsp-frame-buffer-size", DEFAULT_FRAME_BUFFER_SIZE,
+ FRAME_BUFFER_SIZE_TEXT, FRAME_BUFFER_SIZE_LONGTEXT,
+ true )
vlc_module_end ()
char const* applicationName, portNumBits tunnelOverHTTPPortNum,
demux_sys_t *p_sys) :
RTSPClient( env, rtspURL, verbosityLevel, applicationName,
- tunnelOverHTTPPortNum )
+ tunnelOverHTTPPortNum
+#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1373932800
+ , -1
+#endif
+ )
{
this->p_sys = p_sys;
}
static unsigned char* parseH264ConfigStr( char const* configStr,
unsigned int& configSize );
+static unsigned char* parseVorbisConfigStr( char const* configStr,
+ unsigned int& configSize );
/*****************************************************************************
* DemuxOpen:
p_demux->p_sys = p_sys = (demux_sys_t*)calloc( 1, sizeof( demux_sys_t ) );
if( !p_sys ) return VLC_ENOMEM;
- msg_Dbg( p_demux, "version "LIVEMEDIA_LIBRARY_VERSION_STRING );
+ msg_Dbg( p_demux, "version " LIVEMEDIA_LIBRARY_VERSION_STRING );
TAB_INIT( p_sys->i_track, p_sys->track );
p_sys->f_npt = 0.;
// 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_code == 0
&& result_string != NULL
&& strstr( result_string, "GET_PARAMETER" ) != NULL;
client->sendDescribeCommand( continueAfterDESCRIBE );
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 );
+ "LibVLC/" VERSION, i_http_port, p_sys );
if( !p_sys->rtsp )
{
msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
bool b_rtsp_tcp;
int i_client_port;
int i_return = VLC_SUCCESS;
- unsigned int i_buffer = 0;
+ 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) */
+ const char *p_sess_lang = NULL;
+ const char *p_lang;
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 ) ) )
{
return VLC_EGENERIC;
}
+ if( strcmp( p_sys->p_sdp, "m=" ) != 0 )
+ {
+ const char *p_sess_attr_end;
+
+ p_sess_attr_end = strstr( p_sys->p_sdp, "\nm=" );
+ if( !p_sess_attr_end )
+ p_sess_attr_end = strstr( p_sys->p_sdp, "\rm=" );
+
+ p_sess_lang = p_sess_attr_end ? strstr( p_sys->p_sdp, "a=lang:" ) : NULL;
+ if( p_sess_lang &&
+ p_sess_lang - p_sys->p_sdp > p_sess_attr_end - p_sys->p_sdp )
+ p_sess_lang = NULL;
+ }
+
/* Initialise each media subsession */
iter = new MediaSubsessionIterator( *p_sys->ms );
while( ( sub = iter->next() ) != NULL )
/* Value taken from mplayer */
if( !strcmp( sub->mediumName(), "audio" ) )
- i_buffer = 100000;
+ i_receive_buffer = 100000;
else if( !strcmp( sub->mediumName(), "video" ) )
- i_buffer = 2000000;
+ {
+ 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;
int fd = sub->rtpSource()->RTPgs()->socketNum();
/* Increase the buffer size */
- if( i_buffer > 0 )
- increaseReceiveBufferTo( *p_sys->env, fd, i_buffer );
+ 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);
tk->i_pts = VLC_TS_INVALID;
tk->f_npt = 0.;
tk->b_selected = true;
- tk->i_buffer = 65536;
- tk->p_buffer = (uint8_t *)malloc( 65536 );
+ tk->i_buffer = i_frame_buffer;
+ tk->p_buffer = (uint8_t *)malloc( i_frame_buffer );
+
if( !tk->p_buffer )
{
free( tk );
else if( !strcmp( sub->codecName(), "SPEEX" ) )
{
tk->fmt.i_codec = VLC_FOURCC( 's', 'p', 'x', 'r' );
- if ( sub->rtpTimestampFrequency() )
- tk->fmt.audio.i_rate = sub->rtpTimestampFrequency();
- else
+ }
+ 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 ) ) )
{
- msg_Warn( p_demux,"Using 8kHz as default sample rate." );
- tk->fmt.audio.i_rate = 8000;
+ 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->codecName(), "OPUS" ) )
+ {
+ tk->fmt.i_codec = VLC_CODEC_OPUS;
}
}
else if( !strcmp( sub->mediumName(), "video" ) )
delete[] p_extra;
}
}
+#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1393372800 // 2014.02.26
+ else if( !strcmp( sub->codecName(), "H265" ) )
+ {
+ unsigned int i_extra1 = 0, i_extra2 = 0, i_extra3 = 0, i_extraTot;
+ uint8_t *p_extra1 = NULL, *p_extra2 = NULL, *p_extra3 = NULL;
+
+ tk->fmt.i_codec = VLC_CODEC_HEVC;
+ tk->fmt.b_packetized = false;
+
+ p_extra1 = parseH264ConfigStr( sub->fmtp_spropvps(), i_extra1 );
+ p_extra2 = parseH264ConfigStr( sub->fmtp_spropsps(), i_extra2 );
+ p_extra3 = parseH264ConfigStr( sub->fmtp_sproppps(), i_extra3 );
+ i_extraTot = i_extra1 + i_extra2 + i_extra3;
+ if( i_extraTot > 0 )
+ {
+ tk->fmt.i_extra = i_extraTot;
+ tk->fmt.p_extra = xmalloc( i_extraTot );
+ if( p_extra1 )
+ {
+ memcpy( tk->fmt.p_extra, p_extra1, i_extra1 );
+ }
+ if( p_extra2 )
+ {
+ memcpy( ((char*)tk->fmt.p_extra)+i_extra1, p_extra2, i_extra2 );
+ }
+ if( p_extra3 )
+ {
+ memcpy( ((char*)tk->fmt.p_extra)+i_extra1+i_extra2, p_extra3, i_extra3 );
+ }
+
+ delete[] p_extra1; delete[] p_extra2; delete[] p_extra3;
+ }
+ }
+#endif
else if( !strcmp( sub->codecName(), "JPEG" ) )
{
tk->fmt.i_codec = VLC_CODEC_MJPG;
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->codecName(), "THEORA" ) )
+ {
+ tk->fmt.i_codec = VLC_CODEC_THEORA;
+ 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 theora header." );
+ }
}
else if( !strcmp( sub->mediumName(), "text" ) )
{
}
}
+ /* Try and parse a=lang: attribute */
+ p_lang = strstr( sub->savedSDPLines(), "a=lang:" );
+ if( !p_lang )
+ p_lang = p_sess_lang;
+
+ if( p_lang )
+ {
+ unsigned i_lang_len;
+ p_lang += 7;
+ i_lang_len = strcspn( p_lang, " \r\n" );
+ tk->fmt.psz_language = strndup( p_lang, i_lang_len );
+ }
+
if( !tk->b_quicktime && !tk->b_muxed && !tk->b_asf )
{
tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
p_sys->i_timeout = 60; /* default value from RFC2326 */
/* start timeout-thread only if GET_PARAMETER is supported by the server */
- if( !p_sys->p_timeout && p_sys->b_get_param )
+ /* 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) );
TaskToken task;
bool b_send_pcr = true;
- int64_t i_pcr = 0;
int i;
/* Check if we need to send the server a Keep-A-Live signal */
{
bool b;
es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, tk->p_es, &b );
- if( !b && tk->b_selected )
+ if( !b && tk->b_selected && p_sys->rtsp )
{
tk->b_selected = false;
p_sys->rtsp->sendTeardownCommand( *tk->sub, NULL );
if( tk->b_asf || tk->b_muxed )
b_send_pcr = false;
-#if 0
- if( i_pcr == 0 )
- {
- i_pcr = tk->i_pts;
- }
- else if( tk->i_pts != 0 && i_pcr > tk->i_pts )
- {
- i_pcr = tk->i_pts ;
- }
-#endif
}
if( p_sys->i_pcr > 0 )
{
tk->f_npt = 0.;
p_sys->i_pcr = 0;
p_sys->f_npt = 0.;
- i_pcr = 0;
}
}
}
p_sys->i_pcr = 0;
- /* Retrieve RTP-Info values */
for( i = 0; i < p_sys->i_track; i++ )
{
p_sys->track[i]->b_rtcp_sync = false;
QuickTimeGenericRTPSource::QTState &qtState = qtRTPSource->qtState;
uint8_t *sdAtom = (uint8_t*)&qtState.sdAtom[4];
- /* Get codec informations from the quicktime atoms :
+ /* Get codec information from the quicktime atoms :
* http://developer.apple.com/quicktime/icefloe/dispatch026.html */
if( tk->fmt.i_cat == VIDEO_ES ) {
if( qtState.sdAtomSize < 16 + 32 )
if( tk->sub->rtpSource()->curPacketMarkerBit() )
p_block->i_flags |= BLOCK_FLAG_END_OF_FRAME;
}
- else if( tk->fmt.i_codec == VLC_CODEC_H264 )
+ else if( tk->fmt.i_codec == VLC_CODEC_H264 || tk->fmt.i_codec == VLC_CODEC_HEVC )
{
- if( (tk->p_buffer[0] & 0x1f) >= 24 )
+ if( tk->fmt.i_codec == VLC_CODEC_H264 && (tk->p_buffer[0] & 0x1f) >= 24 )
msg_Warn( p_demux, "unsupported NAL type for H264" );
+ else if( tk->fmt.i_codec == VLC_CODEC_HEVC && ((tk->p_buffer[0] & 0x7e)>>1) >= 48 )
+ msg_Warn( p_demux, "unsupported NAL type for H265" );
/* Normal NAL type */
p_block = block_Alloc( i_size + 4 );
}
/* Update our global npt value */
- if( tk->f_npt > 0 && tk->f_npt > p_sys->f_npt &&
+ if( tk->f_npt > 0 &&
( tk->f_npt < p_sys->f_npt_length || p_sys->f_npt_length <= 0 ) )
p_sys->f_npt = tk->f_npt;
free( dup );
return cfg;
}
+
+static uint8_t *parseVorbisConfigStr( char const* configStr,
+ unsigned int& configSize )
+{
+ configSize = 0;
+ if( configStr == NULL || *configStr == '\0' )
+ return NULL;
+#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1332115200 // 2012.03.20
+ unsigned char *p_cfg = base64Decode( configStr, configSize );
+#else
+ char* configStr_dup = strdup( configStr );
+ unsigned char *p_cfg = base64Decode( configStr_dup, configSize );
+ free( configStr_dup );
+#endif
+ uint8_t *p_extra = NULL;
+ /* skip header count, ident number and length (cf. RFC 5215) */
+ const unsigned int headerSkip = 9;
+ if( configSize > headerSkip && ((uint8_t*)p_cfg)[3] == 1 )
+ {
+ configSize -= headerSkip;
+ p_extra = (uint8_t*)xmalloc( configSize );
+ memcpy( p_extra, p_cfg+headerSkip, configSize );
+ }
+ delete[] p_cfg;
+ return p_extra;
+}
+