[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