Fix (some) demuxed mp4 time codes

This commit is contained in:
Nikolaj Olsson 2018-04-02 18:39:47 +02:00
parent 871469ee9b
commit 25902a30d1
5 changed files with 93 additions and 72 deletions

View File

@ -9,17 +9,23 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
{
public class Stbl : Box
{
public List<string> Texts = new List<string>();
public List<SubPicture> SubPictures = new List<SubPicture>();
public List<double> StartTimeCodes = new List<double>();
public List<double> EndTimeCodes = new List<double>();
public ulong StszSampleCount = 0;
private Mdia _mdia;
public List<string> Texts;
public List<SubPicture> SubPictures;
public ulong StszSampleCount;
public ulong TimeScale { get; set; }
private readonly Mdia _mdia;
public List<uint> SampleSizes;
public List<SampleTimeInfo> Ssts { get; set; }
public Stbl(Stream fs, ulong maximumLength, ulong timeScale, string handlerType, Mdia mdia)
{
TimeScale = timeScale;
_mdia = mdia;
Position = (ulong)fs.Position;
Ssts = new List<SampleTimeInfo>();
SampleSizes = new List<uint>();
Texts = new List<string>();
SubPictures = new List<SubPicture>();
while (fs.Position < (long)maximumLength)
{
if (!InitializeSizeAndName(fs))
@ -37,7 +43,7 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
{
uint offset = GetUInt(8 + i * 4);
if (lastOffset + 5 < offset)
ReadText(fs, offset, handlerType);
ReadText(fs, offset, handlerType, i);
lastOffset = offset;
}
}
@ -53,7 +59,7 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
{
ulong offset = GetUInt64(8 + i * 8);
if (lastOffset + 8 < offset)
ReadText(fs, offset, handlerType);
ReadText(fs, offset, handlerType, i);
lastOffset = offset;
}
}
@ -70,6 +76,7 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
if (12 + i * 4 + 4 < Buffer.Length)
{
uint sampleSize = GetUInt(12 + i * 4);
SampleSizes.Add(sampleSize);
}
}
}
@ -81,21 +88,13 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
fs.Read(Buffer, 0, Buffer.Length);
int version = Buffer[0];
uint numberOfSampleTimes = GetUInt(4);
double totalTime = 0;
if (_mdia.IsClosedCaption)
{
for (int i = 0; i < numberOfSampleTimes; i++)
{
uint sampleCount = GetUInt(8 + i * 8);
uint sampleDelta = GetUInt(12 + i * 8);
for (int j = 0; j < sampleCount; j++)
{
totalTime += sampleDelta / (double)timeScale;
if (StartTimeCodes.Count > 0)
EndTimeCodes[EndTimeCodes.Count - 1] = totalTime - 0.001;
StartTimeCodes.Add(totalTime);
EndTimeCodes.Add(totalTime + 2.5);
}
Ssts.Add(new SampleTimeInfo { SampleCount = sampleCount, SampleDelta = sampleDelta });
}
}
else
@ -104,11 +103,7 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
{
uint sampleCount = GetUInt(8 + i * 8);
uint sampleDelta = GetUInt(12 + i * 8);
totalTime += sampleDelta / (double)timeScale;
if (StartTimeCodes.Count <= EndTimeCodes.Count)
StartTimeCodes.Add(totalTime);
else
EndTimeCodes.Add(totalTime);
Ssts.Add(new SampleTimeInfo { SampleCount = sampleCount, SampleDelta = sampleDelta });
}
}
}
@ -133,8 +128,11 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
}
}
private void ReadText(Stream fs, ulong offset, string handlerType)
private void ReadText(Stream fs, ulong offset, string handlerType, int index)
{
if (handlerType == "vide")
return;
fs.Seek((long)offset, SeekOrigin.Begin);
var data = new byte[4];
fs.Read(data, 0, 2);
@ -152,6 +150,11 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
}
else
{
if (handlerType == "text" && index + 1 < SampleSizes.Count && SampleSizes[index + 1] <= 2)
{
return;
}
if (textSize == 0)
{
fs.Read(data, 2, 2);
@ -189,11 +192,41 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4.Boxes
}
Texts.Add(text.Replace(Environment.NewLine, "\n").Replace("\n", Environment.NewLine));
}
else
}
}
public List<Paragraph> GetParagraphs()
{
var paragraphs = new List<Paragraph>();
double totalTime = 0;
var allTimes = new List<double>();
// expand time codes
foreach (var timeInfo in Ssts)
{
for (var i = 0; i < timeInfo.SampleCount; i++)
{
Texts.Add(string.Empty);
totalTime += timeInfo.SampleDelta / (double)TimeScale;
allTimes.Add(totalTime);
}
}
var index = 0;
var textIndex = 0;
while (index < allTimes.Count - 1)
{
if (index > 0 && SampleSizes[index + 1] == 2)
index++;
var timeStart = allTimes[index];
var timeEnd = timeStart + 2;
if (index + 1 < allTimes.Count)
timeEnd = allTimes[index + 1];
if (Texts.Count > textIndex)
paragraphs.Add(new Paragraph(Texts[textIndex], timeStart * 1000.0, timeEnd * 1000.0));
index++;
textIndex++;
}
return paragraphs;
}
}

View File

@ -0,0 +1,8 @@
namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4
{
public class SampleTimeInfo
{
public uint SampleCount { get; set; }
public uint SampleDelta { get; set; }
}
}

View File

