Improve handling of zlib compression in Matroska - thx mkver

Fix #2566
This commit is contained in:
Nikolaj Olsson 2017-09-10 20:50:28 +02:00
parent f4f9a08de3
commit 6ed048ff79
11 changed files with 148 additions and 107 deletions

View File

@ -29,6 +29,8 @@
* Change some combo boxes to allow for better searching
* Make "sort by" more consistant - thx Skrity
* Add "Bridge gaps" (in durations) to "Batch convert"
* Minor improvements for "Binary image compare" OCR - thx Boulder
* Make "Status log" window non-modal - thx Skrity
* FIXED:
* Fix memory leak regarding "mpv" + subtitle change - thx darnn
* Fix missing space in format WebVTT
@ -56,6 +58,7 @@
* Fix "Translation mode" Shortcut not working - thx OmrSi/darnn
* Fix waveform performance for long texts - thx Skrity
* Improve handling of advanced ASS tags - thx Skrity
* Improve handling of zlib compression in Matroska - thx mkver
3.5.3 (12th May 2017)

View File

@ -157,6 +157,7 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska
string language = "eng"; // default value
string codecId = string.Empty;
string codecPrivate = string.Empty;
byte[] codecPrivateRaw = null;
//var biCompression = string.Empty;
int contentCompressionAlgorithm = -1;
int contentEncodingType = -1;
@ -203,7 +204,9 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska
}
break;
case ElementId.CodecPrivate:
codecPrivate = ReadString((int)element.DataSize, Encoding.UTF8);
codecPrivateRaw = new byte[element.DataSize];
_stream.Read(codecPrivateRaw, 0, codecPrivateRaw.Length);
codecPrivate = Encoding.UTF8.GetString(codecPrivateRaw);
//if (codecPrivate.Length > 20)
// biCompression = codecPrivate.Substring(16, 4);
break;
@ -230,6 +233,7 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska
Language = language,
CodecId = codecId,
CodecPrivate = codecPrivate,
CodecPrivateRaw = codecPrivateRaw,
Name = name,
ContentEncodingType = contentEncodingType,
ContentCompressionAlgorithm = contentCompressionAlgorithm

View File

@ -1,10 +1,11 @@
using System;
using System.IO;
namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska
{
public class MatroskaSubtitle
{
public byte[] Data { get; set; }
internal byte[] Data { get; set; }
public long Start { get; set; }
public long Duration { get; set; }
@ -15,6 +16,38 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska
Duration = duration;
}
/// <summary>
/// Get data, if contentEncodingType == 0, then data is compressed with zlib
/// </summary>
/// <param name="matroskaTrackInfo"></param>
/// <returns>Data byte array (uncompressed)</returns>
public byte[] GetData(MatroskaTrackInfo matroskaTrackInfo)
{
if (matroskaTrackInfo.ContentEncodingType != MatroskaTrackInfo.ContentEncodingTypeCompression)
{
return Data;
}
var outStream = new MemoryStream();
var outZStream = new zlib.ZOutputStream(outStream);
var inStream = new MemoryStream(Data);
byte[] buffer;
try
{
MatroskaTrackInfo.CopyStream(inStream, outZStream);
//inStream.CopyTo(outZStream);
buffer = new byte[outZStream.TotalOut];
outStream.Position = 0;
outStream.Read(buffer, 0, buffer.Length);
}
finally
{
outZStream.Close();
inStream.Close();
}
return buffer;
}
public MatroskaSubtitle(byte[] data, long start)
: this(data, start, 0)
{
@ -28,14 +61,12 @@ namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska
}
}
public string Text
public string GetText(MatroskaTrackInfo matroskaTrackInfo)
{
get
{
if (Data != null)
return System.Text.Encoding.UTF8.GetString(Data).Replace("\\N", Environment.NewLine);
return string.Empty;
}
var data = GetData(matroskaTrackInfo);
if (data != null)
return System.Text.Encoding.UTF8.GetString(data).Replace("\\N", Environment.NewLine);
return string.Empty;
}
}
}

View File

