[vlc-devel] [PATCH 2/2] RTP Reception Hint Track playback support for presynchronized streams. H.264 video, GSM and Speex audio are supported.

Joni Räsänen joni.rasanen at tut.fi
Tue Apr 5 15:18:43 CEST 2016


essetup.c:
Switched strtok to strtok_r.
Added checks for premature ending of string.
Removed strcmp for single char checks.
Added error message in case of unhandled codec.
Added comment to explain 0x03.
Combined the RTP reception hint track function for audio and video into one function.

mp4.c:
Malloc test.
Added a separate memory to be used as new sample for H264 in RTP Reception Hint track.
Added buffer length tests.
Took account for endianess of the machine running the code.
Used block_alloc and block_realloc instead of moving pointers within existing blocks.



---
 modules/demux/mp4/essetup.c | 156 +++++++++++++++++++++++++++++++++++++-
 modules/demux/mp4/mp4.c     | 178 ++++++++++++++++++++++++++++++++++++++++++++
 modules/demux/mp4/mp4.h     |  27 +++++++
 3 files changed, 360 insertions(+), 1 deletion(-)

diff --git a/modules/demux/mp4/essetup.c b/modules/demux/mp4/essetup.c
index bca3b7b..24dfb67 100644
--- a/modules/demux/mp4/essetup.c
+++ b/modules/demux/mp4/essetup.c
@@ -31,6 +31,11 @@
 #include <vlc_aout.h>
 #include <assert.h>
 
+
+
+int SetupRTPReceptionHintTrack( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample );
+
+
 static void SetupGlobalExtensions( mp4_track_t *p_track, MP4_Box_t *p_sample )
 {
     if( !p_track->fmt.i_bitrate )
@@ -164,6 +169,144 @@ static void SetupESDS( demux_t *p_demux, mp4_track_t *p_track, const MP4_descrip
     }
 }
 
+int SetupRTPReceptionHintTrack( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample )
+{
+    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;
+    }
+    MP4_Box_t *p_sdp = p_track->p_sdp;
+    char *strtok_state;
+    char * pch = strtok_r(BOXDATA(p_sdp)->psz_text, " =\n", &strtok_state); /* media entry */
+    if( pch && pch[0] != 'm' )
+    {
+        msg_Err(p_demux, "No Media entry found in SDP:%s", pch);
+        return 0;
+    }
+
+    if( !( pch = strtok_r(NULL, " =\n", &strtok_state) ) ) /* media type */
+        return 0;
+    /* media type has already been checked */
+    msg_Dbg(p_demux, "sdp: media type:%s", pch);
+    if( !( pch = strtok_r(NULL, " =\n", &strtok_state) ) ) /* port */
+        return 0;
+    msg_Dbg(p_demux, "sdp: port:%s", pch);
+    if( !( pch = strtok_r(NULL, " =\n", &strtok_state) ) ) /* protocol */
+        return 0;
+    msg_Dbg(p_demux, "sdp: protocol:%s", pch);
+
+    if( !( pch = strtok_r(NULL, " =\n", &strtok_state) ) ) /* fmt */
+        return 0;
+
+    bool codec_set = false;
+    /* process rtp types until we get an attribute field or end of sdp */
+    while( pch && pch[0] != 'a' )
+    {
+        int rtp_payload = atoi(pch);
+        msg_Dbg(p_demux, "sdp: payload type:%d", rtp_payload);
+
+        if( !codec_set )
+        {
+            /* Payload types 34 and under have a set type and can be identified here */
+            switch( rtp_payload )
+            {
+             case 3:
+                p_track->fmt.i_codec = VLC_CODEC_GSM;
+                codec_set = true;
+                break;
+             default:
+                break;
+            }
+        }
+        pch = strtok_r(NULL, " =\n", &strtok_state); /* attribute or additional payload type */
+        if( !pch && !codec_set )
+            return 0;
+    }
+
+    while( pch && pch[0] == 'a' )
+    {
+        if( !( pch = strtok_r(NULL, " :=\n", &strtok_state) ) ) /* attribute type */
+            return 0;
+        msg_Dbg(p_demux, "sdp: atrribute type:%s", pch);
+
+        if( !strcmp(pch, "rtpmap") )
+        {
+            if( !( pch = strtok_r(NULL, " :=\n", &strtok_state) ) ) /* payload type */
+                return 0;
+            msg_Dbg(p_demux, "sdp: payload type:%s", pch);
+            if( !(pch = strtok_r(NULL, " /:=\n", &strtok_state) ) ) /* encoding name */
+                return 0;
+            msg_Dbg(p_demux, "sdp: encoding name:%s", pch);
+
+            /* Simply adding codec recognition should work for most codecs */
+            /* Codecs using slices need their picture constructed from sample */
+            if( !strcmp(pch, "H264") )
+            {
+                p_track->fmt.i_codec = VLC_CODEC_H264;
+            }
+            else if( !strcmp(pch, "GSM") )
+            {
+                p_track->fmt.i_codec = VLC_CODEC_GSM;
+            }
+            else if( !strcmp(pch, "Speex") )
+            {
+                p_track->fmt.i_codec = VLC_CODEC_SPEEX;
+            }
+            else if( !codec_set )
+            {
+                msg_Err(p_demux, "Support for codec contained in RTP \
+                        Reception Hint Track RTP stream has not been added");
+                return 0;
+            }
+
+            if( !( pch = strtok_r(NULL, " :=\n", &strtok_state) ) ) /* clock rate */
+                return 0;
+            int clock_rate = atoi(pch);
+            msg_Dbg(p_demux, "sdp clock rate:%d", clock_rate);
+            if( p_track->fmt.i_cat == AUDIO_ES )
+                p_track->fmt.audio.i_rate = clock_rate;
+        }
+        pch = strtok_r(NULL, " =\n", &strtok_state); /* 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;
+        /* take the 2 last bits which indicate the synchronization mode */
+        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;
+    }
+    return 1;
+}
+
+
 int SetupVideoES( demux_t *p_demux, mp4_track_t *p_track, MP4_Box_t *p_sample )
 {
     MP4_Box_data_sample_vide_t *p_vide = p_sample->data.p_sample_vide;
@@ -239,7 +382,12 @@ 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 */
+        {
+            if( !SetupRTPReceptionHintTrack( p_demux, p_track, p_sample ) )
+                p_track->fmt.i_codec = p_sample->i_type;
+            break;
+        }
         default:
             p_track->fmt.i_codec = p_sample->i_type;
             break;
@@ -564,6 +712,12 @@ 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( !SetupRTPReceptionHintTrack( p_demux, p_track, p_sample ) )
+                return 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 9081d6d..d537eae 100644
--- a/modules/demux/mp4/mp4.c
+++ b/modules/demux/mp4/mp4.c
@@ -134,6 +134,10 @@ static void MP4_GetDefaultSizeAndDuration( demux_t *p_demux,
                                            uint32_t *pi_default_size,
                                            uint32_t *pi_default_duration );
 
+static int MP4_PacketsToFrame( demux_t *p_demux,
+                                block_t **pp_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 );
@@ -858,6 +862,108 @@ error:
     return VLC_EGENERIC;
 }
 
