[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
Tue Feb 16 11:30:59 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
-- 
2.5.0



More information about the vlc-devel mailing list