/* * (C) 2003-2006 Gabest * (C) 2006-2013 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "stdafx.h" #include #include "MpegSplitterFile.h" #ifdef STANDALONE_FILTER #include #endif #include "moreuuids.h" #define MEGABYTE 1024*1024 #define PTS_MAX_BEFORE_WRAP (((1i64 << 33) - 1) * 10000 / 90) CMpegSplitterFile::CMpegSplitterFile(IAsyncReader* pAsyncReader, HRESULT& hr, bool bIsHdmv, CHdmvClipInfo& ClipInfo, int guid_flag, bool ForcedSub, int AC3CoreOnly, bool AlternativeDuration) : CBaseSplitterFileEx(pAsyncReader, hr, DEFAULT_CACHE_LENGTH, false, true) , m_type(mpeg_us) , m_rate(0) , m_rtMin(0), m_rtMax(0) , m_posMin(0), m_posMax(0) , m_rtPrec(_I64_MIN) , m_bPTSWrap(false) , m_bIsHdmv(bIsHdmv) , m_ClipInfo(ClipInfo) , m_nVC1_GuidFlag(guid_flag) , m_ForcedSub(ForcedSub) , m_AC3CoreOnly(AC3CoreOnly) , m_AlternativeDuration(AlternativeDuration) , m_init(false) { if (SUCCEEDED(hr)) { hr = Init(pAsyncReader); } } HRESULT CMpegSplitterFile::Init(IAsyncReader* pAsyncReader) { HRESULT hr; // get the type first m_type = mpeg_us; Seek(0); if (m_type == mpeg_us) { if (BitRead(32, true) == 'TFrc') { Seek(0x67c); } int cnt = 0, limit = 4; for (tshdr h; cnt < limit && Read(h); cnt++) { Seek(h.next); } if (cnt >= limit) { m_type = mpeg_ts; } } Seek(0); if (m_type == mpeg_us) { if (BitRead(32, true) == 'TFrc') { Seek(0xE80); } int cnt = 0, limit = 4; for (tshdr h; cnt < limit && Read(h); cnt++) { Seek(h.next); } if (cnt >= limit) { m_type = mpeg_ts; } } Seek(0); if (m_type == mpeg_us) { int cnt = 0, limit = 4; for (pvahdr h; cnt < limit && Read(h); cnt++) { Seek(GetPos() + h.length); } if (cnt >= limit) { m_type = mpeg_pva; } } Seek(0); if (m_type == mpeg_us) { BYTE b; for (int i = 0; (i < 4 || GetPos() < MAX_PROBE_SIZE) && m_type == mpeg_us && NextMpegStartCode(b); i++) { if (b == 0xba) { pshdr h; if (Read(h)) { m_type = mpeg_ps; m_rate = int(h.bitrate / 8); break; } } else if ((b & 0xe0) == 0xc0 // audio, 110xxxxx, mpeg1/2/3 || (b & 0xf0) == 0xe0 // video, 1110xxxx, mpeg1/2 // || (b&0xbd) == 0xbd) // private stream 1, 0xbd, ac3/dts/lpcm/subpic || b == 0xbd) { // private stream 1, 0xbd, ac3/dts/lpcm/subpic peshdr h; if (Read(h, b) && BitRead(24, true) == 0x000001) { m_type = mpeg_es; } } } } Seek(0); if (m_type == mpeg_us) { return E_FAIL; } // min/max pts & bitrate m_rtMin = m_posMin = _I64_MAX; m_rtMax = m_posMax = 0; m_rtPrec = _I64_MIN; m_bPTSWrap = false; m_init = true; if (IsRandomAccess() || IsStreaming()) { if (IsStreaming()) { for (int i = 0; i < 20 || i < 50 && S_OK != HasMoreData(MEGABYTE, 100); i++) { ; } } SearchPrograms(0, min(GetLength(), MEGABYTE * 5)); // max 5Mb for search a valid Program Map Table __int64 pfp = 0; const int k = 5; for (int i = 0; i <= k; i++) { __int64 fp = i * GetLength() / k; fp = min(GetLength() - MEGABYTE / 8, fp); fp = max(pfp, fp); __int64 nfp = fp + (pfp == 0 ? 10 * MEGABYTE : MEGABYTE / 8); if (FAILED(hr = SearchStreams(fp, nfp, pAsyncReader))) { return hr; } pfp = nfp; } } else { if (FAILED(hr = SearchStreams(0, MEGABYTE / 8, pAsyncReader))) { return hr; } } if (m_type == mpeg_ts) { if (IsRandomAccess() || IsStreaming()) { if (IsStreaming()) { for (int i = 0; i < 20 || i < 50 && S_OK != HasMoreData(1024 * 100, 100); i++) { ; } } __int64 pfp = 0; const int k = 5; for (int i = 0; i <= k; i++) { __int64 fp = i * GetLength() / k; fp = min(GetLength() - MEGABYTE / 8, fp); fp = max(pfp, fp); __int64 nfp = fp + (pfp == 0 ? 10 * MEGABYTE : MEGABYTE / 8); if (FAILED(hr = SearchStreams(fp, nfp, pAsyncReader, TRUE))) { return hr; } pfp = nfp; } } else { if (FAILED(hr = SearchStreams(0, MEGABYTE / 8, pAsyncReader, TRUE))) { return hr; } } } if (m_posMax - m_posMin <= 0 || (m_rtMax - m_rtMin <= 0 && !m_bPTSWrap)) { return E_FAIL; } m_init = false; int indicated_rate = m_rate; REFERENCE_TIME dur = !m_bPTSWrap ? (m_rtMax - m_rtMin) : (PTS_MAX_BEFORE_WRAP - m_rtMin + m_rtMax); int detected_rate = int(10000000i64 * (m_posMax - m_posMin) / dur); m_rate = detected_rate ? detected_rate : m_rate; #if (0) // normally "detected" should always be less than "indicated", but sometimes it can be a few percent higher (+10% is allowed here) // (update: also allowing +/-50k/s) if (indicated_rate == 0 || ((float)detected_rate / indicated_rate) < 1.1 || abs(detected_rate - indicated_rate) < 50 * 1024) { m_rate = detected_rate; } else { ; // TODO: in this case disable seeking, or try doing something less drastical... } #endif // Add fake subtitle stream... if (m_streams[video].GetCount() && m_streams[subpic].GetCount()) { if (m_type == mpeg_ts && m_bIsHdmv) { AddHdmvPGStream(NO_SUBTITLE_PID, "---"); } else { stream s; s.pid = NO_SUBTITLE_PID; s.mt.majortype = m_streams[subpic].GetHead().mt.majortype; s.mt.subtype = m_streams[subpic].GetHead().mt.subtype; s.mt.formattype = m_streams[subpic].GetHead().mt.formattype; m_streams[subpic].Insert(s, this); } } Seek(0); return S_OK; } void CMpegSplitterFile::OnComplete(IAsyncReader* pAsyncReader) { __int64 pos = GetPos(); if (SUCCEEDED(SearchStreams(GetLength() - 500 * 1024, GetLength(), pAsyncReader, TRUE))) { int indicated_rate = m_rate; REFERENCE_TIME dur = !m_bPTSWrap ? (m_rtMax - m_rtMin) : (PTS_MAX_BEFORE_WRAP - m_rtMin + m_rtMax); int detected_rate = int(10000000i64 * (m_posMax - m_posMin) / dur); m_rate = detected_rate ? detected_rate : m_rate; #if (0) // normally "detected" should always be less than "indicated", but sometimes it can be a few percent higher (+10% is allowed here) // (update: also allowing +/-50k/s) if (indicated_rate == 0 || ((float)detected_rate / indicated_rate) < 1.1 || abs(detected_rate - indicated_rate) < 50 * 1024) { m_rate = detected_rate; } else { ; // TODO: in this case disable seeking, or try doing something less drastical... } #endif } Seek(pos); } REFERENCE_TIME CMpegSplitterFile::NextPTS(DWORD TrackNum) { REFERENCE_TIME rt = -1; __int64 rtpos = -1; BYTE b; while (GetRemaining()) { if (m_type == mpeg_ps || m_type == mpeg_es) { if (!NextMpegStartCode(b)) { // continue; ASSERT(0); break; } rtpos = GetPos() - 4; if ((b >= 0xbd && b < 0xf0) || (b == 0xfd)) { peshdr h; if (!Read(h, b) || !h.len) { continue; } __int64 pos = GetPos(); if (h.fpts && AddStream(0, b, h.id_ext, h.len) == TrackNum) { //ASSERT(h.pts >= m_rtMin && h.pts <= m_rtMax); rt = h.pts; break; } Seek(pos + h.len); } } else if (m_type == mpeg_ts) { tshdr h; if (!Read(h)) { continue; } rtpos = GetPos() - 4; if (h.payload && h.payloadstart && ISVALIDPID(h.pid)) { peshdr h2; if (NextMpegStartCode(b, 4) && Read(h2, b)) { // pes packet if (h2.fpts && AddStream(h.pid, b, 0, DWORD(h.bytes - (GetPos() - rtpos)) == TrackNum)) { //ASSERT(h2.pts >= m_rtMin && h2.pts <= m_rtMax); rt = h2.pts; break; } } } Seek(h.next); } else if (m_type == mpeg_pva) { pvahdr h; if (!Read(h)) { continue; } if (h.fpts) { rt = h.pts; break; } } } if (rtpos >= 0) { Seek(rtpos); } if (rt >= 0) { rt -= m_rtMin; } return rt; } void CMpegSplitterFile::SearchPrograms(__int64 start, __int64 stop) { if (m_type != mpeg_ts) { return; } Seek(start); stop = min(stop, GetLength()); while (GetPos() < stop) { tshdr h; if (!Read(h)) { continue; } UpdatePrograms(h); Seek(h.next); } } HRESULT CMpegSplitterFile::SearchStreams(__int64 start, __int64 stop, IAsyncReader* pAsyncReader, BOOL CalcDuration) { Seek(start); stop = min(stop, GetLength()); while (GetPos() < stop) { BYTE b; if (m_type == mpeg_ps || m_type == mpeg_es) { if (!NextMpegStartCode(b)) { continue; } if (b == 0xba) { // program stream header pshdr h; if (!Read(h)) { continue; } m_rate = int(h.bitrate / 8); } else if (b == 0xbb) { // program stream system header pssyshdr h; if (!Read(h)) { continue; } } else if ((b >= 0xbd && b < 0xf0) || (b == 0xfd)) { // pes packet peshdr h; if (!Read(h, b) || !h.len) { continue; } if (h.type == mpeg2 && h.scrambling) { ASSERT(0); return E_FAIL; } if (h.fpts) { if (m_rtMin == _I64_MAX) { m_rtMin = h.pts; m_posMin = GetPos(); TRACE(_T("m_rtMin(SearchStreams)=%s\n"), ReftimeToString(m_rtMin)); } if (m_rtMin < h.pts && m_rtMax < h.pts) { m_rtMax = h.pts; m_posMax = GetPos(); TRACE(_T("m_rtMax(SearchStreams)=%s\n"), ReftimeToString(m_rtMax)); } } __int64 pos = GetPos(); AddStream(0, b, h.id_ext, h.len); if (h.len) { Seek(pos + h.len); } } } else if (m_type == mpeg_ts) { tshdr h; if (!Read(h)) { continue; } __int64 pos = GetPos(); //UpdatePrograms(h); if (h.payload && ISVALIDPID(h.pid)) { peshdr h2; if (h.payloadstart && NextMpegStartCode(b, 4) && Read(h2, b)) { // pes packet if (h2.type == mpeg2 && h2.scrambling) { ASSERT(0); return E_FAIL; } if (h2.fpts && CalcDuration && (m_AlternativeDuration || (GetMasterStream() && GetMasterStream()->GetHead() == h.pid))) { if (m_rtPrec != _I64_MIN && abs(h2.pts - m_rtPrec) >= PTS_MAX_BEFORE_WRAP / 2) { m_bPTSWrap = true; m_rtMax = h2.pts; m_posMax = GetPos(); TRACE(_T("PTS Wrap detected --> m_rtMax(SearchStreams)=%s\n"), ReftimeToString(m_rtMax)); } m_rtPrec = h2.pts; if ((m_rtMin == _I64_MAX) || (m_rtMin > h2.pts && m_rtMax > h2.pts && !m_bPTSWrap)) { m_rtMin = h2.pts; m_posMin = GetPos(); TRACE(_T("m_rtMin(SearchStreams)=%s, PID=%d\n"), ReftimeToString(m_rtMin), h.pid); } if (m_rtMax < h2.pts && (m_rtMin < h2.pts || m_bPTSWrap)) { m_rtMax = h2.pts; m_posMax = GetPos(); TRACE(_T("m_rtMax(SearchStreams)=%s, PID=%d\n"), ReftimeToString(m_rtMax), h.pid); // Ugly code : to support BRD H264 seamless playback, CMultiFiles need to update m_rtPTSOffset variable // each time a new part is open... // use this code only if Blu-ray is detected if (m_ClipInfo.IsHdmv()) { for (size_t i = 0; i < m_ClipInfo.GetStreamNumber(); i++) { CHdmvClipInfo::Stream* stream = m_ClipInfo.GetStreamByIndex(i); if (stream->m_Type == VIDEO_STREAM_H264 && m_rtMin == 116506666) { CComQIPtr pReader = pAsyncReader; if (pReader) { pReader->SetPTSOffset(&m_rtPTSOffset); } //TRACE(_T("UPDATE m_rtPTSOffset(SearchStreams)=%s\n"), ReftimeToString(m_rtPTSOffset)); //TRACE(_T("m_rtMin(Boucle)=%s\n"), ReftimeToString(m_rtMin)); //TRACE(_T("stream=%d\n"), stream->m_Type); //TRACE(_T("m_rtMax(Boucle)=%s\n"), ReftimeToString(m_rtMax)); //TRACE(_T("m_rtMax - m_rtMin(Boucle)=%s\n"), ReftimeToString(m_rtMax - m_rtMin)); } } } } } } else { b = 0; } if (!CalcDuration) { AddStream(h.pid, b, 0, DWORD(h.bytes - (GetPos() - pos))); } } Seek(h.next); } else if (m_type == mpeg_pva) { pvahdr h; if (!Read(h)) { continue; } if (h.fpts) { if (m_rtMin == _I64_MAX) { m_rtMin = h.pts; m_posMin = GetPos(); } if (m_rtMin < h.pts && m_rtMax < h.pts) { m_rtMax = h.pts; m_posMax = GetPos(); } } __int64 pos = GetPos(); if (h.streamid == 1) { AddStream(h.streamid, 0xe0, 0, h.length); } else if (h.streamid == 2) { AddStream(h.streamid, 0xc0, 0, h.length); } if (h.length) { Seek(pos + h.length); } } } return S_OK; } #define IsMpegAudio(stream_type) (stream_type == AUDIO_STREAM_MPEG1 || stream_type == AUDIO_STREAM_MPEG2) #define IsAACAudio(stream_type) (stream_type == AUDIO_STREAM_AAC) #define IsAACLATMAudio(stream_type) (stream_type == AUDIO_STREAM_AAC_LATM) #define IsAC3Audio(stream_type) ( \ stream_type == AUDIO_STREAM_AC3 || stream_type == AUDIO_STREAM_AC3_PLUS || \ stream_type == AUDIO_STREAM_AC3_TRUE_HD || stream_type == SECONDARY_AUDIO_AC3_PLUS || \ stream_type == PES_PRIVATE) #define IsMPEG2Video(stream_type) (stream_type == VIDEO_STREAM_MPEG2) #define IsH264Video(stream_type) (stream_type == VIDEO_STREAM_H264) #define IsVC1Video(stream_type) (stream_type == VIDEO_STREAM_VC1) DWORD CMpegSplitterFile::AddStream(WORD pid, BYTE pesid, BYTE ps1id, DWORD len) { if (pid) { if (pesid) { m_pid2pes[pid] = pesid; } else { m_pid2pes.Lookup(pid, pesid); } } stream s; s.pid = pid; s.pesid = pesid; s.ps1id = ps1id; const __int64 start = GetPos(); int type = unknown; if (pesid >= 0xe0 && pesid < 0xf0) { // mpeg video // MPEG2 if (type == unknown) { CMpegSplitterFile::seqhdr h; if (!m_streams[video].Find(s) && Read(h, len, &s.mt)) { PES_STREAM_TYPE stream_type = INVALID; if (GetStreamType(s.pid, stream_type)) { if (IsMPEG2Video(stream_type)) { type = video; } } else { type = video; } } } // H.264 if (type == unknown) { Seek(start); // PPS and SPS can be present on differents packets // and can also be split into multiple packets if (!avch.Lookup(pid)) { memset(&avch[pid], 0, sizeof(CMpegSplitterFile::avchdr)); } #if defined(MVC_SUPPORT) if (!m_streams[video].Find(s) && !m_streams[stereo].Find(s) && Read(avch[pid], len, &s.mt)) { if (avch[pid].spspps[index_subsetsps].complete) { type = stereo; } else { type = video; } } #else if (!m_streams[video].Find(s) && Read(avch[pid], len, &s.mt)) { PES_STREAM_TYPE stream_type = INVALID; if (GetStreamType(s.pid, stream_type)) { if (IsH264Video(stream_type)) { type = video; } } else { type = video; } } #endif } } else if (pesid >= 0xc0 && pesid < 0xe0) { // mpeg audio // AAC if (type == unknown) { CMpegSplitterFile::aachdr h; if (!m_streams[audio].Find(s) && Read(h, len, &s.mt, m_type)) { PES_STREAM_TYPE stream_type = INVALID; if (GetStreamType(s.pid, stream_type)) { if (IsAACAudio(stream_type)) { type = audio; } } else { type = audio; } } } // AAC LATM if (type == unknown) { Seek(start); CMpegSplitterFile::latm_aachdr h; if (!m_streams[audio].Find(s) && Read(h, len, &s.mt)) { PES_STREAM_TYPE stream_type = INVALID; if (GetStreamType(s.pid, stream_type)) { if (IsAACLATMAudio(stream_type)) { type = audio; } } else { type = audio; } } } // MPEG Audio if (type == unknown) { Seek(start); CMpegSplitterFile::mpahdr h; if (!m_streams[audio].Find(s) && Read(h, len, false, &s.mt)) { PES_STREAM_TYPE stream_type = INVALID; if (GetStreamType(s.pid, stream_type)) { if (IsMpegAudio(stream_type)) { type = audio; } } else { type = audio; } } } } else if (pesid == 0xbd || pesid == 0xfd) { // private stream 1 if (s.pid) { if (!m_streams[audio].Find(s) && !m_streams[video].Find(s)) { // AC3, E-AC3, TrueHD if (type == unknown) { CMpegSplitterFile::ac3hdr h; if (Read(h, len, &s.mt, true, (m_AC3CoreOnly == 1))) { PES_STREAM_TYPE stream_type = INVALID; if (GetStreamType(s.pid, stream_type)) { if (IsAC3Audio(stream_type)) { type = audio; } } else { type = audio; } } } // DTS, DTS HD, DTS HD MA if (type == unknown) { Seek(start); CMpegSplitterFile::dtshdr h; if (Read(h, len, &s.mt, false)) { type = audio; } } // VC1 if (type == unknown) { Seek(start); CMpegSplitterFile::vc1hdr h; if (!m_streams[video].Find(s) && Read(h, len, &s.mt, m_nVC1_GuidFlag)) { PES_STREAM_TYPE stream_type = INVALID; if (GetStreamType(s.pid, stream_type)) { if (IsVC1Video(stream_type)) { type = video; } } else { type = video; } } } // DVB subtitles if (type == unknown) { Seek(start); CMpegSplitterFile::dvbsub h; if (!m_streams[video].Find(s) && Read(h, len, &s.mt)) { type = subpic; } } int iProgram; const CHdmvClipInfo::Stream* pClipInfo; const program* pProgram = FindProgram(s.pid, iProgram, pClipInfo); if ((type == unknown) && (pProgram != nullptr)) { PES_STREAM_TYPE StreamType = INVALID; Seek(start); StreamType = pProgram->streams[iProgram].type; switch (StreamType) { case AUDIO_STREAM_LPCM: { CMpegSplitterFile::hdmvlpcmhdr h; if (!m_streams[audio].Find(s) && Read(h, &s.mt)) { type = audio; } } break; case PRESENTATION_GRAPHICS_STREAM: { CMpegSplitterFile::hdmvsubhdr h; if (!m_streams[subpic].Find(s) && Read(h, &s.mt, pClipInfo ? pClipInfo->m_LanguageCode : nullptr)) { m_bIsHdmv = true; type = subpic; } } break; } } } else if ((m_AC3CoreOnly != 1) && m_init) { int iProgram; const CHdmvClipInfo::Stream* pClipInfo; const program* pProgram = FindProgram(s.pid, iProgram, pClipInfo); if ((type == unknown) && (pProgram != nullptr) && AUDIO_STREAM_AC3_TRUE_HD == pProgram->streams[iProgram].type) { const stream* source = m_streams[audio].FindStream(s.pid); if (source && source->mt.subtype == MEDIASUBTYPE_DOLBY_AC3) { CMpegSplitterFile::ac3hdr h; if (Read(h, len, &s.mt, false, (m_AC3CoreOnly == 1)) && s.mt.subtype == MEDIASUBTYPE_DOLBY_TRUEHD) { m_streams[audio].Replace((stream&)*source, s, this); } } } } } else if (pesid == 0xfd) { CMpegSplitterFile::vc1hdr h; if (!m_streams[video].Find(s) && Read(h, len, &s.mt, m_nVC1_GuidFlag)) { type = video; } } else { BYTE b = (BYTE)BitRead(8, true); WORD w = (WORD)BitRead(16, true); DWORD dw = (DWORD)BitRead(32, true); if (b >= 0x80 && b < 0x88 || w == 0x0b77) { // ac3 s.ps1id = (b >= 0x80 && b < 0x88) ? (BYTE)(BitRead(32) >> 24) : 0x80; CMpegSplitterFile::ac3hdr h; if (!m_streams[audio].Find(s) && Read(h, len, &s.mt)) { type = audio; } } else if (b >= 0x88 && b < 0x90 || dw == 0x7ffe8001) { // dts s.ps1id = (b >= 0x88 && b < 0x90) ? (BYTE)(BitRead(32) >> 24) : 0x88; CMpegSplitterFile::dtshdr h; if (!m_streams[audio].Find(s) && Read(h, len, &s.mt)) { type = audio; } } else if (b >= 0xa0 && b < 0xa8) { // lpcm s.ps1id = (BYTE)BitRead(8); do { // DVD-Audio LPCM if (b == 0xa0) { BitRead(8); // Continuity Counter - counts from 0x00 to 0x1f and then wraps to 0x00. DWORD headersize = (DWORD)BitRead(16); // LPCM_header_length if (headersize >= 8 && headersize + 4 < len) { CMpegSplitterFile::dvdalpcmhdr h; if (Read(h, len - 4, &s.mt)) { Seek(start + 4 + headersize); type = audio; break; } } } // DVD-Audio MLP else if (b == 0xa1 && len > 10) { BYTE counter = (BYTE)BitRead(8); // Continuity Counter: 0x00..0x1f or 0x20..0x3f or 0x40..0x5f BitRead(8); // some unknown data DWORD headersize = (DWORD)BitRead(8); // MLP_header_length (always equal 6?) BitRead(32); // some unknown data WORD unknown1 = (WORD)BitRead(16); // 0x0000 or 0x0400 if (counter <= 0x5f && headersize == 6 && (unknown1 == 0x0000 || unknown1 == 0x0400)) { // Maybe it's MLP? // MLP header may be missing in the first package CMpegSplitterFile::mlphdr h; if (!m_streams[audio].Find(s) && Read(h, len - 10, &s.mt, true)) { // This is exactly the MLP. Seek(start + 10); type = audio; } Seek(start + 10); break; } } // DVD LPCM if (m_streams[audio].Find(s)) { Seek(start + 7); } else { Seek(start + 4); CMpegSplitterFile::lpcmhdr h; if (Read(h, &s.mt)) { type = audio; } } } while (false); } else if (b >= 0x20 && b < 0x40) { // DVD subpic s.ps1id = (BYTE)BitRead(8); CMpegSplitterFile::dvdspuhdr h; if (!m_streams[subpic].Find(s) && Read(h, &s.mt)) { type = subpic; } } else if (b >= 0x70 && b < 0x80) { // SVCD subpic s.ps1id = (BYTE)BitRead(8); CMpegSplitterFile::svcdspuhdr h; if (!m_streams[subpic].Find(s) && Read(h, &s.mt)) { type = subpic; } } else if (b >= 0x00 && b < 0x10) { // CVD subpic s.ps1id = (BYTE)BitRead(8); CMpegSplitterFile::cvdspuhdr h; if (!m_streams[subpic].Find(s) && Read(h, &s.mt)) { type = subpic; } } else if (w == 0xffa0 || w == 0xffa1) { // ps2-mpg audio s.ps1id = (BYTE)BitRead(8); s.pid = (WORD)((BitRead(8) << 8) | BitRead(16)); // pid = 0xa000 | track id CMpegSplitterFile::ps2audhdr h; if (!m_streams[audio].Find(s) && Read(h, &s.mt)) { type = audio; } } else if (w == 0xff90) { // ps2-mpg ac3 or subtitles s.ps1id = (BYTE)BitRead(8); s.pid = (WORD)((BitRead(8) << 8) | BitRead(16)); // pid = 0x9000 | track id w = (WORD)BitRead(16, true); if (w == 0x0b77) { CMpegSplitterFile::ac3hdr h; if (!m_streams[audio].Find(s) && Read(h, len, &s.mt)) { type = audio; } } else if (w == 0x0000) { // usually zero... CMpegSplitterFile::ps2subhdr h; if (!m_streams[subpic].Find(s) && Read(h, &s.mt)) { type = subpic; } } } else if (b >= 0xc0 && b < 0xcf) { // dolby digital/dolby digital + s.ps1id = (BYTE)BitRead(8); // skip audio header - 3-byte BitRead(8); BitRead(8); BitRead(8); CMpegSplitterFile::ac3hdr h; if (!m_streams[audio].Find(s) && Read(h, len, &s.mt)) { type = audio; } } else if (b >= 0xb0 && b < 0xbf) { // truehd s.ps1id = (BYTE)BitRead(8); // skip audio header - 3-byte BitRead(8); BitRead(8); BitRead(8); // TrueHD audio has a 4-byte header BitRead(8); CMpegSplitterFile::ac3hdr h; if (!m_streams[audio].Find(s) && Read(h, len, &s.mt, false, false)) { type = audio; } } } } else if (pesid == 0xbe) { // padding } else if (pesid == 0xbf) { // private stream 2 } if (type != unknown && !m_streams[type].Find(s)) { if (s.pid) { for (int i = 0; i < unknown; i++) { if (m_streams[i].Find(s)) { return s; } } } m_streams[type].Insert(s, this); } return s; } void CMpegSplitterFile::AddHdmvPGStream(WORD pid, const char* language_code) { stream s; s.pid = pid; s.pesid = 0xbd; CMpegSplitterFile::hdmvsubhdr h; if (!m_streams[subpic].Find(s) && Read(h, &s.mt, language_code)) { m_streams[subpic].Insert(s, this); } } CAtlList* CMpegSplitterFile::GetMasterStream() { return !m_streams[video].IsEmpty() ? &m_streams[video] : !m_streams[audio].IsEmpty() ? &m_streams[audio] : !m_streams[subpic].IsEmpty() ? &m_streams[subpic] : #if defined(MVC_SUPPORT) !m_streams[stereo].IsEmpty() ? &m_streams[stereo] : #endif nullptr; } void CMpegSplitterFile::UpdatePrograms(const tshdr& h, bool UpdateLang) { CAutoLock cAutoLock(&m_csProps); if (h.payload && h.payloadstart && h.pid == 0) { trsechdr h2; if (Read(h2) && h2.table_id == 0) { CAtlMap newprograms; int len = h2.section_length; len -= 5 + 4; for (int i = len / 4; i > 0; i--) { WORD program_number = (WORD)BitRead(16); BYTE reserved = (BYTE)BitRead(3); WORD pid = (WORD)BitRead(13); UNREFERENCED_PARAMETER(reserved); if (program_number != 0) { m_programs[pid].program_number = program_number; newprograms[program_number] = true; } } POSITION pos = m_programs.GetStartPosition(); while (pos) { const CAtlMap::CPair* pPair = m_programs.GetNext(pos); if (!newprograms.Lookup(pPair->m_value.program_number)) { m_programs.RemoveKey(pPair->m_key); } } } } else if (CAtlMap::CPair* pPair = m_programs.Lookup(h.pid)) { if (h.payload && h.payloadstart) { trsechdr h2; if (Read(h2) && h2.table_id == 2) { int len = h2.section_length; len -= 5 + 4; BYTE buffer[1024]; ByteRead(buffer, len); int max_len = h.bytes - 9; if (len > max_len) { memset(pPair->m_value.ts_buffer, 0, sizeof(pPair->m_value.ts_buffer)); pPair->m_value.ts_len_cur = max_len; pPair->m_value.ts_len_packet = len; memcpy(pPair->m_value.ts_buffer, buffer, max_len); } else { CGolombBuffer gb(buffer, len); UpdatePrograms(gb, h.pid, UpdateLang); } } } else { if (pPair->m_value.ts_len_cur > 0) { int len = pPair->m_value.ts_len_packet - pPair->m_value.ts_len_cur; if (len > h.bytes) { ByteRead(pPair->m_value.ts_buffer + pPair->m_value.ts_len_cur, h.bytes); pPair->m_value.ts_len_cur += h.bytes; } else { ByteRead(pPair->m_value.ts_buffer + pPair->m_value.ts_len_cur, pPair->m_value.ts_len_packet - pPair->m_value.ts_len_cur); CGolombBuffer gb(pPair->m_value.ts_buffer, pPair->m_value.ts_len_packet); UpdatePrograms(gb, h.pid, UpdateLang); } } } } } void CMpegSplitterFile::UpdatePrograms(CGolombBuffer gb, WORD pid, bool UpdateLang) { if (CAtlMap::CPair* pPair = m_programs.Lookup(pid)) { memset(pPair->m_value.streams, 0, sizeof(pPair->m_value.streams)); int len = gb.GetSize(); BYTE reserved1 = (BYTE)gb.BitRead(3); WORD PCR_PID = (WORD)gb.BitRead(13); BYTE reserved2 = (BYTE)gb.BitRead(4); WORD program_info_length = (WORD)gb.BitRead(12); UNREFERENCED_PARAMETER(reserved1); UNREFERENCED_PARAMETER(PCR_PID); UNREFERENCED_PARAMETER(reserved2); len -= (4 + program_info_length); if (len <= 0) { return; } while (program_info_length-- > 0) { gb.BitRead(8); } for (int i = 0; i < _countof(pPair->m_value.streams) && len >= 5; i++) { BYTE stream_type = (BYTE)gb.BitRead(8); BYTE nreserved1 = (BYTE)gb.BitRead(3); WORD pid = (WORD)gb.BitRead(13); BYTE nreserved2 = (BYTE)gb.BitRead(4); WORD ES_info_length = (WORD)gb.BitRead(12); UNREFERENCED_PARAMETER(nreserved1); UNREFERENCED_PARAMETER(nreserved2); pPair->m_value.streams[i].pid = pid; pPair->m_value.streams[i].type = (PES_STREAM_TYPE)stream_type; if (m_ForcedSub) { if (stream_type == PRESENTATION_GRAPHICS_STREAM) { stream s; s.pid = pid; CMpegSplitterFile::hdmvsubhdr hdr; if (Read(hdr, &s.mt, nullptr)) { if (!m_streams[subpic].Find(s)) { m_streams[subpic].Insert(s, this); } } } } len -= (5 + ES_info_length); if (len < 0) { break; } if (ES_info_length <= 2) { continue; } if (UpdateLang) { int info_length = ES_info_length; for (;;) { BYTE descriptor_tag = (BYTE)gb.BitRead(8); BYTE descriptor_length = (BYTE)gb.BitRead(8); info_length -= (2 + descriptor_length); if (info_length < 0) { break; } char ch[4]; switch (descriptor_tag) { case 0x0a: // ISO 639 language descriptor case 0x56: // Teletext descriptor case 0x59: // Subtitling descriptor gb.ReadBuffer((BYTE*)ch, 3); ch[3] = 0; for (int j = 3; j < descriptor_length; j++) { gb.BitRead(8); } if (!(ch[0] == 'u' && ch[1] == 'n' && ch[2] == 'd')) { m_pPMT_Lang[pid] = ISO6392ToLanguage(ch); } break; default: for (int j = 0; j < descriptor_length; j++) { gb.BitRead(8); } break; } if (info_length <= 2) { break; } } } else { while (ES_info_length-- > 0) { gb.BitRead(8); } } } pPair->m_value.ts_len_cur = 0; pPair->m_value.ts_len_packet = 0; } } UINT32 SwapLE(const UINT32& _Value) { return (_Value & 0xFF) << 24 | ((_Value >> 8) & 0xFF) << 16 | ((_Value >> 16) & 0xFF) << 8 | ((_Value >> 24) & 0xFF) << 0; } UINT16 SwapLE(const UINT16& _Value) { return (_Value & 0xFF) << 8 | ((_Value >> 8) & 0xFF) << 0; } const CMpegSplitterFile::program* CMpegSplitterFile::FindProgram(WORD pid, int& iStream, const CHdmvClipInfo::Stream*& _pClipInfo) { _pClipInfo = m_ClipInfo.FindStream(pid); iStream = -1; POSITION pos = m_programs.GetStartPosition(); while (pos) { program* p = &m_programs.GetNextValue(pos); for (int i = 0; i < _countof(p->streams); i++) { if (p->streams[i].pid == pid) { iStream = i; return p; } } } return nullptr; } bool CMpegSplitterFile::GetStreamType(WORD pid, PES_STREAM_TYPE& stream_type) { int iProgram; const CHdmvClipInfo::Stream* pClipInfo; const program* pProgram = FindProgram(pid, iProgram, pClipInfo); if (pProgram) { stream_type = pProgram->streams[iProgram].type; if (stream_type != INVALID) { return true; } } return false; }