[vlc-devel] [PATCH 2/2] RTP Reception Hint Track playback support for presynchronized streams, H.264 video and GSM audio are supported.
Joni Räsänen
joni.rasanen at tut.fi
Fri Feb 12 15:16:22 CET 2016
---
modules/demux/mp4/essetup.c | 206 ++++++++++++++++++++++++++++++++++++++++++++
modules/demux/mp4/mp4.c | 143 ++++++++++++++++++++++++++++++
modules/demux/mp4/mp4.h | 27 ++++++
3 files changed, 376 insertions(+)
diff --git a/modules/demux/mp4/essetup.c b/modules/demux/mp4/essetup.c
index 501f22a..4557c17 100644
--- a/modules/demux/mp4/essetup.c
+++ b/modules/demux/mp4/essetup.c
@@ -239,7 +239,111 @@ int SetupVideoES( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample )
break;
}
break;
+ case( VLC_FOURCC( 'r', 'r', 't', 'p' ) ): /* RTP Reception Hint Track */
+ {
+ p_track->fmt.i_original_fourcc = p_sample->i_type;
+
+ if( !p_track->p_sdp )
+ {
+ msg_Err(p_demux, "Required 'sdp '-box not found");
+ return 0;
+ }
+ /* There should be checks for NULL if the sdp message is not correct */
+ MP4_Box_t *p_sdp = p_track->p_sdp;
+ char * pch = strtok( BOXDATA(p_sdp)->psz_text," =\n" ); /* media entry */
+ if( strcmp(pch, "m") )
+ {
+ msg_Err( p_demux, "No Media entry found in SDP:%s", pch );
+ break;
+ }
+
+ pch = strtok( NULL," =\n" ); /* media type */
+ /* media type has already been checked and it is video */
+ msg_Dbg(p_demux, "sdp: media type:%s", pch);
+ pch = strtok( NULL," =\n" ); /* port */
+ msg_Dbg(p_demux, "sdp: port:%s", pch);
+ pch = strtok( NULL," =\n" ); /* protocol */
+ msg_Dbg(p_demux, "sdp: protocol:%s", pch);
+ pch = strtok( NULL," =\n" ); /* fmt */
+
+ msg_Dbg( p_demux, "sdp: fmt:%s", pch );
+
+ int rtp_payload = atoi(pch);
+ msg_Dbg( p_demux, "sdp: fmt int:%d", rtp_payload );
+
+ pch = strtok (NULL," =\n"); /* attribute or another payload type */
+ msg_Dbg( p_demux, "sdp: next entry:%s", pch );
+ /* skip rest of the rtp types in list */
+ while( pch != NULL && strcmp(pch, "a") )
+ {
+ pch = strtok( NULL," =\n" ); /* additional payload type or attribute */
+ msg_Dbg( p_demux, "sdp: next entry:%s", pch );
+ }
+
+ while( pch != NULL && !strcmp(pch, "a") )
+ {
+ pch = strtok( NULL," :=\n" ); /* attribute type */
+ msg_Dbg( p_demux, "sdp: atrribute type:%s", pch );
+
+ if( !strcmp(pch, "rtpmap") )
+ {
+ pch = strtok( NULL," :=\n" ); /* payload type */
+ msg_Dbg( p_demux, "sdp: payload type:%s", pch );
+ pch = strtok( NULL," /:=\n" ); /* encoding name */
+ msg_Dbg( p_demux, "sdp: encoding name:%s", pch );
+ if( !strcmp(pch, "H264") )
+ {
+ p_track->fmt.i_codec = VLC_CODEC_H264;
+ }
+ else if( !strcmp(pch, "H263") )
+ {
+ p_track->fmt.i_codec = VLC_CODEC_H263;
+ }
+ else if( !strcmp(pch, "H261") )
+ {
+ p_track->fmt.i_codec = VLC_CODEC_H261;
+ }
+
+ pch = strtok( NULL," /:=\n" ); /* clock rate */
+ int clock_rate = atoi( pch );
+ msg_Dbg( p_demux, "sdp clock rate:%d", clock_rate );
+ }
+ pch = strtok( NULL," =\n" ); /* next attribute */
+ }
+
+ MP4_Box_t *p_tims_box = MP4_BoxGet( p_sample, "tims", 0 );
+ if( p_tims_box != NULL )
+ {
+ MP4_Box_data_tims_t *p_tims = p_tims_box->data.p_tims;
+ p_track->i_timescale = p_tims->i_timescale;
+ }
+ else
+ msg_Warn( p_demux, "Missing mandatory box tims" );
+
+ MP4_Box_t *p_tssy_box = MP4_BoxGet( p_sample, "tssy", 0 );
+ if( p_tssy_box != NULL )
+ {
+ MP4_Box_data_tssy_t *p_tssy = p_tssy_box->data.p_tssy;
+ uint8_t temp = p_tssy->i_reserved_timestamp_sync & 0x03;
+ p_track->sync_mode = (RTP_timstamp_synchronization_t)temp;
+ }
+
+ MP4_Box_t *p_tsro_box = MP4_BoxGet( p_sample, "tsro", 0 );
+ if( p_tsro_box != NULL )
+ {
+ MP4_Box_data_tsro_t *p_tsro = p_tsro_box->data.p_tsro;
+ msg_Dbg( p_demux, "setting tsro: %d",
+ p_tsro->i_offset );
+ p_track->i_tsro_offset = p_tsro->i_offset;
+ }
+ else
+ {
+ msg_Dbg( p_demux, "No tsro box present. Assuming 0 as track offset" );
+ p_track->i_tsro_offset = 0;
+ }
+ break;
+ }
default:
p_track->fmt.i_codec = p_sample->i_type;
break;
@@ -564,6 +668,108 @@ int SetupAudioES( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample )
/* It's a little ugly but .. there are special cases */
switch( p_sample->i_type )
{
+ case( VLC_FOURCC( 'r', 'r', 't', 'p' ) ): /* RTP Reception Hint Track */
+ {
+ if( !p_track->p_sdp )
+ {
+ msg_Err( p_demux, "Required 'sdp '-box not found" );
+ return 0;
+ }
+ /* There should be checks for NULL if the sdp message is not correct */
+ MP4_Box_t *p_sdp = p_track->p_sdp;
+ char * pch = strtok( BOXDATA(p_sdp)->psz_text," =\n" ); /* media entry */
+ if( strcmp(pch, "m") )
+ {
+ msg_Err( p_demux, "No Media entry found in SDP:%s", pch );
+ break;
+ }
+
+ pch = strtok( NULL," =\n" ); /* media type */
+ pch = strtok( NULL," =\n" ); /* port */
+ msg_Dbg( p_demux, "sdp: port:%s", pch );
+ pch = strtok( NULL," =\n" ); /* protocol */
+ msg_Dbg( p_demux, "sdp: protocol:%s", pch );
+ pch = strtok( NULL," =\n" ); /* fmt */
+
+ msg_Dbg( p_demux, "sdp: fmt:%s", pch );
+
+ int rtp_payload = atoi( pch );
+ msg_Dbg( p_demux, "sdp: fmt int:%d", rtp_payload );
+
+ /* Some codecs may need additional parsing before sending to decoder */
+ switch( rtp_payload )
+ {
+ case 3:
+ p_track->fmt.i_codec = VLC_CODEC_GSM;
+ break;
+ default:
+ break;
+ }
+
+ pch = strtok( NULL," =\n" ); /* attribute or another payload type */
+ msg_Dbg( p_demux, "sdp: next entry:%s", pch );
+
+ /* skip rest of the rtp types in list */
+ while( pch != NULL && strcmp(pch, "a") )
+ {
+ pch = strtok( NULL," =\n" ); /* another payload type or attribute */
+ msg_Dbg( p_demux, "sdp: next entry:%s", pch );
+ }
+
+ while( pch != NULL && !strcmp(pch, "a") )
+ {
+ pch = strtok( NULL," :=\n" ); /* attribute type */
+ msg_Dbg( p_demux, "sdp: atrribute type:%s", pch );
+
+ if( !strcmp(pch, "rtpmap") )
+ {
+ pch = strtok( NULL," :=\n" ); /* payload type */
+ msg_Dbg( p_demux, "sdp: payload type:%s", pch );
+ pch = strtok( NULL," /:=\n" ); /* encoding name */
+ msg_Dbg( p_demux, "sdp: encoding name:%s", pch );
+
+ pch = strtok( NULL," /:=\n" ); /* clock rate */
+ int clock_rate = atoi( pch );
+ msg_Dbg( p_demux, "sdp clock rate:%d", clock_rate );
+ p_track->fmt.audio.i_rate = clock_rate;
+ }
+ }
+
+ pch = strtok( NULL," =\n" ); /* next attribute */
+ }
+
+ MP4_Box_t *p_tims_box = MP4_BoxGet( p_sample, "tims", 0 );
+ if( p_tims_box != NULL )
+ {
+ MP4_Box_data_tims_t *p_tims = p_tims_box->data.p_tims;
+ p_track->i_timescale = p_tims->i_timescale;
+ }
+ else
+ msg_Warn( p_demux, "Missing mandatory box tims" );
+
+ MP4_Box_t *p_tssy_box = MP4_BoxGet( p_sample, "tssy", 0 );
+ if( p_tssy_box != NULL )
+ {
+ MP4_Box_data_tssy_t *p_tssy = p_tssy_box->data.p_tssy;
+ uint8_t temp = p_tssy->i_reserved_timestamp_sync & 0x03;
+ p_track->sync_mode = (RTP_timstamp_synchronization_t)temp;
+ }
+
+ MP4_Box_t *p_tsro_box = MP4_BoxGet( p_sample, "tsro", 0 );
+ if( p_tsro_box != NULL )
+ {
+ MP4_Box_data_tsro_t *p_tsro = p_tsro_box->data.p_tsro;
+ msg_Dbg( p_demux, "setting tsro: %d",
+ p_tsro->i_offset );
+ p_track->i_tsro_offset = p_tsro->i_offset;
+ }
+ else
+ {
+ msg_Dbg( p_demux, "No tsro box present. Assuming 0 as track offset" );
+ p_track->i_tsro_offset = 0;
+ }
+ break;
+ }
case ATOM_agsm: /* Apple gsm 33 bytes != MS GSM (agsm fourcc, 65 bytes) */
p_track->fmt.i_codec = VLC_CODEC_GSM;
break;
diff --git a/modules/demux/mp4/mp4.c b/modules/demux/mp4/mp4.c
index 3175ed3..9c0bedd 100644
--- a/modules/demux/mp4/mp4.c
+++ b/modules/demux/mp4/mp4.c
@@ -133,6 +133,7 @@ static void MP4_GetDefaultSizeAndDuration( demux_t *p_demux,
uint32_t *pi_default_size,
uint32_t *pi_default_duration );
+static void MP4_PacketsToFrame( block_t* p_block, uint32_t packetcount );
static bool AddFragment( demux_t *p_demux, MP4_Box_t *p_moox );
static int ProbeFragments( demux_t *p_demux, bool b_force );
static int ProbeIndex( demux_t *p_demux );
@@ -874,6 +875,75 @@ error:
return VLC_EGENERIC;
}
+const unsigned int RTPHEADERSIZE = 12;
+
+/*****************************************************************************
+ * * MP4_PacketsToFrame: converts RTP Reception Hint Track sample to H.264 frame
+ * *****************************************************************************/
+static void MP4_PacketsToFrame( block_t *p_block, uint32_t packetcount )
+{
+ uint8_t *sample_beginning = p_block->p_buffer - 4;
+ uint32_t frameLength = p_block->i_buffer;
+
+ uint32_t sampleLength = 0;
+
+ uint8_t *temp_sample = malloc(frameLength);
+ uint8_t *dst = temp_sample;
+
+ for( unsigned int i = 0; i < packetcount; ++i )
+ {
+ /* skip RTP header in sample. Could be used to detect packet losses */
+ p_block->p_buffer += RTPHEADERSIZE;
+ p_block->i_buffer -= RTPHEADERSIZE;
+
+ mp4_rtpsamplecontructor_t *p_sample_cons = (mp4_rtpsamplecontructor_t*)(p_block->p_buffer);
+
+ /* not RTPsampleconstructor */
+ if( p_sample_cons->type != 2 )
+ {
+ return;
+ }
+
+ uint8_t* src = sample_beginning + p_sample_cons->sampleoffset;
+
+ uint8_t type = (*src) & ((1<<5)-1);
+
+ /* skip packet constructor */
+ p_block->p_buffer += sizeof(mp4_rtpsamplecontructor_t);
+ p_block->i_buffer -= sizeof(mp4_rtpsamplecontructor_t);
+
+ if( src[0]==0 && src[1]==0 && src[2]==0 && src[3]==1 )
+ {
+ memcpy( dst,src,p_sample_cons->length );
+ dst+=p_sample_cons->length;
+ }
+ else
+ {
+ if( type == 7 || type == 8 )
+ {
+ *dst++=0;
+ ++sampleLength;
+ }
+ sampleLength += p_sample_cons->length + 3;
+
+ *dst++=0;
+ *dst++=0;
+ *dst++=1;
+ *dst++ = *src++;
+
+ while( src < (sample_beginning + p_sample_cons->sampleoffset + p_sample_cons->length - 3) )
+ {
+ *dst++ = *src++;
+ }
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ }
+ }
+ p_block->p_buffer = temp_sample;
+ p_block->i_buffer = sampleLength;
+}
+
/*****************************************************************************
* Demux: read packet and send them to decoders
*****************************************************************************
@@ -1032,7 +1102,40 @@ static int Demux( demux_t *p_demux )
MP4_TrackUnselect( p_demux, tk );
goto end;
}
+ if( tk->fmt.i_original_fourcc == VLC_FOURCC( 'r','r','t','p') )
+ {
+ for( unsigned int i = 0; i < i_nb_samples; ++i )
+ {
+ uint16_t packetcount = (uint16_t)*(p_block->p_buffer);
+ p_block->p_buffer += sizeof(uint16_t);
+ p_block->i_buffer -= sizeof(uint16_t);
+
+ /* reserved */
+ p_block->p_buffer += sizeof(uint16_t);
+ p_block->i_buffer -= sizeof(uint16_t);
+ if( packetcount == 1 )
+ {
+ /* skip packet header */
+ p_block->p_buffer += RTPHEADERSIZE;
+ p_block->i_buffer -= RTPHEADERSIZE;
+
+ /* skip packet constructor */
+ p_block->p_buffer += sizeof(mp4_rtpsamplecontructor_t);
+ p_block->i_buffer -= sizeof(mp4_rtpsamplecontructor_t);
+ }
+ else
+ {
+ if(tk->fmt.i_codec == VLC_CODEC_H264)
+ MP4_PacketsToFrame( p_block, packetcount );
+ else
+ msg_Warn( p_demux, "Unsupported rrtp codec" );
+ }
+
+ p_block->p_start = p_block->p_buffer;
+ p_block->i_size = p_block->i_buffer;
+ }
+ }
/* dts */
p_block->i_dts = VLC_TS_0 + MP4_TrackGetDTS( p_demux, tk );
/* pts */
@@ -2647,6 +2750,7 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track,
p_track->p_track = p_box_trak;
char language[4] = { '\0' };
+ char sdp_media_type[8] = { '\0' };
es_format_Init( &p_track->fmt, UNKNOWN_ES, 0 );
@@ -2703,6 +2807,39 @@ static void MP4_TrackCreate( demux_t *p_demux, mp4_track_t *p_track,
p_track->fmt.i_cat = VIDEO_ES;
break;
+ case( ATOM_hint ):
+ if( !MP4_BoxGet( p_box_trak, "mdia/minf/hmhd" ) )
+ {
+ break;
+ }
+ MP4_Box_t *p_sdp;
+
+ /* parse the sdp message to find out whether the RTP stream contained audio or video */
+ if( !( p_sdp = MP4_BoxGet( p_box_trak, "udta/hnti/sdp " ) ) )
+ {
+ msg_Warn( p_demux, "Didn't find sdp box to determine stream type" );
+ return;
+ }
+
+ memcpy( sdp_media_type, BOXDATA(p_sdp)->psz_text, 7 );
+ if( !strcmp(sdp_media_type, "m=audio") )
+ {
+ msg_Dbg( p_demux, "Found audio Rtp: %s", sdp_media_type );
+ p_track->fmt.i_cat = AUDIO_ES;
+ }
+ else if( !strcmp(sdp_media_type, "m=video") )
+ {
+ msg_Dbg( p_demux, "Found video Rtp: %s", sdp_media_type );
+ p_track->fmt.i_cat = VIDEO_ES;
+ }
+ else
+ {
+ msg_Warn( p_demux, "Malformed track SDP message: %s", sdp_media_type );
+ return;
+ }
+ p_track->p_sdp = p_sdp;
+ break;
+
case( ATOM_tx3g ):
case( ATOM_text ):
case( ATOM_subp ):
@@ -3060,6 +3197,12 @@ static uint32_t MP4_TrackGetReadSize( mp4_track_t *p_track, uint32_t *pi_nb_samp
return i_packets * p_soun->i_constbytesperaudiopacket;
}
+ if( p_track->fmt.i_original_fourcc == VLC_FOURCC('r','r','t','p') )
+ {
+ *pi_nb_samples = 1;
+ return p_track->i_sample_size;
+ }
+
/* all samples have a different size */
if( p_track->i_sample_size == 0 )
{
diff --git a/modules/demux/mp4/mp4.h b/modules/demux/mp4/mp4.h
index 2119748..cea7fe8 100644
--- a/modules/demux/mp4/mp4.h
+++ b/modules/demux/mp4/mp4.h
@@ -65,6 +65,11 @@ typedef struct
} mp4_chunk_t;
+typedef enum RTP_timstamp_synchronization_s
+{
+ UNKNOWN_SYNC = 0, UNSYNCHRONIZED = 1, SYNCHRONIZED = 2
+} RTP_timstamp_synchronization_t;
+
/* Contain all needed information for read all track with vlc */
typedef struct
{
@@ -129,6 +134,14 @@ typedef struct
mtime_t i_time; // track scaled
+ /* rrtp reception hint track */
+ MP4_Box_t *p_sdp; /* parsed for codec and other info */
+ RTP_timstamp_synchronization_t sync_mode; /* whether track is already in sync */
+
+ /* First recorded RTP timestamp offset.
+ * Needed for rrtp synchronization */
+ int32_t i_tsro_offset;
+
struct
{
/* for moof parsing */
@@ -150,4 +163,18 @@ int SetupAudioES( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample );
int SetupSpuES( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample );
int SetupCCES( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample );
void SetupMeta( vlc_meta_t *p_meta, MP4_Box_t *p_udta );
+
+/* format of RTP reception hint track sample constructor */
+typedef struct
+{
+ uint8_t type;
+ int8_t trackrefindex;
+ uint16_t length;
+ uint32_t samplenumber;
+ uint32_t sampleoffset;/* indicates where the payload is located within sample*/
+ uint16_t bytesperblock;
+ uint16_t samplesperblock;
+
+} mp4_rtpsamplecontructor_t;
+
#endif
--
1.8.4.msysgit.0
More information about the vlc-devel
mailing list