[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