Skip to content

Commit 5488671

Browse files
committed
MP4 : Detect duration on files where duration info is under the moof atom
1 parent 03cc17b commit 5488671

File tree

1 file changed

+60
-2
lines changed

1 file changed

+60
-2
lines changed

ATL/AudioData/IO/MP4.cs

+60-2
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ private bool readMP4(BinaryReader source, ReadTagParams readTagParams)
408408
}
409409
while (trakSize > 0);
410410

411+
if (Utils.ApproxEquals(calculatedDurationMs, 0)) readMoof(source.BaseStream);
412+
411413
// Look for uuid atoms
412414
source.BaseStream.Seek(sizeInfo.ID3v2Size, SeekOrigin.Begin);
413415
Uuid uuid;
@@ -589,6 +591,62 @@ private bool readMP4(BinaryReader source, ReadTagParams readTagParams)
589591
return true;
590592
}
591593

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+
592650
private long readTrack(
593651
BinaryReader source,
594652
ReadTagParams readTagParams,
@@ -710,7 +768,7 @@ long moovSize
710768
else if ("soun".Equals(mediaType) || "vide".Equals(mediaType))
711769
{
712770
mediaTrackOffsets.Add(trakPosition);
713-
isCurrentTrackFirstAudioTrack = (1 == mediaTrackOffsets.Count);
771+
isCurrentTrackFirstAudioTrack = 1 == mediaTrackOffsets.Count;
714772
}
715773

716774
if (readTagParams.PrepareForWriting && isCurrentTrackOtherChapterTrack && !isCurrentTrackFirstChapterTextTrack)
@@ -719,7 +777,7 @@ long moovSize
719777
trackZoneName = ZONE_MP4_QT_CHAP_TXT_TRAK + "." + trackId;
720778
structureHelper.AddZone(trakPosition, (int)trakSize, trackZoneName);
721779
structureHelper.AddSize(moovPosition - 8, moovSize, trackZoneName);
722-
structureHelper.AddCounter(trackCounterOffset, (1 == trackId) ? 2 : 1, trackZoneName);
780+
structureHelper.AddCounter(trackCounterOffset, (1 != trackId) ? 1 : 2, trackZoneName);
723781
}
724782

725783
source.BaseStream.Seek(mdiaPosition, SeekOrigin.Begin);

0 commit comments

Comments
 (0)