namespace TsRemux
{
- static const char PesTemplate[] = { 0x00, 0x00, 0x01, 0xbd};
+static const char PesTemplate[] = { 0x00, 0x00, 0x01, 0xbd};
+
+public abstract class PesFile
+{
+ protected PesFile(BackgroundWorker openWorker)
+ {
+ fs = null;
+ tsior = null;
+ fileType = TsFileType.UNKNOWN;
+ startPcr = -1;
+ endPcr = -1;
+ sis = null;
+ pcrDelegate = null;
+ ptsDelegate = null;
+ this.openWorker = openWorker;
+ lastPercent = 0;
+ }
+
+ protected void ReportProgress(int percent)
+ {
+ if (openWorker.CancellationPending)
+ {
+ throw new ApplicationException("Opening file canceled");
+ }
+ if (lastPercent != percent && percent > 0 && percent <= 100)
+ {
+ lastPercent = percent;
+ openWorker.ReportProgress(percent);
+ }
+ }
+
+ public void SetPcrDelegate(PcrChanged pcr)
+ {
+ pcrDelegate = pcr;
+ }
+
+ public Int64 StartPcr
+ {
+ get { return startPcr; }
+ }
+
+ public Int64 EndPcr
+ {
+ get { return endPcr; }
+ }
+
+ public void SetPtsDelegate(PtsChanged pts)
+ {
+ ptsDelegate = pts;
+ }
+
+ public StreamInfo GetStreamInfo(ushort pid)
+ {
+ foreach (StreamInfo si in sis)
+ if (si.ElementaryPID == pid)
+ return si;
+ return null;
+ }
+
+ private void ParseElementaryStreams()
+ {
+ Seek(-1);
+ for (int i = 0; i < 300;)
+ {
+ ReportProgress(50 + ((50 * i) / 300));
+ PesPacket[] ppa = GetNextPesPackets();
+ if(ppa == null)
+ goto done;
+ foreach (PesPacket pp in ppa)
+ {
+ if (null != pp)
+ {
+ byte[] payload = pp.GetPayload();
+ StreamInfo si = GetStreamInfo(pp.PID);
+ if (si != null)
+ ParseStream(si, payload);
+ i++;
+ }
+ else
+ goto done;
+ }
+ }
+ done:
+ Seek(-1);
+ }
+
+ private void ParseStream(StreamInfo si, byte[] payload)
+ {
+ switch (si.StreamType)
+ {
+ case ElementaryStreamTypes.VIDEO_STREAM_VC1:
+ if ((si.ElementaryDescriptors == null) ||
+ si.FrameRate == FrameRate.Reserved ||
+ si.VideoFormat == VideoFormat.Reserved ||
+ si.AspectRatio == AspectRatio.Reserved)
+ {
+ VC1SequenceInfo sq = new VC1SequenceInfo(payload, 0);
+ if (sq.Valid)
+ {
+ if (sq.AspectRatio != AspectRatio.Reserved)
+ si.AspectRatio = sq.AspectRatio;
+ if (sq.FrameRate != FrameRate.Reserved)
+ si.FrameRate = sq.FrameRate;
+ if (sq.VideoFormat != VideoFormat.Reserved)
+ si.VideoFormat = sq.VideoFormat;
+ if (si.ElementaryDescriptors == null)
+ si.ElementaryDescriptors = Constants.vc1_descriptor;
+ }
+ }
+ break;
+ case ElementaryStreamTypes.AUDIO_STREAM_AC3:
+ if ((si.ElementaryDescriptors == null) ||
+ si.AudioPresentationType == AudioPresentationType.Reserved ||
+ si.SamplingFrequency == SamplingFrequency.Reserved)
+ {
+ AC3Info ac3 = new AC3Info(payload, 0);
+ if (ac3.Valid)
+ {
+ if (ac3.AudioPresentationType != AudioPresentationType.Reserved)
+ si.AudioPresentationType = ac3.AudioPresentationType;
+ if (ac3.SamplingFrequency != SamplingFrequency.Reserved)
+ si.SamplingFrequency = ac3.SamplingFrequency;
+ if (si.ElementaryDescriptors == null)
+ si.ElementaryDescriptors = ac3.ElementaryDescriptors;
+ if (ac3.SyntaxType == Ac3SyntaxType.Enhanced)
+ si.StreamType = ElementaryStreamTypes.AUDIO_STREAM_AC3_PLUS;
+ }
+ }
+ break;
+ case ElementaryStreamTypes.AUDIO_STREAM_AC3_PLUS:
+ if (si.AudioPresentationType == AudioPresentationType.Reserved ||
+ si.SamplingFrequency == SamplingFrequency.Reserved)
+ {
+ AC3Info ac3 = new AC3Info(payload, 0);
+ if (ac3.Valid)
+ {
+ if (ac3.AudioPresentationType != AudioPresentationType.Reserved)
+ si.AudioPresentationType = ac3.AudioPresentationType;
+ if (ac3.SamplingFrequency != SamplingFrequency.Reserved)
+ si.SamplingFrequency = ac3.SamplingFrequency;
+ if (si.ElementaryDescriptors == null)
+ si.ElementaryDescriptors = ac3.ElementaryDescriptors;
+ }
+ }
+ break;
+ case ElementaryStreamTypes.AUDIO_STREAM_AC3_TRUE_HD:
+ if (si.AudioPresentationType == AudioPresentationType.Reserved ||
+ si.SamplingFrequency == SamplingFrequency.Reserved)
+ {
+ MlpInfo ac3 = new MlpInfo(payload, 0);
+ if (ac3.Valid)
+ {
+ if (ac3.AudioPresentationType != AudioPresentationType.Reserved)
+ si.AudioPresentationType = ac3.AudioPresentationType;
+ if (ac3.SamplingFrequency != SamplingFrequency.Reserved)
+ si.SamplingFrequency = ac3.SamplingFrequency;
+ if (si.ElementaryDescriptors == null)
+ si.ElementaryDescriptors = ac3.ElementaryDescriptors;
+ }
+ }
+ break;
+ case ElementaryStreamTypes.VIDEO_STREAM_H264:
+ if ((si.ElementaryDescriptors == null) ||
+ si.FrameRate == FrameRate.Reserved ||
+ si.VideoFormat == VideoFormat.Reserved ||
+ si.AspectRatio == AspectRatio.Reserved)
+ {
+ H264Info h264 = new H264Info(payload, 0);
+ if(h264.Valid)
+ {
+ if (h264.AspectRatio != AspectRatio.Reserved)
+ si.AspectRatio = h264.AspectRatio;
+ if (h264.FrameRate != FrameRate.Reserved)
+ si.FrameRate = h264.FrameRate;
+ if (h264.VideoFormat != VideoFormat.Reserved)
+ si.VideoFormat = h264.VideoFormat;
+ if (si.ElementaryDescriptors == null)
+ si.ElementaryDescriptors = h264.ElementaryDescriptors;
+ }
+ }
+ break;
+ case ElementaryStreamTypes.AUDIO_STREAM_DTS:
+ case ElementaryStreamTypes.AUDIO_STREAM_DTS_HD:
+ case ElementaryStreamTypes.AUDIO_STREAM_DTS_HD_MASTER_AUDIO:
+ if (si.AudioPresentationType == AudioPresentationType.Reserved ||
+ si.SamplingFrequency == SamplingFrequency.Reserved)
+ {
+ DtsInfo dts = new DtsInfo(payload, 0);
+ if (dts.Valid)
+ {
+ if (dts.AudioPresentationType != AudioPresentationType.Reserved)
+ si.AudioPresentationType = dts.AudioPresentationType;
+ if (dts.SamplingFrequency != SamplingFrequency.Reserved)
+ si.SamplingFrequency = dts.SamplingFrequency;
+ }
+ }
+ break;
+ case ElementaryStreamTypes.VIDEO_STREAM_MPEG2:
+ if ((si.ElementaryDescriptors == null) ||
+ si.FrameRate == FrameRate.Reserved ||
+ si.VideoFormat == VideoFormat.Reserved ||
+ si.AspectRatio == AspectRatio.Reserved)
+ {
+ Mpeg2Info mpeg2 = new Mpeg2Info(payload, 0);
+ if (mpeg2.Valid)
+ {
+ if (mpeg2.AspectRatio != AspectRatio.Reserved)
+ si.AspectRatio = mpeg2.AspectRatio;
+ if (mpeg2.FrameRate != FrameRate.Reserved)
+ si.FrameRate = mpeg2.FrameRate;
+ if (mpeg2.VideoFormat != VideoFormat.Reserved)
+ si.VideoFormat = mpeg2.VideoFormat;
+ if (si.ElementaryDescriptors == null)
+ si.ElementaryDescriptors = mpeg2.ElementaryDescriptors;
+ }
+ }
+ break;
+ }
+ }
+
+TsFileType PesFile::GetFileType(FileStream fs)
+{
+ pByte inBuff = pByte(new byte[Constant::STRIDE_SIZE * 7]);
+ int offset;
+ fs.Seek(0, SeekOrigin.Begin);
+
+ if (fs.Read(inBuff, 0, sizeof(inBuff.get())) != sizeof(inBuff.get()))
+ throw std::invalid_argument("The specified file is too short");
+
+ // try ts
+ for (offset = 0; offset < Constants::STRIDE_SIZE; offset++)
+ {
+ if (inBuff[offset] == Constants::SYNC_BYTE)
+ {
+ if (inBuff[offset + Constants::TS_SIZE] == Constants::SYNC_BYTE &&
+ inBuff[offset + (Constants::TS_SIZE * 2)] == Constants::SYNC_BYTE &&
+ inBuff[offset + (Constants::TS_SIZE * 3)] == Constants::SYNC_BYTE &&
+ inBuff[offset + (Constants::TS_SIZE * 4)] == Constants::SYNC_BYTE &&
+ inBuff[offset + (Constants::TS_SIZE * 5)] == Constants::SYNC_BYTE)
+ return TsFileType::TS;
+ else if (inBuff[offset + Constants::STRIDE_SIZE] == Constants::SYNC_BYTE &&
+ inBuff[offset + (Constants::STRIDE_SIZE * 2)] == Constants::SYNC_BYTE &&
+ inBuff[offset + (Constants::STRIDE_SIZE * 3)] == Constants::SYNC_BYTE &&
+ inBuff[offset + (Constants::STRIDE_SIZE * 4)] == Constants::SYNC_BYTE &&
+ inBuff[offset + (Constants::STRIDE_SIZE * 5)] == Constants::SYNC_BYTE)
+ return TsFileTyp::M2TS;
+ }
+ }
+ // try SUP
+ if ((inBuff[0] == (byte)'P' || inBuff[0] == (byte)'p') &&
+ (inBuff[1] == (byte)'G' || inBuff[1] == (byte)'g'))
+ return TsFileType::SUP_ELEMENTARY;
+ // try EVOB
+ UInt32 marker = 0xffffffff;
+ int stuffing = 0;
+ bool packstart = false;
+ for (int i = 0; i < sizeof(inBuff.get()); i++)
+ {
+ marker = marker << 8;
+ marker &= 0xffffff00;
+ marker += inBuff[i];
+ if ((marker & 0xffffff00) == 0x00000100)
+ {
+ switch (marker & 0xff)
+ {
+ case 0xba:
+ // pack start code
+ if (sizeof(inBuff.get()) < i + 11)
+ {
+ i = sizeof(inBuff.get());
+ break;
+ }
+ i += 10;
+ stuffing = inBuff[i] & 0x7;
+ if (sizeof(inBuff.get()) < i + stuffing + 1)
+ {
+ i = sizeof(inBuff.get());
+ break;
+ }
+ i += stuffing;
+ stuffing = 0;
+ marker = 0xffffffff;
+ packstart = true;
+ break;
+ default:
+ // other PES packet
+ if (sizeof(inBuff.get()) < i + 3)
+ {
+ i = sizeof(inBuff.get());
+ break;
+ }
+ stuffing = (inBuff[i + 1] << 8) + inBuff[i + 2];
+ i += 2;
+ if (sizeof(inBuff.get()) < i + stuffing)
+ {
+ i = sizeof(inBuff.get());
+ break;
+ }
+ i += stuffing;
+ stuffing = 0;
+ marker = 0xffffffff;
+ break;
+ }
+ }
+ }
+ if (packstart)
+ return TsFileType::EVOB;
+ // try MKV
+ marker = 0xffffffff;
+ for (int i = 0; i < sizeof(inBuff.get()); i++)
+ {
+ marker = marker << 8;
+ marker &= 0xffffff00;
+ marker += inBuff[i];
+ if (marker == Constants::MKVFILE_START)
+ return TsFileType::MKV;
+ }
+ throw std::invalid_argument("The specified file is not a valid TS/M2TS/EVOB/MKV file");
+}
+
+boost::shared_ptr<PesFile> PesFile::OpenFile(string path, bool useAsync, BackgroundWorker openWorker)
+{
+ PesFile pf = null;
+ FileStream fs = null;
+ // pending
+ // fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, Constant::DISK_BUFFER, useAsync);
+ try
+ {
+ TsIo tsior = new TsIo(fs, null, Constants::DISK_BUFFER );
+ TsFileType fileType = GetFileType(fs);
+ switch (fileType)
+ {
+ case TsFileType::EVOB:
+ pf = new EvoPesFile(openWorker);
+ break;
+ case TsFileType::M2TS:
+ pf = new TsPesFile(openWorker,4);
+ break;
+ case TsFileType::TS:
+ pf = new TsPesFile(openWorker,0);
+ break;
+ case TsFileType::SUP_ELEMENTARY:
+ pf = new SupPesFile(openWorker);
+ break;
+ case TsFileType::MKV:
+ pf = new MkvPesFile(openWorker);
+ break;
+ }
+ pf.fileType = fileType;
+ pf.fs = fs;
+ pf.tsior = tsior;
+ pf.GetInitialValues();
+ pf.ParseElementaryStreams();
+ }
+ catch (Exception)
+ {
+ fs.Close();
+ throw;
+ }
+ return pf;
+}
+
+ public void CloseFile()
+ {
+ fs.Close();
+ fs = null;
+ tsior = null;
+ }
+
+ public void Clear()
+ {
+ tsior.Clear();
+ }
+
+ public TsFileType FileType
+ {
+ get { return fileType; }
+ }
+
+ public StreamInfo[] StreamInfos
+ {
+ get { return sis; }
+ }
+
+ public TimeSpan VideoLength
+ {
+ get
+ {
+ Int64 span = endPcr - startPcr;
+ if (span < 0)
+ span += Constants.MAX_MPEG2TS_CLOCK;
+ return new TimeSpan((span * 10) / 27);
+ }
+ }
+
+ public abstract PesPacket[] GetNextPesPackets();
+ public abstract void Seek(Int64 pcr);
+ public abstract DTCP_Descriptor DtcpInfo { get; }
+ protected abstract void GetInitialValues();
+ }
+
+ public class EvoPesFile : PesFile
+ {
+ private Dictionary<ushort, List<byte>> soundFrames;
+ private Int64 lastPcr;
+
+ public EvoPesFile(BackgroundWorker bw)
+ : base(bw)
+ {
+ soundFrames = new Dictionary<ushort, List<byte>>();
+ lastPcr = 0 - Constants.MPEG2TS_CLOCK_RATE;
+ }
+
+ public override DTCP_Descriptor DtcpInfo
+ {
+ get { return null; }
+ }
+
+ protected override void GetInitialValues()
+ {
+ GetTimeStamps();
+
+ Dictionary<byte, StreamInfo> evoStreams = new Dictionary<byte, StreamInfo>();
+ fs.Seek(0, SeekOrigin.Begin);
+ byte[] inBuff = new byte[Constants.DISK_BUFFER]; // 72MB IS TOO BIG! CAUSES FAILURE: << 2]; note this was line 463
+ int Length = fs.Read(inBuff, 0, inBuff.Length);
+ UInt32 marker = 0xffffffff;
+ int stuffing = 0;
+ for (int i = 0; i < Length; i++)
+ {
+ ReportProgress(50 * i / Length);
+ marker = marker << 8;
+ marker &= 0xffffff00;
+ marker += inBuff[i];
+ if ((marker & 0xffffff00) == 0x00000100)
+ {
+ switch (marker & 0xff)
+ {
+ case 0xba:
+ // pack start code
+ if (Length <= i + 10)
+ {
+ i = Length;
+ break;
+ }
+ i += 10;
+ stuffing = inBuff[i] & 0x7;
+ if (Length <= i + stuffing)
+ {
+ i = Length;
+ break;
+ }
+ i += stuffing;
+ stuffing = 0;
+ break;
+ default:
+ if (inBuff[i] == Constants.PES_PRIVATE1)
+ {
+ // skip to the end of the header
+ int endOfHeader = i + 6 + inBuff[i + 5];
+ if (inBuff.Length > endOfHeader + 4)
+ {
+ if ((inBuff[endOfHeader] & 0xe0) == Constants.PES_PRIVATE_SUBTITLE)
+ {
+ byte subid = inBuff[endOfHeader];
+ if (evoStreams.ContainsKey(subid) == false)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.PRESENTATION_GRAPHICS_STREAM, subid);
+ evoStreams.Add(subid, si);
+ }
+ }
+ else if ((inBuff[endOfHeader] & 0xf8) == Constants.PES_PRIVATE_AC3)
+ {
+ byte subid = inBuff[endOfHeader];
+ if (evoStreams.ContainsKey(subid) == false)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_AC3, subid);
+ evoStreams.Add(subid, si);
+ }
+ }
+ else if ((inBuff[endOfHeader] & 0xf8) == Constants.PES_PRIVATE_DTS_HD)
+ {
+ byte subid = inBuff[endOfHeader];
+ if (evoStreams.ContainsKey(subid) == false)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_DTS_HD, subid);
+ evoStreams.Add(subid, si);
+ }
+ }
+ else if ((inBuff[endOfHeader] & 0xf0) == Constants.PES_PRIVATE_AC3_PLUS)
+ {
+ byte subid = inBuff[endOfHeader];
+ if (evoStreams.ContainsKey(subid) == false)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_AC3_PLUS, subid);
+ evoStreams.Add(subid, si);
+ }
+ }
+ else if ((inBuff[endOfHeader] & 0xf8) == Constants.PES_PRIVATE_LPCM)
+ {
+ byte subid = inBuff[endOfHeader];
+ if (evoStreams.ContainsKey(subid) == false)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_LPCM, subid);
+ evoStreams.Add(subid, si);
+ }
+ }
+ else if ((inBuff[endOfHeader] & 0xf8) == Constants.PES_PRIVATE_AC3_TRUE_HD)
+ {
+ byte subid = inBuff[endOfHeader];
+ if (evoStreams.ContainsKey(subid) == false)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_AC3_TRUE_HD, subid);
+ evoStreams.Add(subid, si);
+ }
+ }
+ }
+ }
+ else if ((inBuff[i] & 0xe0) == Constants.PES_AUDIO_MPEG)
+ {
+ byte subid = inBuff[i];
+ if (evoStreams.ContainsKey(subid) == false)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_MPEG2, subid);
+ evoStreams.Add(subid, si);
+ }
+ }
+ else if ((inBuff[i] & 0xf0) == Constants.PES_VIDEO)
+ {
+ byte subid = inBuff[i];
+ if (evoStreams.ContainsKey(subid) == false)
+ {
+ // need to differenciate between H.264 and MPEG2
+ if (Length <= i + 5)
+ {
+ i = Length;
+ break;
+ }
+ ElementaryStreamTypes videoFormat = ElementaryStreamTypes.INVALID;
+ int startIndex = i + 6 + inBuff[i + 5];
+ if (Length <= startIndex + 3)
+ {
+ i = Length;
+ break;
+ }
+ UInt32 format = (UInt32)inBuff[startIndex] << 24;
+ format += (UInt32)inBuff[startIndex + 1] << 16;
+ format += (UInt32)inBuff[startIndex + 2] << 8;
+ format += inBuff[startIndex + 3];
+ if ((format & 0xffff) == 0x1)
+ {
+ videoFormat = ElementaryStreamTypes.VIDEO_STREAM_H264;
+ StreamInfo si = new StreamInfo(videoFormat, subid);
+ evoStreams.Add(subid, si);
+ }
+ else if ((format & 0xffffff00) == 0x100)
+ {
+ videoFormat = ElementaryStreamTypes.VIDEO_STREAM_MPEG2;
+ StreamInfo si = new StreamInfo(videoFormat, subid);
+ evoStreams.Add(subid, si);
+ }
+ }
+ }
+ else if (inBuff[i] == Constants.PES_VIDEO_VC1)
+ {
+ byte subid = GetVC1SubstreamId(inBuff, i + 3);
+ if (subid != 0 && evoStreams.ContainsKey(subid) == false)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.VIDEO_STREAM_VC1, subid);
+ si.ElementaryDescriptors = Constants.vc1_descriptor;
+ evoStreams.Add(subid, si);
+ }
+ }
+
+ // other PES packet
+ if (Length < i + 3)
+ {
+ i = Length;
+ break;
+ }
+ stuffing = (inBuff[i + 1] << 8) + inBuff[i + 2];
+ i += 2;
+ if (Length < i + stuffing)
+ {
+ i = Length;
+ break;
+ }
+ i += stuffing;
+ stuffing = 0;
+ break;
+ }
+ marker = 0xffffffff;
+ }
+ }
+ if (evoStreams.Count > 0)
+ {
+ sis = new StreamInfo[evoStreams.Values.Count];
+ evoStreams.Values.CopyTo(sis, 0);
+ }
+ }
+
+ public override PesPacket[] GetNextPesPackets()
+ {
+ // there was a big optimization opportunity here:
+ // this method added to partial packets one byte at a time
+ // the calls to PesPacket.AddData used about 40% of total CPU
+ byte[] inData = new byte[1];
+ byte[] inData2 = new byte[128];
+ int bytesRead = 0;
+ int bytesToRead = 0;
+ int bytesToGo = 0;
+ UInt32 marker = 0xffffffff;
+ PesPacket pp = null;
+ PesPacket partial = null;
+ int i = 0;
+ while (tsior.Read(inData, 0, inData.Length) == inData.Length)
+ {
+ marker = marker << 8;
+ marker &= 0xffffff00;
+ marker += inData[0];
+ if ((marker & 0xffffff00) == 0x00000100)
+ {
+ if ((marker & 0xff) == Constants.PACK_ID)
+ {
+ // pack start code
+ UInt64 header = 0;
+ for (i = 4; i < 10; i++)
+ {
+ if (tsior.Read(inData, 0, inData.Length) != inData.Length)
+ goto done;
+ header <<= 8;
+ header |= inData[0];
+ }
+ for (i = 10; i < 14; i++) // skip mux rate etc.
+ {
+ if (tsior.Read(inData, 0, inData.Length) != inData.Length)
+ goto done;
+ }
+ Int64 pcr = (Int64)((header & 0x380000000000) >> 13);
+ pcr |= (Int64)((header & 0x3fff8000000) >> 12);
+ pcr |= (Int64)((header & 0x3fff800) >> 11);
+ pcr *= 300;
+ pcr |= (Int64)((header & 0x3fe) >> 1);
+ Int64 delta = pcr - lastPcr;
+ if (delta > Constants.MPEG2TS_CLOCK_RATE / 9)
+ {
+ if (pcrDelegate != null)
+ pcrDelegate(pcr);
+ lastPcr = pcr;
+ }
+ }
+ else if (((marker & 0xff) == Constants.SYS_ID)
+ || ((marker & 0xff) == Constants.MAP_ID)
+ || ((marker & 0xff) == Constants.PAD_ID)
+ || ((marker & 0xff) == Constants.DIR_ID))
+ {
+ ushort Length = 0;
+ for (i = 4; i < 6; i++)
+ {
+ if (tsior.Read(inData, 0, inData.Length) != inData.Length)
+ goto done;
+ Length <<= 8;
+ Length |= inData[0];
+ }
+ Length += 6;
+ for (i = 6; i < Length; i++)
+ {
+ if (tsior.Read(inData, 0, inData.Length) != inData.Length)
+ goto done;
+ }
+ }
+ else
+ {
+ byte end = inData[0];
+ inData[0] = 0;
+ partial = new PesPacket(inData, 0, 1, 0);
+ partial.AddData(inData, 0, 1);
+ inData[0] = 1;
+ partial.AddData(inData, 0, 1);
+ inData[0] = end;
+ partial.AddData(inData, 0, 1);
+ ushort Length = 0;
+ for (i = 4; i < 6; i++)
+ {
+ if (tsior.Read(inData, 0, inData.Length) != inData.Length)
+ goto done;
+ partial.AddData(inData, 0, 1);
+ Length <<= 8;
+ Length |= inData[0];
+ }
+ Length += 6;
+ // we don't need this byte-by-byte loop here, as we have just gotten the length:
+ /*
+ for (i = 6; i < Length; i++)
+ {
+ if (tsior.Read(inData, 0, inData.Length) != inData.Length)
+ goto done;
+ partial.AddData(inData, 0, 1);
+ }
+ */
+ bytesToGo = Length - 6;
+ while (bytesToGo > 0)
+ {
+ bytesToRead = (bytesToGo > inData2.Length) ? inData2.Length : bytesToGo;
+ if ((bytesRead = tsior.Read(inData2, 0, bytesToRead)) != bytesToRead)
+ goto done;
+ partial.AddData(inData2, 0, bytesRead);
+ bytesToGo -= bytesRead;
+ }
+ pp = partial;
+ PesHeader ph = partial.GetHeader();
+ if (partial.BaseId == Constants.PES_PRIVATE1)
+ {
+ if ((partial.ExtendedId & 0xf0) == Constants.PES_PRIVATE_AC3_PLUS ||
+ (partial.ExtendedId & 0xf8) == Constants.PES_PRIVATE_AC3)
+ {
+ if (soundFrames.ContainsKey(partial.ExtendedId) == false)
+ soundFrames.Add(partial.ExtendedId, new List<byte>());
+ byte[] tempd = partial.GetPayload();
+ for (int j = 4; j < tempd.Length; j++)
+ soundFrames[partial.ExtendedId].Add(tempd[j]);
+ tempd = soundFrames[partial.ExtendedId].ToArray();
+ int len = 0;
+ ushort mk2 = 0xffff;
+ if (tempd.Length > 2 && tempd[0] == (byte)(Constants.AC3_SYNC >> 8) &&
+ tempd[1] == (byte)(Constants.AC3_SYNC & 0xff))
+ {
+ pp = null;
+ while (tempd.Length > 2 && tempd[0] == (byte)(Constants.AC3_SYNC >> 8)
+ && tempd[1] == (byte)(Constants.AC3_SYNC & 0xff))
+ {
+ AC3Info ac3 = new AC3Info(tempd, 0);
+ if (ac3.Valid == false || ac3.FrameLength == 0 || tempd.Length < ac3.FrameLength)
+ break;
+ if (pp == null)
+ pp = new PesPacket(partial.GetData(), 0, ph.TotalHeaderLength, partial.ExtendedId);
+ pp.AddData(tempd, 0, ac3.FrameLength);
+ soundFrames[partial.ExtendedId].RemoveRange(0, ac3.FrameLength);
+ tempd = soundFrames[partial.ExtendedId].ToArray();
+ }
+ if (pp != null)
+ {
+ pp.Complete = true;
+ goto done;
+ }
+ }
+ for (int j = 2; j < tempd.Length; j++)
+ {
+ mk2 <<= 8;
+ mk2 |= tempd[j];
+ if (mk2 == Constants.AC3_SYNC)
+ {
+ len = j - 1;
+ mk2 = 0xffff;
+ break;
+ }
+ }
+ if (len == 0)
+ len = tempd.Length;
+ soundFrames[partial.ExtendedId].RemoveRange(0, len);
+ pp = new PesPacket(partial.GetData(), 0, ph.TotalHeaderLength, partial.ExtendedId);
+ pp.AddData(tempd, 0, len);
+ pp.Complete = true;
+ }
+ else if ((partial.ExtendedId & 0xf8) == Constants.PES_PRIVATE_AC3_TRUE_HD)
+ {
+ if (soundFrames.ContainsKey(partial.ExtendedId) == false)
+ soundFrames.Add(partial.ExtendedId, new List<byte>());
+ byte[] tempd = partial.GetPayload();
+ for (int j = 5; j < tempd.Length; j++)
+ soundFrames[partial.ExtendedId].Add(tempd[j]);
+ tempd = soundFrames[partial.ExtendedId].ToArray();
+ int len = tempd.Length;
+ /*
+ UInt32 mk2 = 0xffffffff;
+ for (int j = 5; j < tempd.Length; j++)
+ {
+ mk2 <<= 8;
+ mk2 |= tempd[j];
+ if (mk2 == Constants.MLP_SYNC)
+ {
+ len = j - 3;
+ mk2 = 0xffffffff;
+ }
+ }
+ if (len == 0)
+ {
+ len = tempd.Length;
+ }
+ */
+ soundFrames[partial.ExtendedId].RemoveRange(0, len);
+ pp = new PesPacket(partial.GetData(), 0, ph.TotalHeaderLength, partial.ExtendedId);
+ pp.AddData(tempd, 0, len);
+ pp.Complete = true;
+ }
+ else if ((partial.ExtendedId & 0xf8) == Constants.PES_PRIVATE_LPCM)
+ {
+ if (soundFrames.ContainsKey(partial.ExtendedId) == false)
+ soundFrames.Add(partial.ExtendedId, new List<byte>());
+ byte[] tempd = partial.GetPayload();
+ for (int j = 7; j < tempd.Length; j++)
+ soundFrames[partial.ExtendedId].Add(tempd[j]);
+ tempd = soundFrames[partial.ExtendedId].ToArray();
+ int len = tempd.Length;
+ /*
+ UInt32 mk2 = 0xffffffff;
+ for (int j = 5; j < tempd.Length; j++)
+ {
+ mk2 <<= 8;
+ mk2 |= tempd[j];
+ if (mk2 == Constants.MLP_SYNC)
+ {
+ len = j - 3;
+ mk2 = 0xffffffff;
+ }
+ }
+ if (len == 0)
+ {
+ len = tempd.Length;
+ }
+ */
+ soundFrames[partial.ExtendedId].RemoveRange(0, len);
+ pp = new PesPacket(partial.GetData(), 0, ph.TotalHeaderLength, partial.ExtendedId);
+ pp.AddData(tempd, 0, len);
+ pp.Complete = true;
+ }
+ else if ((partial.ExtendedId & 0xf8) == Constants.PES_PRIVATE_DTS_HD)
+ {
+ if (soundFrames.ContainsKey(partial.ExtendedId) == false)
+ soundFrames.Add(partial.ExtendedId, new List<byte>());
+ byte[] tempd = partial.GetPayload();
+ for (int j = 4; j < tempd.Length; j++)
+ soundFrames[partial.ExtendedId].Add(tempd[j]);
+ tempd = soundFrames[partial.ExtendedId].ToArray();
+ int len = 0;
+ UInt32 mk2 = 0xffffffff;
+ for (int j = 4; j < tempd.Length; j++)
+ {
+ mk2 <<= 8;
+ mk2 |= tempd[j];
+ if (mk2 == Constants.DTS_SYNC)
+ {
+ len = j - 3;
+ mk2 = 0xffffffff;
+ }
+ }
+ if (len == 0)
+ {
+ DtsInfo dts = new DtsInfo(tempd, 0);
+ if (dts.Valid && (int)dts.FrameSize < tempd.Length &&
+ (tempd.Length - (int)dts.FrameSize < 4))
+ len = (int)dts.FrameSize;
+ else
+ len = tempd.Length;
+ }
+ soundFrames[partial.ExtendedId].RemoveRange(0, len);
+ pp = new PesPacket(partial.GetData(), 0, ph.TotalHeaderLength, partial.ExtendedId);
+ pp.AddData(tempd, 0, len);
+ pp.Complete = true;
+ }
+ else
+ pp.PID = pp.ExtendedId;
+ }
+ else if (pp.BaseId == Constants.PES_PADDING || pp.BaseId == Constants.PES_PRIVATE2)
+ {
+ marker = 0xffffffff;
+ continue;
+ }
+ else if (((pp.BaseId & 0xe0) == Constants.PES_AUDIO_MPEG)
+ || ((pp.BaseId & 0xf0) == Constants.PES_VIDEO))
+ pp.PID = pp.BaseId;
+ else if (pp.BaseId == Constants.PES_VIDEO_VC1 && ph != null)
+ pp.PID = ph.Extention2;
+ if (ph != null && ph.HasPts)
+ {
+ if (ptsDelegate != null)
+ ptsDelegate(ph.Pts, pp.PID);
+ }
+ goto done;
+ }
+ marker = 0xffffffff;
+ }
+ }
+ done:
+ if (null != pp)
+ {
+ PesPacket[] ppa = new PesPacket[1];
+ ppa[0] = pp;
+ return ppa;
+ }
+ else
+ return null;
+ }
+
+ public override void Seek(long pcr)
+ {
+ tsior.Clear();
+
+ if (pcr == -1)
+ {
+ fs.Seek(0, SeekOrigin.Begin);
+ return;
+ }
+
+ int packetSize = sizeof(byte);
+ Int64 span = endPcr - startPcr;
+ if (span < 0)
+ span += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 pcrPerByte = span / fs.Length;
+ Int64 seek = pcr - startPcr;
+ if (seek < 0)
+ seek += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 length = seek / pcrPerByte;
+ length /= packetSize;
+ length *= packetSize;
+ if (length > fs.Length)
+ length = fs.Length;
+ fs.Seek(length, SeekOrigin.Begin);
+ bool found = false;
+
+ while (found == false)
+ {
+ Int64 offset = GetCurrentPcrFromFile();
+ if (-1 == offset)
+ offset = endPcr;
+ offset -= pcr;
+ if (offset > Constants.MAX_MPEG2TS_CLOCK / 2)
+ offset -= Constants.MAX_MPEG2TS_CLOCK;
+ else if (offset < (0 - (Constants.MAX_MPEG2TS_CLOCK / 2)))
+ offset += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 target = offset / Constants.MPEG2TS_CLOCK_RATE;
+ if (target > 0)
+ {
+ offset /= pcrPerByte;
+ offset >>= 2;
+ offset /= packetSize;
+ offset *= packetSize;
+ fs.Seek(0 - offset, SeekOrigin.Current);
+ }
+ else if (target < 0)
+ {
+ offset /= pcrPerByte;
+ offset >>= 2;
+ offset /= packetSize;
+ offset *= packetSize;
+ fs.Seek(0 - offset, SeekOrigin.Current);
+ }
+ else
+ break;
+ }
+ }
+
+ private void GetTimeStamps()
+ {
+ fs.Seek(0, SeekOrigin.Begin);
+ UInt64 header = 0xffffffff;
+ byte b = 0;
+ int packsize_reading = -1;
+ for (int i = 0; i < Constants.DISK_BUFFER; i++)
+ {
+ int ib = fs.ReadByte();
+ if (ib == -1)
+ throw new FormatException("The specified file is too short");
+ b = (byte)(ib & 0xff);
+ header <<= 8;
+ header |= b;
+ if ((header & 0xffffffff) == 0x000001ba)
+ {
+ // pack start code
+ packsize_reading = 5;
+ }
+ else if (packsize_reading > 0)
+ packsize_reading--;
+ else if (packsize_reading == 0)
+ {
+ startPcr = (Int64)((header & 0x380000000000) >> 13);
+ startPcr |= (Int64)((header & 0x3fff8000000) >> 12);
+ startPcr |= (Int64)((header & 0x3fff800) >> 11);
+ startPcr *= 300;
+ startPcr |= (Int64)((header & 0x3fe) >> 1);
+ break;
+ }
+ }
+ int lseek = -15;
+ fs.Seek(lseek, SeekOrigin.End);
+ header = 0xffffffff;
+ b = 0;
+ packsize_reading = -1;
+ for (int i = 0; i < Constants.DISK_BUFFER; i++)
+ {
+ for (int j = 0; j < 12; j++)
+ {
+ int ib = fs.ReadByte();
+ if (ib == -1)
+ throw new FormatException("The specified file is too short");
+ b = (byte)(ib & 0xff);
+ header <<= 8;
+ header |= b;
+ if ((header & 0xffffffff) == 0x000001ba)
+ {
+ // pack start code
+ packsize_reading = 5;
+ }
+ else if (packsize_reading > 0)
+ packsize_reading--;
+ else if (packsize_reading == 0)
+ {
+ endPcr = (Int64)((header & 0x380000000000) >> 13);
+ endPcr |= (Int64)((header & 0x3fff8000000) >> 12);
+ endPcr |= (Int64)((header & 0x3fff800) >> 11);
+ endPcr *= 300;
+ endPcr |= (Int64)((header & 0x3fe) >> 1);
+ break;
+ }
+ }
+ if (packsize_reading == 0)
+ break;
+ lseek--;
+ fs.Seek(lseek, SeekOrigin.End);
+
+ }
+ }
+
+ private byte GetVC1SubstreamId(byte[] buff, int offset)
+ {
+ if (buff.Length <= offset + 3)
+ return 0;
+ if ((buff[offset] & 0xc0) != 0x80)
+ return 0; // first two bits must be '10'
+ byte PTS_DTS_flags = (byte)(buff[offset + 1] & 0xc0);
+ byte ESCR_flag = (byte)(buff[offset + 1] & 0x20);
+ byte ES_rate_flag = (byte)(buff[offset + 1] & 0x10);
+ byte DSM_trick_mode_flag = (byte)(buff[offset + 1] & 0x08);
+ byte additional_copy_info_flag = (byte)(buff[offset + 1] & 0x04);
+ byte PES_CRC_flag = (byte)(buff[offset + 1] & 0x02);
+ byte PES_extension_flag = (byte)(buff[offset + 1] & 0x01);
+ if (buff[offset + 2] == 0)
+ return 0;
+ int length = offset + buff[offset + 2] + 3;
+ if (buff.Length < length)
+ return 0;
+ offset += 3;
+ if (PTS_DTS_flags == 0x80)
+ offset += 5;
+ if (PTS_DTS_flags == 0xc0)
+ offset += 10;
+ if (ESCR_flag > 0)
+ offset += 6;
+ if (ES_rate_flag > 0)
+ offset += 3;
+ if (DSM_trick_mode_flag > 0)
+ offset += 1;
+ if (additional_copy_info_flag > 0)
+ offset += 1;
+ if (PES_CRC_flag > 0)
+ offset += 2;
+ if (PES_extension_flag == 0)
+ return 0;
+ byte PES_private_data_flag = (byte)(buff[offset] & 0x80);
+ byte pack_header_field_flag = (byte)(buff[offset] & 0x40);
+ byte program_packet_sequence_counter_flag = (byte)(buff[offset] & 0x20);
+ byte PSTD_buffer_flag = (byte)(buff[offset] & 0x10);
+ byte PES_extension_flag_2 = (byte)(buff[offset] & 0x01);
+ offset++;
+ if (PES_private_data_flag > 0)
+ offset += 25;
+ if (pack_header_field_flag > 0)
+ offset += (buff[offset] + 1);
+ if (program_packet_sequence_counter_flag > 0)
+ offset += 2;
+ if (PSTD_buffer_flag > 0)
+ offset += 2;
+ if (PES_extension_flag_2 == 0)
+ return 0;
+ if (buff[offset] != 0x81)
+ return 0;
+ return buff[offset + 1];
+ }
+
+ private Int64 GetCurrentPcrFromFile()
+ {
+ UInt64 header = 0xffffffff;
+ byte b = 0;
+ int packsize_reading = -1;
+ for (int i = 0; i < Constants.DISK_BUFFER; i++)
+ {
+ int ib = fs.ReadByte();
+ if (ib == -1)
+ return -1;
+ b = (byte)(ib & 0xff);
+ header <<= 8;
+ header |= b;
+ if ((header & 0xffffffff) == 0x000001ba)
+ {
+ // pack start code
+ packsize_reading = 5;
+ }
+ else if (packsize_reading > 0)
+ packsize_reading--;
+ else if (packsize_reading == 0)
+ {
+ Int64 pcr = (Int64)((header & 0x380000000000) >> 13);
+ pcr |= (Int64)((header & 0x3fff8000000) >> 12);
+ pcr |= (Int64)((header & 0x3fff800) >> 11);
+ pcr *= 300;
+ pcr |= (Int64)((header & 0x3fe) >> 1);
+ return pcr;
+ }
+ }
+ return -1;
+ }
+ }
+
+ public class SupPesFile : PesFile
+ {
+ private static readonly byte[] PesTemplate = new byte[4] {
+ 0x00, 0x00, 0x01, 0xbd};
+
+ public SupPesFile(BackgroundWorker bw) : base(bw) { }
+
+ public override DTCP_Descriptor DtcpInfo
+ {
+ get { return null; }
+ }
+
+ protected override void GetInitialValues()
+ {
+ GetTimeStamps();
+ this.sis = new StreamInfo[1];
+ this.sis[0] = new StreamInfo(ElementaryStreamTypes.PRESENTATION_GRAPHICS_STREAM, Constants.DEFAULT_PRESENTATION_GRAPHICS_PID);
+ }
+
+ public override PesPacket[] GetNextPesPackets()
+ {
+ byte b0 = 0;
+ byte b1 = 0;
+ byte[] inData = new byte[1];
+ byte[] pgHeader = new byte[3];
+
+ while (tsior.Read(inData, 0, inData.Length) == inData.Length)
+ {
+ b0 = b1;
+ b1 = inData[0];
+ if ((b0 == (byte)'P' || b0 == (byte)'p') &&
+ (b1 == (byte)'G' || b1 == (byte)'g'))
+ {
+ Int64 Pts = 0;
+ Int64 Dts = 0;
+ ushort len = 0;
+ for (int k = 0; k < 4; k++)
+ {
+ if (0 == tsior.Read(inData, 0, inData.Length))
+ return null;
+ Pts <<= 8;
+ Pts |= inData[0];
+ }
+ Pts <<= 1;
+ if (ptsDelegate != null)
+ ptsDelegate(Pts, sis[0].ElementaryPID);
+ for (int k = 0; k < 4; k++)
+ {
+ if (0 == tsior.Read(inData, 0, inData.Length))
+ return null;
+ Dts <<= 8;
+ Dts |= inData[0];
+ }
+ Dts <<= 1;
+ for (int k = 0; k < 3; k++)
+ {
+ if (0 == tsior.Read(inData, 0, inData.Length))
+ return null;
+ pgHeader[k] = inData[0];
+ if (k > 0)
+ {
+ len <<= 8;
+ len |= inData[0];
+ }
+ }
+ byte hlen = (byte)(Dts == 0 ? 0x05 : 0x0a);
+ ushort plen = (ushort)(6 + hlen + len);
+ PesPacket pp = new PesPacket(PesTemplate, 0, PesTemplate.Length, sis[0].ElementaryPID);
+ inData[0] = (byte)((plen >> 8) & 0xff);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)(plen & 0xff);
+ pp.AddData(inData, 0, 1);
+ inData[0] = 0x81;
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)(Dts == 0 ? 0x80 : 0xc0);
+ pp.AddData(inData, 0, 1);
+ inData[0] = hlen;
+ pp.AddData(inData, 0, 1);
+ byte old = (byte)(Dts == 0 ? 0x21 : 0x31);
+ inData[0] = (byte)(((Pts & 0x1C0000000) >> 29) | old);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)((Pts & 0x3fC00000) >> 22);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)(((Pts & 0x3f8000) >> 14) | 0x01);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)((Pts & 0x7f80) >> 7);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)(((Pts & 0x7f) << 1) | 0x01);
+ pp.AddData(inData, 0, 1);
+ if (Dts != 0)
+ {
+ inData[0] = (byte)(((Dts & 0x1C0000000) >> 29) | 0x11);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)((Dts & 0x3fC00000) >> 22);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)(((Dts & 0x3f8000) >> 14) | 0x01);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)((Dts & 0x7f80) >> 7);
+ pp.AddData(inData, 0, 1);
+ inData[0] = (byte)(((Dts & 0x7f) << 1) | 0x01);
+ pp.AddData(inData, 0, 1);
+ }
+ pp.AddData(pgHeader, 0, pgHeader.Length);
+ for (int k = 0; k < len; k++)
+ {
+ if (0 == tsior.Read(inData, 0, inData.Length))
+ return null;
+ pp.AddData(inData, 0, 1);
+ }
+ PesPacket[] ppa = new PesPacket[1];
+ ppa[0] = pp;
+ return ppa;
+ }
+ }
+ return null;
+ }
+
+ public override void Seek(long pcr)
+ {
+ tsior.Clear();
+
+ if (pcr == -1)
+ {
+ fs.Seek(0, SeekOrigin.Begin);
+ return;
+ }
+
+ int packetSize = sizeof(byte);
+ Int64 span = endPcr - startPcr;
+ if (span < 0)
+ span += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 pcrPerByte = span / fs.Length;
+ Int64 seek = pcr - startPcr;
+ if (seek < 0)
+ seek += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 length = seek / pcrPerByte;
+ length /= packetSize;
+ length *= packetSize;
+ if (length > fs.Length)
+ length = fs.Length;
+ fs.Seek(length, SeekOrigin.Begin);
+ bool found = false;
+
+ while (found == false)
+ {
+ Int64 offset = GetCurrentPcrFromFile();
+ if (-1 == offset)
+ offset = endPcr;
+ offset -= pcr;
+ if (offset > Constants.MAX_MPEG2TS_CLOCK / 2)
+ offset -= Constants.MAX_MPEG2TS_CLOCK;
+ else if (offset < (0 - (Constants.MAX_MPEG2TS_CLOCK / 2)))
+ offset += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 target = offset / Constants.MPEG2TS_CLOCK_RATE;
+ if (target > 0)
+ {
+ offset /= pcrPerByte;
+ offset >>= 2;
+ offset /= packetSize;
+ offset *= packetSize;
+ fs.Seek(0 - offset, SeekOrigin.Current);
+ }
+ else if (target < 0)
+ {
+ offset /= pcrPerByte;
+ offset >>= 2;
+ offset /= packetSize;
+ offset *= packetSize;
+ fs.Seek(0 - offset, SeekOrigin.Current);
+ }
+ else
+ break;
+ }
+ }
+
+ private Int64 GetCurrentPcrFromFile()
+ {
+ byte b0 = 0;
+ byte b1 = 0;
+ Int64 pcr = 0;
+ while (true)
+ {
+ int ib = fs.ReadByte();
+ if (ib == -1)
+ return -1;
+ b0 = b1;
+ b1 = (byte)(ib & 0xff);
+ if ((b0 == (byte)'P' || b0 == (byte)'p') &&
+ (b1 == (byte)'G' || b1 == (byte)'g'))
+ {
+ pcr = 0;
+ for (int k = 0; k < 4; k++)
+ {
+ ib = fs.ReadByte();
+ if (ib == -1)
+ return -1;
+ b1 = (byte)(ib & 0xff);
+ pcr <<= 8;
+ pcr |= b1;
+ }
+ pcr *= (Constants.MPEG2TS_CLOCK_RATE / Constants.PTS_CLOCK_RATE);
+ break;
+ }
+ }
+ return pcr;
+ }
+
+ private void GetTimeStamps()
+ {
+ fs.Seek(0, SeekOrigin.Begin);
+ byte b0 = 0;
+ byte b1 = 0;
+ for (int i = 0; i < Constants.DISK_BUFFER; i++)
+ {
+ int ib = fs.ReadByte();
+ if (ib == -1)
+ throw new FormatException("The specified file is too short");
+ b0 = b1;
+ b1 = (byte)(ib & 0xff);
+ if ((b0 == (byte)'P' || b0 == (byte)'p') &&
+ (b1 == (byte)'G' || b1 == (byte)'g'))
+ {
+ startPcr = 0;
+ for (int k = 0; k < 4; k++)
+ {
+ ib = fs.ReadByte();
+ if (ib == -1)
+ throw new FormatException("The specified file is too short");
+ b1 = (byte)(ib & 0xff);
+ startPcr <<= 8;
+ startPcr |= b1;
+ }
+ startPcr *= (Constants.MPEG2TS_CLOCK_RATE / Constants.PTS_CLOCK_RATE);
+ break;
+ }
+ }
+ int lseek = -10;
+ fs.Seek(lseek, SeekOrigin.End);
+ b0 = b1 = 0;
+ bool found = false;
+ for (int i = 0; i < Constants.DISK_BUFFER; i++)
+ {
+ for (int j = 0; j < 2; j++)
+ {
+ int ib = fs.ReadByte();
+ if (ib == -1)
+ throw new FormatException("The specified file is too short");
+ b0 = b1;
+ b1 = (byte)(ib & 0xff);
+ if ((b0 == (byte)'P' || b0 == (byte)'p') &&
+ (b1 == (byte)'G' || b1 == (byte)'g'))
+ {
+ endPcr = 0;
+ for (int k = 0; k < 4; k++)
+ {
+ ib = fs.ReadByte();
+ if (ib == -1)
+ throw new FormatException("The specified file is too short");
+ b1 = (byte)(ib & 0xff);
+ endPcr <<= 8;
+ endPcr |= b1;
+ }
+ endPcr *= (Constants.MPEG2TS_CLOCK_RATE / Constants.PTS_CLOCK_RATE);
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ break;
+ lseek--;
+ fs.Seek(lseek, SeekOrigin.End);
+ }
+ }
+ }
+
+ public class TsPesFile : PesFile
+ {
+ private int stride;
+ private byte[] tsPack;
+ TsPacket ts;
+ private Dictionary<ushort, PesPacket> PesPackets;
+ private DTCP_Descriptor dt;
+ private ushort pcrPID;
+
+ public TsPesFile(BackgroundWorker bw, int offset)
+ : base(bw)
+ {
+ stride = offset;
+ tsPack = new byte[Constants.TS_SIZE + offset];
+ ts = new TsPacket();
+ PesPackets = new Dictionary<ushort, PesPacket>();
+ dt = null;
+ pcrPID = 0xffff;
+ }
+
+ public override DTCP_Descriptor DtcpInfo
+ {
+ get { return dt; }
+ }
+
+ public override PesPacket[] GetNextPesPackets()
+ {
+ int i;
+ while (true)
+ {
+ for (i = tsior.Read(tsPack, 0, 1 + stride); (i > 0) && (tsPack[stride] != Constants.SYNC_BYTE); i = tsior.Read(tsPack, stride, 1))
+ ; // ensure a good sync byte
+ if (i == 0)
+ {
+ List<PesPacket> ppa = new List<PesPacket>();
+ foreach (ushort pd in PesPackets.Keys)
+ {
+ PesPacket pp = PesPackets[pd];
+ pp.Complete = true;
+ ppa.Add(pp);
+ }
+ PesPackets.Clear();
+ if (ppa.Count == 0)
+ return null; // end of stream
+ else
+ {
+ return ppa.ToArray();
+ }
+ }
+ i = tsior.Read(tsPack, stride + 1, tsPack.Length - (stride + 1));
+ if (i < tsPack.Length - (stride + 1))
+ {
+ List<PesPacket> ppa = new List<PesPacket>();
+ foreach (ushort pd in PesPackets.Keys)
+ {
+ PesPacket pp = PesPackets[pd];
+ pp.Complete = true;
+ ppa.Add(pp);
+ }
+ PesPackets.Clear();
+ if (ppa.Count == 0)
+ return null; // end of stream
+ else
+ return ppa.ToArray();
+ }
+ ts.SetData(tsPack, stride);
+ ushort pid = ts.PID;
+ byte[] payload = ts.Payload;
+ if (ts.HasPcr)
+ {
+ if (null != pcrDelegate && pcrPID == pid)
+ pcrDelegate(ts.Pcr);
+ }
+ if (ts.HasPesHeader)
+ {
+ if (payload != null && payload.Length > 3 && payload[0] == 0x00
+ && payload[1] == 0x00 && payload[2] == 0x01)
+ {
+ PesPacket newpp = new PesPacket(payload, 0, payload.Length, pid);
+ newpp.Priority = ts.Priority;
+ PesHeader ph = newpp.GetHeader();
+ if (ph != null && ph.HasPts)
+ {
+ if (ptsDelegate != null)
+ ptsDelegate(ph.Pts, pid);
+ }
+ if (PesPackets.ContainsKey(pid))
+ {
+ PesPacket pp = PesPackets[ts.PID];
+ pp.Complete = true;
+ PesPackets[ts.PID] = newpp;
+ PesPacket[] ppa = new PesPacket[1];
+ ppa[0] = pp;
+ return ppa;
+ }
+ else
+ {
+ if (newpp.Complete == true)
+ {
+ PesPacket[] ppa = new PesPacket[1];
+ ppa[0] = newpp;
+ return ppa;
+ }
+ PesPackets[ts.PID] = newpp;
+ }
+ }
+ else
+ {
+ throw new InvalidDataException("invalid stream");
+ }
+ }
+ else if (PesPackets.ContainsKey(pid))
+ {
+ PesPacket pp = PesPackets[ts.PID];
+ if (payload != null)
+ {
+ pp.AddData(payload, 0, payload.Length);
+ if (pp.Complete)
+ {
+ PesPackets.Remove(pid);
+ PesPacket[] ppa = new PesPacket[1];
+ ppa[0] = pp;
+ return ppa;
+ }
+ }
+ }
+ }
+ }
+
+ private void GetTimestamps()
+ {
+ int i = 0;
+
+ startPcr = -1;
+ endPcr = -1;
+ if(null != tsior)
+ tsior.Clear();
+ fs.Seek(0, SeekOrigin.Begin);
+
+ while (true)
+ {
+ for (i = tsior.Read(tsPack, 0, 1 + stride); (i > 0) && (tsPack[stride] != Constants.SYNC_BYTE); i = tsior.Read(tsPack, stride, 1))
+ ; // ensure a good sync byte
+ if (i == 0)
+ return; // end of stream
+ i = tsior.Read(tsPack, stride + 1, tsPack.Length - (stride + 1));
+ if (i < tsPack.Length - (stride + 1))
+ return; // end of stream
+ ts.SetData(tsPack, stride);
+ if (ts.HasPcr)
+ {
+ startPcr = ts.Pcr;
+ pcrPID = ts.PID;
+ break;
+ }
+ if (fs.Position > (Constants.DISK_BUFFER << 1))
+ return;
+ }
+ tsior.Clear();
+ int packSize = Constants.TS_SIZE + stride;
+ fs.Seek(0, SeekOrigin.End);
+ for (i = packSize; (fs.Length - fs.Position) < Constants.DISK_BUFFER; i += packSize)
+ {
+ fs.Seek(0 - i, SeekOrigin.End);
+ if (fs.Read(tsPack, 0, packSize) == packSize)
+ {
+ if (tsPack[stride] != Constants.SYNC_BYTE)
+ {
+ i++;
+ i -= packSize;
+ continue;
+ }
+ ts.SetData(tsPack, stride);
+ if (ts.HasPcr && pcrPID == ts.PID)
+ {
+ endPcr = ts.Pcr;
+ Seek(-1);
+ return;
+ }
+ }
+ else
+ break;
+ }
+ endPcr = startPcr = -1;
+ }
+
+ protected override void GetInitialValues()
+ {
+ GetTimestamps();
+ Seek(-1);
+ PatPacket pp = null;
+ PmtPacket pm = null;
+ bool bluray = false;
+ bool validPmt = false;
+ ushort pmtPid = 0;
+ int i = 0;
+ Dictionary<ushort, StreamInfo> streams = new Dictionary<ushort, StreamInfo>();
+ while (true)
+ {
+ for (i = tsior.Read(tsPack, 0, 1 + stride); (i > 0) && (tsPack[stride] != Constants.SYNC_BYTE); i = tsior.Read(tsPack, stride, 1))
+ ; // ensure a good sync byte
+ if (i == 0)
+ break; // end of stream
+ i = tsior.Read(tsPack, stride + 1, tsPack.Length - (stride + 1));
+ if (i < tsPack.Length - (stride + 1))
+ break; // end of stream
+ ts.SetData(tsPack, stride);
+ if (ts.PID == Constants.PAT_PID)
+ {
+ pp = new PatPacket(ts.GetData());
+ ProgramInfo[] pi = pp.Programs;
+ if (null != pi && pi.Length == 2)
+ {
+ if ((pi[0].ProgramNumber == 0 && pi[0].ProgramPID == 0x001f && pi[1].ProgramNumber == 1 && pi[1].ProgramPID == 0x0100)
+ || (pi[1].ProgramNumber == 0 && pi[1].ProgramPID == 0x001f && pi[0].ProgramNumber == 1 && pi[0].ProgramPID == 0x0100))
+ {
+ bluray = true;
+ }
+ }
+ else if (null != pi && pi[0] != null && pi[0].ProgramNumber == 1)
+ pmtPid = pi[0].ProgramPID;
+ }
+ else if (bluray && ts.PID == 0x0100)
+ {
+ if (pm == null)
+ pm = new PmtPacket(ts.GetData());
+ else if (pm.Complete == false)
+ pm.AddData(ts.Payload, 0, ts.Payload.Length);
+ if (pm.Complete)
+ {
+ sis = pm.ElementaryStreams;
+ dt = pm.DtcpInfo;
+ return;
+ }
+ }
+ else if (ts.PID == pmtPid)
+ {
+ pm = new PmtPacket(ts.GetData());
+ sis = pm.ElementaryStreams;
+ dt = pm.DtcpInfo;
+ }
+ else if (streams.ContainsKey(ts.PID) == false && ts.HasPesHeader)
+ {
+ byte[] payload = ts.Payload;
+ PesHeader ph = new PesHeader(ts.Payload);
+ PesPacket pes = new PesPacket(payload, 0, payload.Length, ts.PID);
+ if (ph.StreamId == Constants.PES_PRIVATE1)
+ {
+ byte[] audio = pes.GetPayload();
+ AC3Info ac3 = new AC3Info(audio, 0);
+ DtsInfo dts = new DtsInfo(audio, 0);
+ MlpInfo mlp = new MlpInfo(audio, 0);
+ if (ac3.Valid)
+ {
+ if (ac3.SyntaxType == Ac3SyntaxType.Standard || ac3.SyntaxType == Ac3SyntaxType.Alternative)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_AC3, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ else if (ac3.SyntaxType == Ac3SyntaxType.Enhanced)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_AC3_PLUS, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ }
+ else if (dts.Valid)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_DTS_HD, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ else if (mlp.Valid)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_AC3_TRUE_HD, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ else if ((pes.ExtendedId & 0xf8) == Constants.PES_PRIVATE_LPCM)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_LPCM, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ }
+ else if ((ph.StreamId & 0xe0) == Constants.PES_AUDIO_MPEG)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.AUDIO_STREAM_MPEG2, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ else if ((ph.StreamId & 0xf0) == Constants.PES_VIDEO)
+ {
+ UInt32 format = pes.ExtendedType;
+ if ((format & 0xffff) == 0x1)
+ {
+ }
+ else if ((format & 0xffffff00) == 0x100)
+ {
+ }
+
+ H264Info h264 = new H264Info(payload, 0);
+ Mpeg2Info mpg2 = new Mpeg2Info(payload, 0);
+ if (h264.Valid)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.VIDEO_STREAM_H264, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ else if (mpg2.Valid)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.VIDEO_STREAM_MPEG2, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ }
+ else if (ph.StreamId == Constants.PES_VIDEO_VC1)
+ {
+ StreamInfo si = new StreamInfo(ElementaryStreamTypes.VIDEO_STREAM_VC1, ts.PID);
+ streams.Add(ts.PID, si);
+ }
+ }
+ if (sis != null)
+ {
+ validPmt = true;
+ foreach (StreamInfo si in sis)
+ {
+ if (streams.ContainsKey(si.ElementaryPID) == false)
+ {
+ validPmt = false;
+ break;
+ }
+ }
+ if (validPmt)
+ return;
+ }
+ if (fs.Position > (Constants.DISK_BUFFER << 1))
+ break;
+ }
+ sis = new StreamInfo[streams.Values.Count];
+ streams.Values.CopyTo(sis, 0);
+ }
+
+ private Int64 GetCurrentPcrFromFile()
+ {
+ int packetSize = 0;
+ if (fileType == TsFileType.TS)
+ packetSize = Constants.TS_SIZE;
+ else if (fileType == TsFileType.M2TS)
+ packetSize = Constants.STRIDE_SIZE;
+ byte[] inBuff = new byte[packetSize];
+ TsPacket tp = new TsPacket();
+ Int64 filePos = fs.Position;
+
+ while (fs.Read(inBuff, 0, packetSize) == packetSize && fs.Position < (filePos + 1000000))
+ {
+ if (packetSize == Constants.STRIDE_SIZE)
+ tp.SetData(inBuff, 4);
+ else if (packetSize == Constants.TS_SIZE)
+ tp.SetData(inBuff, 0);
+ if (tp.HasPcr)
+ {
+ fs.Seek(filePos, SeekOrigin.Begin);
+ return tp.Pcr;
+ }
+ }
+ return -1;
+ }
+
+ public override void Seek(Int64 pcr)
+ {
+ tsior.Clear();
+ PesPackets.Clear();
+
+ if (pcr == -1)
+ {
+ fs.Seek(0, SeekOrigin.Begin);
+ return;
+ }
+
+ int packetSize = 0;
+ if (fileType == TsFileType.TS)
+ packetSize = Constants.TS_SIZE;
+ else if (fileType == TsFileType.M2TS)
+ packetSize = Constants.STRIDE_SIZE;
+ else
+ packetSize = sizeof(byte);
+ Int64 span = endPcr - startPcr;
+ if (span < 0)
+ span += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 pcrPerByte = span / fs.Length;
+ Int64 seek = pcr - startPcr;
+ if (seek < 0)
+ seek += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 length = seek / pcrPerByte;
+ length /= packetSize;
+ length *= packetSize;
+ if (length > fs.Length)
+ length = fs.Length;
+ fs.Seek(length, SeekOrigin.Begin);
+
+ for(int i = 0; i < 100; i++)
+ {
+ Int64 offset = GetCurrentPcrFromFile();
+ if (-1 == offset)
+ offset = endPcr;
+ offset -= pcr;
+ if (offset > Constants.MAX_MPEG2TS_CLOCK / 2)
+ offset -= Constants.MAX_MPEG2TS_CLOCK;
+ else if (offset < (0 - (Constants.MAX_MPEG2TS_CLOCK / 2)))
+ offset += Constants.MAX_MPEG2TS_CLOCK;
+ Int64 target = offset / Constants.MPEG2TS_CLOCK_RATE;
+ if (target > 0)
+ {
+ offset /= pcrPerByte;
+ offset >>= 2;
+ offset /= packetSize;
+ offset *= packetSize;
+ fs.Seek(0 - offset, SeekOrigin.Current);
+ }
+ else if (target < 0)
+ {
+ offset /= pcrPerByte;
+ offset >>= 2;
+ offset /= packetSize;
+ offset *= packetSize;
+ fs.Seek(0 - offset, SeekOrigin.Current);
+ }
+ else
+ break;
+ }
+ }
+ }
+
+public class TsIo
+{
+TsIo::TsIo(FileStream read, FileStream write)
+{
+ init(read, write, Constants::DISK_BUFFER);
+}
+
+TsIo::TsIo(FileStream read, FileStream write, int buffsize)
+{
+ init(read, write, buffsize);
+}
+
+TsIo::init(FileStream read, FileStream write, int buffsize)
+{
+ fsr = read;
+ fsw = write;
+ if (fsr != null)
+ {
+ ReadBuffer1 = pByte(new byte[buffsize]);
+ ReadBuffer2 = pByte(new byte[buffsize]);
+ ReadBuffer = ReadBuffer1;
+ }
+ else
+ {
+ ReadBuffer.reset();
+ ReadBuffer1.reset();
+ ReadBuffer2.reset();
+ }
+ if (fsw != null)
+ {
+ WriteBuffer1 = pByte(new byte[buffsize]);
+ WriteBuffer2 = pByte(new byte[buffsize]);
+ WriteBuffer = WriteBuffer1;
+ }
+ else
+ {
+ WriteBuffer.reset();
+ WriteBuffer1.reset();
+ WriteBuffer2.reset();
+ }
+ readIndex = 0;
+ readSize = 0;
+ writeIndex = 0;
+ asyncRead = null;
+ asyncWrite = null;
+}
+
+int TsIo::Read(byte[] target, int offset, int count)
+ {
+ if (readIndex == readSize)
+ {
+ // this buffer is finished, get next one
+ if (asyncRead != null)
+ {
+ if (asyncRead.IsCompleted == false)
+ GC.Collect(); // garbage collection during spare cycles
+ readSize = fsr.EndRead(asyncRead);
+ if (ReadBuffer == ReadBuffer1)
+ ReadBuffer = ReadBuffer2;
+ else ReadBuffer = ReadBuffer1;
+ readIndex = 0;
+ if (readSize == 0)
+ {
+ asyncRead = null;
+ return 0; // end of file
+ }
+ }
+ else
+ {
+ // one time init - prepopulate buffer
+ readSize = fsr.Read(ReadBuffer, 0, ReadBuffer.Length);
+ if (readSize == 0)
+ return 0;
+ readIndex = 0;
+ }
+ if (ReadBuffer == ReadBuffer1)
+ asyncRead = fsr.BeginRead(ReadBuffer2, 0, ReadBuffer2.Length, null, null);
+ else
+ asyncRead = fsr.BeginRead(ReadBuffer1, 0, ReadBuffer1.Length, null, null);
+ }
+ if (readIndex < readSize)
+ {
+ int len = count;
+ while (len > (readSize - readIndex))
+ {
+ int ret = Read(target, offset, (int)(readSize - readIndex));
+ offset += ret;
+ len -= ret;
+ if (ret == 0)
+ break;
+ }
+ if (len <= (readSize - readIndex))
+ {
+ // this optimization saves a LOT of time in PS files
+ if (len == 1)
+ target[offset] = ReadBuffer[readIndex];
+ else
+ Array.Copy(ReadBuffer, readIndex, target, offset, len);
+ readIndex += len;
+ return count;
+ }
+ else
+ {
+ return count - len;
+ }
+ }
+ throw new ArgumentException("read index out of bounds");
+ }
+
+ public void Write(byte[] target, int offset, int count)
+ {
+ if (writeIndex == WriteBuffer.Length)
+ {
+ // this buffer is finished, get next one
+ if (asyncWrite != null)
+ {
+ if (asyncWrite.IsCompleted == false)
+ GC.Collect(); // garbage collection during spare cycles
+ fsw.EndWrite(asyncWrite);
+ }
+ asyncWrite = fsw.BeginWrite(WriteBuffer, 0, WriteBuffer.Length, null, null);
+ if (WriteBuffer == WriteBuffer1)
+ WriteBuffer = WriteBuffer2;
+ else
+ WriteBuffer = WriteBuffer1;
+ writeIndex = 0;
+ }
+ if (writeIndex < WriteBuffer.Length)
+ {
+ while (count > (int)(WriteBuffer.Length - writeIndex))
+ {
+ int diff = (int)(WriteBuffer.Length - writeIndex);
+ Write(target, offset, diff);
+ offset += diff;
+ count -= diff;
+ }
+ Array.Copy(target, offset, WriteBuffer, writeIndex, count);
+ writeIndex += count;
+ return;
+ }
+ throw new ArgumentException("write index out of bounds");
+ }
+
+ public void Flush()
+ {
+ if (asyncWrite != null)
+ {
+ fsw.EndWrite(asyncWrite);
+ asyncWrite = null;
+ }
+ if (writeIndex > 0)
+ {
+ fsw.Write(WriteBuffer, 0, (int)writeIndex);
+ writeIndex = 0;
+ }
+ }
+
+ public void Clear()
+ {
+ if (asyncRead != null)
+ {
+ fsr.EndRead(asyncRead);
+ asyncRead = null;
+ }
+ readIndex = readSize = 0;
+ }
+ }
+}
+
}