+const unsigned int SAMPLEHEADERSIZE = 4;
+const unsigned int RTPPACKETSIZE = 12;
+const unsigned int CONSTRUCTORSIZE = 16;
+
+/*******************************************************************************
+ * MP4_PacketsToFrame: converts RTP Reception Hint Track sample to H.264 frame
+ *******************************************************************************/
+static int MP4_PacketsToFrame( demux_t *p_demux, block_t **pp_block, uint32_t packetcount )
+{
+    block_t* p_block = *pp_block;
+    uint32_t frameLength = p_block->i_buffer;
+    uint8_t *currentSlice = p_block->p_buffer + SAMPLEHEADERSIZE;
+    uint32_t sampleLength = 0;
+
+    if( currentSlice + RTPPACKETSIZE + CONSTRUCTORSIZE > p_block->p_buffer + p_block->i_buffer )
+    {
+        msg_Err( p_demux, "Sample not large enough for necessary structs");
+        return VLC_EGENERIC;
+    }
+
+    uint8_t *temp_sample = malloc(frameLength + packetcount*10);
+    if( !temp_sample )
+    {
+        msg_Err( p_demux, "Failed to allocate memory. Enough memory available?");
+        return VLC_ENOMEM;
+    }
+
+    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 */
+        currentSlice += RTPPACKETSIZE;
+
+        mp4_rtpsampleconstructor_t sample_cons;
+
+        sample_cons.type =                      currentSlice[0];
+        sample_cons.trackrefindex =             currentSlice[1];
+        sample_cons.length =          GetWBE(  &currentSlice[2] );
+        sample_cons.samplenumber =    GetDWBE( &currentSlice[4] );
+        sample_cons.sampleoffset =    GetDWBE( &currentSlice[8] );
+        sample_cons.bytesperblock =   GetWBE(  &currentSlice[12] );
+        sample_cons.samplesperblock = GetWBE(  &currentSlice[14] );
+
+        /* skip packet constructor */
+        currentSlice += CONSTRUCTORSIZE;
+
+        /* check that is RTPsampleconstructor, referencing itself and no weird audio stuff */
+        if( sample_cons.type != 2||sample_cons.trackrefindex != -1
+            ||sample_cons.samplesperblock != 1||sample_cons.bytesperblock != 1 )
+        {
+            msg_Err(p_demux, "Unhandled constructor in RTP Reception Hint Track. Type:%u", sample_cons.type);
+            free(temp_sample);
+            return VLC_EGENERIC;
+        }
+
+        /* slice doesn't fit in buffer */
+        if( sample_cons.sampleoffset + sample_cons.length > p_block->i_buffer)
+        {
+            msg_Err(p_demux, "Sample buffer is smaller than sample" );
+            free(temp_sample);
+            return VLC_EGENERIC;
+        }
+
+        uint8_t* src = p_block->p_buffer + sample_cons.sampleoffset;
+        uint8_t type = (*src) & ((1<<5)-1);
+
+        if( src[0] == 0 && src[1] == 0 && src[2] == 0 && src[3] == 1 )
+        {
+            memcpy( dst,src,sample_cons.length );
+            dst+=sample_cons.length;
+            sampleLength += sample_cons.length;
+        }
+        else
+        {
+            if( type == 7 || type == 8 )
+            {
+                *dst++=0;
+                ++sampleLength;
+            }
+            sampleLength += sample_cons.length + 3;
+
+            *dst++ = 0;
+            *dst++ = 0;
+            *dst++ = 1;
+
+            memcpy(dst, src, sample_cons.length);
+            dst += sample_cons.length;
+        }
+    }
+    block_Release( p_block );
+    p_block = block_Alloc( sampleLength );
+    if( !p_block )
+      return VLC_ENOMEM;
+    memcpy( p_block->p_buffer, temp_sample, sampleLength );
+
+    *pp_block = p_block;
+    free( temp_sample );
+
+    return VLC_SUCCESS;
+}
+
 /*****************************************************************************
  * Demux: read packet and send them to decoders
  *****************************************************************************
@@ -1015,7 +1121,39 @@ static int Demux( demux_t *p_demux )
             MP4_TrackUnselect( p_demux, tk );
             goto end;
         }
+        /* RTP Reception Hint Track */
+        if( tk->fmt.i_original_fourcc == VLC_FOURCC( 'r','r','t','p') )
+        {
+            for( unsigned int i = 0; i < i_nb_samples; ++i )
+            {
+                /* number of RTP packets contained in this sample */
+                uint16_t packetcount = GetWBE( p_block->p_buffer );
 
+                if( packetcount == 1 )
+                {
+                    int skip = SAMPLEHEADERSIZE + RTPPACKETSIZE + CONSTRUCTORSIZE;
+                    p_block = block_Realloc( p_block, -skip, p_block->i_buffer);
+                }
+                if( packetcount > 1 )
+                {
+                    if(tk->fmt.i_codec == VLC_CODEC_H264)
+                    {
+                        int status = MP4_PacketsToFrame( p_demux, &p_block, packetcount );
+                        if( status != VLC_SUCCESS )
+                          return status;
+                    }
+                    else
+                    {
+                        msg_Warn( p_demux, "Unsupported rrtp codec" );
+                        /* this may work with some codecs, skip all unnecessary stuff leaving payloads */
+                        int skip = SAMPLEHEADERSIZE + (RTPPACKETSIZE + CONSTRUCTORSIZE) * packetcount;
+                        p_block = block_Realloc( p_block, -skip, p_block->i_buffer);
+                    }
+                }
+                if( !p_block )
+                    return VLC_ENOMEM;
+            }
+        }
         /* dts */
         p_block->i_dts = VLC_TS_0 + MP4_TrackGetDTS( p_demux, tk );
         /* pts */
@@ -2682,6 +2820,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 );
 
@@ -2738,6 +2877,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 ):
@@ -3095,6 +3267,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 228c37c..9ee71d7 100644
--- a/modules/demux/mp4/mp4.h
+++ b/modules/demux/mp4/mp4.h
@@ -61,6 +61,11 @@ typedef struct
 
 } mp4_chunk_t;
 
+typedef enum RTP_timstamp_synchronization_s
+{
+    UNKNOWN_SYNC = 0, UNSYNCHRONIZED = 1, SYNCHRONIZED = 2, RESERVED = 3
+} RTP_timstamp_synchronization_t;
+
  /* Contain all needed information for read all track with vlc */
 typedef struct
 {
@@ -125,6 +130,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 */
@@ -146,4 +159,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_rtpsampleconstructor_t;
+
 #endif
-- 
2.5.0



More information about the vlc-devel mailing list