mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-11-22 11:12:36 +01:00
618 lines
28 KiB
C#
618 lines
28 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace Nikse.SubtitleEdit.Core
|
|
{
|
|
public class IfoParser : IDisposable
|
|
{
|
|
public struct AudioStream
|
|
{
|
|
public int LanguageTypeSpecified;
|
|
public string Language;
|
|
public string LanguageCode;
|
|
public string CodingMode;
|
|
public int Channels;
|
|
public string Extension;
|
|
};
|
|
|
|
public struct VideoStream
|
|
{
|
|
public string Aspect;
|
|
public string Standard;
|
|
public string CodingMode;
|
|
public string Resolution;
|
|
}
|
|
|
|
public class VtsVobs
|
|
{
|
|
public int NumberOfAudioStreams;
|
|
public int NumberOfSubtitles;
|
|
public VideoStream VideoStream;
|
|
public List<AudioStream> AudioStreams;
|
|
public List<string> Subtitles;
|
|
public List<string> SubtitleIDs;
|
|
public List<string> SubtitleTypes;
|
|
|
|
public List<string> GetAllLanguages()
|
|
{
|
|
var list = new List<string>();
|
|
for (int i = 0; i < Subtitles.Count; i++)
|
|
{
|
|
if (i < SubtitleIDs.Count && i < SubtitleTypes.Count)
|
|
{
|
|
var ids = SubtitleIDs[i].Split(',');
|
|
var types = SubtitleTypes[i].Split(',');
|
|
if (ids.Length == 2 && ids[0].Trim() == ids[1].Trim() || ids.Length == 3 && ids[0].Trim() == ids[1].Trim() && ids[1].Trim() == ids[2].Trim())
|
|
{
|
|
list.Add(Subtitles[i] + " (" + ids[0].Trim() + ")");
|
|
}
|
|
else
|
|
{
|
|
if (ids.Length >= 1 && types.Length >= 1)
|
|
{
|
|
list.Add(Subtitles[i] + ", " + types[0].Trim() + " (" + ids[0].Trim() + ")");
|
|
}
|
|
if (ids.Length >= 2 && types.Length >= 2)
|
|
{
|
|
list.Add(Subtitles[i] + ", " + types[1].Trim() + " (" + ids[1].Trim() + ")");
|
|
}
|
|
if (ids.Length >= 3 && types.Length >= 3)
|
|
{
|
|
list.Add(Subtitles[i] + ", " + types[2].Trim() + " (" + ids[2].Trim() + ")");
|
|
}
|
|
if (ids.Length >= 4 && types.Length >= 4)
|
|
{
|
|
list.Add(Subtitles[i] + ", " + types[3].Trim() + " (" + ids[3].Trim() + ")");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public VtsVobs()
|
|
{
|
|
VideoStream = new VideoStream();
|
|
AudioStreams = new List<AudioStream>();
|
|
Subtitles = new List<string>();
|
|
SubtitleIDs = new List<string>();
|
|
SubtitleTypes = new List<string>();
|
|
}
|
|
};
|
|
|
|
public class ProgramChain
|
|
{
|
|
public int NumberOfPgc;
|
|
public int NumberOfCells;
|
|
public string PlaybackTime;
|
|
public List<byte> PgcEntryCells;
|
|
public List<string> PgcPlaybackTimes;
|
|
public List<string> PgcStartTimes;
|
|
public List<char> AudioStreamsAvailable;
|
|
public List<byte[]> SubtitlesAvailable;
|
|
public List<Color> ColorLookupTable;
|
|
|
|
public ProgramChain()
|
|
{
|
|
PgcEntryCells = new List<byte>();
|
|
PgcPlaybackTimes = new List<string>();
|
|
PgcStartTimes = new List<string>();
|
|
AudioStreamsAvailable = new List<char>();
|
|
SubtitlesAvailable = new List<byte[]>();
|
|
ColorLookupTable = new List<Color>();
|
|
}
|
|
|
|
public bool Has43Subs { get; set; }
|
|
public bool HasWideSubs { get; set; }
|
|
public bool HasLetterSubs { get; set; }
|
|
public bool HasPanSubs { get; set; }
|
|
public bool HasNoSpecificSubs { get; set; }
|
|
};
|
|
|
|
public class VtsPgci
|
|
{
|
|
public int NumberOfProgramChains;
|
|
public List<ProgramChain> ProgramChains;
|
|
|
|
public VtsPgci()
|
|
{
|
|
ProgramChains = new List<ProgramChain>();
|
|
}
|
|
};
|
|
|
|
private readonly List<string> _arrayOfAudioMode = new List<string> { "AC3", "...", "MPEG1", "MPEG2", "LPCM", "...", "DTS" };
|
|
private readonly List<string> _arrayOfAudioExtension = new List<string> { "unspecified", "normal", "for visually impaired", "director's comments", "alternate director's comments" };
|
|
private readonly List<string> _arrayOfAspect = new List<string> { "4:3", "...", "...", "16:9" };
|
|
private readonly List<string> _arrayOfStandard = new List<string> { "NTSC", "PAL", "...", "..." };
|
|
private readonly List<string> _arrayOfCodingMode = new List<string> { "MPEG1", "MPEG2" };
|
|
private readonly List<string> _arrayOfNtscResolution = new List<string> { "720x480", "704x480", "352x480", "352x240" };
|
|
private readonly List<string> _arrayOfPalResolution = new List<string> { "720x576", "704x576", "352x576", "352x288" };
|
|
public static List<string> ArrayOfLanguageCode = new List<string> { " ", "aa", "ab", "af", "am", "ar", "as", "ay", "az", "ba", "be", "bg", "bh", "bi", "bn", "bo", "br", "ca", "co", "cs", "cy", "da", "de", "dz", "el",
|
|
"en", "eo", "es", "et", "eu", "fa", "fi", "fj", "fo", "fr", "fy", "ga", "gd", "gl", "gn", "gu", "ha", "he", "hi", "hr", "hu", "hy", "ia", "id", "ie", "ik",
|
|
"in", "is", "it", "iu", "iw", "ja", "ji", "jw", "ka", "kk", "kl", "km", "kn", "ko", "ks", "ku", "ky", "la", "ln", "lo", "lt", "lv", "mg", "mi", "mk", "ml",
|
|
"mn", "mo", "mr", "ms", "mt", "my", "na", "ne", "nl", "no", "oc", "om", "or", "pa", "pl", "ps", "pt", "qu", "rm", "rn", "ro", "ru", "rw", "sa", "sd", "sg",
|
|
"sh", "si", "sk", "sl", "sm", "sn", "so", "sq", "sr", "ss", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "ti", "tk", "tl", "tn", "to", "tr", "ts", "tt",
|
|
"tw", "ug", "uk", "ur", "uz", "vi", "vo", "wo", "xh", "yi", "yo", "za", "zh", "zu", ""};
|
|
public static List<string> ArrayOfLanguage = new List<string> { "Not Specified", "Afar", "Abkhazian", "Afrikaans", "Amharic", "Arabic", "Assamese", "Aymara", "Azerbaijani", "Bashkir", "Byelorussian", "Bulgarian", "Bihari", "Bislama", "Bengali; Bangla", "Tibetan", "Breton", "Catalan", "Corsican", "Czech(Ceske)", "Welsh", "Dansk", "Deutsch", "Bhutani", "Greek",
|
|
"English", "Esperanto", "Espanol", "Estonian", "Basque", "Persian", "Suomi", "Fiji", "Faroese", "Français", "Frisian", "Irish", "Scots Gaelic", "Galician", "Guarani", "Gujarati", "Hausa", "Hebrew", "Hindi", "Hrvatski", "Magyar", "Armenian", "Interlingua", "Indonesian", "Interlingue", "Inupiak",
|
|
"Indonesian", "Islenska", "Italiano", "Inuktitut", "Hebrew", "Japanese", "Yiddish", "Javanese", "Georgian", "Kazakh", "Greenlandic", "Cambodian", "Kannada", "Korean", "Kashmiri", "Kurdish", "Kirghiz", "Latin", "Lingala", "Laothian", "Lithuanian", "Latvian, Lettish", "Malagasy", "Maori", "Macedonian", "Malayalam",
|
|
"Mongolian", "Moldavian", "Marathi", "Malay", "Maltese", "Burmese", "Nauru", "Nepali", "Nederlands", "Norsk", "Occitan", "(Afan) Oromo", "Oriya", "Punjabi", "Polish", "Pashto, Pushto", "Portugues", "Quechua", "Rhaeto-Romance", "Kirundi", "Romanian", "Russian", "Kinyarwanda", "Sanskrit", "Sindhi", "Sangho",
|
|
"Serbo-Croatian", "Sinhalese", "Slovak", "Slovenian", "Samoan", "Shona", "Somali", "Albanian", "Serbian", "Siswati", "Sesotho", "Sundanese", "Svenska", "Swahili", "Tamil", "Telugu", "Tajik", "Thai", "Tigrinya", "Turkmen", "Tagalog", "Setswana", "Tonga", "Turkish", "Tsonga", "Tatar",
|
|
"Twi", "Uighur", "Ukrainian", "Urdu", "Uzbek", "Vietnamese", "Volapuk", "Wolof", "Xhosa", "Yiddish", "Yoruba", "Zhuang", "Chinese", "Zulu", "???"};
|
|
|
|
public VtsPgci VideoTitleSetProgramChainTable { get { return _vtsPgci; } }
|
|
public VtsVobs VideoTitleSetVobs { get { return _vtsVobs; } }
|
|
public string ErrorMessage { get; private set; }
|
|
|
|
private readonly VtsVobs _vtsVobs = new VtsVobs();
|
|
private readonly VtsPgci _vtsPgci = new VtsPgci();
|
|
private FileStream _fs;
|
|
|
|
public IfoParser(string fileName)
|
|
{
|
|
try
|
|
{
|
|
_fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
|
|
var buffer = new byte[12];
|
|
_fs.Position = 0;
|
|
_fs.Read(buffer, 0, 12);
|
|
string id = Encoding.UTF8.GetString(buffer);
|
|
if (id != "DVDVIDEO-VTS")
|
|
{
|
|
ErrorMessage = string.Format(Configuration.Settings.Language.DvdSubRip.WrongIfoType, id, Environment.NewLine, fileName);
|
|
return;
|
|
}
|
|
ParseVtsVobs();
|
|
ParseVtsPgci();
|
|
_fs.Close();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
ErrorMessage = exception.Message + Environment.NewLine + exception.StackTrace;
|
|
}
|
|
}
|
|
|
|
private void ParseVtsVobs()
|
|
{
|
|
var buffer = new byte[16];
|
|
|
|
//retrieve video info
|
|
_fs.Position = 0x200;
|
|
var data = IntToBin(GetEndian(2), 16);
|
|
_vtsVobs.VideoStream.CodingMode = _arrayOfCodingMode[BinToInt(MidStr(data, 0, 2))];
|
|
_vtsVobs.VideoStream.Standard = _arrayOfStandard[BinToInt(MidStr(data, 2, 2))];
|
|
_vtsVobs.VideoStream.Aspect = _arrayOfAspect[BinToInt(MidStr(data, 4, 2))];
|
|
if (_vtsVobs.VideoStream.Standard == "PAL")
|
|
_vtsVobs.VideoStream.Resolution = _arrayOfPalResolution[BinToInt(MidStr(data, 13, 2))];
|
|
else if (_vtsVobs.VideoStream.Standard == "NTSC")
|
|
_vtsVobs.VideoStream.Resolution = _arrayOfNtscResolution[BinToInt(MidStr(data, 13, 2))];
|
|
|
|
//retrieve audio info
|
|
_fs.Position = 0x202; //useless but here for readability
|
|
_vtsVobs.NumberOfAudioStreams = GetEndian(2);
|
|
for (int i = 0; i < _vtsVobs.NumberOfAudioStreams; i++)
|
|
{
|
|
var audioStream = new AudioStream();
|
|
data = IntToBin(GetEndian(2), 16);
|
|
audioStream.LanguageTypeSpecified = Convert.ToInt32(MidStr(data, 4, 2));
|
|
audioStream.CodingMode = _arrayOfAudioMode[(BinToInt(MidStr(data, 0, 3)))];
|
|
audioStream.Channels = BinToInt(MidStr(data, 13, 3)) + 1;
|
|
_fs.Read(buffer, 0, 2);
|
|
audioStream.LanguageCode = new string(new[] { Convert.ToChar(buffer[0]), Convert.ToChar(buffer[1]) });
|
|
if (ArrayOfLanguageCode.Contains(audioStream.LanguageCode))
|
|
audioStream.Language = ArrayOfLanguage[ArrayOfLanguageCode.IndexOf(audioStream.LanguageCode)];
|
|
_fs.Seek(1, SeekOrigin.Current);
|
|
audioStream.Extension = _arrayOfAudioExtension[_fs.ReadByte()];
|
|
_fs.Seek(2, SeekOrigin.Current);
|
|
_vtsVobs.AudioStreams.Add(audioStream);
|
|
}
|
|
|
|
//retrieve subs info (only name)
|
|
_fs.Position = 0x254;
|
|
_vtsVobs.NumberOfSubtitles = GetEndian(2);
|
|
_fs.Position += 2;
|
|
for (int i = 0; i < _vtsVobs.NumberOfSubtitles; i++)
|
|
{
|
|
_fs.Read(buffer, 0, 2);
|
|
var languageTwoLetter = new string(new[] { Convert.ToChar(buffer[0]), Convert.ToChar(buffer[1]) });
|
|
_vtsVobs.Subtitles.Add(InterpretLanguageCode(languageTwoLetter));
|
|
_fs.Read(buffer, 0, 2); // reserved for language code extension + code extension
|
|
|
|
//switch (buffer[0]) // 4, 8, 10-12 unused
|
|
//{
|
|
// // http://dvd.sourceforge.net/dvdinfo/sprm.html
|
|
// case 1: subtitleFormat = "(caption/normal size char)"; break; //0 = unspecified caption
|
|
// case 2: subtitleFormat = "(caption/large size char)"; break;
|
|
// case 3: subtitleFormat = "(caption for children)"; break;
|
|
// case 5: subtitleFormat = "(closed caption/normal size char)"; break;
|
|
// case 6: subtitleFormat = "(closed caption/large size char)"; break;
|
|
// case 7: subtitleFormat = "(closed caption for children)"; break;
|
|
// case 9: subtitleFormat = "(forced caption)"; break;
|
|
// case 13: subtitleFormat = "(director comments/normal size char)"; break;
|
|
// case 14: subtitleFormat = "(director comments/large size char)"; break;
|
|
// case 15: subtitleFormat = "(director comments for children)"; break;
|
|
//}
|
|
|
|
_fs.Position += 2;
|
|
}
|
|
}
|
|
|
|
private static int BinToInt(string p)
|
|
{
|
|
return Convert.ToInt32(p, 2);
|
|
}
|
|
|
|
private static string MidStr(string data, int start, int count)
|
|
{
|
|
return data.Substring(start, count);
|
|
}
|
|
|
|
private static string IntToBin(int value, int digits)
|
|
{
|
|
string result = Convert.ToString(value, 2);
|
|
while (result.Length < digits)
|
|
result = "0" + result;
|
|
return result;
|
|
}
|
|
|
|
private int GetEndian(int count)
|
|
{
|
|
int result = 0;
|
|
for (int i = count; i > 0; i--)
|
|
{
|
|
int b = _fs.ReadByte();
|
|
result = (result << 8) + b;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static string InterpretLanguageCode(string code)
|
|
{
|
|
int i = 0;
|
|
while (ArrayOfLanguageCode[i] != code && i < 143)
|
|
{
|
|
i++;
|
|
}
|
|
return ArrayOfLanguage[i];
|
|
}
|
|
|
|
private void ParseVtsPgci()
|
|
{
|
|
const int sectorSize = 2048;
|
|
|
|
_fs.Position = 0xCC; //Get VTS_PGCI adress
|
|
int tableStart = sectorSize * GetEndian(4);
|
|
|
|
_fs.Position = tableStart;
|
|
_vtsPgci.NumberOfProgramChains = GetEndian(2);
|
|
_vtsPgci.ProgramChains = new List<ProgramChain>();
|
|
|
|
for (int i = 0; i < _vtsPgci.NumberOfProgramChains; i++)
|
|
{
|
|
//Parse PGC Header
|
|
var programChain = new ProgramChain();
|
|
_fs.Position = tableStart + 4 + 8 * (i + 1); //Get PGC adress
|
|
int programChainAdress = GetEndian(4);
|
|
_fs.Position = tableStart + programChainAdress + 2; //Move to PGC
|
|
programChain.NumberOfPgc = _fs.ReadByte();
|
|
programChain.NumberOfCells = _fs.ReadByte();
|
|
programChain.PlaybackTime = InterpretTime(GetEndian(4));
|
|
_fs.Seek(4, SeekOrigin.Current);
|
|
|
|
// check if audio streams are available for this PGC
|
|
_fs.Position = tableStart + programChainAdress + 0xC;
|
|
for (int j = 0; j < _vtsVobs.NumberOfAudioStreams; j++)
|
|
{
|
|
string temp = IntToBin(_fs.ReadByte(), 8);
|
|
programChain.AudioStreamsAvailable.Add(temp[0]);
|
|
_fs.Seek(1, SeekOrigin.Current);
|
|
}
|
|
|
|
// check if subtitles are available for this PGC
|
|
_fs.Position = tableStart + programChainAdress + 0x1C;
|
|
for (int j = 0; j < _vtsVobs.NumberOfSubtitles; j++)
|
|
{
|
|
// read and save full subpicture stream info inside program chain
|
|
var subtitle = new byte[4];
|
|
_fs.Read(subtitle, 0, 4);
|
|
programChain.SubtitlesAvailable.Add(subtitle);
|
|
}
|
|
|
|
CalculateSubtitleTypes(programChain);
|
|
|
|
//Parse Color LookUp Table (CLUT) - offset 00A4, 16*4 (0, Y, Cr, Cb)
|
|
_fs.Position = tableStart + programChainAdress + 0xA4;
|
|
for (int colorNumber = 0; colorNumber < 16; colorNumber++)
|
|
{
|
|
var colors = new byte[4];
|
|
_fs.Read(colors, 0, 4);
|
|
int y = colors[1] - 16;
|
|
int cr = colors[2] - 128;
|
|
int cb = colors[3] - 128;
|
|
int r = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 1.596F * cr), 0), 255);
|
|
int g = (int)Math.Min(Math.Max(Math.Round(1.1644F * y - 0.813F * cr - 0.391F * cb), 0), 255);
|
|
int b = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 2.018F * cb), 0), 255);
|
|
|
|
programChain.ColorLookupTable.Add(Color.FromArgb(r, g, b));
|
|
}
|
|
|
|
//Parse Program Map
|
|
_fs.Position = tableStart + programChainAdress + 0xE6;
|
|
_fs.Position = tableStart + programChainAdress + GetEndian(2);
|
|
for (int j = 0; j < programChain.NumberOfPgc; j++)
|
|
{
|
|
programChain.PgcEntryCells.Add((byte)_fs.ReadByte());
|
|
}
|
|
|
|
// Cell Playback Info Table to retrieve duration
|
|
_fs.Position = tableStart + programChainAdress + 0xE8;
|
|
_fs.Position = tableStart + programChainAdress + GetEndian(2);
|
|
var timeArray = new List<int>();
|
|
for (int k = 0; k < programChain.NumberOfPgc; k++)
|
|
{
|
|
int time = 0;
|
|
int max;
|
|
if (k == programChain.NumberOfPgc - 1)
|
|
max = programChain.NumberOfCells;
|
|
else
|
|
max = programChain.PgcEntryCells[k + 1] - 1;
|
|
for (int j = programChain.PgcEntryCells[k]; j <= max; j++)
|
|
{
|
|
_fs.Seek(4, SeekOrigin.Current);
|
|
time += TimeToMs(GetEndian(4));
|
|
_fs.Seek(16, SeekOrigin.Current);
|
|
}
|
|
programChain.PgcPlaybackTimes.Add(MsToTime(time));
|
|
timeArray.Add(time);
|
|
|
|
//convert to start time
|
|
time = 0;
|
|
for (int l = 1; l <= k; l++)
|
|
{
|
|
time += timeArray[l - 1];
|
|
}
|
|
if (k == 0)
|
|
programChain.PgcStartTimes.Add(MsToTime(0));
|
|
if (k > 0)
|
|
programChain.PgcStartTimes.Add(MsToTime(time));
|
|
}
|
|
_vtsPgci.ProgramChains.Add(programChain);
|
|
}
|
|
}
|
|
|
|
private void CalculateSubtitleTypes(ProgramChain programChain)
|
|
{
|
|
// Additional Code to analyse stream bytes
|
|
if (_vtsVobs.NumberOfSubtitles > 0)
|
|
{
|
|
// load the 'last' subpicture stream info,
|
|
// because if we have more than one subtitle stream,
|
|
// all subtitle positions > 0
|
|
// lastSubtitle[0] is related to 4:3
|
|
// lastSubtitle[1] is related to Wide
|
|
// lastSubtitle[2] is related to letterboxed
|
|
// lastSubtitle[3] is related to pan&scan
|
|
byte[] lastSubtitle = programChain.SubtitlesAvailable[programChain.SubtitlesAvailable.Count - 1];
|
|
|
|
int countSubs = 0;
|
|
|
|
// set defaults for all possible subpicture types and positions
|
|
programChain.Has43Subs = false;
|
|
programChain.HasWideSubs = false;
|
|
programChain.HasLetterSubs = false;
|
|
programChain.HasPanSubs = false;
|
|
programChain.HasNoSpecificSubs = true;
|
|
|
|
int pos43Subs = -1;
|
|
int posWideSubs = -1;
|
|
int posLetterSubs = -1;
|
|
int posPanSubs = -1;
|
|
|
|
// parse different subtitle bytes
|
|
if (lastSubtitle[0] > 0x80)
|
|
{
|
|
programChain.Has43Subs = true;
|
|
countSubs++; // 4:3
|
|
}
|
|
if (lastSubtitle[1] > 0)
|
|
{
|
|
programChain.HasWideSubs = true;
|
|
countSubs++; // wide
|
|
}
|
|
if (lastSubtitle[2] > 0)
|
|
{
|
|
programChain.HasLetterSubs = true;
|
|
countSubs++; // letterboxed
|
|
}
|
|
if (lastSubtitle[3] > 0)
|
|
{
|
|
programChain.HasPanSubs = true;
|
|
countSubs++; // pan&scan
|
|
}
|
|
|
|
if (countSubs == 0)
|
|
{
|
|
// may be, only a 4:3 stream exists
|
|
// -> lastSubtitle[0] = 0x80
|
|
}
|
|
else
|
|
{
|
|
if (_vtsVobs.NumberOfSubtitles == 1)
|
|
{
|
|
// only 1 stream exists, may be letterboxed
|
|
// if so we cound't find wide id, because lastSubtitle[1] = 0 !!
|
|
// corresponding wide stream byte is 0 => wide id = 0x20
|
|
// letterboxed = 0x21
|
|
if (programChain.HasLetterSubs && !programChain.HasWideSubs)
|
|
{
|
|
// repair it
|
|
programChain.HasWideSubs = true;
|
|
}
|
|
}
|
|
programChain.HasNoSpecificSubs = false;
|
|
}
|
|
|
|
// subpucture streams start with 0x20
|
|
int subStream = 0x20;
|
|
|
|
// Now we know all about available subpicture streams, including position type
|
|
// And we can create whole complete definitions for all avalable streams
|
|
foreach (byte[] subtitle in programChain.SubtitlesAvailable)
|
|
{
|
|
if (programChain.HasNoSpecificSubs)
|
|
{
|
|
// only one unspezified subpicture stream exists
|
|
_vtsVobs.SubtitleIDs.Add(string.Format("0x{0:x2}", subStream++));
|
|
_vtsVobs.SubtitleTypes.Add("unspecific");
|
|
}
|
|
else
|
|
{
|
|
// read stream position for evey subtitle type from subtitle byte
|
|
if (programChain.Has43Subs)
|
|
{
|
|
pos43Subs = subtitle[0] - 0x80;
|
|
}
|
|
if (programChain.HasWideSubs)
|
|
{
|
|
posWideSubs = subtitle[1];
|
|
}
|
|
if (programChain.HasLetterSubs)
|
|
{
|
|
posLetterSubs = subtitle[2];
|
|
}
|
|
if (programChain.HasPanSubs)
|
|
{
|
|
posPanSubs = subtitle[3];
|
|
}
|
|
|
|
// Now we can create subpicture id's and types for every stream
|
|
// All used subpicture id's and types will beappended to string, separated by colon
|
|
// So it's possible to split it later
|
|
string sub = string.Empty;
|
|
string subType = string.Empty;
|
|
if (programChain.Has43Subs)
|
|
{
|
|
sub = string.Format("0x{0:x2}", subStream + pos43Subs);
|
|
subType = "4:3";
|
|
}
|
|
if (programChain.HasWideSubs)
|
|
{
|
|
if (sub.Length > 0)
|
|
{
|
|
sub += ", ";
|
|
subType += ", ";
|
|
}
|
|
sub += string.Format("0x{0:x2}", subStream + posWideSubs);
|
|
subType += "wide";
|
|
}
|
|
if (programChain.HasLetterSubs)
|
|
{
|
|
if (sub.Length > 0)
|
|
{
|
|
sub += ", ";
|
|
subType += ", ";
|
|
}
|
|
sub += string.Format("0x{0:x2}", subStream + posLetterSubs);
|
|
subType += "letterboxed";
|
|
}
|
|
if (programChain.HasPanSubs)
|
|
{
|
|
if (sub.Length > 0)
|
|
{
|
|
sub += ", ";
|
|
subType += ", ";
|
|
}
|
|
sub += string.Format("0x{0:x2}", subStream + posPanSubs);
|
|
subType += "pan&scan";
|
|
}
|
|
|
|
_vtsVobs.SubtitleIDs.Add(sub);
|
|
_vtsVobs.SubtitleTypes.Add(subType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static int TimeToMs(int time)
|
|
{
|
|
double fps;
|
|
|
|
var temp = IntToBin(time, 32);
|
|
var result = StrToInt(IntToHex(BinToInt(MidStr(temp, 0, 8)), 1)) * 3600000;
|
|
result = result + StrToInt(IntToHex(BinToInt(MidStr(temp, 8, 8)), 2)) * 60000;
|
|
result = result + StrToInt(IntToHex(BinToInt(MidStr(temp, 16, 8)), 2)) * 1000;
|
|
if (temp.Substring(24, 2) == "11")
|
|
fps = 30;
|
|
else
|
|
fps = 25;
|
|
result += (int)Math.Round((TimeCode.BaseUnit / fps) * StrToFloat(IntToHex(BinToInt(MidStr(temp, 26, 6)), 3)));
|
|
return result;
|
|
}
|
|
|
|
private static double StrToFloat(string p)
|
|
{
|
|
return Convert.ToDouble(p, System.Globalization.CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
private static int StrToInt(string p)
|
|
{
|
|
return int.Parse(p);
|
|
}
|
|
|
|
private static string IntToHex(int value, int digits)
|
|
{
|
|
string hex = value.ToString("X");
|
|
|
|
return hex.PadLeft(digits, '0');
|
|
}
|
|
|
|
private static string MsToTime(double milliseconds)
|
|
{
|
|
var ts = TimeSpan.FromMilliseconds(milliseconds);
|
|
string s = string.Format("{0:#0}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
|
|
return s;
|
|
}
|
|
|
|
private static string InterpretTime(int timeNumber)
|
|
{
|
|
string timeBytes = IntToBin(timeNumber, 32);
|
|
int h = StrToInt(IntToHex(BinToInt(timeBytes.Substring(0, 8)), 1));
|
|
int m = StrToInt(IntToHex(BinToInt(timeBytes.Substring(8, 8)), 2));
|
|
int s = StrToInt(IntToHex(BinToInt(timeBytes.Substring(16, 8)), 2));
|
|
int fps = 25;
|
|
if (timeBytes.Substring(24, 2) == "11")
|
|
fps = 30;
|
|
int milliseconds = (int)Math.Round((TimeCode.BaseUnit / fps) * StrToFloat(IntToHex(BinToInt(timeBytes.Substring(26, 6)), 3)));
|
|
var ts = new TimeSpan(0, h, m, s, milliseconds);
|
|
return MsToTime(ts.TotalMilliseconds);
|
|
}
|
|
|
|
private void ReleaseManagedResources()
|
|
{
|
|
if (_fs != null)
|
|
{
|
|
_fs.Dispose();
|
|
_fs = null;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
ReleaseManagedResources();
|
|
}
|
|
}
|
|
|
|
}
|
|
} |