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

Denis Charmet typx at dinauz.org
Mon Feb 15 15:00:25 CET 2016


On 2016-02-12 15:16, Joni Räsänen wrote:
> ---
>  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 */
Exactly :) so the rest of the function will crash in case of corrupted 
data.

> +            MP4_Box_t *p_sdp = p_track->p_sdp;
> +            char * pch = strtok( BOXDATA(p_sdp)->psz_text," =\n" );
> /* media entry */
> +            if( strcmp(pch, "m") )
strcmp for 2 char might be overkill and will crash with a NULL pointer.
> +            {
> +                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);
atoi will crash with a NULL input
> +            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;
> +                    }
maybe an else {msg_Err(...)} for unhandled input.
> +
> +                    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;
A little comment to explain this 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 */
Always.

> +            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 );
this looks highly redundant with the previous function. Could it be 
factorized so the NULL checks are done only once?
> +
> +            /* 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);

please test.

> +    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;

I can't say that I like seeing pointer arithmetic on 
p_block->p_buffer...

> +
> +        mp4_rtpsamplecontructor_t *p_sample_cons =
> (mp4_rtpsamplecontructor_t*)(p_block->p_buffer);

This lacks size checking and might cause a read out of boundaries

> +
> +        /* not RTPsampleconstructor */
> +        if( p_sample_cons->type != 2 )
> +        {
> +            return;
> +        }
> +
> +        uint8_t* src = sample_beginning + 
> p_sample_cons->sampleoffset;

Don't assume the endianness of the machine running the code.

> +
> +        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++;
> +            }
memcpy?
> +            *dst++ = *src++;
> +            *dst++ = *src++;
> +            *dst++ = *src++;
> +        }
> +    }
> +    p_block->p_buffer = temp_sample;
> +    p_block->i_buffer = sampleLength;

You cannot do that. Usually block_Alloc allocate the block size + a 
block header and p_buffer just points to p_block+sizeof(header), which 
means that block_Free will just free p_block.
If you want to override this you need to specify a p_block->pf_release 
to actually free p_buffer.

> +}
> +
>  
> /*****************************************************************************
>   * 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);
endianness?
> +                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

Regards,
-- 
Denis Charmet - TypX
Le mauvais esprit est un art de vivre


More information about the vlc-devel mailing list