#define AUTO_MTU_LONGTEXT N_( \
"Allows growing the MTU if truncated packets are found" )
-#define RTP_LATE_TEXT N_("Reorder timeout in ms for late RTP packets")
+#define RTP_LATE_TEXT N_("RTP reordering timeout in ms")
#define RTP_LATE_LONGTEXT N_( \
- "Allows you to modify the RTP packets reorder and late behaviour. " \
- "If enabled (value>0) then out-of-order packets will be held for the " \
- "specified timeout in ms. " \
- "The default behaviour is not to reorder." )
+ "Allows you to modify the RTP reordering behaviour. " \
+ "RTP input will wait for late packets upto " \
+ "the specified timeout in milisecond units." )
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
add_integer( "udp-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT,
CACHING_LONGTEXT, VLC_TRUE );
+ add_integer( "rtp-late", 100, NULL, RTP_LATE_TEXT, RTP_LATE_LONGTEXT, VLC_TRUE );
+
add_bool( "udp-auto-mtu", 1, NULL,
AUTO_MTU_TEXT, AUTO_MTU_LONGTEXT, VLC_TRUE );
- add_integer( "rtp-late", 0, NULL, RTP_LATE_TEXT, RTP_LATE_LONGTEXT, VLC_TRUE );
-
set_capability( "access2", 0 );
add_shortcut( "udp" );
add_shortcut( "udpstream" );
* Local prototypes
*****************************************************************************/
#define RTP_HEADER_LEN 12
-#define RTP_SEQ_NUM_SIZE 65536
static block_t *BlockUDP( access_t * );
static block_t *BlockRTP( access_t * );
int i_mtu;
vlc_bool_t b_auto_mtu;
- /* rtp only */
- uint16_t i_sequence_number;
- vlc_bool_t b_first_seqno;
-
- /* reorder rtp packets when out-of-bounds
- * the packets hold queue is one level deep
- */
- uint32_t i_rtp_late; /* number of ms an RTP packet may be too late*/
- uint32_t i_last_pcr; /* last known good PCR */
- block_t *p_list; /* list of packets to rearrange */
- block_t *p_end; /* last packet in p_list */
+ /* reorder rtp packets when out-of-sequence */
+ int64_t i_rtp_late;
+ uint16_t i_last_seqno;
+ block_t *p_list;
+ block_t *p_end;
};
/*****************************************************************************
p_access->info.i_size = 0;
p_access->info.i_pos = 0;
p_access->info.b_eof = VLC_FALSE;
+ p_access->info.b_prebuffered = VLC_FALSE;
p_access->info.i_title = 0;
p_access->info.i_seekpoint = 0;
/* Update default_pts to a suitable value for udp access */
var_Create( p_access, "udp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
- /* Keep track of RTP sequence number */
- p_sys->i_sequence_number = 0;
- p_sys->b_first_seqno = VLC_TRUE;
- /* RTP reordering out-of-bound packets */
- p_sys->i_last_pcr = 0;
- p_sys->i_rtp_late = var_CreateGetInteger( p_access, "rtp-late" );
+ /* RTP reordering for out-of-sequence packets */
+ p_sys->i_rtp_late = var_CreateGetInteger( p_access, "rtp-late" ) * 1000;
+ p_sys->i_last_seqno = 0;
p_sys->p_list = NULL;
p_sys->p_end = NULL;
-
return VLC_SUCCESS;
}
* rtp_ChainInsert - insert a p_block in the chain and
* look at the sequence numbers.
*/
-static inline void rtp_ChainInsert( access_t *p_access, block_t *p_block )
+static inline vlc_bool_t rtp_ChainInsert( access_t *p_access, block_t *p_block )
{
access_sys_t *p_sys = (access_sys_t *) p_access->p_sys;
- block_t *p_list = p_sys->p_list;
- block_t *p_end = p_sys->p_end;
- block_t *p = NULL;
- uint16_t i_new = 0;
- uint16_t i_cur = 0;
- uint16_t i_expected = 0;
- uint32_t i_pcr_new = 0;
-
- if( !p_block ) return;
- if( !p_list )
- {
- p_sys->p_list = p_block;
- p_sys->p_end = p_block;
- return;
- }
+ block_t *p_prev = NULL;
+ block_t *p = p_sys->p_end;
+ uint16_t i_new = p_block->i_seqno;
+ uint16_t i_tmp = 0;
- /* Appending packets at the end of the chain is the normal case */
- i_pcr_new = ( (p_block->p_buffer[4] << 24) +
- (p_block->p_buffer[5] << 16) +
- (p_block->p_buffer[6] << 8) +
- p_block->p_buffer[7] );
- i_new = ( (p_block->p_buffer[2] << 8 ) + p_block->p_buffer[3] );
- i_cur = ( (p_end->p_buffer[2] << 8 ) + p_end->p_buffer[3] );
-
- i_expected = i_cur + 1;
- if( (i_new - i_expected) >= 0 ) /* Append at the end? */
- {
- msg_Dbg( p_access, "RTP: append %u after %u", i_new, i_cur );
- p_end->p_next = p_block;
- p_sys->p_end = p_end->p_next;
- return;
- }
- /* Add to the front fo the chain? */
- p = p_list;
- i_cur = ( (p->p_buffer[2] << 8 ) + p->p_buffer[3] );
- if( (i_expected - i_new) > 0 )
+ if( !p_sys->p_list )
{
- msg_Dbg( p_access, "RTP: prepend %u before %u", i_cur, i_new );
- p_block->p_next = p;
p_sys->p_list = p_block;
- return;
+ p_sys->p_end = p_block;
+ return VLC_TRUE;
}
- /* The packet can't be added to the front or the end of the chain,
- * thus walk the chain from the start.
- */
- while( p )
+ /* walk through the queue from top down since the new packet is in
+ most cases just appended to the end */
+
+ for( ;; )
{
- i_cur = (p->p_buffer[2] << 8 ) + p->p_buffer[3];
- i_expected = i_cur+1;
+ i_tmp = i_new - p->i_seqno;
- if( (i_cur - i_new) == 0 )
- {
- uint32_t i_pcr_cur = ( (p->p_buffer[4] << 24) +
- (p->p_buffer[5] << 16) +
- (p->p_buffer[6] << 8) +
- p->p_buffer[7] );
- /* This packet might be a duplicate, so check PCR's */
- if( i_pcr_cur >= i_pcr_new )
- {
- /* packet way too late drop it. */
- block_Release( p_block );
- return;
- }
- /* Add it to list later on
- * else if( i_pcr_cur < i_pcr_new ) */
- break;
- }
- else if( (i_expected - i_new) >= 0 ) /* insert in chain */
- {
- block_t *p_tmp = NULL;
+ if( !i_tmp ) /* trash duplicate */
+ break;
- p_tmp = p->p_next;
- msg_Dbg( p_access, "RTP: insert %u after %u", i_new, i_cur );
+ if ( i_tmp < 32768 )
+ { /* insert after this block ( i_new > p->i_seqno ) */
+ p_block->p_next = p->p_next;
p->p_next = p_block;
- p_block->p_next = p_tmp;
- return;
- }
- if( !p->p_next ) break;
- p = p->p_next;
- }
- msg_Dbg(p_access, "RTP: trashing duplicate %d", i_new );
- block_Release( p_block );
-}
-
-/*
- * rtp_ChainSend - look which packets are ready for sending.
- */
-static inline block_t *rtp_ChainSend( access_t *p_access, block_t **pp_list, uint16_t i_seq )
-{
- access_sys_t *p_sys = (access_sys_t *) p_access->p_sys;
- uint16_t i_cur = 0;
-
- if( *pp_list )
- {
- /* Parse RTP header */
- int i_skip = 0;
- int i_extension_bit = 0;
- int i_extension_length = 0;
- int i_CSRC_count = 0;
- int i_payload_type = 0;
- uint32_t i_pcr_prev = 0;
- uint16_t i_seq_prev = 0;
- /* Data pointers */
- block_t *p_prev = NULL;
- block_t *p_send = *pp_list;
- block_t *p = *pp_list;
-
- while( p )
- {
- i_cur = (p->p_buffer[2] << 8 ) + p->p_buffer[3];
- if( (i_cur - i_seq) == 0 )
+ p_block->p_prev = p;
+ if (p_prev)
{
- msg_Dbg( p_access, "rtp_ChainSend: sequence number %u", i_seq );
-
- i_seq++; /* sent all packets that are received in order */
-
- /* Remember PCR and sequence number of packet
- * for next iteration */
- i_pcr_prev = ( (p->p_buffer[4] << 24) +
- (p->p_buffer[5] << 16) +
- (p->p_buffer[6] << 8) +
- p->p_buffer[7] );
- i_seq_prev = ( (p->p_buffer[2] << 8 ) +
- p->p_buffer[3] );
-
- /* Parse headerfields we need */
- i_CSRC_count = p->p_buffer[0] & 0x0F;
- i_payload_type = (p->p_buffer[1] & 0x7F);
- i_extension_bit = ( p->p_buffer[0] & 0x10 ) >> 4;
- if ( i_extension_bit == 1)
- i_extension_length = ( (p->p_buffer[14] << 8 ) +
- p->p_buffer[15] );
-
- /* Skip header + CSRC extension field n*(32 bits) + extention */
- i_skip = RTP_HEADER_LEN + 4*i_CSRC_count + i_extension_length;
- if( i_payload_type == 14 ) i_skip += 4;
-
- /* Return the packet without the RTP header. */
- p->i_buffer -= i_skip;
- p->p_buffer += i_skip;
+ p_prev->p_prev = p_block;
+ msg_Dbg(p_access, "RTP reordering: insert after %d, new %d",
+ p->i_seqno, i_new );
}
- else if( (i_cur - i_seq) > 0 )
+ else
{
- if( p_prev )
- {
- p_sys->p_list = p;
- p_prev->p_next = NULL;
- p_sys->i_last_pcr = i_pcr_prev;
- p_sys->i_sequence_number = i_seq_prev;
- return p_send;
- }
- goto out;
+ p_sys->p_end = p_block;
}
- p_prev = p;
- if (!p->p_next) break;
- p = p->p_next;
+ return VLC_TRUE;
}
+ if( p == p_sys->p_list )
+ { /* we've reached bottom of chain */
+ i_tmp = p_sys->i_last_seqno - i_new;
+ if( !p_access->info.b_prebuffered || (i_tmp > 32767) )
+ {
+ msg_Dbg(p_access, "RTP reordering: prepend %d before %d",
+ i_new, p->i_seqno );
+ p_block->p_next = p;
+ p->p_prev = p_block;
+ p_sys->p_list = p_block;
+ return VLC_TRUE;
+ }
-out:
- /* We have walked through the complete chain and all packets are
- * in sequence - so send the whole chain
- */
- i_payload_type = (p->p_buffer[1] & 0x7F);
- i_CSRC_count = p->p_buffer[0] & 0x0F;
- i_extension_bit = ( p->p_buffer[0] & 0x10 ) >> 4;
- if( i_extension_bit == 1)
- i_extension_length = ( (p->p_buffer[14] << 8 ) + p->p_buffer[15] );
-
- /* Skip header + CSRC extension field n*(32 bits) + extention */
- i_skip = RTP_HEADER_LEN + 4*i_CSRC_count + i_extension_length;
- if( i_payload_type == 14 ) i_skip += 4;
-
- /* Update the list pointers */
- p_sys->p_list = NULL;
- p_sys->p_end = NULL;
- p_sys->i_sequence_number = ( (p->p_buffer[2] << 8 ) +
- p->p_buffer[3] );
- p_sys->i_last_pcr = ( (p->p_buffer[4] << 24) +
- (p->p_buffer[5] << 16) +
- (p->p_buffer[6] << 8) +
- p->p_buffer[7] );
- /* Return the packet without the RTP header. */
- p->i_buffer -= i_skip;
- p->p_buffer += i_skip;
- return p_send;
+ if( !i_tmp ) /* trash duplicate */
+ break;
+
+ /* reordering failed - append the packet to the end of queue */
+ msg_Dbg(p_access, "RTP: sequence changed (or buffer too small) "
+ "new: %d, buffer %d...%d", i_new, p->i_seqno,
+ p_sys->p_end->i_seqno);
+ p_sys->p_end->p_next = p_block;
+ p_block->p_prev = p_sys->p_end;
+ p_sys->p_end = p_block;
+ return VLC_TRUE;
+ }
+ p_prev = p;
+ p = p->p_prev;
}
- return NULL;
+ block_Release( p_block );
+ return VLC_FALSE;
}
/*****************************************************************************
int i_CSRC_count;
int i_payload_type;
int i_skip = 0;
- uint16_t i_sequence_number = 0;
- uint16_t i_sequence_expected = 0;
- int i_extension_bit = 0;
+ int i_extension_flag = 0;
int i_extension_length = 0;
- uint32_t i_pcr = 0;
+ uint16_t i_sequence_number = 0;
if( p_block == NULL )
return NULL;
if( p_block->i_buffer < RTP_HEADER_LEN )
- {
- msg_Warn( p_access, "received a too short packet for RTP" );
- block_Release( p_block );
- return NULL;
- }
+ goto trash;
+
/* Parse the header and make some verifications.
* See RFC 3550. */
i_rtp_version = ( p_block->p_buffer[0] & 0xC0 ) >> 6;
i_CSRC_count = p_block->p_buffer[0] & 0x0F;
+ i_extension_flag = p_block->p_buffer[0] & 0x10;
i_payload_type = p_block->p_buffer[1] & 0x7F;
- i_sequence_number = (p_block->p_buffer[2] << 8) +
- p_block->p_buffer[3];
- i_pcr = ( (p_block->p_buffer[4] << 24) +
- (p_block->p_buffer[5] << 16) +
- (p_block->p_buffer[6] << 8) +
- p_block->p_buffer[7] );
- i_extension_bit = ( p_block->p_buffer[0] & 0x10 ) >> 4;
+ i_sequence_number = (p_block->p_buffer[2] << 8 ) + p_block->p_buffer[3];
if( i_rtp_version != 2 )
msg_Dbg( p_access, "RTP version is %u, should be 2", i_rtp_version );
i_skip = 4;
else if( i_payload_type != 33 && i_payload_type != 32 )
msg_Dbg( p_access, "unsupported RTP payload type (%u)", i_payload_type );
-
- if( i_extension_bit == 1)
+ if( i_extension_flag )
i_extension_length = 4 +
4 * ( (p_block->p_buffer[14] << 8) + p_block->p_buffer[15] );
- /* Skip header + CSRC extension field n*(32 bits) + extention */
+ /* Skip header + CSRC extension field n*(32 bits) + extension */
i_skip += RTP_HEADER_LEN + 4*i_CSRC_count + i_extension_length;
+
if( i_skip >= p_block->i_buffer )
- {
- msg_Warn( p_access, "received a too short packet for RTP" );
- block_Release( p_block );
- return NULL;
- }
+ goto trash;
- /* Detect RTP packet loss through tracking sequence numbers,
- * and take RTP PCR into account.
- * See RFC 3550.
- */
- if( p_sys->b_first_seqno )
- {
- p_sys->i_sequence_number = i_sequence_number;
- p_sys->i_last_pcr = i_pcr;
- p_sys->b_first_seqno = VLC_FALSE;
- i_sequence_expected = i_sequence_number;
- }
- else
- i_sequence_expected = p_sys->i_sequence_number + 1;
+ /* Return the packet without the RTP header, remember seqno */
+ p_block->i_buffer -= i_skip;
+ p_block->p_buffer += i_skip;
+ p_block->i_seqno = i_sequence_number;
#if 0
/* Emulate packet loss */
}
#endif
- if( (i_sequence_expected - i_sequence_number) != 0 )
- {
- /* Handle out of order packets */
- if( p_sys->i_rtp_late > 0 )
- {
- if( (i_sequence_number - i_sequence_expected) > 0 )
- {
- msg_Warn( p_access,
- "RTP packet out of order (too early) expected %u, current %u",
- i_sequence_expected, i_sequence_number );
- if( (i_pcr - p_sys->i_last_pcr) >= (p_sys->i_rtp_late*90) )
- {
- block_t *p_start = p_sys->p_list;
- uint16_t i_start = (!p_start) ? p_sys->i_sequence_number :
- (p_start->p_buffer[2] << 8) +
- p_start->p_buffer[3];
- /* Gap too big, we have been holding this data for too long,
- * send what we have.
- */
- msg_Warn( p_access,
- "Gap too big resyncing: delta %u, held for %d ms",
- (i_pcr - p_sys->i_last_pcr), p_sys->i_rtp_late );
- rtp_ChainInsert( p_access, p_block );
- return rtp_ChainSend( p_access, &p_sys->p_list, i_start );
- }
- /* hold packets that arrive too early. */
- rtp_ChainInsert( p_access, p_block );
- return rtp_ChainSend( p_access, &p_sys->p_list, i_sequence_expected );
- }
- else if( /* ((i_sequence_expected - i_sequence_number ) > 0) && */
- (p_sys->i_last_pcr - i_pcr) >= 0 )
- {
- msg_Warn( p_access,
- "RTP packet out of order (duplicate or too late) expected %u, current %u .. trashing it",
- i_sequence_expected, i_sequence_number );
- block_Release( p_block );
- p_sys->i_sequence_number = i_sequence_number;
- p_sys->i_last_pcr = i_pcr;
- return NULL;
- }
+ return p_block;
- if( p_sys->p_list )
- {
- block_t *p = NULL;
- block_t **p_send = &p_sys->p_list;
-
- msg_Warn( p_access,
- "RTP packet (unexpected condition) expected %u, current %u",
- i_sequence_expected, i_sequence_number );
-
- /* Append block to the end of chain and send whole chain */
- block_ChainLastAppend( &p_send, p_block );
- p_sys->p_list = p_sys->p_end = NULL;
- p_sys->i_sequence_number = i_sequence_number;
- p_sys->i_last_pcr = i_pcr;
-
- /* Return the packet without the RTP header. */
- p = *p_send;
- while( p )
- {
- p->i_buffer -= i_skip;
- p->p_buffer += i_skip;
- if( !p->p_next ) break;
- p = p->p_next;
- }
- return *p_send;
- }
- /* This code should never be reached !! */
- msg_Err( p_access,
- "Bug in algorithme: (unexpected condition) expected %u (pcr=%u), current %u (pcr=%u)",
- i_sequence_expected, i_sequence_number, p_sys->i_last_pcr, i_pcr );
- }
- msg_Warn( p_access,
- "RTP packet(s) lost, expected sequence number %d got %d",
- i_sequence_expected, i_sequence_number );
- /* Mark transport error in the first TS packet in the RTP stream. */
- if( (i_payload_type == 33) && (p_block->p_buffer[0] == 0x47) )
- p_block->p_buffer[1] |= 0x80;
- }
- else if( (p_sys->i_rtp_late > 0) && p_sys->p_list )
+trash:
+ msg_Warn( p_access, "received a too short packet for RTP" );
+ block_Release( p_block );
+ return NULL;
+}
+
+static block_t *BlockPrebufferRTP( access_t *p_access, block_t *p_block )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int64_t i_first = mdate();
+ int i_count = 0;
+ block_t *p = p_block;
+
+ for( ;; )
{
- if( (p_sys->i_last_pcr - i_pcr) >= 0 )
+ int64_t i_date = mdate();
+
+ if( p && rtp_ChainInsert( p_access, p ))
+ i_count++;
+
+ /* Require at least 3 packets in the buffer */
+ if( i_count > 3 && (i_date - i_first) > p_sys->i_rtp_late )
+ break;
+
+ p = BlockParseRTP( p_access, BlockUDP( p_access ));
+ if( !p && (i_date - i_first) > p_sys->i_rtp_late )
{
- msg_Warn( p_access,
- "RTP packet out of order (duplicate) expected %u, current %u .. trashing it",
- i_sequence_expected, i_sequence_number );
- block_Release( p_block );
- p_sys->i_sequence_number = i_sequence_number;
- p_sys->i_last_pcr = i_pcr;
- return NULL;
+ msg_Err( p_access, "Error in RTP prebuffering!" );
+ break;
}
- rtp_ChainInsert( p_access, p_block );
- return rtp_ChainSend( p_access, &p_sys->p_list, i_sequence_expected );
}
- /* This is the normal case when no packet reordering is effective */
- p_sys->i_sequence_number = i_sequence_number;
- p_sys->i_last_pcr = i_pcr;
-
- /* Return the packet without the RTP header. */
- p_block->i_buffer -= i_skip;
- p_block->p_buffer += i_skip;
- return p_block;
+ msg_Dbg( p_access, "RTP: prebuffered %d packets", i_count - 1 );
+ p_access->info.b_prebuffered = VLC_TRUE;
+ p = p_sys->p_list;
+ p_sys->p_list = p_sys->p_list->p_next;
+ p_sys->i_last_seqno = p->i_seqno;
+ p->p_next = NULL;
+ return p;
}
static block_t *BlockRTP( access_t *p_access )
{
- block_t *p_block = BlockUDP( p_access );
+ access_sys_t *p_sys = p_access->p_sys;
+ block_t *p;
- if ( p_block != NULL )
- return BlockParseRTP( p_access, p_block );
- else
+again:
+ p = BlockParseRTP( p_access, BlockUDP( p_access ));
+
+ if ( !p )
return NULL;
+
+ if ( !p_access->info.b_prebuffered )
+ return BlockPrebufferRTP( p_access, p );
+
+ if( !rtp_ChainInsert( p_access, p ))
+ goto again;
+
+ p = p_sys->p_list;
+ p_sys->p_list = p_sys->p_list->p_next;
+ p_sys->i_last_seqno++;
+ if( p_sys->i_last_seqno != p->i_seqno )
+ {
+ msg_Dbg( p_access, "RTP: packet(s) lost, expected %d, got %d",
+ p_sys->i_last_seqno, p->i_seqno );
+ p_sys->i_last_seqno = p->i_seqno;
+ }
+ p->p_next = NULL;
+ return p;
}
/*****************************************************************************
return p_block;
}
+ if( !BlockParseRTP( p_access, p_block )) return NULL;
+
p_access->pf_block = BlockRTP;
- return BlockParseRTP( p_access, p_block );
+ return BlockPrebufferRTP( p_access, p_block );
}