[vlc-commits] [Git][videolan/vlc][master] 7 commits: demux: libmp4: read roll distance
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Sat Sep 2 10:33:57 UTC 2023
Felix Paul Kühne pushed to branch master at VideoLAN / VLC
Commits:
2ff94f38 by Francois Cartegnie at 2023-09-02T10:19:04+00:00
demux: libmp4: read roll distance
- - - - -
b8106fe8 by Francois Cartegnie at 2023-09-02T10:19:04+00:00
demux: mp4: handle roll distance
- - - - -
1f5b1653 by Francois Cartegnie at 2023-09-02T10:19:04+00:00
demux: mp4: handle decoder delay in seek
- - - - -
c6dc5ca6 by Francois Cartegnie at 2023-09-02T10:19:04+00:00
demux: mp4: handle itun meta
- - - - -
0dc2dbf9 by Francois Cartegnie at 2023-09-02T10:19:04+00:00
demux: mp4: handle itunes decoder delay
- - - - -
ab9852f5 by Francois Cartegnie at 2023-09-02T10:19:04+00:00
demux: mp4: handle itunes replay gain
- - - - -
d3799595 by Francois Cartegnie at 2023-09-02T10:19:04+00:00
demux: mp4: handle itunes encoding params
- - - - -
6 changed files:
- modules/demux/mp4/libmp4.c
- modules/demux/mp4/libmp4.h
- modules/demux/mp4/meta.c
- modules/demux/mp4/meta.h
- modules/demux/mp4/mp4.c
- modules/demux/mp4/mp4.h
Changes:
=====================================
modules/demux/mp4/libmp4.c
=====================================
@@ -2223,6 +2223,7 @@ static int MP4_ReadBox_sgpd( stream_t *p_stream, MP4_Box_t *p_box )
switch( p_sgpd->i_grouping_type )
{
case SAMPLEGROUP_rap:
+ case SAMPLEGROUP_roll:
break;
default:
@@ -2282,6 +2283,23 @@ static int MP4_ReadBox_sgpd( stream_t *p_stream, MP4_Box_t *p_box )
}
break;
+ case SAMPLEGROUP_roll:
+ {
+ if( i_read < 2 )
+ {
+ free( p_sgpd->p_entries );
+ MP4_READBOX_EXIT( 0 );
+ }
+ union
+ {
+ uint16_t u;
+ int16_t s;
+ } readsigned;
+ MP4_GET2BYTES( readsigned.u );
+ p_sgpd->p_entries[i].roll.i_roll_distance = readsigned.s;
+ }
+ break;
+
default:
vlc_assert_unreachable();
free( p_sgpd->p_entries );
=====================================
modules/demux/mp4/libmp4.h
=====================================
@@ -451,6 +451,7 @@ typedef int64_t stime_t;
#define HANDLER_ID32 ATOM_ID32
#define SAMPLEGROUP_rap VLC_FOURCC('r', 'a', 'p', ' ')
+#define SAMPLEGROUP_roll VLC_FOURCC('r', 'o', 'l', 'l')
/* tref reference type boxes */
#define ATOM_chap VLC_FOURCC( 'c', 'h', 'a', 'p' )
=====================================
modules/demux/mp4/meta.c
=====================================
@@ -331,32 +331,98 @@ static int ExtractIntlStrings( vlc_meta_t *p_meta, const MP4_Box_t *p_box )
return i_read == 0;
}
-static void ExtractItunesInfoTriplets( vlc_meta_t *p_meta, const MP4_Box_t *p_box )
+static void ParseTriplet( const MP4_Box_t *p_box,
+ const char *psz_ns,
+ const char *psz_key,
+ void (*pf_callback)(const char *psz_key,
+ const MP4_Box_data_data_t *, void *),
+ void *p_priv )
{
- if( p_box->i_type != ATOM_ITUN )
- return;
+ size_t i_ns = psz_ns ? strlen(psz_ns) : 0;
+ size_t i_key = psz_key ? strlen(psz_key) : 0;
const MP4_Box_t *p_mean = MP4_BoxGet( p_box, "mean" );
const MP4_Box_t *p_name = MP4_BoxGet( p_box, "name" );
const MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
- if( !p_mean || p_mean->data.p_binary->i_blob < 4 + 16 ||
- !p_name || p_name->data.p_binary->i_blob < 5 ||
+ if( !p_mean || p_mean->data.p_binary->i_blob < 4 + i_ns ||
+ !p_name || p_name->data.p_binary->i_blob < 4 + i_key ||
!p_data || !BOXDATA(p_data) )
return;
- if( !strncmp( &((char*)p_mean->data.p_binary->p_blob)[4], "com.apple.iTunes",
- p_mean->data.p_binary->i_blob - 4 ) )
+ if( p_name->data.p_binary->i_blob - 4 != i_ns &&
+ strncmp( &((const char*)p_mean->data.p_binary->p_blob)[4], psz_ns, i_ns ) )
+ return;
+
+ if( psz_key != NULL &&
+ p_name->data.p_binary->i_blob - 4 != i_key &&
+ strncmp( &((const char*)p_name->data.p_binary->p_blob)[4], psz_key, i_key ) )
+ return;
+
+ char *psz_name = strndup( &((const char*)p_name->data.p_binary->p_blob)[4],
+ p_name->data.p_binary->i_blob - 4 );
+ if( psz_name )
+ pf_callback( psz_name, p_data->data.p_data, p_priv );
+ free( psz_name );
+}
+
+struct itunprivcallbackctx
+{
+ vlc_meta_t *p_meta;
+ qt_itunes_callback ituncb;
+ void *priv;
+};
+
+static void iTUNTripletCallback( const char *psz_key,
+ const MP4_Box_data_data_t *p_data, void *priv )
+{
+ struct itunprivcallbackctx *ctx = priv;
+
+ if( !strcmp(psz_key, "iTunSMPB") )
+ {
+ struct qt_itunes_triplet_data data = {.type = iTunSMPB };
+ char *psz_val = StringConvert( p_data );
+ if( psz_val && sscanf(psz_val, "%*"PRIX32" %"PRIX32" %"PRIX32" %"PRIX64,
+ &data.SMPB.delay, &data.SMPB.padding,
+ &data.SMPB.original_samplescount) == 3 )
+ ctx->ituncb( &data, ctx->priv );
+ free( psz_val );
+ }
+ else if( !strcmp(psz_key, "iTunNORM") )
{
- char *psz_name = strndup( &((char*)p_name->data.p_binary->p_blob)[4],
- p_name->data.p_binary->i_blob - 4 );
- char *psz_value = ExtractString( p_data );
- if( psz_name && psz_value )
- vlc_meta_SetExtra( p_meta, psz_name, psz_value );
- free( psz_name );
- free( psz_value );
+ struct qt_itunes_triplet_data data = {.type = iTunNORM };
+ char *psz_val = StringConvert( p_data );
+ uint32_t values[4];
+ if( psz_val && sscanf(psz_val, "%"PRIX32" %"PRIX32" %*"PRIX32" %*"PRIX32
+ "%*"PRIX32" %*"PRIX32" %"PRIX32" %"PRIX32,
+ &values[0], &values[1], &values[2], &values[3] ) == 4 )
+ {
+ values[0] = __MIN(values[0], values[1]); /* left / right volume */
+ if( values[0] )
+ data.NORM.volume_adjust = -10.0 * log10( values[0] / 10000.0 );
+ values[2] = __MAX(values[2], values[3]); /* left / right peak */
+ if( values[2] )
+ data.NORM.peak = values[2] / 32768.0;
+ if( values[0] && values[2] )
+ ctx->ituncb( &data, ctx->priv );
+ }
+ free( psz_val );
+ }
+ else if( !strcmp(psz_key, "Encoding Params"))
+ {
+ struct qt_itunes_triplet_data data = {.type = iTunEncodingParams,
+ .EncodingParams = { 0 } };
+ for( size_t i=0; i<p_data->i_blob; i += 8 )
+ {
+ const char *id = (const char*) &p_data->p_blob[i];
+ if( !strncmp(id, "brat", 4) )
+ data.EncodingParams.target_bitrate = GetDWBE(&p_data->p_blob[i+4]);
+ else if( !strncmp(id, "vbrq", 4) )
+ data.EncodingParams.target_quality = GetDWBE(&p_data->p_blob[i+4]);
+ }
+ ctx->ituncb( &data, ctx->priv );
}
}
-static void SetupmdirMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_box )
+static void SetupmdirMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_box, void *priv )
{
const MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
@@ -459,7 +525,7 @@ static void SetupmdirMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_box )
break;
}
case ATOM_ITUN:
- ExtractItunesInfoTriplets( p_meta, p_box );
+ ParseTriplet( p_box, "com.apple.iTunes", NULL, iTUNTripletCallback, priv );
break;
default:
SetMeta( p_meta, p_box->i_type, NULL, p_box );
@@ -520,7 +586,8 @@ static void SetupID3v2Meta( vlc_meta_t *p_meta, const MP4_Box_t *p_box )
ID3TAG_Parse_Handler, p_meta );
}
-void SetupMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_udta )
+void SetupMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_udta,
+ qt_itunes_callback ituncb, void *priv )
{
uint32_t i_handler = 0;
if ( p_udta->p_father )
@@ -543,8 +610,11 @@ void SetupMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_udta )
case HANDLER_mdir:
default:
- SetupmdirMeta( p_meta, p_box );
+ {
+ struct itunprivcallbackctx ctx = { .priv = priv, .p_meta = p_meta, .ituncb = ituncb };
+ SetupmdirMeta( p_meta, p_box, &ctx );
break;
+ }
}
}
}
=====================================
modules/demux/mp4/meta.h
=====================================
@@ -20,6 +20,39 @@
#ifndef VLC_MP4_META_H_
#define VLC_MP4_META_H_
-void SetupMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_udta );
+struct qt_itunes_triplet_data
+{
+ enum
+ {
+ iTunSMPB,
+ iTunNORM,
+ iTunEncodingParams,
+ } type;
+ union
+ {
+ struct
+ {
+ uint32_t delay;
+ uint32_t padding;
+ uint64_t original_samplescount;
+ } SMPB;
+ struct
+ {
+ float volume_adjust;
+ float peak;
+ } NORM;
+ struct
+ {
+ uint32_t target_bitrate;
+ uint32_t target_quality;
+ } EncodingParams;
+ };
+};
+
+typedef void (*qt_itunes_callback)(const struct qt_itunes_triplet_data *, void *);
+
+void SetupMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_udta,
+ qt_itunes_callback, void *priv );
+
#endif
=====================================
modules/demux/mp4/mp4.c
=====================================
@@ -140,6 +140,13 @@ typedef struct
int i_seekpoint;
input_title_t *p_title;
vlc_meta_t *p_meta;
+ struct
+ {
+ uint32_t i_delay_samples;
+ float f_replay_gain_norm;
+ float f_replay_gain_peak;
+ uint32_t i_target_bitrate;
+ } qt;
/* ASF in MP4 */
asf_packet_sys_t asfpacketsys;
@@ -2179,6 +2186,24 @@ static int FragSeekToPos( demux_t *p_demux, double f, bool b_accurate )
MP4_rescale_mtime( i_duration, p_sys->i_timescale ) ), b_accurate );
}
+static void ItunMetaCallback( const struct qt_itunes_triplet_data *data, void *priv )
+{
+ demux_sys_t *p_sys = priv;
+ if( data->type == iTunSMPB )
+ {
+ p_sys->qt.i_delay_samples = data->SMPB.delay;
+ }
+ else if( data->type == iTunNORM )
+ {
+ p_sys->qt.f_replay_gain_norm = data->NORM.volume_adjust;
+ p_sys->qt.f_replay_gain_peak = data->NORM.peak;
+ }
+ else if( data->type == iTunEncodingParams )
+ {
+ p_sys->qt.i_target_bitrate = data->EncodingParams.target_bitrate;
+ }
+}
+
static int MP4_LoadMeta( demux_sys_t *p_sys, vlc_meta_t *p_meta )
{
if( !p_meta )
@@ -2190,7 +2215,7 @@ static int MP4_LoadMeta( demux_sys_t *p_sys, vlc_meta_t *p_meta )
MP4_GetCoverMetaURI( p_sys->p_root, p_metaroot, psz_metapath, p_meta );
if( p_metaroot )
- SetupMeta( p_meta, p_metaroot );
+ SetupMeta( p_meta, p_metaroot, ItunMetaCallback, p_sys );
return VLC_SUCCESS;
}
@@ -3210,6 +3235,86 @@ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track,
p_arg->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = f_gain > 0;
}
}
+
+ if( p_sys->qt.f_replay_gain_peak > 0 )
+ {
+ audio_replay_gain_t *p_arg = &p_fmt->audio_replay_gain;
+ if( !p_arg->pb_gain[AUDIO_REPLAY_GAIN_TRACK] )
+ {
+ p_arg->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = p_sys->qt.f_replay_gain_norm;
+ p_arg->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true;
+ }
+ if( !p_arg->pb_peak[AUDIO_REPLAY_GAIN_TRACK] )
+ {
+ p_arg->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = p_sys->qt.f_replay_gain_peak;
+ p_arg->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true;
+ }
+ }
+
+ if( p_sys->qt.i_target_bitrate )
+ {
+ p_fmt->i_bitrate = p_sys->qt.i_target_bitrate;
+ }
+
+ switch( p_fmt->i_codec )
+ {
+ /* When not specified, set max standard decoder delay */
+ case VLC_CODEC_MP4A:
+ case VLC_CODEC_OPUS:
+ case VLC_CODEC_MP3:
+ case VLC_CODEC_MP2:
+ case VLC_CODEC_MPGA:
+ {
+ const MP4_Box_t *p_elst = p_track->p_elst;
+ if( p_elst && BOXDATA(p_elst)->i_entry_count &&
+ BOXDATA(p_elst)->entries[0].i_media_time < 0 )
+ {
+ p_track->i_decoder_delay = MP4_rescale( BOXDATA(p_elst)->entries[0].i_segment_duration,
+ p_sys->i_timescale,
+ p_track->i_timescale );
+ }
+ else if( p_sys->qt.i_delay_samples > 0 )
+ {
+ p_track->i_decoder_delay = MP4_rescale_qtime(
+ vlc_tick_from_samples( p_sys->qt.i_delay_samples, p_fmt->audio.i_rate ),
+ p_track->i_timescale );
+ }
+ /* AAC historical Apple decoder delay 2112 > 2048 */
+ else if( p_fmt->i_codec == VLC_CODEC_MP4A )
+ {
+ p_track->i_decoder_delay = MP4_rescale_qtime(
+ vlc_tick_from_samples( 2112, p_fmt->audio.i_rate ),
+ p_track->i_timescale );
+ }
+ /* Opus has an expected 80ms discard on seek */
+ else if( p_fmt->i_codec == VLC_CODEC_OPUS )
+ {
+ p_track->i_decoder_delay = MP4_rescale_qtime(
+ vlc_tick_from_samples( 80 * 48, 48000 ),
+ p_track->i_timescale );
+ }
+ /* MPEG have One frame delay */
+ else if( p_fmt->i_codec == VLC_CODEC_MP3 )
+ {
+ /* https://lame.sourceforge.io/tech-FAQ.txt
+ * https://www.compuphase.com/mp3/mp3loops.htm
+ https://www.iis.fraunhofer.de/content/dam/iis/de/doc/ame/conference/AES-116-Convention_guideline-to-audio-codec-delay_AES116.pdf */
+ p_track->i_decoder_delay = MP4_rescale_qtime(
+ vlc_tick_from_samples( 576, p_fmt->audio.i_rate ),
+ p_track->i_timescale );
+ }
+ else if( p_fmt->i_codec == VLC_CODEC_MPGA ||
+ p_fmt->i_codec == VLC_CODEC_MP2 )
+ {
+ p_track->i_decoder_delay = MP4_rescale_qtime(
+ vlc_tick_from_samples( 240, p_fmt->audio.i_rate ),
+ p_track->i_timescale );
+ }
+ }
+ break;
+ default:
+ break;
+ }
break;
default:
break;
@@ -3404,29 +3509,61 @@ static int TrackTimeToSampleChunk( demux_t *p_demux, mp4_track_t *p_track,
/* *** Try to find nearest sync points *** */
- uint32_t i_sync_sample;
+ uint32_t i_sync_sample = i_sample;
if( VLC_SUCCESS ==
TrackGetNearestSeekPoint( p_demux, p_track, i_sample, &i_sync_sample ) )
{
- /* Go to chunk */
- if( i_sync_sample <= i_sample )
+ msg_Dbg(p_demux,"track[Id 0x%x] sync point found sample %"PRIu32"(-%"PRIu32")",
+ p_track->i_track_ID, i_sync_sample, i_sample - i_sync_sample);
+ }
+ else
+ {
+ const MP4_Box_data_sgpd_entry_t *p_entrydesc;
+ if( MP4_SampleToGroupInfo( p_track->p_stbl, i_sample,
+ SAMPLEGROUP_roll, 0, &i_sync_sample,
+ SAMPLE_GROUP_MATCH_EXACT, &p_entrydesc ) )
{
- while( i_chunk > 0 &&
- i_sync_sample < p_track->chunk[i_chunk].i_sample_first )
- i_chunk--;
+ msg_Dbg(p_demux, "track[Id 0x%x] preroll offset: %"PRId16" samples",
+ p_track->i_track_ID, p_entrydesc->roll.i_roll_distance );
+ if( p_entrydesc->roll.i_roll_distance < 0 )
+ i_sync_sample += p_entrydesc->roll.i_roll_distance;
}
- else
+ else if( p_track->i_decoder_delay > 0 &&
+ p_track->i_decoder_delay <= i_start )
{
- while( i_chunk < p_track->i_chunk_count - 1 &&
- i_sync_sample >= p_track->chunk[i_chunk].i_sample_first +
- p_track->chunk[i_chunk].i_sample_count )
- i_chunk++;
+ uint32_t i_withdelay_sample, i_withdelay_chunk;
+ if( STTSToSampleChunk( p_track, i_start - p_track->i_decoder_delay,
+ &i_withdelay_chunk,
+ &i_withdelay_sample ) == VLC_SUCCESS )
+ {
+ msg_Warn( p_demux, "track[Id 0x%x] has %ldµs decoder delay from %ld",
+ p_track->i_track_ID,
+ MP4_rescale_mtime( p_track->i_decoder_delay, p_track->i_timescale),
+ i_start );
+ *pi_chunk = i_withdelay_chunk;
+ *pi_sample = i_withdelay_sample;
+ return VLC_SUCCESS;
+ }
}
- i_sample = i_sync_sample;
+ }
+
+ /* Go to sync point chunk */
+ if( i_sync_sample <= i_sample )
+ {
+ while( i_chunk > 0 &&
+ i_sync_sample < p_track->chunk[i_chunk].i_sample_first )
+ i_chunk--;
+ }
+ else
+ {
+ while( i_chunk < p_track->i_chunk_count - 1 &&
+ i_sync_sample >= p_track->chunk[i_chunk].i_sample_first +
+ p_track->chunk[i_chunk].i_sample_count )
+ i_chunk++;
}
*pi_chunk = i_chunk;
- *pi_sample = i_sample;
+ *pi_sample = i_sync_sample;
return VLC_SUCCESS;
}
=====================================
modules/demux/mp4/mp4.h
=====================================
@@ -137,6 +137,7 @@ typedef struct
stime_t i_next_dts;
int64_t i_cts_shift;
vlc_tick_t i_pts_offset;
+ stime_t i_decoder_delay;
/* give the next sample to read, i_chunk is to find quickly where
the sample is located */
uint32_t i_sample; /* next sample to read */
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d314736cef648841deee50ba310e1c659c85e329...d379959582948258005c9f7f0594972b5f144490
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d314736cef648841deee50ba310e1c659c85e329...d379959582948258005c9f7f0594972b5f144490
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list