HRESULT CMpegSplitterFile::Init() { HRESULT hr; SVP_LogMsg5(L"HRESULT CMpegSplitterFile::Init()"); // get the type first m_type = us; Seek(0); if(m_type == us) { if(BitRead(32, true) == 'TFrc') Seek(0x67c); int cnt = 0, limit = 4; for(trhdr h; cnt < limit && Read(h); cnt++) Seek(h.next); if(cnt >= limit) m_type = ts; } Seek(0); if(m_type == us) { int cnt = 0, limit = 4; for(pvahdr h; cnt < limit && Read(h); cnt++) Seek(GetPos() + h.length); if(cnt >= limit) m_type = pva; } Seek(0); if(m_type == us) { BYTE b; for(int i = 0; (i < 4 || GetPos() < 65536) && m_type == us && NextMpegStartCode(b); i++) { if(b == 0xba) { pshdr h; if(Read(h)) { m_type = 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 = es; } } } } Seek(0); if(m_type == us) { return E_FAIL; } // min/max pts & bitrate m_rtMin = m_posMin = _I64_MAX; m_rtMax = m_posMax = 0; if(IsRandomAccess() || IsStreaming()) { if(IsStreaming()) { for(int i = 0; i < 20 || i < 50 && S_OK != HasMoreData(1024*100, 100); i++); } CAtlList<__int64> fps; for(int i = 0, j = 5; i <= j; i++) fps.AddTail(i*GetLength()/j); for(__int64 pfp = 0; fps.GetCount(); ) { __int64 fp = fps.RemoveHead(); fp = min(GetLength() - MEGABYTE/8, fp); fp = max(pfp, fp); __int64 nfp = fp + (pfp == 0 ? 5*MEGABYTE : MEGABYTE/8); if(FAILED(hr = SearchStreams(fp, nfp))) return hr; pfp = nfp; } } else { if(FAILED(hr = SearchStreams(0, MEGABYTE/8))) return hr; } if(m_posMax - m_posMin <= 0 || m_rtMax - m_rtMin <= 0) return E_FAIL; int indicated_rate = m_rate; int detected_rate = int(10000000i64 * (m_posMax - m_posMin) / (m_rtMax - m_rtMin)); // 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... //#ifndef DEBUG if(m_streams[audio].GetCount() < 1 && m_type == ts){ SVP_LogMsg5(_T("ts and no audio %d %d"), m_streams[audio].GetCount(), m_streams[unknown].GetCount()); return E_FAIL; } if(m_streams[video].GetCount()) { if (!m_bIsHdmv && m_streams[subpic].GetCount()) { stream s; s.mt.majortype = MEDIATYPE_Video; s.mt.subtype = MEDIASUBTYPE_DVD_SUBPICTURE; s.mt.formattype = FORMAT_None; m_streams[subpic].Insert(s, this); } else { // Add fake stream for "No subtitle" AddHdmvPGStream (NO_SUBTITLE_PID, "---"); } }else if(m_type == ts){ SVP_LogMsg5(_T("ts and no video")); return E_FAIL; } //#endif Seek(0); SVP_LogMsg5(_T("ts %d %d %d"), m_streams[video].GetCount() , m_streams[audio].GetCount(), m_bIsHdmv); return S_OK; }
HRESULT CMpegSplitterFile::SearchStreams(__int64 start, __int64 stop) { Seek(start); stop = min(stop, GetLength()); while(GetPos() < stop) { BYTE b; if(m_type == ps || m_type == es) { if(!NextMpegStartCode(b)) continue; if(b == 0xba) // program stream header { pshdr h; if(!Read(h)) continue; } else if(b == 0xbb) // program stream system header { pssyshdr h; if(!Read(h)) continue; } #if (EVO_SUPPORT == 0) else if(b >= 0xbd && b < 0xf0) // pes packet #else else if((b >= 0xbd && b < 0xf0) || (b == 0xfd)) // pes packet #endif { peshdr h; if(!Read(h, b)) 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();} if(m_rtMin < h.pts && m_rtMax < h.pts) {m_rtMax = h.pts; m_posMax = GetPos();} /* int rate = 10000000i64 * (m_posMax - m_posMin) / (m_rtMax - m_rtMin); if(m_rate == 0) m_rate = rate; TRACE(_T("rate = %d (%d), (h.pts = %I64d)\n"), rate, rate - m_rate, h.pts); m_rate = rate; */ } __int64 pos = GetPos(); AddStream(0, b, h.len); if(h.len) Seek(pos + h.len); } } else if(m_type == ts) { trhdr h; if(!Read(h)) continue; // if(h.scrambling) {ASSERT(0); return E_FAIL;} __int64 pos = GetPos(); if(h.payload && h.payloadstart) { 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) { if(m_rtMin == _I64_MAX) {m_rtMin = h2.pts; m_posMin = GetPos();} if(m_rtMin < h2.pts && m_rtMax < h2.pts) {m_rtMax = h2.pts; m_posMax = GetPos();} } } else { b = 0; } AddStream(h.pid, b, DWORD(h.bytes - (GetPos() - pos))); } Seek(h.next); } else if(m_type == 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, h.length); else if(h.streamid == 2) AddStream(h.streamid, 0xc0, h.length); if(h.length) Seek(pos + h.length); } }
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; }
REFERENCE_TIME CMpegSplitterFile::NextPTS(DWORD TrackNum) { REFERENCE_TIME rt = -1; __int64 rtpos = -1; BYTE b; while(GetRemaining()) { if(m_type == ps || m_type == es) { if(!NextMpegStartCode(b)) // continue; {ASSERT(0); break;} rtpos = GetPos()-4; #if (EVO_SUPPORT == 0) if(b >= 0xbd && b < 0xf0) #else if((b >= 0xbd && b < 0xf0) || (b == 0xfd)) #endif { peshdr h; if(!Read(h, b) || !h.len) continue; __int64 pos = GetPos(); if(h.fpts && AddStream(0, b, h.len) == TrackNum) { ASSERT(h.pts >= m_rtMin && h.pts <= m_rtMax); rt = h.pts; break; } Seek(pos + h.len); } } else if(m_type == ts) { trhdr 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, 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 == 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; }
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<ISyncReader> 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; }