/* * (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 "MatroskaSplitter.h" #include "../../../DSUtil/AudioParser.h" #ifdef STANDALONE_FILTER #include #endif #include "moreuuids.h" #include using namespace MatroskaReader; #ifdef STANDALONE_FILTER const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] = { {&MEDIATYPE_Stream, &MEDIASUBTYPE_Matroska}, {&MEDIATYPE_Stream, &MEDIASUBTYPE_NULL} }; const AMOVIESETUP_PIN sudpPins[] = { {L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, nullptr, _countof(sudPinTypesIn), sudPinTypesIn}, {L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, nullptr, 0, nullptr} }; const AMOVIESETUP_FILTER sudFilter[] = { {&__uuidof(CMatroskaSplitterFilter), MatroskaSplitterName, MERIT_NORMAL, _countof(sudpPins), sudpPins, CLSID_LegacyAmFilterCategory}, {&__uuidof(CMatroskaSourceFilter), MatroskaSourceName, MERIT_NORMAL, 0, nullptr, CLSID_LegacyAmFilterCategory}, }; CFactoryTemplate g_Templates[] = { {sudFilter[0].strName, sudFilter[0].clsID, CreateInstance, nullptr, &sudFilter[0]}, {sudFilter[1].strName, sudFilter[1].clsID, CreateInstance, nullptr, &sudFilter[1]}, }; int g_cTemplates = _countof(g_Templates); STDAPI DllRegisterServer() { RegisterSourceFilter( __uuidof(CMatroskaSourceFilter), MEDIASUBTYPE_Matroska, _T("0,4,,1A45DFA3"), _T(".mkv"), _T(".mka"), _T(".mks"), nullptr); return AMovieDllRegisterServer2(TRUE); } STDAPI DllUnregisterServer() { UnRegisterSourceFilter(MEDIASUBTYPE_Matroska); return AMovieDllRegisterServer2(FALSE); } #include "../../FilterApp.h" CFilterApp theApp; #endif // // CMatroskaSplitterFilter // CMatroskaSplitterFilter::CMatroskaSplitterFilter(LPUNKNOWN pUnk, HRESULT* phr) : CBaseSplitterFilter(NAME("CMatroskaSplitterFilter"), pUnk, phr, __uuidof(this)) { } CMatroskaSplitterFilter::~CMatroskaSplitterFilter() { } STDMETHODIMP CMatroskaSplitterFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv) { CheckPointer(ppv, E_POINTER); return QI(ITrackInfo) __super::NonDelegatingQueryInterface(riid, ppv); } STDMETHODIMP CMatroskaSplitterFilter::QueryFilterInfo(FILTER_INFO* pInfo) { CheckPointer(pInfo, E_POINTER); ValidateReadWritePtr(pInfo, sizeof(FILTER_INFO)); if (m_pName && m_pName[0] == L'M' && m_pName[1] == L'P' && m_pName[2] == L'C') { (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName); } else { wcscpy_s(pInfo->achName, MatroskaSourceName); } pInfo->pGraph = m_pGraph; if (m_pGraph) { m_pGraph->AddRef(); } return S_OK; } HRESULT CMatroskaSplitterFilter::CreateOutputs(IAsyncReader* pAsyncReader) { CheckPointer(pAsyncReader, E_POINTER); HRESULT hr = E_FAIL; m_pFile.Free(); m_pTrackEntryMap.RemoveAll(); m_pOrderedTrackArray.RemoveAll(); CAtlArray pinOut; CAtlArray pinOutTE; m_pFile.Attach(DEBUG_NEW CMatroskaFile(pAsyncReader, hr)); if (!m_pFile) { return E_OUTOFMEMORY; } if (FAILED(hr)) { m_pFile.Free(); return hr; } m_rtNewStart = m_rtCurrent = 0; m_rtNewStop = m_rtStop = m_rtDuration = 0; int iVideo = 1, iAudio = 1, iSubtitle = 1; bool bHasVideo = 0; POSITION pos = m_pFile->m_segment.Tracks.GetHeadPosition(); while (pos) { Track* pT = m_pFile->m_segment.Tracks.GetNext(pos); POSITION pos2 = pT->TrackEntries.GetHeadPosition(); while (pos2) { TrackEntry* pTE = pT->TrackEntries.GetNext(pos2); bool isSub = false; if (!pTE->Expand(pTE->CodecPrivate, ContentEncoding::TracksPrivateData)) { continue; } CStringA CodecID = pTE->CodecID.ToString(); CStringW Name; Name.Format(L"Output %I64u", (UINT64)pTE->TrackNumber); CMediaType mt; CAtlArray mts; mt.SetSampleSize(1); if (pTE->TrackType == TrackEntry::TypeVideo) { Name.Format(L"Video %d", iVideo++); mt.majortype = MEDIATYPE_Video; if (CodecID == "V_MS/VFW/FOURCC") { mt.formattype = FORMAT_VideoInfo; VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + pTE->CodecPrivate.GetCount() - sizeof(BITMAPINFOHEADER)); memset(mt.Format(), 0, mt.FormatLength()); memcpy(&pvih->bmiHeader, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); mt.subtype = FOURCCMap(pvih->bmiHeader.biCompression); switch (pvih->bmiHeader.biCompression) { case BI_RGB: case BI_BITFIELDS: mt.subtype = pvih->bmiHeader.biBitCount == 1 ? MEDIASUBTYPE_RGB1 : pvih->bmiHeader.biBitCount == 4 ? MEDIASUBTYPE_RGB4 : pvih->bmiHeader.biBitCount == 8 ? MEDIASUBTYPE_RGB8 : pvih->bmiHeader.biBitCount == 16 ? MEDIASUBTYPE_RGB565 : pvih->bmiHeader.biBitCount == 24 ? MEDIASUBTYPE_RGB24 : pvih->bmiHeader.biBitCount == 32 ? MEDIASUBTYPE_ARGB32 : MEDIASUBTYPE_NULL; break; // case BI_RLE8: mt.subtype = MEDIASUBTYPE_RGB8; break; // case BI_RLE4: mt.subtype = MEDIASUBTYPE_RGB4; break; } if (!bHasVideo) { mts.Add(mt); if (mt.subtype == MEDIASUBTYPE_WVC1) { mt.subtype = MEDIASUBTYPE_WVC1_CYBERLINK; mts.Add(mt); } } bHasVideo = true; } else if (CodecID == "V_UNCOMPRESSED") { } else if (CodecID.Find("V_MPEG4/ISO/AVC") == 0 && pTE->CodecPrivate.GetCount() >= 6) { BYTE sps = pTE->CodecPrivate[5] & 0x1f; std::vector avcC; for (size_t i = 0, j = pTE->CodecPrivate.GetCount(); i < j; i++) { avcC.push_back(pTE->CodecPrivate[i]); } std::vector sh; unsigned jj = 6; while (sps--) { if (jj + 2 > avcC.size()) { goto avcfail; } unsigned spslen = ((unsigned)avcC[jj] << 8) | avcC[jj + 1]; if (jj + 2 + spslen > avcC.size()) { goto avcfail; } unsigned cur = sh.size(); sh.resize(cur + spslen + 2, 0); std::copy(avcC.begin() + jj, avcC.begin() + jj + 2 + spslen, sh.begin() + cur); jj += 2 + spslen; } if (jj + 1 > avcC.size()) { continue; } unsigned pps = avcC[jj++]; while (pps--) { if (jj + 2 > avcC.size()) { goto avcfail; } unsigned ppslen = ((unsigned)avcC[jj] << 8) | avcC[jj + 1]; if (jj + 2 + ppslen > avcC.size()) { goto avcfail; } unsigned cur = sh.size(); sh.resize(cur + ppslen + 2, 0); std::copy(avcC.begin() + jj, avcC.begin() + jj + 2 + ppslen, sh.begin() + cur); jj += 2 + ppslen; } goto avcsuccess; avcfail: continue; avcsuccess: CAtlArray data; data.SetCount(sh.size()); std::copy(sh.begin(), sh.end(), data.GetData()); mt.subtype = FOURCCMap('1CVA'); mt.formattype = FORMAT_MPEG2Video; MPEG2VIDEOINFO* pm2vi = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + data.GetCount()); memset(mt.Format(), 0, mt.FormatLength()); pm2vi->hdr.bmiHeader.biSize = sizeof(pm2vi->hdr.bmiHeader); pm2vi->hdr.bmiHeader.biWidth = (LONG)pTE->v.PixelWidth; pm2vi->hdr.bmiHeader.biHeight = (LONG)pTE->v.PixelHeight; pm2vi->hdr.bmiHeader.biCompression = '1CVA'; pm2vi->hdr.bmiHeader.biPlanes = 1; pm2vi->hdr.bmiHeader.biBitCount = 24; pm2vi->dwProfile = pTE->CodecPrivate[1]; pm2vi->dwLevel = pTE->CodecPrivate[3]; pm2vi->dwFlags = (pTE->CodecPrivate[4] & 3) + 1; BYTE* pSequenceHeader = (BYTE*)pm2vi->dwSequenceHeader; memcpy(pSequenceHeader, data.GetData(), data.GetCount()); pm2vi->cbSequenceHeader = (DWORD)data.GetCount(); if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } else if (CodecID.Find("V_MPEG4/") == 0) { mt.subtype = FOURCCMap('V4PM'); mt.formattype = FORMAT_MPEG2Video; MPEG2VIDEOINFO* pm2vi = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + pTE->CodecPrivate.GetCount()); memset(mt.Format(), 0, mt.FormatLength()); pm2vi->hdr.bmiHeader.biSize = sizeof(pm2vi->hdr.bmiHeader); pm2vi->hdr.bmiHeader.biWidth = (LONG)pTE->v.PixelWidth; pm2vi->hdr.bmiHeader.biHeight = (LONG)pTE->v.PixelHeight; pm2vi->hdr.bmiHeader.biCompression = 'V4PM'; pm2vi->hdr.bmiHeader.biPlanes = 1; pm2vi->hdr.bmiHeader.biBitCount = 24; BYTE* pSequenceHeader = (BYTE*)pm2vi->dwSequenceHeader; memcpy(pSequenceHeader, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); pm2vi->cbSequenceHeader = (DWORD)pTE->CodecPrivate.GetCount(); if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } else if (CodecID.Find("V_REAL/RV") == 0) { mt.subtype = FOURCCMap('00VR' + ((CodecID[9] - 0x30) << 16)); mt.formattype = FORMAT_VideoInfo; VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + pTE->CodecPrivate.GetCount()); memset(mt.Format(), 0, mt.FormatLength()); memcpy(mt.Format() + sizeof(VIDEOINFOHEADER), pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); pvih->bmiHeader.biSize = sizeof(pvih->bmiHeader); pvih->bmiHeader.biWidth = (LONG)pTE->v.PixelWidth; pvih->bmiHeader.biHeight = (LONG)pTE->v.PixelHeight; pvih->bmiHeader.biCompression = mt.subtype.Data1; if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } else if (CodecID == "V_DIRAC") { mt.subtype = MEDIASUBTYPE_DiracVideo; mt.formattype = FORMAT_DiracVideoInfo; DIRACINFOHEADER* dvih = (DIRACINFOHEADER*)mt.AllocFormatBuffer(FIELD_OFFSET(DIRACINFOHEADER, dwSequenceHeader) + pTE->CodecPrivate.GetCount()); memset(mt.Format(), 0, mt.FormatLength()); dvih->hdr.bmiHeader.biSize = sizeof(dvih->hdr.bmiHeader); dvih->hdr.bmiHeader.biWidth = (LONG)pTE->v.PixelWidth; dvih->hdr.bmiHeader.biHeight = (LONG)pTE->v.PixelHeight; dvih->hdr.dwPictAspectRatioX = dvih->hdr.bmiHeader.biWidth; dvih->hdr.dwPictAspectRatioY = dvih->hdr.bmiHeader.biHeight; BYTE* pSequenceHeader = (BYTE*)dvih->dwSequenceHeader; memcpy(pSequenceHeader, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); dvih->cbSequenceHeader = (DWORD)pTE->CodecPrivate.GetCount(); if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } else if (CodecID == "V_MPEG2") { BYTE* seqhdr = pTE->CodecPrivate.GetData(); DWORD len = (DWORD)pTE->CodecPrivate.GetCount(); int w = (int)pTE->v.PixelWidth; int h = (int)pTE->v.PixelHeight; if (MakeMPEG2MediaType(mt, seqhdr, len, w, h)) { if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } } else if (CodecID == "V_THEORA") { BYTE* thdr = pTE->CodecPrivate.GetData() + 3; mt.majortype = MEDIATYPE_Video; mt.subtype = FOURCCMap('OEHT'); mt.formattype = FORMAT_MPEG2_VIDEO; MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(sizeof(MPEG2VIDEOINFO) + pTE->CodecPrivate.GetCount()); memset(mt.Format(), 0, mt.FormatLength()); vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader); vih->hdr.bmiHeader.biWidth = *(WORD*)&thdr[10] >> 4; vih->hdr.bmiHeader.biHeight = *(WORD*)&thdr[12] >> 4; vih->hdr.bmiHeader.biCompression = 'OEHT'; vih->hdr.bmiHeader.biPlanes = 1; vih->hdr.bmiHeader.biBitCount = 24; int nFpsNum = (thdr[22] << 24) | (thdr[23] << 16) | (thdr[24] << 8) | thdr[25]; int nFpsDenum = (thdr[26] << 24) | (thdr[27] << 16) | (thdr[28] << 8) | thdr[29]; if (nFpsNum) { vih->hdr.AvgTimePerFrame = (REFERENCE_TIME)(10000000.0 * nFpsDenum / nFpsNum); } vih->hdr.dwPictAspectRatioX = (thdr[14] << 16) | (thdr[15] << 8) | thdr[16]; vih->hdr.dwPictAspectRatioY = (thdr[17] << 16) | (thdr[18] << 8) | thdr[19]; mt.bFixedSizeSamples = 0; vih->cbSequenceHeader = (DWORD)pTE->CodecPrivate.GetCount(); memcpy(&vih->dwSequenceHeader, pTE->CodecPrivate.GetData(), vih->cbSequenceHeader); if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } else if (CodecID.Find("V_VP8") == 0) { mt.subtype = FOURCCMap('08PV'); mt.formattype = FORMAT_VideoInfo; VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + pTE->CodecPrivate.GetCount()); memset(mt.Format(), 0, mt.FormatLength()); memcpy(mt.Format() + sizeof(VIDEOINFOHEADER), pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); pvih->bmiHeader.biSize = sizeof(pvih->bmiHeader); pvih->bmiHeader.biWidth = (LONG)pTE->v.PixelWidth; pvih->bmiHeader.biHeight = (LONG)pTE->v.PixelHeight; pvih->bmiHeader.biCompression = mt.subtype.Data1; if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } else if (CodecID == "V_QUICKTIME" && pTE->CodecPrivate.GetCount() >= 8) { DWORD* type; if (m_pFile->m_ebml.DocTypeReadVersion == 1) { type = (DWORD*)(pTE->CodecPrivate.GetData()); } else { type = (DWORD*)(pTE->CodecPrivate.GetData() + 4); } if (*type == MAKEFOURCC('S', 'V', 'Q', '3') || *type == MAKEFOURCC('S', 'V', 'Q', '1') || *type == MAKEFOURCC('c', 'v', 'i', 'd')) { mt.subtype = FOURCCMap(*type); mt.formattype = FORMAT_VideoInfo; VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + pTE->CodecPrivate.GetCount()); memset(mt.Format(), 0, mt.FormatLength()); memcpy(mt.Format() + sizeof(VIDEOINFOHEADER), pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); pvih->bmiHeader.biSize = sizeof(pvih->bmiHeader); pvih->bmiHeader.biWidth = (LONG)pTE->v.PixelWidth; pvih->bmiHeader.biHeight = (LONG)pTE->v.PixelHeight; pvih->bmiHeader.biCompression = mt.subtype.Data1; if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } } else if (CodecID == "V_DSHOW/MPEG1VIDEO" || CodecID == "V_MPEG1") { mt.majortype = MEDIATYPE_Video; mt.subtype = MEDIASUBTYPE_MPEG1Payload; mt.formattype = FORMAT_MPEGVideo; MPEG1VIDEOINFO* pm1vi = (MPEG1VIDEOINFO*)mt.AllocFormatBuffer(sizeof(MPEG1VIDEOINFO) + pTE->CodecPrivate.GetCount()); memset(mt.Format(), 0, mt.FormatLength()); memcpy(mt.Format() + sizeof(MPEG1VIDEOINFO), pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); pm1vi->hdr.bmiHeader.biSize = sizeof(pm1vi->hdr.bmiHeader); pm1vi->hdr.bmiHeader.biWidth = (LONG)pTE->v.PixelWidth; pm1vi->hdr.bmiHeader.biHeight = (LONG)pTE->v.PixelHeight; pm1vi->hdr.bmiHeader.biBitCount = 12; pm1vi->hdr.bmiHeader.biSizeImage = DIBSIZE(pm1vi->hdr.bmiHeader); mt.SetSampleSize(pm1vi->hdr.bmiHeader.biWidth * pm1vi->hdr.bmiHeader.biHeight * 4); if (!bHasVideo) { mts.Add(mt); } bHasVideo = true; } REFERENCE_TIME AvgTimePerFrame = 0; if (pTE->v.FramePerSec > 0) { AvgTimePerFrame = (REFERENCE_TIME)(10000000i64 / pTE->v.FramePerSec); } else if (pTE->DefaultDuration > 0 && pTE->DefaultDuration != 1000000) { AvgTimePerFrame = (REFERENCE_TIME)pTE->DefaultDuration / 100; } else { // pTE->DefaultDuration == 1000000 or < 0 CMatroskaNode Root(m_pFile); m_pSegment = Root.Child(0x18538067); m_pCluster = m_pSegment->Child(0x1F43B675); MatroskaReader::QWORD lastCueClusterPosition = (MatroskaReader::QWORD) - 1; UINT64 timecode1 = 0; UINT64 timecode2 = 0; unsigned int framecount = 0; bool readmore = true; POSITION pos1 = m_pFile->m_segment.Cues.GetHeadPosition(); while (readmore && pos1) { Cue* pCue = m_pFile->m_segment.Cues.GetNext(pos1); POSITION pos2 = pCue->CuePoints.GetHeadPosition(); while (readmore && pos2) { CuePoint* pCuePoint = pCue->CuePoints.GetNext(pos2); POSITION pos3 = pCuePoint->CueTrackPositions.GetHeadPosition(); while (readmore && pos3) { CueTrackPosition* pCueTrackPositions = pCuePoint->CueTrackPositions.GetNext(pos3); if (pCueTrackPositions->CueTrack != pTE->TrackNumber) { continue; } if (lastCueClusterPosition == pCueTrackPositions->CueClusterPosition) { continue; } lastCueClusterPosition = pCueTrackPositions->CueClusterPosition; m_pCluster->SeekTo(m_pSegment->m_start + pCueTrackPositions->CueClusterPosition); m_pCluster->Parse(); Cluster c; c.ParseTimeCode(m_pCluster); if (CAutoPtr pBlock = m_pCluster->GetFirstBlock()) { do { CBlockGroupNode bgn; if (pBlock->m_id == 0xA0) { bgn.Parse(pBlock, true); } else if (pBlock->m_id == 0xA3) { CAutoPtr bg(DEBUG_NEW BlockGroup()); bg->Block.Parse(pBlock, true); if (!(bg->Block.Lacing & 0x80)) { bg->ReferenceBlock.Set(0); // not a kf } bgn.AddTail(bg); } POSITION pos4 = bgn.GetHeadPosition(); while (pos4) { BlockGroup* bg = bgn.GetNext(pos4); if (bg->Block.TrackNumber != pTE->TrackNumber) { continue; } UINT64 tc = c.TimeCode + bg->Block.TimeCode; TRACE(_T("Frame: %d, TimeCode %I64d\n"), framecount, tc); if (framecount == 0) { timecode1 = tc; timecode2 = tc; } else if (tc == timecode2) { // hmm continue; } else if (tc > timecode2) { // I and P frames if (framecount > 24) { // good for 23.976, 24, 25, 30, 50, 60 fps. // for 29.97 and 59,94 can give a small inaccuracy readmore = false; break; } timecode2 = tc; } //else if (tc < timecode2) {} // B-Frames framecount++; } } while (readmore && pBlock->NextBlock()); } } } } if (framecount > 1) { AvgTimePerFrame = m_pFile->m_segment.SegmentInfo.TimeCodeScale * (timecode2 - timecode1) / (100 * (framecount - 1)); } m_pCluster.Free(); } //if (AvgTimePerFrame < 0) AvgTimePerFrame = 0; for (size_t i = 0; i < mts.GetCount(); i++) { if (mts[i].formattype == FORMAT_VideoInfo || mts[i].formattype == FORMAT_VideoInfo2 || mts[i].formattype == FORMAT_MPEG2Video || mts[i].formattype == FORMAT_MPEGVideo) { if (pTE->v.PixelWidth && pTE->v.PixelHeight) { RECT rect = {(LONG)pTE->v.VideoPixelCropLeft, (LONG)pTE->v.VideoPixelCropTop, (LONG)(pTE->v.PixelWidth - pTE->v.VideoPixelCropRight), (LONG)(pTE->v.PixelHeight - pTE->v.VideoPixelCropBottom) }; VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)mts[i].Format(); vih->rcSource = vih->rcTarget = rect; } if (AvgTimePerFrame) { if (mts[i].subtype == MEDIASUBTYPE_MPEG1Payload) { AvgTimePerFrame *= 2; // Need more testing, but work on all sample that i have :) } ((VIDEOINFOHEADER*)mts[i].Format())->AvgTimePerFrame = AvgTimePerFrame; } } } if (pTE->v.DisplayWidth && pTE->v.DisplayHeight) { for (size_t i = 0; i < mts.GetCount(); i++) { if (mts[i].formattype == FORMAT_VideoInfo) { mt = mts[i]; DWORD vih1 = FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader); DWORD vih2 = FIELD_OFFSET(VIDEOINFOHEADER2, bmiHeader); DWORD bmi = mts[i].FormatLength() - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader); mt.formattype = FORMAT_VideoInfo2; mt.AllocFormatBuffer(vih2 + bmi); memcpy(mt.Format(), mts[i].Format(), vih1); memset(mt.Format() + vih1, 0, vih2 - vih1); memcpy(mt.Format() + vih2, mts[i].Format() + vih1, bmi); CSize aspect((int)pTE->v.DisplayWidth, (int)pTE->v.DisplayHeight); int gcd = GCD(aspect.cx, aspect.cy); if (gcd > 1) { aspect.cx /= gcd; aspect.cy /= gcd; } ((VIDEOINFOHEADER2*)mt.Format())->dwPictAspectRatioX = aspect.cx; ((VIDEOINFOHEADER2*)mt.Format())->dwPictAspectRatioY = aspect.cy; mts.InsertAt(i++, mt); } else if (mts[i].formattype == FORMAT_MPEG2Video) { CSize aspect((int)pTE->v.DisplayWidth, (int)pTE->v.DisplayHeight); int gcd = GCD(aspect.cx, aspect.cy); if (gcd > 1) { aspect.cx /= gcd; aspect.cy /= gcd; } ((MPEG2VIDEOINFO*)mts[i].Format())->hdr.dwPictAspectRatioX = aspect.cx; ((MPEG2VIDEOINFO*)mts[i].Format())->hdr.dwPictAspectRatioY = aspect.cy; } } } } else if (pTE->TrackType == TrackEntry::TypeAudio) { Name.Format(L"Audio %d", iAudio++); mt.majortype = MEDIATYPE_Audio; mt.formattype = FORMAT_WaveFormatEx; WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX)); memset(wfe, 0, mt.FormatLength()); wfe->nChannels = (WORD)pTE->a.Channels; wfe->nSamplesPerSec = (DWORD)pTE->a.SamplingFrequency; wfe->wBitsPerSample = (WORD)pTE->a.BitDepth; wfe->nBlockAlign = (WORD)((wfe->nChannels * wfe->wBitsPerSample) / 8); wfe->nAvgBytesPerSec = wfe->nSamplesPerSec * wfe->nBlockAlign; mt.SetSampleSize(256000); if (CodecID == "A_MPEG/L3") { mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_MPEGLAYER3); mts.Add(mt); } else if (CodecID == "A_MPEG/L2" || CodecID == "A_MPEG/L1") { mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_MPEG); mts.Add(mt); } else if (CodecID == "A_AC3" || CodecID == "A_EAC3") { mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_DOLBY_AC3); mts.Add(mt); } else if (CodecID == "A_TRUEHD" || CodecID == "A_MLP") { wfe->wFormatTag = WAVE_FORMAT_DOLBY_AC3; mt.subtype = MEDIASUBTYPE_DOLBY_TRUEHD; mts.Add(mt); } else if (CodecID == "A_DTS") { mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_DVD_DTS); mts.Add(mt); } else if (CodecID == "A_TTA1") { mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_TTA1); wfe->cbSize = 30; wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + 30); BYTE* p = (BYTE*)(wfe + 1); memcpy(p, (const unsigned char*)"TTA1\x01\x00", 6); memcpy(p + 6, &wfe->nChannels, 2); memcpy(p + 8, &wfe->wBitsPerSample, 2); memcpy(p + 10, &wfe->nSamplesPerSec, 4); memset(p + 14, 0, 30 - 14); mts.Add(mt); } else if (CodecID == "A_AAC") { mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_AAC); wfe->cbSize = (WORD)pTE->CodecPrivate.GetCount(); wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + pTE->CodecPrivate.GetCount()); memcpy(wfe + 1, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); mts.Add(mt); } else if (CodecID == "A_WAVPACK4") { mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_WAVPACK4); wfe->cbSize = (WORD)pTE->CodecPrivate.GetCount(); wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + pTE->CodecPrivate.GetCount()); memcpy(wfe + 1, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); mts.Add(mt); } else if (CodecID == "A_FLAC") { wfe->wFormatTag = WAVE_FORMAT_FLAC; wfe->cbSize = (WORD)pTE->CodecPrivate.GetCount(); wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + pTE->CodecPrivate.GetCount()); memcpy(wfe + 1, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); mt.subtype = MEDIASUBTYPE_FLAC_FRAMED; mts.Add(mt); mt.subtype = MEDIASUBTYPE_FLAC; mts.Add(mt); } else if (CodecID == "A_PCM/INT/LIT") { mt.subtype = MEDIASUBTYPE_PCM; mt.SetSampleSize(wfe->nBlockAlign); if (pTE->a.Channels <= 2 && pTE->a.BitDepth <= 16) { wfe->wFormatTag = WAVE_FORMAT_PCM; } else { WAVEFORMATEXTENSIBLE* wfex = (WAVEFORMATEXTENSIBLE*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEXTENSIBLE)); if (pTE->a.BitDepth & 7) { wfex->Format.wBitsPerSample = (WORD)(pTE->a.BitDepth + 7) & 0xFFF8; wfex->Format.nBlockAlign = wfex->Format.nChannels * wfex->Format.wBitsPerSample / 8; wfex->Format.nAvgBytesPerSec = wfex->Format.nSamplesPerSec * wfex->Format.nBlockAlign; mt.SetSampleSize(wfex->Format.nBlockAlign); } wfex->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfex->Format.cbSize = 22; wfex->Samples.wValidBitsPerSample = (WORD)pTE->a.BitDepth; wfex->dwChannelMask = GetDefChannelMask(wfex->Format.nChannels); wfex->SubFormat = MEDIASUBTYPE_PCM; } mts.Add(mt); } else if (CodecID == "A_PCM/FLOAT/IEEE") { mt.subtype = MEDIASUBTYPE_IEEE_FLOAT; mt.SetSampleSize(wfe->nBlockAlign); if (pTE->a.Channels <= 2) { wfe->wFormatTag = WAVE_FORMAT_IEEE_FLOAT; } else { WAVEFORMATEXTENSIBLE* wfex = (WAVEFORMATEXTENSIBLE*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEXTENSIBLE)); wfex->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfex->Format.cbSize = 22; wfex->Samples.wValidBitsPerSample = (WORD)pTE->a.BitDepth; wfex->dwChannelMask = GetDefChannelMask(wfex->Format.nChannels); wfex->SubFormat = MEDIASUBTYPE_IEEE_FLOAT; } mts.Add(mt); } else if (CodecID == "A_MS/ACM") { wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(pTE->CodecPrivate.GetCount()); memcpy(wfe, (WAVEFORMATEX*)pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); if (wfe->wFormatTag == WAVE_FORMAT_EXTENSIBLE && wfe->cbSize == 22) { mt.subtype = ((WAVEFORMATEXTENSIBLE*)wfe)->SubFormat; } else { mt.subtype = FOURCCMap(wfe->wFormatTag); } mts.Add(mt); } else if (CodecID == "A_VORBIS") { BYTE* p = pTE->CodecPrivate.GetData(); CAtlArray sizes; int totalsize = 0; for (BYTE n = *p++; n > 0; n--) { int size = 0; do { size += *p; } while (*p++ == 0xff); sizes.Add(size); totalsize += size; } sizes.Add((int)(pTE->CodecPrivate.GetData() + pTE->CodecPrivate.GetCount() - p) - totalsize); totalsize += sizes[sizes.GetCount() - 1]; if (sizes.GetCount() == 3) { mt.subtype = MEDIASUBTYPE_Vorbis2; mt.formattype = FORMAT_VorbisFormat2; VORBISFORMAT2* pvf2 = (VORBISFORMAT2*)mt.AllocFormatBuffer(sizeof(VORBISFORMAT2) + (ULONG)totalsize); memset(pvf2, 0, mt.FormatLength()); pvf2->Channels = (WORD)pTE->a.Channels; pvf2->SamplesPerSec = (DWORD)pTE->a.SamplingFrequency; pvf2->BitsPerSample = (DWORD)pTE->a.BitDepth; BYTE* p2 = mt.Format() + sizeof(VORBISFORMAT2); for (size_t i = 0; i < sizes.GetCount(); p += sizes[i], p2 += sizes[i], i++) { memcpy(p2, p, pvf2->HeaderSize[i] = sizes[i]); } mts.Add(mt); } mt.subtype = MEDIASUBTYPE_Vorbis; mt.formattype = FORMAT_VorbisFormat; VORBISFORMAT* vf = (VORBISFORMAT*)mt.AllocFormatBuffer(sizeof(VORBISFORMAT)); memset(vf, 0, mt.FormatLength()); vf->nChannels = (WORD)pTE->a.Channels; vf->nSamplesPerSec = (DWORD)pTE->a.SamplingFrequency; vf->nMinBitsPerSec = vf->nMaxBitsPerSec = vf->nAvgBitsPerSec = (DWORD) - 1; mts.Add(mt); } else if (CodecID.Find("A_AAC/") == 0) { mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_AAC); wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + 5); wfe->cbSize = 2; int profile; if (CodecID.Find("/MAIN") > 0) { profile = 0; } else if (CodecID.Find("/SBR") > 0) { profile = -1; } else if (CodecID.Find("/LC") > 0) { profile = 1; } else if (CodecID.Find("/SSR") > 0) { profile = 2; } else if (CodecID.Find("/LTP") > 0) { profile = 3; } else { continue; } WORD cbSize = MakeAACInitData((BYTE*)(wfe + 1), profile, wfe->nSamplesPerSec, (int)pTE->a.Channels); mts.Add(mt); if (profile < 0) { wfe->cbSize = cbSize; wfe->nSamplesPerSec *= 2; wfe->nAvgBytesPerSec *= 2; mts.InsertAt(0, mt); } } else if (CodecID.Find("A_REAL/") == 0 && CodecID.GetLength() >= 11) { mt.subtype = FOURCCMap((DWORD)CodecID[7] | ((DWORD)CodecID[8] << 8) | ((DWORD)CodecID[9] << 16) | ((DWORD)CodecID[10] << 24)); mt.bTemporalCompression = TRUE; wfe->cbSize = (WORD)pTE->CodecPrivate.GetCount(); wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + pTE->CodecPrivate.GetCount()); memcpy(wfe + 1, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); wfe->cbSize = 0; // IMPORTANT: this is screwed, but cbSize has to be 0 and the extra data from codec priv must be after WAVEFORMATEX mts.Add(mt); } else if (CodecID == "A_QUICKTIME" && pTE->CodecPrivate.GetCount() >= 8) { DWORD* type = (DWORD*)(pTE->CodecPrivate.GetData() + 4); if (*type == MAKEFOURCC('Q', 'D', 'M', '2') || *type == MAKEFOURCC('Q', 'D', 'M', 'C')) { mt.subtype = FOURCCMap(*type); wfe->cbSize = (WORD)pTE->CodecPrivate.GetCount(); wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + pTE->CodecPrivate.GetCount()); memcpy(wfe + 1, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); mts.Add(mt); } } else if (CodecID == "A_ALAC") { mt.subtype = MEDIASUBTYPE_ALAC; WORD cbSize = (WORD)pTE->CodecPrivate.GetCount() + 12; wfe->cbSize = cbSize; wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + cbSize); BYTE* p = (BYTE*)(wfe + 1); memset(p, 0, cbSize); memcpy(p + 3, &cbSize, 1); memcpy(p + 4, (const unsigned char*)"alac", 4); memcpy(p + 12, pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); mts.Add(mt); } } else if (pTE->TrackType == TrackEntry::TypeSubtitle) { if (iSubtitle == 1) { InstallFonts(); } Name.Format(L"Subtitle %d", iSubtitle++); mt.SetSampleSize(1); if (CodecID == "S_TEXT/ASCII") { mt.majortype = MEDIATYPE_Text; mt.subtype = MEDIASUBTYPE_NULL; mt.formattype = FORMAT_None; mts.Add(mt); isSub = true; } else { mt.majortype = MEDIATYPE_Subtitle; mt.formattype = FORMAT_SubtitleInfo; SUBTITLEINFO* psi = (SUBTITLEINFO*)mt.AllocFormatBuffer(sizeof(SUBTITLEINFO) + pTE->CodecPrivate.GetCount()); memset(psi, 0, mt.FormatLength()); strncpy_s(psi->IsoLang, pTE->Language, _countof(psi->IsoLang) - 1); CString subtitle_Name = pTE->Name; if (pTE->FlagForced) { // "Forced" overrides "Default" subtitle_Name += L" [Forced]"; } else if (pTE->FlagDefault) { subtitle_Name += L" [Default]"; } subtitle_Name = subtitle_Name.Trim(); wcsncpy_s(psi->TrackName, subtitle_Name, _countof(psi->TrackName) - 1); memcpy(mt.pbFormat + (psi->dwOffset = sizeof(SUBTITLEINFO)), pTE->CodecPrivate.GetData(), pTE->CodecPrivate.GetCount()); mt.subtype = CodecID == "S_TEXT/UTF8" ? MEDIASUBTYPE_UTF8 : CodecID == "S_TEXT/SSA" || CodecID == "S_SSA" ? MEDIASUBTYPE_SSA : CodecID == "S_TEXT/ASS" || CodecID == "S_ASS" ? MEDIASUBTYPE_ASS : CodecID == "S_TEXT/USF" || CodecID == "S_USF" ? MEDIASUBTYPE_USF : CodecID == "S_HDMV/PGS" ? MEDIASUBTYPE_HDMVSUB : //CodecID == "S_DVBSUB" ? MEDIASUBTYPE_DVB_SUBTITLES : // does not work CodecID == "S_VOBSUB" ? MEDIASUBTYPE_VOBSUB : MEDIASUBTYPE_NULL; if (mt.subtype != MEDIASUBTYPE_NULL) { mts.Add(mt); isSub = true; } } } if (mts.IsEmpty()) { TRACE(_T("CMatroskaSourceFilter: Unsupported TrackType %s (%I64d)\n"), CString(CodecID), (UINT64)pTE->TrackType); continue; } Name = CStringW(pTE->Language.IsEmpty() ? L"English" : CStringW(ISO6392ToLanguage(pTE->Language))) + (pTE->Name.IsEmpty() ? L"" : L", " + pTE->Name) + (L" (" + Name + L")"); if (pTE->FlagForced) { // "Forced" overrides "Default" Name = Name + L" [Forced]"; } else if (pTE->FlagDefault) { Name = Name + L" [Default]"; } HRESULT hr2; CAutoPtr pPinOut(DEBUG_NEW CMatroskaSplitterOutputPin(pTE->MinCache, pTE->DefaultDuration / 100, mts, Name, this, this, &hr2)); if (!pTE->Name.IsEmpty()) { pPinOut->SetProperty(L"NAME", pTE->Name); } if (pTE->Language.GetLength() == 3) { pPinOut->SetProperty(L"LANG", CStringW(CString(pTE->Language))); } if (!isSub) { pinOut.InsertAt((iVideo + iAudio - 3), DEBUG_NEW CMatroskaSplitterOutputPin(pTE->MinCache, pTE->DefaultDuration / 100, mts, Name, this, this, &hr2), 1); pinOutTE.InsertAt((iVideo + iAudio - 3), pTE, 1); } else { pinOut.Add(DEBUG_NEW CMatroskaSplitterOutputPin(pTE->MinCache, pTE->DefaultDuration / 100, mts, Name, this, this, &hr2)); pinOutTE.Add(pTE); } } } for (size_t i = 0; i < pinOut.GetCount(); i++) { CAutoPtr pPinOut; pPinOut.Attach(pinOut[i]); TrackEntry* pTE = pinOutTE[i]; if (pTE != nullptr) { AddOutputPin((DWORD)pTE->TrackNumber, pPinOut); m_pTrackEntryMap[(DWORD)pTE->TrackNumber] = pTE; m_pOrderedTrackArray.Add(pTE); } } Info& info = m_pFile->m_segment.SegmentInfo; if (m_pFile->IsRandomAccess()) { m_rtDuration = (REFERENCE_TIME)(info.Duration * info.TimeCodeScale / 100); } m_rtNewStop = m_rtStop = m_rtDuration; /*#ifdef _DEBUG for (int i = 1, j = GetChapterCount(CHAPTER_ROOT_ID); i <= j; i++) { UINT id = GetChapterId(CHAPTER_ROOT_ID, i); struct ChapterElement ce; BOOL b = GetChapterInfo(id, &ce); BSTR bstr = GetChapterStringInfo(id, "eng", ""); if (bstr) ::SysFreeString(bstr); } #endif*/ SetProperty(L"TITL", info.Title); // TODO // resources { POSITION pos = m_pFile->m_segment.Attachments.GetHeadPosition(); while (pos) { Attachment* pA = m_pFile->m_segment.Attachments.GetNext(pos); POSITION pos = pA->AttachedFiles.GetHeadPosition(); while (pos) { AttachedFile* pF = pA->AttachedFiles.GetNext(pos); CAtlArray pData; pData.SetCount((size_t)pF->FileDataLen); m_pFile->Seek(pF->FileDataPos); if (SUCCEEDED(m_pFile->ByteRead(pData.GetData(), pData.GetCount()))) { ResAppend(pF->FileName, pF->FileDescription, CStringW(pF->FileMimeType), pData.GetData(), (DWORD)pData.GetCount()); } } } } // chapters if (ChapterAtom* caroot = m_pFile->m_segment.FindChapterAtom(0)) { CStringA str; str.ReleaseBufferSetLength(GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, str.GetBuffer(3), 3)); CStringA ChapLanguage = CStringA(ISO6391To6392(str)); if (ChapLanguage.GetLength() < 3) { ChapLanguage = "eng"; } SetupChapters(ChapLanguage, caroot); } return !m_pOutputs.IsEmpty() ? S_OK : E_FAIL; } void CMatroskaSplitterFilter::SetupChapters(LPCSTR lng, ChapterAtom* parent, int level) { CStringW tabs('+', level); if (!tabs.IsEmpty()) { tabs += ' '; } POSITION pos = parent->ChapterAtoms.GetHeadPosition(); while (pos) { // ChapUID zero not allow by Matroska specs UINT64 ChapUID = parent->ChapterAtoms.GetNext(pos)->ChapterUID; ChapterAtom* ca = (ChapUID == 0) ? nullptr : m_pFile->m_segment.FindChapterAtom(ChapUID); if (ca) { CStringW name, first; POSITION pos = ca->ChapterDisplays.GetHeadPosition(); while (pos) { ChapterDisplay* cd = ca->ChapterDisplays.GetNext(pos); if (first.IsEmpty()) { first = cd->ChapString; } if (cd->ChapLanguage == lng) { name = cd->ChapString; } } name = tabs + (!name.IsEmpty() ? name : first); ChapAppend(ca->ChapterTimeStart / 100 - m_pFile->m_rtOffset, name); if (!ca->ChapterAtoms.IsEmpty() && level < 5) { // level < 5 - hard limit for the number of levels SetupChapters(lng, ca, level + 1); } } } } void CMatroskaSplitterFilter::InstallFonts() { POSITION pos = m_pFile->m_segment.Attachments.GetHeadPosition(); while (pos) { Attachment* pA = m_pFile->m_segment.Attachments.GetNext(pos); POSITION p2 = pA->AttachedFiles.GetHeadPosition(); while (p2) { AttachedFile* pF = pA->AttachedFiles.GetNext(p2); if (pF->FileMimeType == "application/x-truetype-font" || pF->FileMimeType == "application/x-font-ttf" || pF->FileMimeType == "application/vnd.ms-opentype") { // assume this is a font resource if (BYTE* pData = DEBUG_NEW BYTE[(UINT)pF->FileDataLen]) { m_pFile->Seek(pF->FileDataPos); if (SUCCEEDED(m_pFile->ByteRead(pData, pF->FileDataLen))) { //m_fontinst.InstallFont(pData, (UINT)pF->FileDataLen); m_fontinst.InstallFontMemory(pData, (UINT)pF->FileDataLen); } delete [] pData; } } } } } void CMatroskaSplitterFilter::SendVorbisHeaderSample() { HRESULT hr; POSITION pos = m_pTrackEntryMap.GetStartPosition(); while (pos) { DWORD TrackNumber = 0; TrackEntry* pTE = nullptr; m_pTrackEntryMap.GetNextAssoc(pos, TrackNumber, pTE); CBaseSplitterOutputPin* pPin = GetOutputPin(TrackNumber); if (!(pTE && pPin && pPin->IsConnected())) { continue; } if (pTE->CodecID.ToString() == "A_VORBIS" && pPin->CurrentMediaType().subtype == MEDIASUBTYPE_Vorbis && !pTE->CodecPrivate.IsEmpty()) { BYTE* ptr = pTE->CodecPrivate.GetData(); CAtlList sizes; int last = 0; for (BYTE n = *ptr++; n > 0; n--) { int size = 0; do { size += *ptr; } while (*ptr++ == 0xff); sizes.AddTail(size); last += size; } sizes.AddTail((int)(pTE->CodecPrivate.GetData() + pTE->CodecPrivate.GetCount() - ptr) - last); hr = S_OK; POSITION pos = sizes.GetHeadPosition(); while (pos && SUCCEEDED(hr)) { long len = sizes.GetNext(pos); CAutoPtr p(DEBUG_NEW Packet()); p->TrackNumber = (DWORD)pTE->TrackNumber; p->rtStart = 0; p->rtStop = 1; p->bSyncPoint = FALSE; p->SetData(ptr, len); ptr += len; hr = DeliverPacket(p); } if (FAILED(hr)) { TRACE(_T("ERROR: Vorbis initialization failed for stream %I64d\n"), TrackNumber); } } } } bool CMatroskaSplitterFilter::DemuxInit() { SetThreadName((DWORD) - 1, "CMatroskaSplitterFilter"); CMatroskaNode Root(m_pFile); if (!m_pFile || !(m_pSegment = Root.Child(0x18538067)) || !(m_pCluster = m_pSegment->Child(0x1F43B675))) { return false; } // reindex if needed if (m_pFile->IsRandomAccess() && m_pFile->m_segment.Cues.IsEmpty()) { m_nOpenProgress = 0; m_pFile->m_segment.SegmentInfo.Duration.Set(0); UINT64 TrackNumber = m_pFile->m_segment.GetMasterTrack(); CAutoPtr pCue(DEBUG_NEW Cue()); do { Cluster c; c.ParseTimeCode(m_pCluster); m_pFile->m_segment.SegmentInfo.Duration.Set((float)c.TimeCode - m_pFile->m_rtOffset / 10000); CAutoPtr pCuePoint(DEBUG_NEW CuePoint()); CAutoPtr pCueTrackPosition(DEBUG_NEW CueTrackPosition()); pCuePoint->CueTime.Set(c.TimeCode); pCueTrackPosition->CueTrack.Set(TrackNumber); pCueTrackPosition->CueClusterPosition.Set(m_pCluster->m_filepos - m_pSegment->m_start); pCuePoint->CueTrackPositions.AddTail(pCueTrackPosition); pCue->CuePoints.AddTail(pCuePoint); m_nOpenProgress = m_pFile->GetPos() * 100 / m_pFile->GetLength(); DWORD cmd; if (CheckRequest(&cmd)) { if (cmd == CMD_EXIT) { m_fAbort = true; } else { Reply(S_OK); } } } while (!m_fAbort && m_pCluster->Next(true)); m_nOpenProgress = 100; if (!m_fAbort) { m_pFile->m_segment.Cues.AddTail(pCue); } m_fAbort = false; if (m_pFile->m_segment.Cues.GetCount()) { Info& info = m_pFile->m_segment.SegmentInfo; m_rtDuration = (REFERENCE_TIME)(info.Duration * info.TimeCodeScale / 100); m_rtNewStop = m_rtStop = m_rtDuration; } } m_pCluster.Free(); m_pBlock.Free(); return true; } void CMatroskaSplitterFilter::DemuxSeek(REFERENCE_TIME rt) { m_pCluster = m_pSegment->Child(0x1F43B675); m_pBlock.Free(); if (rt > 0) { rt += m_pFile->m_rtOffset; MatroskaReader::QWORD lastCueClusterPosition = (MatroskaReader::QWORD) - 1; Segment& s = m_pFile->m_segment; UINT64 TrackNumber = s.GetMasterTrack(); POSITION pos1 = s.Cues.GetHeadPosition(); while (pos1) { Cue* pCue = s.Cues.GetNext(pos1); POSITION pos2 = pCue->CuePoints.GetTailPosition(); while (pos2) { CuePoint* pCuePoint = pCue->CuePoints.GetPrev(pos2); if (rt < s.GetRefTime(pCuePoint->CueTime)) { continue; } POSITION pos3 = pCuePoint->CueTrackPositions.GetHeadPosition(); while (pos3) { CueTrackPosition* pCueTrackPositions = pCuePoint->CueTrackPositions.GetNext(pos3); if (TrackNumber != pCueTrackPositions->CueTrack) { continue; } if (lastCueClusterPosition == pCueTrackPositions->CueClusterPosition) { continue; } lastCueClusterPosition = pCueTrackPositions->CueClusterPosition; m_pCluster->SeekTo(m_pSegment->m_start + pCueTrackPositions->CueClusterPosition); m_pCluster->Parse(); bool fFoundKeyFrame = false; /* if (pCueTrackPositions->CueBlockNumber > 0) { // TODO: CueBlockNumber only tells the block num of the track and not for all mixed in the cluster m_nLastBlock = (int)pCueTrackPositions->CueBlockNumber; fFoundKeyFrame = true; } else */ { Cluster c; c.ParseTimeCode(m_pCluster); if (CAutoPtr pBlock = m_pCluster->GetFirstBlock()) { bool fPassedCueTime = false; do { CBlockGroupNode bgn; if (pBlock->m_id == 0xA0) { bgn.Parse(pBlock, true); } else if (pBlock->m_id == 0xA3) { CAutoPtr bg(DEBUG_NEW BlockGroup()); bg->Block.Parse(pBlock, true); if (!(bg->Block.Lacing & 0x80)) { bg->ReferenceBlock.Set(0); // not a kf } bgn.AddTail(bg); } POSITION pos4 = bgn.GetHeadPosition(); while (!fPassedCueTime && pos4) { BlockGroup* bg = bgn.GetNext(pos4); if (bg->Block.TrackNumber == pCueTrackPositions->CueTrack && rt < s.GetRefTime(c.TimeCode + bg->Block.TimeCode) || rt + 5000000i64 < s.GetRefTime(c.TimeCode + bg->Block.TimeCode)) { // allow 500ms difference between tracks, just in case intreleaving wasn't that much precise fPassedCueTime = true; } else if (bg->Block.TrackNumber == pCueTrackPositions->CueTrack && !bg->ReferenceBlock.IsValid()) { fFoundKeyFrame = true; m_pBlock = pBlock->Copy(); } } } while (!fPassedCueTime && pBlock->NextBlock()); } } if (fFoundKeyFrame) { pos1 = pos2 = pos3 = nullptr; } } } } if (!m_pBlock) { m_pCluster = m_pSegment->Child(0x1F43B675); } } } bool CMatroskaSplitterFilter::DemuxLoop() { HRESULT hr = S_OK; SendVorbisHeaderSample(); // HACK: init vorbis decoder with the headers do { Cluster c; c.ParseTimeCode(m_pCluster); if (!m_pBlock) { m_pBlock = m_pCluster->GetFirstBlock(); } if (!m_pBlock) { continue; } do { CBlockGroupNode bgn; if (m_pBlock->m_id == 0xA0) { bgn.Parse(m_pBlock, true); } else if (m_pBlock->m_id == 0xA3) { CAutoPtr bg(DEBUG_NEW BlockGroup()); bg->Block.Parse(m_pBlock, true); if (!(bg->Block.Lacing & 0x80)) { bg->ReferenceBlock.Set(0); // not a kf } bgn.AddTail(bg); } while (bgn.GetCount() && SUCCEEDED(hr)) { CAutoPtr p(DEBUG_NEW MatroskaPacket()); p->bg = bgn.RemoveHead(); p->bSyncPoint = !p->bg->ReferenceBlock.IsValid(); p->TrackNumber = (DWORD)p->bg->Block.TrackNumber; TrackEntry* pTE = nullptr; if (!m_pTrackEntryMap.Lookup(p->TrackNumber, pTE) || !pTE) { continue; } p->rtStart = m_pFile->m_segment.GetRefTime((REFERENCE_TIME)c.TimeCode + p->bg->Block.TimeCode); p->rtStop = p->rtStart + (p->bg->BlockDuration.IsValid() ? m_pFile->m_segment.GetRefTime(p->bg->BlockDuration) : 1); // Fix subtitle with duration = 0 if (pTE->TrackType == TrackEntry::TypeSubtitle && !p->bg->BlockDuration.IsValid()) { p->bg->BlockDuration.Set(1); // just setting it to be valid p->rtStop = p->rtStart; } POSITION pos = p->bg->Block.BlockData.GetHeadPosition(); while (pos) { CBinary* pb = p->bg->Block.BlockData.GetNext(pos); pTE->Expand(*pb, ContentEncoding::AllFrameContents); } // HACK p->rtStart -= m_pFile->m_rtOffset; p->rtStop -= m_pFile->m_rtOffset; hr = DeliverPacket(p); } } while (m_pBlock->NextBlock() && SUCCEEDED(hr) && !CheckRequest(nullptr)); m_pBlock.Free(); } while (m_pFile->GetPos() < (__int64)(m_pFile->m_segment.pos + m_pFile->m_segment.len) && m_pCluster->Next(true) && SUCCEEDED(hr) && !CheckRequest(nullptr)); m_pCluster.Free(); return true; } // IKeyFrameInfo STDMETHODIMP CMatroskaSplitterFilter::GetKeyFrameCount(UINT& nKFs) { if (!m_pFile) { return E_UNEXPECTED; } HRESULT hr = S_OK; nKFs = 0; POSITION pos = m_pFile->m_segment.Cues.GetHeadPosition(); while (pos) { nKFs += (UINT)m_pFile->m_segment.Cues.GetNext(pos)->CuePoints.GetCount(); } return hr; } STDMETHODIMP CMatroskaSplitterFilter::GetKeyFrames(const GUID* pFormat, REFERENCE_TIME* pKFs, UINT& nKFs) { CheckPointer(pFormat, E_POINTER); CheckPointer(pKFs, E_POINTER); if (!m_pFile) { return E_UNEXPECTED; } if (*pFormat != TIME_FORMAT_MEDIA_TIME) { return E_INVALIDARG; } UINT nKFsTmp = 0; POSITION pos1 = m_pFile->m_segment.Cues.GetHeadPosition(); while (pos1 && nKFsTmp < nKFs) { Cue* pCue = m_pFile->m_segment.Cues.GetNext(pos1); POSITION pos2 = pCue->CuePoints.GetHeadPosition(); while (pos2 && nKFsTmp < nKFs) { pKFs[nKFsTmp++] = m_pFile->m_segment.GetRefTime(pCue->CuePoints.GetNext(pos2)->CueTime); } } nKFs = nKFsTmp; return S_OK; } // // CMatroskaSourceFilter // CMatroskaSourceFilter::CMatroskaSourceFilter(LPUNKNOWN pUnk, HRESULT* phr) : CMatroskaSplitterFilter(pUnk, phr) { m_clsid = __uuidof(this); m_pInput.Free(); } // // CMatroskaSplitterOutputPin // CMatroskaSplitterOutputPin::CMatroskaSplitterOutputPin( unsigned int nMinCache, REFERENCE_TIME rtDefaultDuration, CAtlArray& mts, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : CBaseSplitterOutputPin(mts, pName, pFilter, pLock, phr) , m_nMinCache(nMinCache), m_rtDefaultDuration(rtDefaultDuration) { m_nMinCache = 1;//max(m_nMinCache, 1); } CMatroskaSplitterOutputPin::~CMatroskaSplitterOutputPin() { } HRESULT CMatroskaSplitterOutputPin::DeliverEndFlush() { { CAutoLock cAutoLock(&m_csQueue); m_packets.RemoveAll(); m_rob.RemoveAll(); m_tos.RemoveAll(); } return __super::DeliverEndFlush(); } HRESULT CMatroskaSplitterOutputPin::DeliverEndOfStream() { CAutoLock cAutoLock(&m_csQueue); // send out the last remaining packets from the queue while (m_rob.GetCount()) { MatroskaPacket* mp = m_rob.RemoveHead(); if (m_rob.GetCount() && !mp->bg->BlockDuration.IsValid()) { mp->rtStop = m_rob.GetHead()->rtStart; } else if (m_rob.IsEmpty() && m_rtDefaultDuration > 0) { mp->rtStop = mp->rtStart + m_rtDefaultDuration; } timeoverride to = {mp->rtStart, mp->rtStop}; m_tos.AddTail(to); } while (m_packets.GetCount()) { HRESULT hr = DeliverBlock(m_packets.RemoveHead()); if (hr != S_OK) { return hr; } } return __super::DeliverEndOfStream(); } HRESULT CMatroskaSplitterOutputPin::DeliverPacket(CAutoPtr p) { MatroskaPacket* mp = dynamic_cast(p.m_p); if (!mp) { return __super::DeliverPacket(p); } // don't try to understand what's happening here, it's magic CAutoLock cAutoLock(&m_csQueue); CAutoPtr p2; p.Detach(); p2.Attach(mp); m_packets.AddTail(p2); POSITION pos = m_rob.GetTailPosition(); ASSERT(m_nMinCache > 0); for (int i = m_nMinCache - 1; i > 0 && pos && mp->bg->ReferencePriority < m_rob.GetAt(pos)->bg->ReferencePriority; --i) { m_rob.GetPrev(pos); } if (!pos) { m_rob.AddHead(mp); } else { m_rob.InsertAfter(pos, mp); } mp = nullptr; if (m_rob.GetCount() == m_nMinCache + 1) { ASSERT(m_nMinCache > 0); pos = m_rob.GetHeadPosition(); MatroskaPacket* mp1 = m_rob.GetNext(pos); MatroskaPacket* mp2 = m_rob.GetNext(pos); if (!mp1->bg->BlockDuration.IsValid()) { mp1->bg->BlockDuration.Set(1); // just to set it valid if (mp1->rtStart >= mp2->rtStart) { /* CString str; str.Format(_T("mp1->rtStart (%I64d) >= mp2->rtStart (%I64d)!!!\n"), mp1->rtStart, mp2->rtStart); AfxMessageBox(str); */ // TRACE(_T("mp1->rtStart (%I64d) >= mp2->rtStart (%I64d)!!!\n"), mp1->rtStart, mp2->rtStart); } else { mp1->rtStop = mp2->rtStart; } } } while (m_packets.GetCount()) { mp = m_packets.GetHead(); if (!mp->bg->BlockDuration.IsValid()) { break; } mp = m_rob.RemoveHead(); timeoverride to = {mp->rtStart, mp->rtStop}; m_tos.AddTail(to); HRESULT hr = DeliverBlock(m_packets.RemoveHead()); if (hr != S_OK) { return hr; } } return S_OK; } HRESULT CMatroskaSplitterOutputPin::DeliverBlock(MatroskaPacket* p) { HRESULT hr = S_FALSE; if (m_tos.GetCount()) { timeoverride to = m_tos.RemoveHead(); #if defined(_DEBUG) && 0 TRACE(_T("(track=%d) %I64d, %I64d -> %I64d, %I64d (buffcnt=%d)\n"), p->TrackNumber, p->rtStart, p->rtStop, to.rtStart, to.rtStop, QueueCount()); #endif p->rtStart = to.rtStart; p->rtStop = to.rtStop; } REFERENCE_TIME rtStart = p->rtStart, rtDelta = (p->rtStop - p->rtStart) / p->bg->Block.BlockData.GetCount(), rtStop = p->rtStart + rtDelta; POSITION pos = p->bg->Block.BlockData.GetHeadPosition(); while (pos) { CAutoPtr tmp(DEBUG_NEW Packet()); tmp->TrackNumber = p->TrackNumber; tmp->bDiscontinuity = p->bDiscontinuity; tmp->bSyncPoint = p->bSyncPoint; tmp->rtStart = rtStart; tmp->rtStop = rtStop; tmp->Copy(*p->bg->Block.BlockData.GetNext(pos)); if (S_OK != (hr = DeliverPacket(tmp))) { break; } rtStart += rtDelta; rtStop += rtDelta; p->bSyncPoint = false; p->bDiscontinuity = false; } if (m_mt.subtype == FOURCCMap(WAVE_FORMAT_WAVPACK4)) { POSITION pos = p->bg->ba.bm.GetHeadPosition(); while (pos) { const BlockMore* bm = p->bg->ba.bm.GetNext(pos); CAutoPtr tmp(DEBUG_NEW Packet()); tmp->TrackNumber = p->TrackNumber; tmp->bDiscontinuity = false; tmp->bSyncPoint = false; tmp->rtStart = p->rtStart; tmp->rtStop = p->rtStop; tmp->Copy(bm->BlockAdditional); if (S_OK != (hr = DeliverPacket(tmp))) { break; } } } return hr; } // ITrackInfo TrackEntry* CMatroskaSplitterFilter::GetTrackEntryAt(UINT aTrackIdx) { if (aTrackIdx >= m_pOrderedTrackArray.GetCount()) { return nullptr; } return m_pOrderedTrackArray[aTrackIdx]; } STDMETHODIMP_(UINT) CMatroskaSplitterFilter::GetTrackCount() { return (UINT)m_pTrackEntryMap.GetCount(); } STDMETHODIMP_(BOOL) CMatroskaSplitterFilter::GetTrackInfo(UINT aTrackIdx, struct TrackElement* pStructureToFill) { TrackEntry* pTE = GetTrackEntryAt(aTrackIdx); if (pTE == nullptr) { return FALSE; } pStructureToFill->FlagDefault = !!pTE->FlagDefault; pStructureToFill->FlagForced = !!pTE->FlagForced; pStructureToFill->FlagLacing = !!pTE->FlagLacing; strncpy_s(pStructureToFill->Language, pTE->Language, 3); if (pStructureToFill->Language[0] == '\0') { strncpy_s(pStructureToFill->Language, "eng", 3); } pStructureToFill->Language[3] = '\0'; pStructureToFill->MaxCache = (UINT)pTE->MaxCache; pStructureToFill->MinCache = (UINT)pTE->MinCache; pStructureToFill->Type = (BYTE)pTE->TrackType; return TRUE; } STDMETHODIMP_(BOOL) CMatroskaSplitterFilter::GetTrackExtendedInfo(UINT aTrackIdx, void* pStructureToFill) { TrackEntry* pTE = GetTrackEntryAt(aTrackIdx); if (pTE == nullptr) { return FALSE; } if (pTE->TrackType == TrackEntry::TypeVideo) { TrackExtendedInfoVideo* pTEIV = (TrackExtendedInfoVideo*)pStructureToFill; pTEIV->AspectRatioType = (BYTE)pTE->v.AspectRatioType; pTEIV->DisplayUnit = (BYTE)pTE->v.DisplayUnit; pTEIV->DisplayWidth = (UINT)pTE->v.DisplayWidth; pTEIV->DisplayHeight = (UINT)pTE->v.DisplayHeight; pTEIV->Interlaced = !!pTE->v.FlagInterlaced; pTEIV->PixelWidth = (UINT)pTE->v.PixelWidth; pTEIV->PixelHeight = (UINT)pTE->v.PixelHeight; } else if (pTE->TrackType == TrackEntry::TypeAudio) { TrackExtendedInfoAudio* pTEIA = (TrackExtendedInfoAudio*)pStructureToFill; pTEIA->BitDepth = (UINT)pTE->a.BitDepth; pTEIA->Channels = (UINT)pTE->a.Channels; pTEIA->OutputSamplingFrequency = (float)pTE->a.OutputSamplingFrequency; pTEIA->SamplingFreq = (float)pTE->a.SamplingFrequency; } else { return FALSE; } return TRUE; } STDMETHODIMP_(BSTR) CMatroskaSplitterFilter::GetTrackName(UINT aTrackIdx) { TrackEntry* pTE = GetTrackEntryAt(aTrackIdx); if (pTE == nullptr) { return nullptr; } return pTE->Name.AllocSysString(); } STDMETHODIMP_(BSTR) CMatroskaSplitterFilter::GetTrackCodecID(UINT aTrackIdx) { TrackEntry* pTE = GetTrackEntryAt(aTrackIdx); if (pTE == nullptr) { return nullptr; } return pTE->CodecID.ToString().AllocSysString(); } STDMETHODIMP_(BSTR) CMatroskaSplitterFilter::GetTrackCodecName(UINT aTrackIdx) { TrackEntry* pTE = GetTrackEntryAt(aTrackIdx); if (pTE == nullptr) { return nullptr; } return pTE->CodecName.AllocSysString(); } STDMETHODIMP_(BSTR) CMatroskaSplitterFilter::GetTrackCodecInfoURL(UINT aTrackIdx) { TrackEntry* pTE = GetTrackEntryAt(aTrackIdx); if (pTE == nullptr) { return nullptr; } return pTE->CodecInfoURL.AllocSysString(); } STDMETHODIMP_(BSTR) CMatroskaSplitterFilter::GetTrackCodecDownloadURL(UINT aTrackIdx) { TrackEntry* pTE = GetTrackEntryAt(aTrackIdx); if (pTE == nullptr) { return nullptr; } return pTE->CodecDownloadURL.AllocSysString(); }