@ -67,6 +67,7 @@
<Compile Include="ContainerFormats\Matroska\MatroskaTrackInfo.cs" />
<Compile Include="ContainerFormats\Mp4\Boxes\Box.cs" />
<Compile Include="ContainerFormats\Mp4\Boxes\Moof.cs" />
<Compile Include="ContainerFormats\Mp4\SampleTimeInfo.cs" />
<Compile Include="ContainerFormats\Mp4\Boxes\Tfdt.cs" />
<Compile Include="ContainerFormats\Mp4\Boxes\Trun.cs" />
<Compile Include="ContainerFormats\Mp4\Boxes\Traf.cs" />

View File

@ -10968,27 +10968,7 @@ namespace Nikse.SubtitleEdit.Forms
}
else
{
for (int i = 0; i < mp4SubtitleTrack.Mdia.Minf.Stbl.EndTimeCodes.Count; i++)
{
if (mp4SubtitleTrack.Mdia.Minf.Stbl.Texts.Count > i)
{
var start = TimeSpan.FromSeconds(mp4SubtitleTrack.Mdia.Minf.Stbl.StartTimeCodes[i]);
var end = TimeSpan.FromSeconds(mp4SubtitleTrack.Mdia.Minf.Stbl.EndTimeCodes[i]);
string text = mp4SubtitleTrack.Mdia.Minf.Stbl.Texts[i];
var p = new Paragraph(text, start.TotalMilliseconds, end.TotalMilliseconds);
if (p.EndTime.TotalMilliseconds - p.StartTime.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds)
p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds;
if (mp4SubtitleTrack.Mdia.IsClosedCaption && string.IsNullOrEmpty(text))
{
// do not add empty lines
}
else
{
subtitle.Paragraphs.Add(p);
}
}
}
subtitle.Paragraphs.AddRange(mp4SubtitleTrack.Mdia.Minf.Stbl.GetParagraphs());
}
return subtitle;
}
@ -10998,12 +10978,13 @@ namespace Nikse.SubtitleEdit.Forms
if (mp4SubtitleTrack.Mdia.IsVobSubSubtitle)
{
var subPicturesWithTimeCodes = new List<VobSubOcr.SubPicturesWithSeparateTimeCodes>();
for (int i = 0; i < mp4SubtitleTrack.Mdia.Minf.Stbl.EndTimeCodes.Count; i++)
var paragraphs = mp4SubtitleTrack.Mdia.Minf.Stbl.GetParagraphs();
for (int i = 0; i < paragraphs.Count; i++)
{
if (mp4SubtitleTrack.Mdia.Minf.Stbl.SubPictures.Count > i)
{
var start = TimeSpan.FromSeconds(mp4SubtitleTrack.Mdia.Minf.Stbl.StartTimeCodes[i]);
var end = TimeSpan.FromSeconds(mp4SubtitleTrack.Mdia.Minf.Stbl.EndTimeCodes[i]);
var start = paragraphs[i].StartTime.TimeSpan;
var end = paragraphs[i].EndTime.TimeSpan;
subPicturesWithTimeCodes.Add(new VobSubOcr.SubPicturesWithSeparateTimeCodes(mp4SubtitleTrack.Mdia.Minf.Stbl.SubPictures[i], start, end));
}
}
@ -11040,27 +11021,7 @@ namespace Nikse.SubtitleEdit.Forms
_subtitleListViewIndex = -1;
FileNew();
for (int i = 0; i < mp4SubtitleTrack.Mdia.Minf.Stbl.EndTimeCodes.Count; i++)
{
if (mp4SubtitleTrack.Mdia.Minf.Stbl.Texts.Count > i)
{
var start = TimeSpan.FromSeconds(mp4SubtitleTrack.Mdia.Minf.Stbl.StartTimeCodes[i]);
var end = TimeSpan.FromSeconds(mp4SubtitleTrack.Mdia.Minf.Stbl.EndTimeCodes[i]);
string text = mp4SubtitleTrack.Mdia.Minf.Stbl.Texts[i];
var p = new Paragraph(text, start.TotalMilliseconds, end.TotalMilliseconds);
if (p.EndTime.TotalMilliseconds - p.StartTime.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds)
p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds;
if (mp4SubtitleTrack.Mdia.IsClosedCaption && string.IsNullOrEmpty(text))
{
// do not add empty lines
}
else
{
_subtitle.Paragraphs.Add(p);
}
}
}
_subtitle.Paragraphs.AddRange(mp4SubtitleTrack.Mdia.Minf.Stbl.GetParagraphs());
SetEncoding(Encoding.UTF8);
ShowStatus(_language.SubtitleImportedFromMatroskaFile);

View File

@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Nikse.SubtitleEdit.Core.ContainerFormats.Mp4;
using System.IO;
@ -15,9 +16,26 @@ namespace Test.Logic.Mp4
var parser = new MP4Parser(fileName);
var tracks = parser.GetSubtitleTracks();
var paragraphs = tracks[0].Mdia.Minf.Stbl.GetParagraphs();
//1
//00:00:01,000-- > 00:00:03,000
//Line 1
//2
//00:00:03,024-- > 00:00:05,024
//Line 2
Assert.IsTrue(tracks.Count == 1);
Assert.IsTrue(tracks[0].Mdia.Minf.Stbl.EndTimeCodes.Count == 2);
Assert.IsTrue(paragraphs.Count == 2);
Assert.IsTrue(Math.Abs(paragraphs[0].StartTime.TotalMilliseconds - 1000) < 0.01);
Assert.IsTrue(Math.Abs(paragraphs[0].EndTime.TotalMilliseconds - 3000) < 0.01);
Assert.IsTrue(paragraphs[0].Text == "Line 1");
Assert.IsTrue(Math.Abs(paragraphs[1].StartTime.TotalMilliseconds - 3024) < 0.01);
Assert.IsTrue(Math.Abs(paragraphs[1].EndTime.TotalMilliseconds - 5024) < 0.01);
Assert.IsTrue(paragraphs[1].Text == "Line 2");
}
}