mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-11-26 05:02:36 +01:00
Fix (some) demuxed mp4 time codes
This commit is contained in:
parent
871469ee9b
commit
25902a30d1
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
8
libse/ContainerFormats/Mp4/SampleTimeInfo.cs
Normal file
8
libse/ContainerFormats/Mp4/SampleTimeInfo.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Nikse.SubtitleEdit.Core.ContainerFormats.Mp4
|
||||
{
|
||||
public class SampleTimeInfo
|
||||
{
|
||||
public uint SampleCount { get; set; }
|
||||
public uint SampleDelta { get; set; }
|
||||
}
|
||||
}
|
@ -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" />
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user