@@ -408,6 +408,8 @@ private bool readMP4(BinaryReader source, ReadTagParams readTagParams)
408
408
}
409
409
while ( trakSize > 0 ) ;
410
410
411
+ if ( Utils . ApproxEquals ( calculatedDurationMs , 0 ) ) readMoof ( source . BaseStream ) ;
412
+
411
413
// Look for uuid atoms
412
414
source . BaseStream . Seek ( sizeInfo . ID3v2Size , SeekOrigin . Begin ) ;
413
415
Uuid uuid ;
@@ -589,6 +591,62 @@ private bool readMP4(BinaryReader source, ReadTagParams readTagParams)
589
591
return true ;
590
592
}
591
593
594
+ // Try getting duration from the moof atom, if it exists
595
+ private void readMoof ( Stream s )
596
+ {
597
+ s . Seek ( sizeInfo . ID3v2Size , SeekOrigin . Begin ) ;
598
+ if ( 0 == navigateToAtom ( s , "moof" ) ) return ;
599
+ if ( 0 == navigateToAtom ( s , "traf" ) ) return ;
600
+ long trafOffset = s . Position ;
601
+ if ( 0 == navigateToAtom ( s , "tfhd" ) ) return ;
602
+ s . Seek ( 1 , SeekOrigin . Current ) ; // Version
603
+
604
+ byte [ ] data = new byte [ 4 ] ;
605
+ if ( s . Read ( data , 0 , 3 ) < 3 ) return ;
606
+ int flags = StreamUtils . DecodeBEInt24 ( data ) ;
607
+ if ( 0 == ( flags & 0x00000008 ) ) return ;
608
+
609
+ if ( s . Read ( data , 0 , 4 ) < 4 ) return ; // Track ID
610
+ if ( ( flags & 0x00000001 ) > 0 ) s . Seek ( 8 , SeekOrigin . Current ) ; // base_data_offset
611
+ if ( ( flags & 0x00000002 ) > 0 ) s . Seek ( 4 , SeekOrigin . Current ) ; // sample_description_index
612
+ if ( s . Read ( data , 0 , 4 ) < 4 ) return ;
613
+ int defaultSampleDuration = StreamUtils . DecodeBEInt32 ( data ) ;
614
+
615
+ s . Seek ( trafOffset , SeekOrigin . Begin ) ;
616
+ uint atomSize = navigateToAtom ( s , "trun" ) ;
617
+ long durationAll = 0 ;
618
+ while ( atomSize > 0 )
619
+ {
620
+ var trunOffset = s . Position ;
621
+ s . Seek ( 1 , SeekOrigin . Current ) ; // Version
622
+ if ( s . Read ( data , 0 , 3 ) < 3 ) return ;
623
+ flags = StreamUtils . DecodeBEInt24 ( data ) ;
624
+ if ( s . Read ( data , 0 , 4 ) < 4 ) return ;
625
+ int sampleCount = StreamUtils . DecodeBEInt32 ( data ) ;
626
+
627
+ for ( int i = 0 ; i < sampleCount ; i ++ )
628
+ {
629
+ if ( ( flags & 0x00000100 ) > 0 ) // Sample has its own duration
630
+ {
631
+ if ( ( flags & 0x00000001 ) > 0 ) s . Seek ( 4 , SeekOrigin . Current ) ; // data_offset
632
+ if ( ( flags & 0x00000004 ) > 0 ) s . Seek ( 4 , SeekOrigin . Current ) ; // first_sample_flags
633
+ if ( s . Read ( data , 0 , 4 ) < 4 ) return ;
634
+ durationAll += StreamUtils . DecodeBEInt32 ( data ) ;
635
+ }
636
+ else durationAll += defaultSampleDuration ;
637
+ }
638
+
639
+ // Seek next trun atom
640
+ s . Seek ( trunOffset + atomSize - 8 , SeekOrigin . Begin ) ;
641
+ if ( s . Read ( data , 0 , 4 ) < 4 ) return ;
642
+ atomSize = StreamUtils . DecodeBEUInt32 ( data ) ;
643
+ if ( s . Read ( data , 0 , 4 ) < 4 ) return ;
644
+ if ( ! Utils . Latin1Encoding . GetString ( data ) . Equals ( "trun" ) ) break ;
645
+ }
646
+
647
+ calculatedDurationMs = durationAll * 1000.0 / SampleRate ;
648
+ }
649
+
592
650
private long readTrack (
593
651
BinaryReader source ,
594
652
ReadTagParams readTagParams ,
@@ -710,7 +768,7 @@ long moovSize
710
768
else if ( "soun" . Equals ( mediaType ) || "vide" . Equals ( mediaType ) )
711
769
{
712
770
mediaTrackOffsets . Add ( trakPosition ) ;
713
- isCurrentTrackFirstAudioTrack = ( 1 == mediaTrackOffsets . Count ) ;
771
+ isCurrentTrackFirstAudioTrack = 1 == mediaTrackOffsets . Count ;
714
772
}
715
773
716
774
if ( readTagParams . PrepareForWriting && isCurrentTrackOtherChapterTrack && ! isCurrentTrackFirstChapterTextTrack )
@@ -719,7 +777,7 @@ long moovSize
719
777
trackZoneName = ZONE_MP4_QT_CHAP_TXT_TRAK + "." + trackId ;
720
778
structureHelper . AddZone ( trakPosition , ( int ) trakSize , trackZoneName ) ;
721
779
structureHelper . AddSize ( moovPosition - 8 , moovSize , trackZoneName ) ;
722
- structureHelper . AddCounter ( trackCounterOffset , ( 1 == trackId ) ? 2 : 1 , trackZoneName ) ;
780
+ structureHelper . AddCounter ( trackCounterOffset , ( 1 != trackId ) ? 1 : 2 , trackZoneName ) ;
723
781
}
724
782
725
783
source . BaseStream . Seek ( mdiaPosition , SeekOrigin . Begin ) ;
0 commit comments