@@ -63,13 +63,13 @@ typedef struct IMFVirtualTrackPlaybackCtx {
6363 // Track index in playlist
6464 int32_t index ;
6565 // Time counters
66- int64_t current_timestamp ;
67- int64_t duration ;
66+ AVRational current_timestamp ;
67+ AVRational duration ;
6868 // Resources
6969 unsigned int resource_count ;
7070 IMFVirtualTrackResourcePlaybackCtx * * resources ;
7171 // Decoding cursors
72- IMFVirtualTrackResourcePlaybackCtx * current_resource ;
72+ uint32_t current_resource_index ;
7373 int64_t last_pts ;
7474} IMFVirtualTrackPlaybackCtx ;
7575
@@ -185,9 +185,9 @@ static int parse_assetmap(AVFormatContext *s, const char *url, AVIOContext *in)
185185 AVDictionary * opts = NULL ;
186186 xmlDoc * doc = NULL ;
187187
188- int close_in = 0 ;
189- int ret = 0 ;
190- int64_t filesize = 0 ;
188+ int close_in ;
189+ int ret ;
190+ int64_t filesize ;
191191
192192 const char * base_url = av_dirname (strdup (url ));
193193 if (c -> asset_locator_map == NULL ) {
@@ -253,6 +253,7 @@ static IMFAssetLocator *find_asset_map_locator(IMFAssetLocatorMap *asset_map, UU
253253
254254static int open_track_resource_context (AVFormatContext * s , IMFVirtualTrackResourcePlaybackCtx * track_resource ) {
255255 int ret = 0 ;
256+ int64_t entry_point ;
256257
257258 if (!track_resource -> ctx ) {
258259 track_resource -> ctx = avformat_alloc_context ();
@@ -275,6 +276,22 @@ static int open_track_resource_context(AVFormatContext *s, IMFVirtualTrackResour
275276 goto cleanup ;
276277 }
277278
279+ // Compare the source timebase to the resource edit rate, considering the first stream of the source file
280+ if (av_cmp_q (track_resource -> ctx -> streams [0 ]-> time_base , av_inv_q (track_resource -> resource -> base .edit_rate ))) {
281+ av_log (s , AV_LOG_WARNING , "Incoherent source stream timebase %d/%d regarding resource edit rate: %d/%d" , track_resource -> ctx -> streams [0 ]-> time_base .num , track_resource -> ctx -> streams [0 ]-> time_base .den , track_resource -> resource -> base .edit_rate .den , track_resource -> resource -> base .edit_rate .num );
282+ }
283+
284+ entry_point = (int64_t )track_resource -> resource -> base .entry_point * track_resource -> resource -> base .edit_rate .den * AV_TIME_BASE / track_resource -> resource -> base .edit_rate .num ;
285+
286+ if (entry_point ) {
287+ av_log (s , AV_LOG_DEBUG , "Seek at resource %s entry point: %ld\n" , track_resource -> locator -> absolute_uri , track_resource -> resource -> base .entry_point );
288+ ret = avformat_seek_file (track_resource -> ctx , -1 , entry_point , entry_point , entry_point , 0 );
289+ if (ret < 0 ) {
290+ av_log (s , AV_LOG_ERROR , "Could not seek at %ld on %s: %s\n" , entry_point , track_resource -> locator -> absolute_uri , av_err2str (ret ));
291+ goto cleanup ;
292+ }
293+ }
294+
278295 return ret ;
279296cleanup :
280297 avformat_free_context (track_resource -> ctx );
@@ -286,7 +303,7 @@ static int open_track_file_resource(AVFormatContext *s, IMFTrackFileResource *tr
286303 IMFVirtualTrackResourcePlaybackCtx * track_resource ;
287304 IMFAssetLocator * asset_locator ;
288305
289- int ret = 0 ;
306+ int ret ;
290307
291308 if (!(asset_locator = find_asset_map_locator (c -> asset_locator_map , track_file_resource -> track_file_uuid ))) {
292309 av_log (s , AV_LOG_ERROR , "Could not find asset locator for UUID: " UUID_FORMAT "\n" , UID_ARG (track_file_resource -> track_file_uuid ));
@@ -303,9 +320,11 @@ static int open_track_file_resource(AVFormatContext *s, IMFTrackFileResource *tr
303320 return ret ;
304321 }
305322
306- track -> resources = av_realloc (track -> resources , track -> resource_count + 1 * sizeof (IMFVirtualTrackResourcePlaybackCtx ));
307- track -> resources [track -> resource_count ++ ] = track_resource ;
308- track -> duration += track_resource -> ctx -> duration ;
323+ for (int repetition = 0 ; repetition < track_file_resource -> base .repeat_count ; ++ repetition ) {
324+ track -> resources = av_realloc (track -> resources , (track -> resource_count + 1 ) * sizeof (IMFVirtualTrackResourcePlaybackCtx ));
325+ track -> resources [track -> resource_count ++ ] = track_resource ;
326+ track -> duration = av_add_q (track -> duration , av_make_q ((int )track_resource -> resource -> base .duration * track_resource -> resource -> base .edit_rate .den , track_resource -> resource -> base .edit_rate .num ));
327+ }
309328
310329 return ret ;
311330}
@@ -317,6 +336,7 @@ static int open_virtual_track(AVFormatContext *s, IMFTrackFileVirtualTrack *virt
317336
318337 track = av_mallocz (sizeof (IMFVirtualTrackPlaybackCtx ));
319338 track -> index = track_index ;
339+ track -> duration = av_make_q (0 , INT32_MAX );
320340
321341 for (int i = 0 ; i < virtual_track -> resource_count ; i ++ ) {
322342 av_log (s , AV_LOG_DEBUG , "Open stream from file " UUID_FORMAT ", stream %d\n" , UID_ARG (virtual_track -> resources [i ].track_file_uuid ), i );
@@ -326,7 +346,9 @@ static int open_virtual_track(AVFormatContext *s, IMFTrackFileVirtualTrack *virt
326346 }
327347 }
328348
329- c -> tracks = av_realloc (c -> tracks , c -> track_count + 1 * sizeof (IMFVirtualTrackPlaybackCtx ));
349+ track -> current_timestamp = av_make_q (0 , track -> duration .den );
350+
351+ c -> tracks = av_realloc (c -> tracks , (c -> track_count + 1 ) * sizeof (IMFVirtualTrackPlaybackCtx ));
330352 c -> tracks [c -> track_count ++ ] = track ;
331353
332354 return ret ;
@@ -344,6 +366,7 @@ static void imf_virtual_track_playback_context_free(IMFVirtualTrackPlaybackCtx *
344366
345367 if (track -> resources [i ]-> ctx && track -> resources [i ]-> ctx -> iformat ) {
346368 avformat_free_context (track -> resources [i ]-> ctx );
369+ track -> resources [i ]-> ctx = NULL ;
347370 }
348371 }
349372}
@@ -370,6 +393,7 @@ static int set_context_streams_from_tracks(AVFormatContext *s) {
370393 break ;
371394 }
372395 avpriv_set_pts_info (asset_stream , first_resource_stream -> pts_wrap_bits , first_resource_stream -> time_base .num , first_resource_stream -> time_base .den );
396+ asset_stream -> duration = (int64_t )av_q2d (av_mul_q (c -> tracks [i ]-> duration , av_inv_q (asset_stream -> time_base )));
373397 }
374398
375399 return ret ;
@@ -446,42 +470,41 @@ static IMFVirtualTrackPlaybackCtx *get_next_track_with_minimum_timestamp(AVForma
446470 IMFContext * c = s -> priv_data ;
447471 IMFVirtualTrackPlaybackCtx * track ;
448472
449- int64_t minimum_timestamp = INT64_MAX ;
473+ AVRational minimum_timestamp = av_make_q ( INT32_MAX , 1 ) ;
450474 for (int i = 0 ; i < c -> track_count ; ++ i ) {
451- av_log (s , AV_LOG_DEBUG , "Compare track %p timestamp %ld to minimum %ld (over duration: %ld )\n" , c -> tracks [ i ], c -> tracks [i ]-> current_timestamp , minimum_timestamp , c -> tracks [i ]-> duration );
452- if (c -> tracks [i ]-> current_timestamp < minimum_timestamp ) {
475+ av_log (s , AV_LOG_DEBUG , "Compare track %d timestamp " AVRATIONAL_FORMAT " to minimum " AVRATIONAL_FORMAT " (over duration: " AVRATIONAL_FORMAT " )\n" , i , AVRATIONAL_ARG ( c -> tracks [i ]-> current_timestamp ), AVRATIONAL_ARG ( minimum_timestamp ), AVRATIONAL_ARG ( c -> tracks [i ]-> duration ) );
476+ if (av_cmp_q ( c -> tracks [i ]-> current_timestamp , minimum_timestamp ) < 0 ) {
453477 track = c -> tracks [i ];
454478 minimum_timestamp = track -> current_timestamp ;
455479 }
456480 }
457481
458- av_log (s , AV_LOG_DEBUG , "Found next track to read: %d (timestamp: %ld / %ld )\n" , track -> index , track -> current_timestamp , minimum_timestamp );
482+ av_log (s , AV_LOG_DEBUG , "Found next track to read: %d (timestamp: %lf / %lf )\n" , track -> index , av_q2d ( track -> current_timestamp ), av_q2d ( minimum_timestamp ) );
459483 return track ;
460484}
461485
462486static IMFVirtualTrackResourcePlaybackCtx * get_resource_context_for_timestamp (AVFormatContext * s , IMFVirtualTrackPlaybackCtx * track ) {
463- unsigned long cumulated_duration = 0 ;
464- unsigned long edit_unit_duration ;
487+ AVRational edit_unit_duration = av_inv_q ( track -> resources [ 0 ] -> resource -> base . edit_rate ) ;
488+ AVRational cumulated_duration = av_make_q ( 0 , edit_unit_duration . den ) ;
465489
466- av_log (s , AV_LOG_DEBUG , "Looking for track %d resource for timestamp = %ld / %ld \n" , track -> index , track -> current_timestamp , track -> duration );
490+ av_log (s , AV_LOG_DEBUG , "Looking for track %d resource for timestamp = %lf / %lf \n" , track -> index , av_q2d ( track -> current_timestamp ), av_q2d ( track -> duration ) );
467491 for (int i = 0 ; i < track -> resource_count ; ++ i ) {
468- edit_unit_duration = track -> resources [i ]-> resource -> base .edit_rate .den * AV_TIME_BASE / track -> resources [i ]-> resource -> base .edit_rate .num ;
469- cumulated_duration += track -> resources [i ]-> resource -> base .duration * track -> resources [i ]-> resource -> base .edit_rate .den * AV_TIME_BASE / track -> resources [i ]-> resource -> base .edit_rate .num ;
492+ cumulated_duration = av_add_q (cumulated_duration , av_make_q ((int )track -> resources [i ]-> resource -> base .duration * edit_unit_duration .num , edit_unit_duration .den ));
470493
471- if (track -> current_timestamp + edit_unit_duration <= cumulated_duration ) {
472- av_log (s , AV_LOG_DEBUG , "Found resource %d in track %d to read for timestamp %ld (on cumulated=%ld ): entry=%ld, duration=%lu, editrate=%d/%d | edit_unit_duration=%ld \n" , i , track -> index , track -> current_timestamp , cumulated_duration , track -> resources [i ]-> resource -> base .entry_point , track -> resources [i ]-> resource -> base .duration , track -> resources [i ]-> resource -> base .edit_rate . num , track -> resources [ i ] -> resource -> base . edit_rate . den , edit_unit_duration );
494+ if (av_cmp_q ( av_add_q ( track -> current_timestamp , edit_unit_duration ), cumulated_duration ) <= 0 ) {
495+ av_log (s , AV_LOG_DEBUG , "Found resource %d in track %d to read for timestamp %lf (on cumulated=%lf ): entry=%ld, duration=%lu, editrate=" AVRATIONAL_FORMAT " | edit_unit_duration=%lf \n" , i , track -> index , av_q2d ( track -> current_timestamp ), av_q2d ( cumulated_duration ) , track -> resources [i ]-> resource -> base .entry_point , track -> resources [i ]-> resource -> base .duration , AVRATIONAL_ARG ( track -> resources [i ]-> resource -> base .edit_rate ), av_q2d ( edit_unit_duration ) );
473496
474- if (track -> current_resource != track -> resources [ i ] ) {
497+ if (track -> current_resource_index != i ) {
475498 av_log (s , AV_LOG_DEBUG , "Switch resource on track %d: re-open context\n" , track -> index );
476- if (track -> current_resource != NULL ) {
477- avformat_close_input (& track -> current_resource -> ctx );
499+ if (track -> resources [ track -> current_resource_index ] != NULL ) {
500+ avformat_close_input (& track -> resources [ track -> current_resource_index ] -> ctx );
478501 }
479502 if (open_track_resource_context (s , track -> resources [i ]) != 0 ) {
480503 return NULL ;
481504 }
482- track -> current_resource = track -> resources [ i ] ;
505+ track -> current_resource_index = i ;
483506 }
484- return track -> current_resource ;
507+ return track -> resources [ track -> current_resource_index ] ;
485508 }
486509 }
487510 return NULL ;
@@ -491,31 +514,42 @@ static int ff_imf_read_packet(AVFormatContext *s, AVPacket *pkt) {
491514 IMFContext * c = s -> priv_data ;
492515
493516 IMFVirtualTrackResourcePlaybackCtx * resource_to_read = NULL ;
517+ AVRational edit_unit_duration ;
494518 int ret = 0 ;
495519
496520 IMFVirtualTrackPlaybackCtx * track_to_read = get_next_track_with_minimum_timestamp (s );
497521
498- if (track_to_read -> current_timestamp == track_to_read -> duration ) {
522+ if (av_cmp_q ( track_to_read -> current_timestamp , track_to_read -> duration ) == 0 ) {
499523 return AVERROR_EOF ;
500524 }
501525
502526 resource_to_read = get_resource_context_for_timestamp (s , track_to_read );
503527
504528 if (!resource_to_read ) {
529+ edit_unit_duration = av_inv_q (track_to_read -> resources [track_to_read -> current_resource_index ]-> resource -> base .edit_rate );
530+ if (av_cmp_q (av_add_q (track_to_read -> current_timestamp , edit_unit_duration ), track_to_read -> duration ) > 0 ) {
531+ return AVERROR_EOF ;
532+ }
533+
505534 av_log (s , AV_LOG_ERROR , "Could not find IMF track resource to read\n" );
506535 return AVERROR_STREAM_NOT_FOUND ;
507536 }
508537
509538 while (!ff_check_interrupt (c -> interrupt_callback ) && !ret ) {
510539 ret = av_read_frame (resource_to_read -> ctx , pkt );
511- av_log (s , AV_LOG_DEBUG , "Got packet: pts=%ld, dts=%ld, duration=%ld, stream_index=%d, pos=%ld, \n" , pkt -> pts , pkt -> dts , pkt -> duration , pkt -> stream_index , pkt -> pos );
540+ av_log (s , AV_LOG_DEBUG , "Got packet: pts=%ld, dts=%ld, duration=%ld, stream_index=%d, pos=%ld\n" , pkt -> pts , pkt -> dts , pkt -> duration , pkt -> stream_index , pkt -> pos );
512541 if (ret >= 0 ) {
513542 // Update packet info from track
543+ if (pkt -> dts < s -> streams [track_to_read -> index ]-> cur_dts && track_to_read -> last_pts > 0 ) {
544+ pkt -> dts = s -> streams [track_to_read -> index ]-> cur_dts ;
545+ }
546+
514547 pkt -> pts = track_to_read -> last_pts ;
548+ pkt -> dts = pkt -> dts - (int64_t )track_to_read -> resources [track_to_read -> current_resource_index ]-> resource -> base .entry_point ;
515549 pkt -> stream_index = track_to_read -> index ;
516550
517551 // Update track cursors
518- track_to_read -> current_timestamp += av_rescale ( pkt -> duration , ( int64_t ) resource_to_read -> ctx -> streams [0 ]-> time_base .num * AV_TIME_BASE , resource_to_read -> ctx -> streams [0 ]-> time_base .den );
552+ track_to_read -> current_timestamp = av_add_q ( track_to_read -> current_timestamp , av_make_q (( int ) pkt -> duration * resource_to_read -> ctx -> streams [0 ]-> time_base .num , resource_to_read -> ctx -> streams [0 ]-> time_base .den ) );
519553 track_to_read -> last_pts += pkt -> duration ;
520554
521555 return 0 ;
0 commit comments