@ -1,18 +1,72 @@
namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska
using System.IO;
using System.Text;
namespace Nikse.SubtitleEdit.Core.ContainerFormats.Matroska
{
public class MatroskaTrackInfo
{
public const int ContentEncodingTypeCompression = 0;
public int TrackNumber { get; set; }
public string Uid { get; set; }
public bool IsVideo { get; set; }
public bool IsAudio { get; set; }
public bool IsSubtitle { get; set; }
public string CodecId { get; set; }
public string CodecPrivate { get; set; }
internal string CodecPrivate { get; set; }
internal byte[] CodecPrivateRaw { get; set; }
public int DefaultDuration { get; set; }
public string Language { get; set; }
public string Name { get; set; }
/// <summary>
/// 0 = zlib
/// 1 = bzlib
/// 2 = lzo1x
/// 3 = Header Stripping
/// </summary>
public int ContentCompressionAlgorithm { get; set; }
public int ContentEncodingType { get; set; }
internal static void CopyStream(Stream input, Stream output)
{
var buffer = new byte[128 * 1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
output.Flush();
}
public string GetCodecPrivate()
{
if (ContentEncodingType != ContentEncodingTypeCompression || CodecPrivateRaw == null || CodecPrivateRaw.Length < 3)
{
return CodecPrivate;
}
var outStream = new MemoryStream();
var outZStream = new zlib.ZOutputStream(outStream);
var inStream = new MemoryStream(CodecPrivateRaw);
try
{
//inStream.CopyTo(outZStream);
CopyStream(inStream, outZStream);
var buffer = new byte[outZStream.TotalOut];
outStream.Position = 0;
outStream.Read(buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
catch
{
return CodecPrivate;
}
finally
{
outZStream.Close();
inStream.Close();
}
}
}
}
}

View File

@ -35,6 +35,9 @@
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml" />
<Reference Include="zlib.net, Version=1.0.3.0, Culture=neutral, PublicKeyToken=47d7877cb3620160">
<HintPath>..\src\packages\zlib.net.1.0.4.0\lib\zlib.net.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="BluRaySup\BluRaySupPalette.cs" />
@ -556,6 +559,7 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Resources\NetflixAllowedGlyphs.bin.gz" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -2114,9 +2114,10 @@ namespace Nikse.SubtitleEdit.Core
var isSsa = false;
SubtitleFormat format = new SubRip();
if (matroskaSubtitleInfo.CodecPrivate.Contains("[script info]", StringComparison.OrdinalIgnoreCase))
var codecPrivate = matroskaSubtitleInfo.GetCodecPrivate();
if (codecPrivate.Contains("[script info]", StringComparison.OrdinalIgnoreCase))
{
if (matroskaSubtitleInfo.CodecPrivate.Contains("[V4 Styles]", StringComparison.OrdinalIgnoreCase))
if (codecPrivate.Contains("[V4 Styles]", StringComparison.OrdinalIgnoreCase))
format = new SubStationAlpha();
else
format = new AdvancedSubStationAlpha();
@ -2130,13 +2131,13 @@ namespace Nikse.SubtitleEdit.Core
subtitle.Paragraphs.Add(p);
}
if (!string.IsNullOrEmpty(matroskaSubtitleInfo.CodecPrivate))
if (!string.IsNullOrEmpty(codecPrivate))
{
bool eventsStarted = false;
bool fontsStarted = false;
bool graphicsStarted = false;
var header = new StringBuilder();
foreach (string line in matroskaSubtitleInfo.CodecPrivate.Replace(Environment.NewLine, "\n").Split('\n'))
foreach (string line in codecPrivate.Replace(Environment.NewLine, "\n").Split('\n'))
{
if (!eventsStarted && !fontsStarted && !graphicsStarted)
{
@ -2179,7 +2180,7 @@ namespace Nikse.SubtitleEdit.Core
{
foreach (var p in sub)
{
subtitle.Paragraphs.Add(new Paragraph(p.Text, p.Start, p.End));
subtitle.Paragraphs.Add(new Paragraph(p.GetText(matroskaSubtitleInfo), p.Start, p.End));
}
}
subtitle.Renumber();
@ -2188,13 +2189,14 @@ namespace Nikse.SubtitleEdit.Core
public static Subtitle LoadMatroskaSSA(MatroskaTrackInfo matroskaSubtitleInfo, string fileName, SubtitleFormat format, List<MatroskaSubtitle> sub)
{
var subtitle = new Subtitle { Header = matroskaSubtitleInfo.CodecPrivate };
var codecPrivate = matroskaSubtitleInfo.GetCodecPrivate();
var subtitle = new Subtitle { Header = codecPrivate };
var lines = new List<string>();
foreach (string l in subtitle.Header.Trim().SplitToLines())
lines.Add(l);
var footer = new StringBuilder();
var comments = new Subtitle();
if (!string.IsNullOrEmpty(matroskaSubtitleInfo.CodecPrivate))
if (!string.IsNullOrEmpty(codecPrivate))
{
bool footerOn = false;
char[] splitChars = { ':', '.' };
@ -2283,7 +2285,7 @@ namespace Nikse.SubtitleEdit.Core
comments.Paragraphs.RemoveAt(commentIndex);
}
string text = mp.Text.Replace(Environment.NewLine, "\\N");
string text = mp.GetText(matroskaSubtitleInfo).Replace(Environment.NewLine, "\\N");
int idx = text.IndexOf(',') + 1;
if (idx > 0 && idx < text.Length)
{

4
libse/packages.config Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="zlib.net" version="1.0.4.0" targetFramework="net40" />
</packages>

View File

@ -330,7 +330,7 @@ namespace Nikse.SubtitleEdit.Forms
try
{
InitializeComponent();
Icon = Properties.Resources.SubtitleEditFormIcon;
Icon = Properties.Resources.SubtitleEditFormIcon;
textBoxListViewTextAlternate.Visible = false;
labelAlternateText.Visible = false;
@ -9770,9 +9770,10 @@ namespace Nikse.SubtitleEdit.Forms
}
SubtitleFormat format;
if (matroskaSubtitleInfo.CodecPrivate.Contains("[script info]", StringComparison.OrdinalIgnoreCase))
var codecPrivate = matroskaSubtitleInfo.GetCodecPrivate();
if (codecPrivate.Contains("[script info]", StringComparison.OrdinalIgnoreCase))
{
if (matroskaSubtitleInfo.CodecPrivate.Contains("[V4 Styles]", StringComparison.OrdinalIgnoreCase))
if (codecPrivate.Contains("[V4 Styles]", StringComparison.OrdinalIgnoreCase))
format = new SubStationAlpha();
else
format = new AdvancedSubStationAlpha();
@ -9796,7 +9797,7 @@ namespace Nikse.SubtitleEdit.Forms
{
foreach (var p in sub)
{
subtitle.Paragraphs.Add(new Paragraph(p.Text, p.Start, p.End));
subtitle.Paragraphs.Add(new Paragraph(p.GetText(matroskaSubtitleInfo), p.Start, p.End));
}
}
return subtitle;
@ -9844,7 +9845,7 @@ namespace Nikse.SubtitleEdit.Forms
var format = Utilities.LoadMatroskaTextSubtitle(matroskaSubtitleInfo, matroska, sub, _subtitle);
if (matroskaSubtitleInfo.CodecPrivate.Contains("[script info]", StringComparison.OrdinalIgnoreCase))
if (matroskaSubtitleInfo.GetCodecPrivate().Contains("[script info]", StringComparison.OrdinalIgnoreCase))
{
if (_networkSession == null)
{
@ -9908,9 +9909,10 @@ namespace Nikse.SubtitleEdit.Forms
{
var msub = sub[index];
int idx = -6; // MakeMKV starts at DialogPresentationSegment
if (VobSubParser.IsPrivateStream2(msub.Data, 0))
var data = msub.GetData(matroskaSubtitleInfo);
if (VobSubParser.IsPrivateStream2(data, 0))
idx = 0; // starts with MPEG2 private stream 2 (just to be sure)
var dps = new TextST.DialogPresentationSegment(msub.Data, idx);
var dps = new TextST.DialogPresentationSegment(data, idx);
_subtitle.Paragraphs[index].Text = dps.Text;
}
catch (Exception exception)
@ -9975,26 +9977,27 @@ namespace Nikse.SubtitleEdit.Forms
{
var msub = sub[index];
DvbSubPes pes = null;
if (msub.Data.Length > 9 && msub.Data[0] == 15 && msub.Data[1] >= SubtitleSegment.PageCompositionSegment && msub.Data[1] <= SubtitleSegment.DisplayDefinitionSegment) // sync byte + segment id
var data = msub.GetData(matroskaSubtitleInfo);
if (data != null && data.Length > 9 && data[0] == 15 && data[1] >= SubtitleSegment.PageCompositionSegment && data[1] <= SubtitleSegment.DisplayDefinitionSegment) // sync byte + segment id
{
var buffer = new byte[msub.Data.Length + 3];
Buffer.BlockCopy(msub.Data, 0, buffer, 2, msub.Data.Length);
var buffer = new byte[data.Length + 3];
Buffer.BlockCopy(data, 0, buffer, 2, data.Length);
buffer[0] = 32;
buffer[1] = 0;
buffer[buffer.Length - 1] = 255;
pes = new DvbSubPes(0, buffer);
}
else if (VobSubParser.IsMpeg2PackHeader(msub.Data))
else if (VobSubParser.IsMpeg2PackHeader(data))
{
pes = new DvbSubPes(msub.Data, Mpeg2Header.Length);
pes = new DvbSubPes(data, Mpeg2Header.Length);
}
else if (VobSubParser.IsPrivateStream1(msub.Data, 0))
else if (VobSubParser.IsPrivateStream1(data, 0))
{
pes = new DvbSubPes(msub.Data, 0);
pes = new DvbSubPes(data, 0);
}
else if (msub.Data.Length > 9 && msub.Data[0] == 32 && msub.Data[1] == 0 && msub.Data[2] == 14 && msub.Data[3] == 16)
else if (data.Length > 9 && data[0] == 32 && data[1] == 0 && data[2] == 14 && data[3] == 16)
{
pes = new DvbSubPes(0, msub.Data);
pes = new DvbSubPes(0, data);
}
if (pes == null && subtitle.Paragraphs.Count > 0)
@ -10104,41 +10107,10 @@ namespace Nikse.SubtitleEdit.Forms
_subtitle.Paragraphs.Clear();
List<VobSubMergedPack> mergedVobSubPacks = new List<VobSubMergedPack>();
var idx = new Core.VobSub.Idx(matroskaSubtitleInfo.CodecPrivate.SplitToLines());
var idx = new Core.VobSub.Idx(matroskaSubtitleInfo.GetCodecPrivate().SplitToLines());
foreach (var p in sub)
{
if (matroskaSubtitleInfo.ContentEncodingType == 0) // compressed with zlib
{
bool error = false;
var outStream = new MemoryStream();
var outZStream = new zlib.ZOutputStream(outStream);
var inStream = new MemoryStream(p.Data);
byte[] buffer = null;
try
{
CopyStream(inStream, outZStream);
buffer = new byte[outZStream.TotalOut];
outStream.Position = 0;
outStream.Read(buffer, 0, buffer.Length);
}
catch (Exception exception)
{
MessageBox.Show(exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
error = true;
}
finally
{
outZStream.Close();
inStream.Close();
}
if (!error && buffer.Length > 2)
mergedVobSubPacks.Add(new VobSubMergedPack(buffer, TimeSpan.FromMilliseconds(p.Start), 32, null));
}
else
{
mergedVobSubPacks.Add(new VobSubMergedPack(p.Data, TimeSpan.FromMilliseconds(p.Start), 32, null));
}
mergedVobSubPacks.Add(new VobSubMergedPack(p.GetData(matroskaSubtitleInfo), TimeSpan.FromMilliseconds(p.Start), 32, null));
if (mergedVobSubPacks.Count > 0)
mergedVobSubPacks[mergedVobSubPacks.Count - 1].EndTime = TimeSpan.FromMilliseconds(p.End);
@ -10205,35 +10177,7 @@ namespace Nikse.SubtitleEdit.Forms
var clusterStream = new MemoryStream();
foreach (var p in sub)
{
byte[] buffer = null;
if (matroskaSubtitleInfo.ContentEncodingType == 0) // compressed with zlib
{
var outStream = new MemoryStream();
var outZStream = new zlib.ZOutputStream(outStream);
var inStream = new MemoryStream(p.Data);
try
{
CopyStream(inStream, outZStream);
buffer = new byte[outZStream.TotalOut];
outStream.Position = 0;
outStream.Read(buffer, 0, buffer.Length);
}
catch (Exception exception)
{
var tc = new TimeCode(p.Start);
lastError = tc + ": " + exception.Message + ": " + exception.StackTrace;
noOfErrors++;
}
finally
{
outZStream.Close();
inStream.Close();
}
}
else
{
buffer = p.Data;
}
byte[] buffer = p.GetData(matroskaSubtitleInfo);
if (buffer != null && buffer.Length > 2)
{
clusterStream.Write(buffer, 0, buffer.Length);
@ -19549,10 +19493,9 @@ namespace Nikse.SubtitleEdit.Forms
MainResize();
TextBoxListViewTextTextChanged(null, null);
textBoxListViewTextAlternate_TextChanged(null, null);
if(focusedItem != null)
if (focusedItem != null)
{
SubtitleListview1.FocusedItem = focusedItem;
SubtitleListview1.SelectIndexAndEnsureVisible(SubtitleListview1.FocusedItem.Index);
SubtitleListview1.SelectIndexAndEnsureVisible(focusedItem.Index, true);
}
}

View File

@ -66,9 +66,6 @@
<Reference Include="System.Web.Services" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="zlib.net">
<HintPath>packages\zlib.net.1.0.4.0\lib\zlib.net.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Controls\SETextBox.cs">

View File

@ -50,8 +50,8 @@ namespace Test.Logic.VideoFormats
var tracks = parser.GetTracks(true);
var subtitles = parser.GetSubtitle(Convert.ToInt32(tracks[0].TrackNumber), null);
Assert.IsTrue(subtitles.Count == 2);
Assert.IsTrue(subtitles[0].Text == "Line 1");
Assert.IsTrue(subtitles[1].Text == "Line 2");
Assert.IsTrue(subtitles[0].GetText(tracks[0]) == "Line 1");
Assert.IsTrue(subtitles[1].GetText(tracks[1]) == "Line 2");
}
}

View File

@ -2,5 +2,4 @@
<packages>
<package id="ILRepack" version="2.0.13" targetFramework="net40-client" />
<package id="NHunspell" version="1.2.5554.16953" targetFramework="net40-Client" />
<package id="zlib.net" version="1.0.4.0" targetFramework="net40-Client" />
</packages>