Remove delays for bluray-sup/vobsub time codes (normally 45ms)

This commit is contained in:
Nikolaj Olsson 2018-02-06 20:34:15 +01:00
parent 214ee5aa15
commit 0fd141d2e0
9 changed files with 60 additions and 92 deletions

View File

@ -14,6 +14,7 @@
* limitations under the License.
*
* NOTE: Converted to C# and modified by Nikse.dk@gmail.com
* NOTE: For more info see http://blog.thescorpius.com/index.php/2017/07/15/presentation-graphic-stream-sup-files-bluray-subtitle-format/
*/
using System;
@ -43,11 +44,6 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
/// segment PTS time stamp
/// </summary>
public long PtsTimestamp;
/// <summary>
/// segment DTS time stamp
/// </summary>
public long DtsTimestamp;
}
public class PcsObject
@ -127,12 +123,11 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
bm.LockImage();
BluRaySupPalette pal = DecodePalette(palettes);
int index = 0;
int ofs = 0;
int xpos = 0;
var index = 0;
byte[] buf = data[0].Fragment.ImageBuffer;
index = 0;
do
{
int b = buf[index++] & 0xff;
@ -269,10 +264,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
for (int ioIndex = 0; ioIndex < PcsObjects.Count; ioIndex++)
{
var ioRect = new Rectangle(PcsObjects[ioIndex].Origin, BitmapObjects[ioIndex][0].Size);
if (r.IsEmpty)
r = ioRect;
else
r = Rectangle.Union(r, ioRect);
r = r.IsEmpty ? ioRect : Rectangle.Union(r, ioRect);
}
var mergedBmp = new Bitmap(r.Width, r.Height, PixelFormat.Format32bppArgb);
for (var ioIndex = 0; ioIndex < PcsObjects.Count; ioIndex++)
@ -287,8 +279,8 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
return mergedBmp;
}
public TimeCode StartTimeCode => new TimeCode((StartTime + 45) / 90.0);
public TimeCode EndTimeCode => new TimeCode((EndTime + 45) / 90.0);
public TimeCode StartTimeCode => new TimeCode(StartTime / 90.0);
public TimeCode EndTimeCode => new TimeCode(EndTime / 90.0);
}
public class PdsData
@ -362,7 +354,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
if (buffer[0] == 0x50 && buffer[1] == 0x47) // 80 + 71 - P G
{
segment.PtsTimestamp = BigEndianInt32(buffer, 2); // read PTS
segment.DtsTimestamp = BigEndianInt32(buffer, 6); // read PTS
//segment.DtsTimestamp = BigEndianInt32(buffer, 6); // read DTS - not used
segment.Type = buffer[10];
segment.Size = BigEndianInt16(buffer, 11);
}
@ -375,9 +367,11 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
private static SupSegment ParseSegmentHeaderFromMatroska(byte[] buffer)
{
var segment = new SupSegment();
segment.Type = buffer[0];
segment.Size = BigEndianInt16(buffer, 1);
var segment = new SupSegment
{
Type = buffer[0],
Size = BigEndianInt16(buffer, 1)
};
return segment;
}
@ -385,6 +379,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
/// Parse an PCS packet which contains width/height info
/// </summary>
/// <param name="buffer">Raw data buffer, starting right after segment</param>
/// <param name="offset">Buffer offset</param>
private static PcsObject ParsePcs(byte[] buffer, int offset)
{
var pcs = new PcsObject();
@ -439,7 +434,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
private static bool CompletePcs(PcsData pcs, Dictionary<int, List<OdsData>> bitmapObjects, Dictionary<int, List<PaletteInfo>> palettes)
{
if (pcs == null || pcs.PcsObjects == null || palettes == null)
if (pcs?.PcsObjects == null || palettes == null)
return false;
if (pcs.PcsObjects.Count == 0)
@ -472,8 +467,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
// 8bit palette version number (incremented for each palette change)
int paletteUpdate = buffer[1];
var p = new PaletteInfo();
p.PaletteSize = (segment.Size - 2) / 5;
var p = new PaletteInfo { PaletteSize = (segment.Size - 2) / 5 };
if (p.PaletteSize <= 0)
return new PdsData { Message = "Empty palette" };
@ -495,6 +489,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
/// </summary>
/// <param name="buffer">raw byte date, starting right after segment</param>
/// <param name="segment">object containing info about the current segment</param>
/// <param name="forceFirst"></param>
/// <returns>true if this is a valid new object (neither invalid nor a fragment)</returns>
private static OdsData ParseOds(byte[] buffer, SupSegment segment, bool forceFirst)
{
@ -502,7 +497,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
int objVer = buffer[2]; // 16bit object_id nikse - index 2 or 1???
int objSeq = buffer[3]; // 8bit first_in_sequence (0x80),
// last_in_sequence (0x40), 6bits reserved
bool first = ((objSeq & 0x80) == 0x80) || forceFirst;
bool first = (objSeq & 0x80) == 0x80 || forceFirst;
bool last = (objSeq & 0x40) == 0x40;
var info = new ImageObjectFragment();
@ -550,13 +545,8 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
bool forceFirstOds = true;
var bitmapObjects = new Dictionary<int, List<OdsData>>();
PcsData latestPcs = null;
int latestCompNum = -1;
var pcsList = new List<PcsData>();
byte[] headerBuffer;
if (fromMatroskaFile)
headerBuffer = new byte[3];
else
headerBuffer = new byte[HeaderSize];
var headerBuffer = fromMatroskaFile ? new byte[3] : new byte[HeaderSize];
while (position < ms.Length)
{
@ -564,11 +554,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
// Read segment header
ms.Read(headerBuffer, 0, headerBuffer.Length);
SupSegment segment;
if (fromMatroskaFile)
segment = ParseSegmentHeaderFromMatroska(headerBuffer);
else
segment = ParseSegmentHeader(headerBuffer, log);
var segment = fromMatroskaFile ? ParseSegmentHeaderFromMatroska(headerBuffer) : ParseSegmentHeader(headerBuffer, log);
position += headerBuffer.Length;
// Read segment data
@ -581,7 +567,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
case 0x14: // Palette
if (latestPcs != null)
{
log.AppendLine(string.Format("0x14 - Palette - PDS offset={0} size={1}", position, segment.Size));
log.AppendLine($"0x14 - Palette - PDS offset={position} size={segment.Size}");
PdsData pds = ParsePds(buffer, segment);
log.AppendLine(pds.Message);
if (pds.PaletteInfo != null)
@ -609,7 +595,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
case 0x15: // Image bitmap data
if (latestPcs != null)
{
log.AppendLine(string.Format("0x15 - Bitmap data - ODS offset={0} size={1}", position, segment.Size));
log.AppendLine($"0x15 - Bitmap data - ODS offset={position} size={segment.Size}");
OdsData ods = ParseOds(buffer, segment, forceFirstOds);
log.AppendLine(ods.Message);
if (!latestPcs.PaletteUpdate)
@ -617,8 +603,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
List<OdsData> odsList;
if (ods.IsFirst)
{
odsList = new List<OdsData>();
odsList.Add(ods);
odsList = new List<OdsData> { ods };
bitmapObjects[ods.ObjectId] = odsList;
}
else
@ -629,13 +614,13 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
}
else
{
log.AppendLine(string.Format("INVALID ObjectId {0} in ODS, offset={1}", ods.ObjectId, position));
log.AppendLine($"INVALID ObjectId {ods.ObjectId} in ODS, offset={position}");
}
}
}
else
{
log.AppendLine(string.Format("Bitmap Data Ignore due to PaletteUpdate offset={0}", position));
log.AppendLine($"Bitmap Data Ignore due to PaletteUpdate offset={position}");
}
forceFirstOds = false;
}
@ -648,15 +633,13 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
{
pcsList.Add(latestPcs);
}
latestPcs = null;
}
log.AppendLine(string.Format("0x16 - Picture codes, offset={0} size={1}", position, segment.Size));
log.AppendLine($"0x16 - Picture codes, offset={position} size={segment.Size}");
forceFirstOds = true;
PcsData nextPcs = ParsePicture(buffer, segment);
log.AppendLine(nextPcs.Message);
latestPcs = nextPcs;
latestCompNum = nextPcs.CompNum;
if (latestPcs.CompositionState == CompositionState.EpochStart)
{
bitmapObjects.Clear();
@ -667,7 +650,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
case 0x17: // Window display
if (latestPcs != null)
{
log.AppendLine(string.Format("0x17 - Window display offset={0} size={1}", position, segment.Size));
log.AppendLine($"0x17 - Window display offset={position} size={segment.Size}");
int windowCount = buffer[0];
int offset = 0;
for (int nextWindow = 0; nextWindow < windowCount; nextWindow++)
@ -686,7 +669,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
case 0x80:
forceFirstOds = true;
log.AppendLine(string.Format("0x80 - END offset={0} size={1}", position, segment.Size));
log.AppendLine($"0x80 - END offset={position} size={segment.Size}");
if (latestPcs != null)
{
if (CompletePcs(latestPcs, bitmapObjects, palettes))
@ -698,7 +681,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
break;
default:
log.AppendLine(string.Format("0x?? - END offset={0} UNKOWN SEGMENT TYPE={1}", position, segment.Type));
log.AppendLine($"0x?? - END offset={position} UNKOWN SEGMENT TYPE={segment.Type}");
break;
}
position += segment.Size;
@ -709,7 +692,6 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
{
if (CompletePcs(latestPcs, bitmapObjects, palettes))
pcsList.Add(latestPcs);
latestPcs = null;
}
for (int pcsIndex = 1; pcsIndex < pcsList.Count; pcsIndex++)

View File

@ -14,6 +14,7 @@
* limitations under the License.
*
* NOTE: Converted to C# and modified by Nikse.dk@gmail.com
* NOTE: For more info see http://blog.thescorpius.com/index.php/2017/07/15/presentation-graphic-stream-sup-files-bluray-subtitle-format/
*/
using System;
@ -40,20 +41,14 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
/// </summary>
public long StartTime { get; set; }
public int StartTimeForWrite
{
get { return (int)((StartTime - 45) * 90.0); }
}
public int StartTimeForWrite => (int)(StartTime * 90.0);
/// <summary>
/// end time in milliseconds
/// </summary>
public long EndTime { get; set; }
public int EndTimeForWrite
{
get { return (int)((EndTime - 45) * 90.0); }
}
public int EndTimeForWrite => (int)(EndTime * 90.0);
/// <summary>
/// if true, this is a forced subtitle
@ -506,7 +501,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
if (bufSize > 0xffe4)
bufSize = 0xffe4;
packetHeader[10] = 0x15; // ID
timestamp = dts + imageDecodeTime;
timestamp = 0; //dts + imageDecodeTime;
ToolBox.SetDWord(packetHeader, 2, timestamp); // PTS
ToolBox.SetDWord(packetHeader, 6, dts); // DTS
ToolBox.SetWord(packetHeader, 11, headerOdsFirst.Length + bufSize); // size

View File

@ -30,7 +30,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
var sb = new StringBuilder();
for (int i = index; i < index + digits; i++)
{
string s = string.Format("{0:X}", buffer[i]);
string s = $"{buffer[i]:X}";
if (s.Length < 2)
sb.Append('0');
sb.Append(s);
@ -43,7 +43,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
/// </summary>
public static string ToHex(int number, int digits)
{
string s = string.Format("{0:X}", number);
string s = $"{number:X}";
if (s.Length < digits)
s = s.PadLeft(digits, '0');
return "0x" + s;
@ -54,7 +54,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
* @param ms Time in milliseconds
* @return Array containing hours, minutes, seconds and milliseconds (in this order)
*/
public static int[] MillisecondsToTime(long ms)
public static int[] MillisecondsToTime(double ms)
{
int[] time = new int[4];
// time[0] = hours
@ -77,8 +77,8 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
/// <returns>String in format hh:mm:ss:ms</returns>
public static string PtsToTimeString(long pts)
{
int[] time = MillisecondsToTime((pts + 45) / 90);
return string.Format(@"{0:D2}:{1:D2}:{2:D2}.{3:D3}", time[0], time[1], time[2], time[3]);
int[] time = MillisecondsToTime(pts / 90.0);
return $@"{time[0]:D2}:{time[1]:D2}:{time[2]:D2}.{time[3]:D3}";
}
/// <summary>
@ -92,7 +92,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
buffer[index] = (byte)(val >> 24);
buffer[index + 1] = (byte)(val >> 16);
buffer[index + 2] = (byte)(val >> 8);
buffer[index + 3] = (byte)(val);
buffer[index + 3] = (byte)val;
}
/// <summary>
@ -104,7 +104,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
public static void SetWord(byte[] buffer, int index, int val)
{
buffer[index] = (byte)(val >> 8);
buffer[index + 1] = (byte)(val);
buffer[index + 1] = (byte)val;
}
/// <summary>
@ -115,7 +115,7 @@ namespace Nikse.SubtitleEdit.Core.BluRaySup
/// <param name="val">Integer value of byte to write</param>
public static void SetByte(byte[] buffer, int index, int val)
{
buffer[index] = (byte)(val);
buffer[index] = (byte)val;
}
}

View File

@ -170,10 +170,7 @@ namespace Nikse.SubtitleEdit.Core.TransportStream
}
}
public bool IsDvbSubpicture
{
get { return SubPictureStreamId.HasValue && SubPictureStreamId.Value == 32; }
}
public bool IsDvbSubpicture => SubPictureStreamId.HasValue && SubPictureStreamId.Value == 32;
public int DataIdentifier
{
@ -385,7 +382,7 @@ namespace Nikse.SubtitleEdit.Core.TransportStream
public ulong PresentationTimestampToMilliseconds()
{
if (PresentationTimestamp.HasValue)
return (ulong)Math.Round((PresentationTimestamp.Value + 45.0) / 90.0);
return (ulong)Math.Round(PresentationTimestamp.Value / 90.0);
return 0;
}

View File

@ -224,7 +224,7 @@ namespace Nikse.SubtitleEdit.Core.TransportStream
ulong endMs = 0;
if (k < endMsList.Count)
endMs = endMsList[k];
subList.Add(new TransportStreamSubtitle(bdSup, startMs, endMs, (ulong)((FirstVideoPts + 45) / 90.0)));
subList.Add(new TransportStreamSubtitle(bdSup, startMs, endMs, (ulong)(FirstVideoPts / 90.0)));
}
DvbSubtitlesLookup.Add(pid, subList);
}
@ -262,7 +262,7 @@ namespace Nikse.SubtitleEdit.Core.TransportStream
// Merge packets and set start/end time
DvbSubtitlesLookup = new Dictionary<int, List<TransportStreamSubtitle>>();
var firstVideoMs = (ulong)((FirstVideoPts + 45) / 90.0);
var firstVideoMs = (ulong)(FirstVideoPts / 90.0);
foreach (int pid in SubtitlePacketIds)
{
var subtitles = new List<TransportStreamSubtitle>();

View File

@ -15,7 +15,7 @@ namespace Nikse.SubtitleEdit.Core.VobSub
public SpHeader(byte[] buffer)
{
Identifier = System.Text.Encoding.ASCII.GetString(buffer, 0, 2);
int startMilliseconds = (int)Helper.GetLittleEndian32(buffer, 2) / 90;
int startMilliseconds = (int)Math.Round(Helper.GetLittleEndian32(buffer, 2) / 90.0);
StartTime = TimeSpan.FromMilliseconds(startMilliseconds);
NextBlockPosition = Helper.GetEndianWord(buffer, 10) - 4;
ControlSequencePosition = Helper.GetEndianWord(buffer, 12) - 4;

View File

@ -24,12 +24,12 @@ namespace Nikse.SubtitleEdit.Core.VobSub
public readonly int SubPictureDateSize;
public TimeSpan Delay;
public int BufferSize { get { return _data.Length; } }
public int BufferSize => _data.Length;
private readonly byte[] _data;
public Rectangle ImageDisplayArea;
public bool Forced { get; private set; }
private int _pixelDataAddressOffset;
private int _startDisplayControlSequenceTableAddress;
private readonly int _pixelDataAddressOffset;
private readonly int _startDisplayControlSequenceTableAddress;
public SubPicture(byte[] data)
{
@ -105,14 +105,11 @@ namespace Nikse.SubtitleEdit.Core.VobSub
commandIndex++;
break;
case (int)DisplayControlCommand.StopDisplay: // 2
Delay = TimeSpan.FromMilliseconds(((delayBeforeExecute << 10) + 1023) / 90.0);
Delay = TimeSpan.FromMilliseconds((delayBeforeExecute << 10) / 90.0);
if (createBitmap && Delay.TotalMilliseconds > largestDelay) // in case of more than one images, just use the one with the largest display time
{
largestDelay = Delay.TotalMilliseconds;
if (bmp != null)
{
bmp.Dispose();
}
bmp?.Dispose();
bmp = GenerateBitmap(ImageDisplayArea, imageTopFieldDataAddress, imageBottomFieldDataAddress, fourColors);
bitmapGenerated = true;
}

View File

@ -104,10 +104,7 @@ namespace Nikse.SubtitleEdit.Core.VobSub
WriteEndianWord(startDisplayControlSequenceTableAddress + 24, ms); // start of display control sequence table address
// Control command start
if (p.Forced)
ms.WriteByte(0); // ForcedStartDisplay==0
else
ms.WriteByte(1); // StartDisplay==1
ms.WriteByte(p.Forced ? (byte)0 : (byte)1);
// Control command 3 = SetColor
WriteColors(ms); // 3 bytes
@ -126,7 +123,8 @@ namespace Nikse.SubtitleEdit.Core.VobSub
// Control Sequence Table
// Write delay - subtitle duration
WriteEndianWord(Convert.ToInt32(p.Duration.TotalMilliseconds * 90.0 - 1023) >> 10, ms);
WriteEndianWord(Convert.ToInt32(p.Duration.TotalMilliseconds * 90.0) >> 10, ms);
// WriteEndianWord(Convert.ToInt32(p.Duration.TotalMilliseconds * 90.0 - 1023) >> 10, ms);
// next display control sequence table address (use current is last)
WriteEndianWord(startDisplayControlSequenceTableAddress + 24, ms); // start of display control sequence table address
@ -143,7 +141,7 @@ namespace Nikse.SubtitleEdit.Core.VobSub
public void WriteParagraph(Paragraph p, Bitmap bmp, ContentAlignment alignment, Point? overridePosition = null) // inspired by code from SubtitleCreator
{
// timestamp: 00:00:33:900, filepos: 000000000
_idx.AppendLine(string.Format("timestamp: {0:00}:{1:00}:{2:00}:{3:000}, filepos: {4}", p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds, _subFile.Position.ToString("X").PadLeft(9, '0').ToLower()));
_idx.AppendLine($"timestamp: {p.StartTime.Hours:00}:{p.StartTime.Minutes:00}:{p.StartTime.Seconds:00}:{p.StartTime.Milliseconds:000}, filepos: {_subFile.Position.ToString("X").PadLeft(9, '0').ToLower()}");
var nbmp = new NikseBitmap(bmp);
_emphasis2 = nbmp.ConverToFourColors(_background, _pattern, _emphasis1, _useInnerAntialiasing);
@ -203,12 +201,12 @@ namespace Nikse.SubtitleEdit.Core.VobSub
subHeader[27] = (byte)((ts[0] & 0x7f) << 1 | 0x01);
const string pre = "0010"; // 0011 or 0010 ? (KMPlayer will not understand 0011!!!)
long newPts = (long)(p.StartTime.TotalSeconds * 90000.0 + 0.5);
long newPts = (long)(p.StartTime.TotalSeconds * 90000.0);
string bString = Convert.ToString(newPts, 2).PadLeft(33, '0');
string fiveBytesString = pre + bString.Substring(0, 3) + "1" + bString.Substring(3, 15) + "1" + bString.Substring(18, 15) + "1";
for (int i = 0; i < 5; i++)
{
subHeader[23 + i] = Convert.ToByte(fiveBytesString.Substring((i * 8), 8), 2);
subHeader[23 + i] = Convert.ToByte(fiveBytesString.Substring(i * 8, 8), 2);
}
subHeader[28] = vobSubId;
headerSize = 29;

View File

@ -1191,8 +1191,8 @@ namespace Nikse.SubtitleEdit.Forms.Ocr
{
_bluRaySubtitles.Add(x);
Paragraph p = new Paragraph();
p.StartTime = new TimeCode((x.StartTime + 45) / 90.0);
p.EndTime = new TimeCode((x.EndTime + 45) / 90.0);
p.StartTime = new TimeCode(x.StartTime / 90.0);
p.EndTime = new TimeCode(x.EndTime / 90.0);
_subtitle.Paragraphs.Add(p);
}
}
@ -1612,8 +1612,8 @@ namespace Nikse.SubtitleEdit.Forms.Ocr
else if (_bluRaySubtitlesOriginal != null)
{
var item = _bluRaySubtitles[index];
start = new TimeCode((item.StartTime + 45) / 90.0);
end = new TimeCode((item.EndTime + 45) / 90.0);
start = new TimeCode(item.StartTime / 90.0);
end = new TimeCode(item.EndTime / 90.0);
}
else if (_xSubList != null)
{
@ -1701,8 +1701,7 @@ namespace Nikse.SubtitleEdit.Forms.Ocr
Bitmap old = pictureBoxSubtitleImage.Image as Bitmap;
pictureBoxSubtitleImage.Image = bmp.Clone() as Bitmap;
pictureBoxSubtitleImage.Invalidate();
if (old != null)
old.Dispose();
old?.Dispose();
}
catch
{