/* * (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 "DSMSplitterFile.h" #include "../../../DSUtil/DSUtil.h" #include "moreuuids.h" CDSMSplitterFile::CDSMSplitterFile(IAsyncReader* pReader, HRESULT& hr, IDSMResourceBagImpl& res, IDSMChapterBagImpl& chap) : CBaseSplitterFile(pReader, hr, DEFAULT_CACHE_LENGTH, false) , m_rtFirst(0) , m_rtDuration(0) { if (FAILED(hr)) { return; } hr = Init(res, chap); } HRESULT CDSMSplitterFile::Init(IDSMResourceBagImpl& res, IDSMChapterBagImpl& chap) { Seek(0); if (BitRead(DSMSW_SIZE << 3) != DSMSW || BitRead(5) != DSMP_FILEINFO) { return E_FAIL; } Seek(0); m_mts.RemoveAll(); m_rtFirst = m_rtDuration = 0; m_fim.RemoveAll(); m_sim.RemoveAll(); res.ResRemoveAll(); chap.ChapRemoveAll(); dsmp_t type; UINT64 len; // examine the beginning of the file ... while (Sync(type, len, 0)) { __int64 pos = GetPos(); if (type == DSMP_MEDIATYPE) { BYTE id; CMediaType mt; if (Read(len, id, mt)) { m_mts[id] = mt; } } else if (type == DSMP_SAMPLE) { Packet p; if (Read(len, &p, false) && p.rtStart != Packet::INVALID_TIME) { m_rtFirst = p.rtStart; break; } } else if (type == DSMP_FILEINFO) { if ((BYTE)BitRead(8) > DSMF_VERSION) { return E_FAIL; } Read(len - 1, m_fim); } else if (type == DSMP_STREAMINFO) { Read(len - 1, m_sim[(BYTE)BitRead(8)]); } else if (type == DSMP_SYNCPOINTS) { Read(len, m_sps); } else if (type == DSMP_RESOURCE) { Read(len, res); } else if (type == DSMP_CHAPTERS) { Read(len, chap); } Seek(pos + len); } if (type != DSMP_SAMPLE) { return E_FAIL; } // ... and the end if (IsRandomAccess()) { int limit = MAX_PROBE_SIZE; for (int i = 1, j = (int)((GetLength() + limit / 2) / limit); i <= j; i++) { __int64 seekpos = max(0, (__int64)GetLength() - i * limit); Seek(seekpos); while (Sync(type, len, limit) && GetPos() < seekpos + limit) { __int64 pos = GetPos(); if (type == DSMP_SAMPLE) { Packet p; if (Read(len, &p, false) && p.rtStart != Packet::INVALID_TIME) { m_rtDuration = max(m_rtDuration, p.rtStop - m_rtFirst); // max isn't really needed, only for safety i = j; } } else if (type == DSMP_SYNCPOINTS) { Read(len, m_sps); } else if (type == DSMP_RESOURCE) { Read(len, res); } else if (type == DSMP_CHAPTERS) { Read(len, chap); } Seek(pos + len); } } } if (m_rtFirst < 0) { m_rtDuration += m_rtFirst; m_rtFirst = 0; } return !m_mts.IsEmpty() ? S_OK : E_FAIL; } bool CDSMSplitterFile::Sync(dsmp_t& type, UINT64& len, __int64 limit) { UINT64 pos; return Sync(pos, type, len, limit); } bool CDSMSplitterFile::Sync(UINT64& syncpos, dsmp_t& type, UINT64& len, __int64 limit) { BitByteAlign(); limit += DSMSW_SIZE; for (UINT64 id = 0; (id & ((1ui64 << (DSMSW_SIZE << 3)) - 1)) != DSMSW; id = (id << 8) | (BYTE)BitRead(8)) { if (limit-- <= 0 || GetRemaining() <= 2) { return false; } } syncpos = GetPos() - (DSMSW_SIZE << 3); type = (dsmp_t)BitRead(5); len = BitRead(((int)BitRead(3) + 1) << 3); return true; } bool CDSMSplitterFile::Read(__int64 len, BYTE& id, CMediaType& mt) { id = (BYTE)BitRead(8); ByteRead((BYTE*)&mt.majortype, sizeof(mt.majortype)); ByteRead((BYTE*)&mt.subtype, sizeof(mt.subtype)); mt.bFixedSizeSamples = (BOOL)BitRead(1); mt.bTemporalCompression = (BOOL)BitRead(1); mt.lSampleSize = (ULONG)BitRead(30); ByteRead((BYTE*)&mt.formattype, sizeof(mt.formattype)); len -= 5 + sizeof(GUID) * 3; ASSERT(len >= 0); if (len > 0) { mt.AllocFormatBuffer((LONG)len); ByteRead(mt.Format(), mt.FormatLength()); } else { mt.ResetFormatBuffer(); } return true; } bool CDSMSplitterFile::Read(__int64 len, Packet* p, bool fData) { if (!p) { return false; } p->TrackNumber = (DWORD)BitRead(8); p->bSyncPoint = (BOOL)BitRead(1); bool fSign = !!BitRead(1); int iTimeStamp = (int)BitRead(3); int iDuration = (int)BitRead(3); if (fSign && !iTimeStamp) { ASSERT(!iDuration); p->rtStart = Packet::INVALID_TIME; p->rtStop = Packet::INVALID_TIME + 1; } else { p->rtStart = (REFERENCE_TIME)BitRead(iTimeStamp << 3) * (fSign ? -1 : 1); p->rtStop = p->rtStart + BitRead(iDuration << 3); } if (fData) { p->SetCount((INT_PTR)len - (2 + iTimeStamp + iDuration)); ByteRead(p->GetData(), p->GetCount()); } return true; } bool CDSMSplitterFile::Read(__int64 len, CAtlArray& sps) { SyncPoint sp = {0, 0}; sps.RemoveAll(); while (len > 0) { bool fSign = !!BitRead(1); int iTimeStamp = (int)BitRead(3); int iFilePos = (int)BitRead(3); BitRead(1); // reserved sp.rt += (REFERENCE_TIME)BitRead(iTimeStamp << 3) * (fSign ? -1 : 1); sp.fp += BitRead(iFilePos << 3); sps.Add(sp); len -= 1 + iTimeStamp + iFilePos; } if (len != 0) { sps.RemoveAll(); return false; } // TODO: sort sps return true; } bool CDSMSplitterFile::Read(__int64 len, CStreamInfoMap& im) { while (len >= 5) { CStringA key; ByteRead((BYTE*)key.GetBufferSetLength(4), 4); len -= 4; len -= Read(len, im[key]); } return len == 0; } bool CDSMSplitterFile::Read(__int64 len, IDSMResourceBagImpl& res) { BYTE compression = (BYTE)BitRead(2); BYTE reserved = (BYTE)BitRead(6); UNREFERENCED_PARAMETER(reserved); len--; CDSMResource r; len -= Read(len, r.name); len -= Read(len, r.desc); len -= Read(len, r.mime); if (compression != 0) { return false; // TODO } r.data.SetCount((size_t)len); ByteRead(r.data.GetData(), r.data.GetCount()); res += r; return true; } bool CDSMSplitterFile::Read(__int64 len, IDSMChapterBagImpl& chap) { CDSMChapter c(0, L""); while (len > 0) { bool fSign = !!BitRead(1); int iTimeStamp = (int)BitRead(3); BitRead(4); // reserved len--; c.rt += (REFERENCE_TIME)BitRead(iTimeStamp << 3) * (fSign ? -1 : 1); len -= iTimeStamp; len -= Read(len, c.name); chap += c; } chap.ChapSort(); return len == 0; } __int64 CDSMSplitterFile::Read(__int64 len, CStringW& str) { char c; CStringA s; __int64 i = 0; while (i++ < len && (c = (char)BitRead(8)) != 0) { s += c; } str = UTF8To16(s); return i; } __int64 CDSMSplitterFile::FindSyncPoint(REFERENCE_TIME rt) { if (/*!m_sps.IsEmpty()*/ m_sps.GetCount() > 1) { size_t i = range_bsearch(m_sps, m_rtFirst + rt); return (i != MAXSIZE_T) ? m_sps[i].fp : 0; } if (m_rtDuration <= 0 || rt <= m_rtFirst) { return 0; } // ok, do the hard way then dsmp_t type; UINT64 syncpos, len; // 1. find some boundaries close to rt's position (minpos, maxpos) __int64 minpos = 0, maxpos = GetLength(); for (int i = 0; i < 10 && (maxpos - minpos) >= 1024 * 1024; i++) { Seek((minpos + maxpos) / 2); while (GetPos() < maxpos) { if (!Sync(syncpos, type, len)) { continue; } __int64 pos = GetPos(); if (type == DSMP_SAMPLE) { Packet p; if (Read(len, &p, false) && p.rtStart != Packet::INVALID_TIME) { REFERENCE_TIME dt = (p.rtStart -= m_rtFirst) - rt; if (dt >= 0) { maxpos = max((__int64)syncpos - MAX_PROBE_SIZE, minpos); } else { minpos = syncpos; } break; } } Seek(pos + len); } } // 2. find the first packet just after rt (maxpos) Seek(minpos); while (GetRemaining()) { if (!Sync(syncpos, type, len)) { continue; } __int64 pos = GetPos(); if (type == DSMP_SAMPLE) { Packet p; if (Read(len, &p, false) && p.rtStart != Packet::INVALID_TIME) { REFERENCE_TIME dt = (p.rtStart -= m_rtFirst) - rt; if (dt >= 0) { maxpos = (__int64)syncpos; break; } } } Seek(pos + len); } // 3. iterate backwards from maxpos and find at least one syncpoint for every stream, except for subtitle streams CAtlMap ids; { POSITION pos = m_mts.GetStartPosition(); while (pos) { BYTE id; CMediaType mt; m_mts.GetNextAssoc(pos, id, mt); if (mt.majortype != MEDIATYPE_Text && mt.majortype != MEDIATYPE_Subtitle) { ids[id] = 0; } } } __int64 ret = maxpos; while (maxpos > 0 && !ids.IsEmpty()) { minpos = max(0, maxpos - MAX_PROBE_SIZE); Seek(minpos); while (Sync(syncpos, type, len) && GetPos() < maxpos) { UINT64 pos = GetPos(); if (type == DSMP_SAMPLE) { Packet p; if (Read(len, &p, false) && p.rtStart != Packet::INVALID_TIME && p.bSyncPoint) { BYTE id = (BYTE)p.TrackNumber, tmp; if (ids.Lookup(id, tmp)) { ids.RemoveKey((BYTE)p.TrackNumber); ret = min(ret, (__int64)syncpos); } } } Seek(pos + len); } maxpos = minpos; } return ret; }