I'm using VLC as a RTP/RTSP VOD server and I hope this patch or some parts will be useful for VLC project.
1. H.264 RTP packetizer.
It works fine for streaming video to a VLC Client but QuickTime do not
recognizes the stream. I.e. it plays only a sound and no video. There's also
no "H264" in "Stream Info" page, only audio (AAC in my case). I believe it's a
SDP issue - some parameters are missing.
2. Show client IP and port numbers in telnet's "show" command output.
Like this:
ondemandstream
type : vod
enabled : yes
instances
1375840326 : playing
address : 172.16.1.173:6970/6972
3. Seekable VOD.
It's not perfect but is better than nothing, imho. Particularly, it do not
works (don't know why) if stream is seeked with "dragging" current position
pointer and player tries to play lively seeked position.
4. Stop sending UDP stream if there are errors (e.g. "icmp port unreachable")
It's very useful feature for VOD (if client dies then transmission stops) but
(I've just realized) will probably break current #udp{} setups, so it should
be optional
Code: Select all
Index: src/misc/vlm.c
===================================================================
--- src/misc/vlm.c (revision 15183)
+++ src/misc/vlm.c (working copy)
@@ -1308,6 +1308,12 @@
return VLC_SUCCESS;
}
+ else if( !strcmp( psz_command, "setclientid" ) )
+ {
+ var_Create( p_instance->p_input, "clientid", VLC_VAR_STRING );
+ var_SetString( p_instance->p_input, "clientid", psz_args );
+ return VLC_SUCCESS;
+ }
return VLC_EGENERIC;
}
@@ -1867,18 +1873,30 @@
for( j = 0; j < m->i_instance; j++ )
{
+ vlm_message_t *msg_iinfo;
vlm_media_instance_t *p_instance = m->instance[j];
vlc_value_t val;
if( !p_instance->p_input ) val.i_int = END_S;
else var_Get( p_instance->p_input, "state", &val );
- vlm_MessageAdd( msg_instance,
+ msg_iinfo = vlm_MessageAdd( msg_instance,
vlm_MessageNew( p_instance->psz_name ?
p_instance->psz_name : "default",
val.i_int == PLAYING_S ? "playing" :
val.i_int == PAUSE_S ? "paused" :
"stopped" ) );
+
+ val.psz_string = NULL;
+ if( p_instance->p_input ){
+ var_Get( p_instance->p_input, "clientid", &val );
+ }
+
+ if (val.psz_string){
+ vlm_MessageAdd( msg_iinfo, vlm_MessageNew( "address",
+ "%s", val.psz_string ) );
+ }
+
}
}
@@ -2353,6 +2371,13 @@
break;
}
+ case VOD_MEDIA_SETCLIENTID:
+ {
+ char *clid = (char*)va_arg( args, char* );
+ i_ret = vlm_MediaControl( vlm, vlm->media[i], psz_id, "setclientid", clid);
+ break;
+ }
+
default:
break;
}
Index: modules/access_output/udp.c
===================================================================
--- modules/access_output/udp.c (revision 15183)
+++ modules/access_output/udp.c (working copy)
@@ -338,6 +338,18 @@
{
sout_access_out_sys_t *p_sys = p_access->p_sys;
+ if ( p_sys->p_thread->b_error != 0 ){
+ while( p_buffer ){
+ block_t *p_next;
+
+ p_next = p_buffer->p_next;
+ block_Release( p_buffer );
+ p_buffer = p_next;
+ }
+
+ return -1;
+ }
+
while( p_buffer )
{
block_t *p_next;
@@ -420,6 +432,19 @@
sout_access_out_sys_t *p_sys = p_access->p_sys;
block_t *p_buf;
+ if ( p_sys->p_thread->b_error != 0 ){
+ while( p_buffer ){
+ block_t *p_next;
+
+ p_next = p_buffer->p_next;
+ block_Release( p_buffer );
+ p_buffer = p_next;
+ }
+
+ return -1;
+ }
+
+
while ( p_sys->p_thread->p_empty_blocks->i_depth >= MAX_EMPTY_BLOCKS )
{
p_buf = block_FifoGet(p_sys->p_thread->p_empty_blocks);
@@ -505,6 +530,8 @@
mtime_t i_date_last = -1;
mtime_t i_to_send = p_thread->i_group;
int i_dropped_packets = 0;
+ int i_send_err_count = 0;
+ mtime_t i_last_send_err = -1;
#if defined(WIN32) || defined(UNDER_CE)
char strerror_buf[WINSOCK_STRERROR_SIZE];
# define strerror( x ) winsock_strerror( strerror_buf )
@@ -522,9 +549,9 @@
while( p_tmp ) { p_tmp = p_tmp->p_next; i++;}
p_tmp = p_thread->p_fifo->p_first;
while( p_tmp ) { p_tmp = p_tmp->p_next; j++;}
- msg_Err( p_thread, "fifo depth: %d/%d, empty blocks: %d/%d",
+ msg_Err( p_thread, "fifo depth: %d/%d, empty blocks: %d/%d",
p_thread->p_fifo->i_depth, j,p_thread->p_empty_blocks->i_depth,i );
- }
+ }
#endif
p_pk = block_FifoGet( p_thread->p_fifo );
@@ -557,10 +584,23 @@
mwait( i_date );
i_to_send = p_thread->i_group;
}
+
if( send( p_thread->i_handle, p_pk->p_buffer, p_pk->i_buffer, 0 )
== -1 )
{
- msg_Warn( p_thread, "send error: %s", strerror(errno) );
+ msg_Warn( p_thread, "send error#%d: %s", i_send_err_count, strerror(errno) );
+ i_send_err_count ++;
+ i_last_send_err = i_date;
+ } else {
+ if ( i_send_err_count > 0 )
+ {
+ if ( (i_last_send_err - i_date) > 1000000 )
+ {
+ i_send_err_count -= 5;
+ if ( i_send_err_count < 0 )
+ i_send_err_count = 0;
+ }
+ }
}
if( i_dropped_packets )
@@ -578,6 +618,12 @@
}
#endif
+ if (i_send_err_count > 50){
+ msg_Warn( p_thread, "too many send errors. stopping." );
+ p_thread->b_error = 1;
+ p_thread->b_die = 1;
+ }
+
block_FifoPut( p_thread->p_empty_blocks, p_pk );
i_date_last = i_date;
Index: modules/stream_out/rtp.c
===================================================================
--- modules/stream_out/rtp.c (revision 15183)
+++ modules/stream_out/rtp.c (working copy)
@@ -902,6 +902,7 @@
static int rtp_packetize_mp4a ( sout_stream_t *, sout_stream_id_t *, block_t * );
static int rtp_packetize_mp4a_latm ( sout_stream_t *, sout_stream_id_t *, block_t * );
static int rtp_packetize_h263 ( sout_stream_t *, sout_stream_id_t *, block_t * );
+static int rtp_packetize_h264 ( sout_stream_t *, sout_stream_id_t *, block_t * );
static int rtp_packetize_amr ( sout_stream_t *, sout_stream_id_t *, block_t * );
static void sprintf_hexa( char *s, uint8_t *p_data, int i_data )
@@ -1067,6 +1068,12 @@
id->psz_rtpmap = strdup( "H263-1998/90000" );
id->pf_packetize = rtp_packetize_h263;
break;
+ case VLC_FOURCC( 'h', '2', '6', '4' ):
+ id->i_payload_type = p_sys->i_payload_type++;
+ id->i_clock_rate = 90000;
+ id->psz_rtpmap = strdup( "H264/90000" );
+ id->pf_packetize = rtp_packetize_h264;
+ break;
case VLC_FOURCC( 'm', 'p', '4', 'v' ):
{
@@ -2335,6 +2342,130 @@
return VLC_SUCCESS;
}
+/* rfc3984 */
+static int rtp_packetize_h264( sout_stream_t *p_stream, sout_stream_id_t *id,
+ block_t *in )
+{
+ int i_max = id->i_mtu - 12; /* payload max in one packet */
+ int i_count = ( in->i_buffer + i_max - 1 ) / i_max;
+ uint8_t *p_data = in->p_buffer;
+ int i_data = in->i_buffer;
+ int i;
+
+
+ if (i_data <= i_max) {
+ /* NAL */
+ int i_payload;
+ block_t *out;
+ uint8_t *p = p_data;
+ int i_rest = in->i_buffer;
+
+ while( i_rest > 4 &&
+ ( p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x01 ) )
+ {
+ p++;
+ i_rest--;
+ }
+
+ if (i_rest < 4)
+ return VLC_SUCCESS;
+
+ p+=3;
+ i_rest-=3;
+
+ i_payload = __MIN( i_max, i_rest );
+ out = block_New( p_stream, 12 + i_payload );
+
+ /* rtp common header */
+ rtp_packetize_common( id, out, 1,
+ in->i_pts > 0 ? in->i_pts : in->i_dts );
+
+ memcpy( &out->p_buffer[12], p, i_payload );
+
+ out->i_buffer = 12 + i_payload;
+ out->i_dts = in->i_dts;
+ out->i_length = in->i_length;
+
+ rtp_packetize_send( id, out );
+
+ /*msg_Dbg( p_stream, "nal-out plain %d %02x", i_payload, out->p_buffer[16] );*/
+
+ } else {
+ /* FU-A */
+ int i_payload;
+ block_t *out;
+ uint8_t *p = p_data;
+ int i_rest = in->i_buffer;
+ int start=1, end=0, first=0, nalh=-1;
+
+ while( i_rest > 4 &&
+ ( p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x01 ) )
+ {
+ p++;
+ i_rest--;
+ }
+
+ if (i_rest < 4)
+ return VLC_SUCCESS;
+
+ p+=3;
+ i_rest-=3;
+
+ nalh = *p;
+
+ p++;
+ i_rest--;
+
+ i_max = id->i_mtu - 14;
+ i_count = ( i_rest + i_max - 1 ) / i_max;
+
+
+ /*msg_Dbg( p_stream, "nal-out fragmented %02x %d", nalh, i_rest);*/
+
+ i=0;
+ while (end==0){
+ i_payload = __MIN( i_max, i_rest );
+ out = block_New( p_stream, 14 + i_payload );
+
+ if (i_rest==i_payload)
+ end = 1;
+
+ /* rtp common header */
+ rtp_packetize_common( id, out, (end)?1:0,
+ in->i_pts > 0 ? in->i_pts : in->i_dts );
+
+ /* FU indicator */
+ out->p_buffer[12] = (nalh&0x60)|28;
+ /* FU header */
+ out->p_buffer[13] = (start<<7)|(end<<6)|(nalh&0x1f);
+
+ memcpy( &out->p_buffer[14], p+first, i_payload );
+
+ out->i_buffer = 14 + i_payload;
+
+ // not sure what of these should be used and what it does :)
+ //out->i_dts = in->i_dts + i * in->i_length / i_count;
+ //out->i_length = in->i_length / i_count;
+ out->i_dts = in->i_dts;
+ out->i_length = in->i_length;
+
+ rtp_packetize_send( id, out );
+
+ /*msg_Dbg( p_stream, "nal-out fragmented: frag %d %d %02x %02x %d", start,end,
+ out->p_buffer[12], out->p_buffer[13], i_payload );*/
+
+ i_rest -= i_payload;
+ first += i_payload;
+ i++;
+ start=0;
+ }
+
+ }
+
+ return VLC_SUCCESS;
+}
+
+
static int rtp_packetize_amr( sout_stream_t *p_stream, sout_stream_id_t *id,
block_t *in )
{
Index: modules/misc/rtsp.c
===================================================================
--- modules/misc/rtsp.c (revision 15183)
+++ modules/misc/rtsp.c (working copy)
@@ -125,6 +125,7 @@
char *psz_rtsp_control_v6;
char *psz_rtsp_path;
+ char *psz_client_ip;
int i_port;
int i_port_audio;
int i_port_video;
@@ -353,6 +354,8 @@
}
vlc_mutex_unlock( &p_item->lock );
+ p_media->psz_client_ip = NULL;
+
return p_media;
}
@@ -377,6 +380,9 @@
free( p_media->psz_session_description );
free( p_media->psz_session_url );
free( p_media->psz_session_email );
+
+ if (p_media->psz_client_ip)
+ free( p_media->psz_client_ip );
free( p_media );
}
@@ -434,6 +440,12 @@
p_es->i_payload_type = p_media->i_payload_type++;
p_es->psz_rtpmap = strdup( "H263-1998/90000" );
break;
+ case VLC_FOURCC( 'h', '2', '6', '4' ):
+ p_es->i_payload_type = p_media->i_payload_type++;
+ p_es->psz_rtpmap = strdup( "H264/90000" );
+ p_es->psz_fmtp = strdup("packetization-mode=1");
+
+ break;
case VLC_FOURCC( 'm', 'p', '4', 'v' ):
p_es->i_payload_type = p_media->i_payload_type++;
p_es->psz_rtpmap = strdup( "MP4V-ES/90000" );
@@ -767,6 +779,9 @@
{
char *psz_output, ip[NI_MAXNUMERICHOST];
int i, i_port_audio = 0, i_port_video = 0;
+ int port1 = 0, port2 = 0;
+ char *psz_clid = NULL, *psz_position = NULL;
+ double f_seek_pos = -1.0;
/* for now only multicast so easy */
if( !psz_playnow )
@@ -781,13 +796,46 @@
psz_session = httpd_MsgGet( query, "Session" );
msg_Dbg( p_vod, "HTTPD_MSG_PLAY for session: %s", psz_session );
+ psz_position = httpd_MsgGet( query, "Range" );
+ if( psz_position ) psz_position = strstr( psz_position, "npt=" );
+
+ if (psz_position){
+ char *end;
+
+ msg_Dbg( p_vod, "seeking request: %s", psz_position );
+
+ psz_position += 4;
+
+ /* FIXME: npt= is not necessarily formatted as a float */
+ f_seek_pos = us_strtod( psz_position, &end );
+ }
+
p_rtsp = RtspClientGet( p_media, psz_session );
if( !p_rtsp ) break;
+ if(p_rtsp->b_playing && (f_seek_pos >= 0.0)){
+ if(p_rtsp->b_paused == VLC_FALSE)
+ {
+ vod_MediaControl( p_vod, p_media, psz_session,
+ VOD_MEDIA_PAUSE );
+
+ p_rtsp->b_paused = VLC_TRUE;
+ }
+ }
+
if( p_rtsp->b_playing && p_rtsp->b_paused )
{
+
+ if(f_seek_pos > 0.0)
+ {
+ f_seek_pos /= ((double)(p_media->i_length))/1000 /1000 / 100;
+ vod_MediaControl( p_vod, p_media, psz_session,
+ VOD_MEDIA_SEEK, f_seek_pos );
+ }
+
vod_MediaControl( p_vod, p_media, psz_session,
VOD_MEDIA_PAUSE );
+
p_rtsp->b_paused = VLC_FALSE;
break;
}
@@ -806,27 +854,40 @@
i_port_video = p_rtsp->es[i]->i_port;
}
+
if( p_media->psz_mux )
{
if( p_media->b_raw )
{
asprintf( &psz_output, "std{access=udp,dst=%s:%i,mux=%s}",
ip, i_port, p_media->psz_mux );
+ port1 = i_port;
}
else
{
asprintf( &psz_output, "rtp{dst=%s,port=%i,mux=%s}",
ip, i_port_video, p_media->psz_mux );
+ port1 = i_port_video;
}
}
else
{
asprintf( &psz_output, "rtp{dst=%s,port-video=%i,"
"port-audio=%i}", ip, i_port_video, i_port_audio );
+ port1 = i_port_video;
+ port2 = i_port_audio;
}
vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_PLAY,
psz_output );
+
+ if ( p_media->psz_client_ip )
+ free( p_media->psz_client_ip );
+ asprintf(&psz_clid, "%s:%d/%d", ip, port1, port2);
+ p_media->psz_client_ip = psz_clid;
+ vod_MediaControl( p_vod, p_media, psz_session,
+ VOD_MEDIA_SETCLIENTID, p_media->psz_client_ip );
+
free( psz_output );
break;
}
Index: include/vlc_vod.h
===================================================================
--- include/vlc_vod.h (revision 15183)
+++ include/vlc_vod.h (working copy)
@@ -63,6 +63,7 @@
VOD_MEDIA_PAUSE, /* arg1= double * res= */
VOD_MEDIA_STOP, /* arg1= double res=can fail */
VOD_MEDIA_SEEK, /* arg1= double * res= */
+ VOD_MEDIA_SETCLIENTID,
};
#endif