Skip to content

Commit

Permalink
when video has discontinuity, we update the sample duration to reflec…
Browse files Browse the repository at this point in the history
…t the discontinuity
  • Loading branch information
pohhsu committed Aug 18, 2023
1 parent 41bdf9f commit 3653357
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 0 deletions.
6 changes: 6 additions & 0 deletions transform/BasePackager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ abstract class BasePackager : IPackager

public bool TranscodeAudio { get; protected set; }

public bool TranscodeVideo { get; protected set; }

public TranscodeAudioInfo TranscodeAudioInfoData { get; protected set; } = new();

public IDictionary<string, IList<Track>> FileToTrackMap => _fileToTrackMap;
Expand Down Expand Up @@ -237,6 +239,10 @@ await Task.Run(() => _transMuxer.TranscodeAudioAsync(
TranscodeAudioInfoData,
cancellationToken));
}
if (TranscodeVideo && track.Type == StreamType.Video)
{
_transMuxer.TranscodeVideo(filePath);
}
}
else
{
Expand Down
5 changes: 5 additions & 0 deletions transform/ShakaPackager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ public ShakaPackager(MigratorOptions options, AssetDetails assetDetails, TransMu
_logger.LogDebug("video / audio tracks start time not within 0.1 sec and audio stream has discontinuities, transcode required");
TranscodeAudio = true;
}

if (videoStream.HasDiscontinuities())
{
TranscodeVideo = true;
}
}
}
}
Expand Down
139 changes: 139 additions & 0 deletions transform/TransMuxer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,144 @@ public async Task TranscodeAudioAsync(string source, string destination, Transco
// FFmpeg will zero out tdft baseMediaDecodeTime, add it back in place.
AddOffsetToTfdtBox(destination, (ulong)transcodeAudioInfo.VideoStartTimeInAudioTimeScale);
}

private static List<ulong> GetDecodeMediaTime(string fileName)
{
using var stream = File.Open(fileName, FileMode.Open, FileAccess.Read);
var reader = new MP4Reader(stream);
stream.Position = 0;
List<ulong> decodeTimes = new();
while (stream.Position < stream.Length)
{
Int64 startPosition = reader.BaseStream.Position;
Int64 size = reader.ReadUInt32(); // size of current box
UInt32 type = reader.ReadUInt32(); // type of current box

// Parse extended size
if (size == 0)
{
throw new InvalidDataException("Invalid size.");
}
else if (size == 1)
{
size = reader.ReadInt64();
}

if (type == MP4BoxType.moof)
{
stream.Position = startPosition; // rewind
var moofBox = MP4BoxFactory.ParseSingleBox<moofBox>(reader);

foreach (var c in moofBox.Children)
{
if (c.Type == MP4BoxType.traf)
{
foreach (var cc in c.Children)
{
if (cc.Type == MP4BoxType.tfdt)
{
tfdtBox tfdtBox = (tfdtBox)cc; // will throw
decodeTimes.Add(tfdtBox.DecodeTime);
break;
}
}
}
}
}
//skip till the beginning of the next box.
stream.Position = startPosition + size;
}
return decodeTimes;
}

private void UpdateTrackRunDuration(string fileName)
{
List<ulong> decodeTimes = GetDecodeMediaTime(fileName);

using var stream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
var reader = new MP4Reader(stream);
var writer = new MP4Writer(stream);
stream.Position = 0;


int totalDecodeTime = decodeTimes.Count;
int curDecodeTimesIndex = 0;

while (stream.Position < stream.Length)
{
Int64 startPosition = reader.BaseStream.Position;
Int64 size = reader.ReadUInt32(); // size of current box
UInt32 type = reader.ReadUInt32(); // type of current box

// Parse extended size
if (size == 0)
{
throw new InvalidDataException("Invalid size.");
}
else if (size == 1)
{
size = reader.ReadInt64();
}

if (type == MP4BoxType.moof)
{
stream.Position = startPosition; // rewind
var moofBox = MP4BoxFactory.ParseSingleBox<moofBox>(reader);

ulong offsetToTrun = moofBox.ComputeBaseSizeBox();
foreach (var c in moofBox.Children)
{
if (c.Type == MP4BoxType.traf)
{
offsetToTrun += moofBox.ComputeBaseSizeBox();
foreach (var cc in c.Children)
{
if (cc.Type == MP4BoxType.trun)
{
trunBox trunBox = (trunBox)cc; // will throw
trunBox.TrunFlags flag = (trunBox.TrunFlags) trunBox.Flags;
if ((flag & trunBox.TrunFlags.SampleDurationPresent) != trunBox.TrunFlags.SampleDurationPresent)
{
throw new InvalidDataException("Unexpected, sampleDurationPresent must be present");
}

long trunPosition = startPosition + (long)offsetToTrun;
stream.Position = trunPosition;
ulong totalDuration = 0;
for (int i = 0; i < trunBox.Entries.Count; ++i)
{
totalDuration += (ulong)trunBox.Entries[i].SampleDuration!;
}
if (curDecodeTimesIndex + 1 < decodeTimes.Count)
{
if (decodeTimes[curDecodeTimesIndex] + totalDuration < decodeTimes[curDecodeTimesIndex + 1])
{
ulong offset = decodeTimes[curDecodeTimesIndex + 1] - decodeTimes[curDecodeTimesIndex] - totalDuration;
_logger.LogTrace("Update duration due to discontinuity at dt {0}, offset {1}.", decodeTimes[curDecodeTimesIndex], offset);
trunBox.Entries[trunBox.Entries.Count - 1].SampleDuration += (uint)offset;
}
}
trunBox.WriteTo(writer);
curDecodeTimesIndex++;
break;
}
offsetToTrun += cc.Size.Value;
}
}
else
{
offsetToTrun += c.Size.Value;
}
}
}
//skip till the beginning of the next box.
stream.Position = startPosition + size;
}
}

public void TranscodeVideo(string destination)
{
UpdateTrackRunDuration(destination);
}
}
}

0 comments on commit 3653357

Please sign in to comment.