/* * (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 "MP4Splitter.h" #include "../../../DSUtil/GolombBuffer.h" #include "../../../DSUtil/AudioParser.h" #ifdef STANDALONE_FILTER #include #endif #include "moreuuids.h" #include "Ap4.h" #include "Ap4File.h" #include "Ap4StssAtom.h" #include "Ap4StsdAtom.h" #include "Ap4IsmaCryp.h" #include "Ap4AvcCAtom.h" #include "Ap4ChplAtom.h" #include "Ap4FtabAtom.h" #include "Ap4DataAtom.h" #include "Ap4PaspAtom.h" #ifdef STANDALONE_FILTER const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] = { {&MEDIATYPE_Stream, &MEDIASUBTYPE_MP4}, {&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(CMP4SplitterFilter), MP4SplitterName, MERIT_NORMAL, _countof(sudpPins), sudpPins, CLSID_LegacyAmFilterCategory}, {&__uuidof(CMP4SourceFilter), MP4SourceName, MERIT_NORMAL, 0, nullptr, CLSID_LegacyAmFilterCategory}, {&__uuidof(CMPEG4VideoSplitterFilter), L"MPC MPEG4 Video Splitter", MERIT_NORMAL, _countof(sudpPins), sudpPins, CLSID_LegacyAmFilterCategory}, {&__uuidof(CMPEG4VideoSourceFilter), L"MPC MPEG4 Video Source", 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]}, {sudFilter[2].strName, sudFilter[2].clsID, CreateInstance, nullptr, &sudFilter[2]}, {sudFilter[3].strName, sudFilter[3].clsID, CreateInstance, nullptr, &sudFilter[3]}, }; int g_cTemplates = _countof(g_Templates); STDAPI DllRegisterServer() { DeleteRegKey(_T("Media Type\\Extensions\\"), _T(".mp4")); DeleteRegKey(_T("Media Type\\Extensions\\"), _T(".mov")); CAtlList chkbytes; // mp4 chkbytes.AddTail(_T("4,4,,66747970")); // ftyp chkbytes.AddTail(_T("4,4,,6d6f6f76")); // moov chkbytes.AddTail(_T("4,4,,6d646174")); // mdat chkbytes.AddTail(_T("4,4,,736b6970")); // skip chkbytes.AddTail(_T("4,12,ffffffff00000000ffffffff,77696465027fe3706d646174")); // wide ? mdat // mpeg4 video chkbytes.AddTail(_T("3,3,,000001")); RegisterSourceFilter(CLSID_AsyncReader, MEDIASUBTYPE_MP4, chkbytes, nullptr); return AMovieDllRegisterServer2(TRUE); } STDAPI DllUnregisterServer() { UnRegisterSourceFilter(MEDIASUBTYPE_MP4); return AMovieDllRegisterServer2(FALSE); } #include "../../FilterApp.h" CFilterApp theApp; #endif struct SSACharacter { CString pre, post; WCHAR c; }; static CStringW ConvertTX3GToSSA( CStringW str, const AP4_Tx3gSampleEntry::AP4_Tx3gDescription& desc, CStringW font, const AP4_Byte* mods, int size, CSize framesize, CPoint translation, int durationms, CRect& rbox) { int str_len = str.GetLength(); SSACharacter* chars = DEBUG_NEW SSACharacter[str_len]; for (int i = 0; i < str_len; ++i) { chars[i].c = str[i]; } str.Empty(); // rbox.SetRect(desc.TextBox.Left, desc.TextBox.Top, desc.TextBox.Right, desc.TextBox.Bottom); int align = 2; signed char h = (signed char)desc.HorizontalJustification; signed char v = (signed char)desc.VerticalJustification; if (h == 0 && v < 0) { align = 1; } else if (h > 0 && v < 0) { align = 2; } else if (h < 0 && v < 0) { align = 3; } else if (h == 0 && v > 0) { align = 4; } else if (h > 0 && v > 0) { align = 5; } else if (h < 0 && v > 0) { align = 6; } else if (h == 0 && v == 0) { align = 7; } else if (h > 0 && v == 0) { align = 8; } else if (h < 0 && v == 0) { align = 9; } str.Format(L"{\\an%d}%s", align, CStringW(str)); if (!font.CompareNoCase(L"serif")) { font = L"Times New Roman"; } else if (!font.CompareNoCase(L"sans-serif")) { font = L"Arial"; } else if (!font.CompareNoCase(L"monospace")) { font = L"Courier New"; } str.Format(L"{\\fn%s}%s", font, CStringW(str)); const AP4_Byte* fclr = (const AP4_Byte*)&desc.Style.Font.Color; CStringW font_color; font_color.Format(L"{\\1c%02x%02x%02x\\1a%02x}", fclr[2], fclr[1], fclr[0], 255 - fclr[3]); str = font_color + str; str.Format(L"%s{\\2c%02x%02x%02x\\2a%02x}", CString(str), fclr[2], fclr[1], fclr[0], 255 - fclr[3]); CStringW font_size; font_size.Format(L"{\\fs%u}", desc.Style.Font.Size); str = font_size + str; CStringW font_flags; font_flags.Format(L"{\\b%d\\i%d\\u%d}", (desc.Style.Font.Face & 1) ? 1 : 0, (desc.Style.Font.Face & 2) ? 1 : 0, (desc.Style.Font.Face & 4) ? 1 : 0); str = font_flags + str; // const AP4_Byte* hclr = (const AP4_Byte*)&desc.BackgroundColor; while (size > 8) { DWORD tag_size = (mods[0] << 24) | (mods[1] << 16) | (mods[2] << 8) | (mods[3]); mods += 4; DWORD tag = (mods[0] << 24) | (mods[1] << 16) | (mods[2] << 8) | (mods[3]); mods += 4; size -= tag_size; tag_size -= 8; const AP4_Byte* next = mods + tag_size; if (tag == 'styl') { WORD styl_count = (mods[0] << 8) | (mods[1]); mods += 2; while (styl_count-- > 0) { WORD start = (mods[0] << 8) | (mods[1]); mods += 2; WORD end = (mods[0] << 8) | (mods[1]); mods += 2; WORD font_id = (mods[0] << 8) | (mods[1]); mods += 2; WORD flags = mods[0]; mods += 1; WORD size = mods[0]; mods += 1; const AP4_Byte* color = mods; mods += 4; if (end > str_len) { end = str_len; } if (start < end) { CStringW s; s.Format(L"{\\1c%02x%02x%02x\\1a%02x}", color[2], color[1], color[0], 255 - color[3]); chars[start].pre += s; chars[end - 1].post += font_color; s.Format(L"{\\fs%u}", size); chars[start].pre += s; chars[end - 1].post += font_size; s.Format(L"{\\b%d\\i%d\\u%d}", (flags & 1) ? 1 : 0, (flags & 2) ? 1 : 0, (flags & 4) ? 1 : 0); chars[start].pre += s; chars[end - 1].post += font_flags; } } } else if (tag == 'hclr') { hclr = mods; } else if (tag == 'hlit') { WORD start = (mods[0] << 8) | (mods[1]); mods += 2; WORD end = (mods[0] << 8) | (mods[1]); mods += 2; if (end > str_len) { end = str_len; } if (start < end) { CStringW s; s.Format(L"{\\3c%02x%02x%02x\\3a%02x}", hclr[2], hclr[1], hclr[0], 255 - hclr[3]); chars[start].pre += s; chars[end - 1].post += font_color; chars[start].pre += L"{\\bord0.1}"; chars[end - 1].post += L"{\\bord}"; } } else if (tag == 'blnk') { WORD start = (mods[0] << 8) | (mods[1]); mods += 2; WORD end = (mods[0] << 8) | (mods[1]); mods += 2; if (end > str_len) { end = str_len; } if (start < end) { // cheap... for (int i = 0, alpha = 255; i < durationms; i += 750, alpha = 255 - alpha) { CStringW s; s.Format(L"{\\t(%d, %d, \\alpha&H%02x&)}", i, i + 750, alpha); chars[start].pre += s; } chars[end - 1].post += L"{\\alpha}"; } } else if (tag == 'tbox') { rbox.top = (mods[0] << 8) | (mods[1]); mods += 2; rbox.left = (mods[0] << 8) | (mods[1]); mods += 2; rbox.bottom = (mods[0] << 8) | (mods[1]); mods += 2; rbox.right = (mods[0] << 8) | (mods[1]); mods += 2; } else if (tag == 'krok' && !(desc.DisplayFlags & 0x800)) { DWORD start_time = (mods[0] << 24) | (mods[1] << 16) | (mods[2] << 8) | (mods[3]); mods += 4; WORD krok_count = (mods[0] << 8) | (mods[1]); mods += 2; while (krok_count-- > 0) { DWORD end_time = (mods[0] << 24) | (mods[1] << 16) | (mods[2] << 8) | (mods[3]); mods += 4; WORD start = (mods[0] << 8) | (mods[1]); mods += 2; WORD end = (mods[0] << 8) | (mods[1]); mods += 2; if (end > str_len) { end = str_len; } if (start < end) { CStringW s; s.Format(L"{\\kt%u\\kf%u}", start_time / 10, (end_time - start_time) / 10); chars[start].pre += s; s.Format(L"{\\1c%02x%02x%02x\\1a%02x}", hclr[2], hclr[1], hclr[0], 255 - hclr[3]); chars[start].pre += s; chars[end - 1].post += L"{\\kt}" + font_color; } start_time = end_time; } } mods = next; } // continous karaoke if (desc.DisplayFlags & 0x800) { CStringW s; s.Format(L"{\\1c%02x%02x%02x\\1a%02x}", hclr[2], hclr[1], hclr[0], 255 - hclr[3]); str += s; int breaks = 0; for (int i = 0, j = 0; i <= str_len; ++i) { if (chars[i].c == '\n' /*|| chars[i].c == ' '*/) { ++breaks; } } if (str_len > breaks) { float dur = (float)max(durationms - 500, 0) / 10; for (int i = 0, j = 0; i <= str_len; ++i) { if (i == str_len || chars[i].c == '\n' /*|| chars[i].c == ' '*/) { s.Format(L"{\\kf%d}", (int)(dur * (i - j) / (str_len - breaks))); chars[j].pre += s; j = i + 1; } } } } // for (int i = 0; i < str_len; ++i) { str += chars[i].pre; str += chars[i].c; if (desc.DisplayFlags & 0x20000) { str += L"\\N"; } str += chars[i].post; } delete [] chars; // if (rbox.IsRectEmpty()) { rbox.SetRect(0, 0, framesize.cx, framesize.cy); } rbox.OffsetRect(translation); CRect rbox2 = rbox; rbox2.DeflateRect(2, 2); CRect r(0, 0, 0, 0); if (rbox2.Height() > 0) { r.top = rbox2.top; r.bottom = framesize.cy - rbox2.bottom; } if (rbox2.Width() > 0) { r.left = rbox2.left; r.right = framesize.cx - rbox2.right; } CStringW hdr; hdr.Format(L"0,0,Text,,%d,%d,%d,%d,,{\\clip(%d,%d,%d,%d)}", r.left, r.right, r.top, r.bottom, rbox.left, rbox.top, rbox.right, rbox.bottom); // return hdr + str; } // // CMP4SplitterFilter // CMP4SplitterFilter::CMP4SplitterFilter(LPUNKNOWN pUnk, HRESULT* phr) : CBaseSplitterFilter(NAME("CMP4SplitterFilter"), pUnk, phr, __uuidof(this)) { } CMP4SplitterFilter::~CMP4SplitterFilter() { } STDMETHODIMP CMP4SplitterFilter::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, MP4SourceName); } pInfo->pGraph = m_pGraph; if (m_pGraph) { m_pGraph->AddRef(); } return S_OK; } void SetTrackName(CString* TrackName, CString Suffix) { if (TrackName->IsEmpty()) { *TrackName = Suffix; } else { *TrackName += _T(" - "); *TrackName += Suffix; } } HRESULT CMP4SplitterFilter::CreateOutputs(IAsyncReader* pAsyncReader) { CheckPointer(pAsyncReader, E_POINTER); HRESULT hr = E_FAIL; //bool b_HasVideo = false; m_trackpos.RemoveAll(); m_pFile.Free(); m_pFile.Attach(DEBUG_NEW CMP4SplitterFile(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; REFERENCE_TIME rtVideoDuration = 0; m_framesize.SetSize(640, 480); if (AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie()) { // looking for main video track (skip tracks with motionless frames) AP4_UI32 mainvideoID = 0; for (AP4_List::Item* item = movie->GetTracks().FirstItem(); item; item = item->GetNext()) { AP4_Track* track = item->GetData(); if (track->GetType() != AP4_Track::TYPE_VIDEO) { continue; } if (!mainvideoID) { mainvideoID = track->GetId(); } if (AP4_StssAtom* stss = dynamic_cast(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss"))) { if (stss->m_Entries.ItemCount() > 0) { mainvideoID = track->GetId(); break; } } } // process the tracks for (AP4_List::Item* item = movie->GetTracks().FirstItem(); item; item = item->GetNext()) { AP4_Track* track = item->GetData(); if (track->GetType() != AP4_Track::TYPE_VIDEO && track->GetType() != AP4_Track::TYPE_AUDIO && track->GetType() != AP4_Track::TYPE_TEXT && track->GetType() != AP4_Track::TYPE_SUBP) { continue; } //if (b_HasVideo && track->GetType() == AP4_Track::TYPE_VIDEO) { if (track->GetType() == AP4_Track::TYPE_VIDEO && track->GetId() != mainvideoID) { continue; } AP4_Sample sample; if (!AP4_SUCCEEDED(track->GetSample(0, sample)) || sample.GetDescriptionIndex() == 0xFFFFFFFF) { continue; } CStringW TrackName = UTF8ToStringW(track->GetTrackName().c_str()); if (TrackName.IsEmpty()) { TrackName = LocalToStringW(track->GetTrackName().c_str()); //Trying Local... } if (TrackName.GetLength() && TrackName[0] < 0x20) { TrackName.Delete(0); } TrackName.Trim(); CStringA TrackLanguage = track->GetTrackLanguage().c_str(); CAtlArray mts; CMediaType mt; mt.SetSampleSize(1); VIDEOINFOHEADER* vih = nullptr; WAVEFORMATEX* wfe = nullptr; AP4_DataBuffer empty; if (AP4_SampleDescription* desc = track->GetSampleDescription(sample.GetDescriptionIndex())) { AP4_MpegSampleDescription* mpeg_desc = nullptr; if (desc->GetType() == AP4_SampleDescription::TYPE_MPEG) { mpeg_desc = dynamic_cast(desc); } else if (desc->GetType() == AP4_SampleDescription::TYPE_ISMACRYP) { AP4_IsmaCrypSampleDescription* isma_desc = dynamic_cast(desc); mpeg_desc = isma_desc->GetOriginalSampleDescription(); } if (mpeg_desc) { CStringW TypeString = CStringW(mpeg_desc->GetObjectTypeString(mpeg_desc->GetObjectTypeId())); if ((TypeString.Find(_T("UNKNOWN")) == -1) && (TypeString.Find(_T("INVALID")) == -1)) { SetTrackName(&TrackName, TypeString); } } if (AP4_MpegVideoSampleDescription* video_desc = dynamic_cast(mpeg_desc)) { const AP4_DataBuffer* di = video_desc->GetDecoderInfo(); if (!di) { di = ∅ } LONG biWidth = (LONG)video_desc->GetWidth(); LONG biHeight = (LONG)video_desc->GetHeight(); if (!biWidth || !biHeight) { if (AP4_TkhdAtom* tkhd = dynamic_cast(track->GetTrakAtom()->GetChild(AP4_ATOM_TYPE_TKHD))) { biWidth = tkhd->GetWidth() >> 16; biHeight = tkhd->GetHeight() >> 16; } } if (!biWidth || !biHeight) { continue; } mt.majortype = MEDIATYPE_Video; mt.formattype = FORMAT_VideoInfo; vih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + di->GetDataSize()); memset(vih, 0, mt.FormatLength()); vih->dwBitRate = video_desc->GetAvgBitrate() / 8; vih->bmiHeader.biSize = sizeof(vih->bmiHeader); vih->bmiHeader.biWidth = biWidth; vih->bmiHeader.biHeight = biHeight; memcpy(vih + 1, di->GetData(), di->GetDataSize()); switch (video_desc->GetObjectTypeId()) { case AP4_MPEG4_VISUAL_OTI: mt.subtype = FOURCCMap('v4pm'); mt.formattype = FORMAT_MPEG2Video; { MPEG2VIDEOINFO* mvih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + di->GetDataSize()); memset(mvih, 0, mt.FormatLength()); mvih->hdr.bmiHeader.biSize = sizeof(mvih->hdr.bmiHeader); mvih->hdr.bmiHeader.biWidth = biWidth; mvih->hdr.bmiHeader.biHeight = biHeight; mvih->hdr.bmiHeader.biCompression = 'v4pm'; mvih->hdr.bmiHeader.biPlanes = 1; mvih->hdr.bmiHeader.biBitCount = 24; mvih->hdr.dwPictAspectRatioX = mvih->hdr.bmiHeader.biWidth; mvih->hdr.dwPictAspectRatioY = mvih->hdr.bmiHeader.biHeight; mvih->cbSequenceHeader = di->GetDataSize(); memcpy(mvih->dwSequenceHeader, di->GetData(), di->GetDataSize()); mts.Add(mt); mt.subtype = FOURCCMap(mvih->hdr.bmiHeader.biCompression = 'V4PM'); mts.Add(mt); //b_HasVideo = true; } break; case AP4_JPEG_OTI: mt.subtype = FOURCCMap('gepj'); mt.formattype = FORMAT_MPEG2Video; { MPEG2VIDEOINFO* mvih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + di->GetDataSize()); memset(mvih, 0, mt.FormatLength()); mvih->hdr.bmiHeader.biSize = sizeof(mvih->hdr.bmiHeader); mvih->hdr.bmiHeader.biWidth = biWidth; mvih->hdr.bmiHeader.biHeight = biHeight; mvih->hdr.bmiHeader.biCompression = 'gepj'; mvih->hdr.bmiHeader.biPlanes = 1; mvih->hdr.bmiHeader.biBitCount = 24; mvih->hdr.dwPictAspectRatioX = mvih->hdr.bmiHeader.biWidth; mvih->hdr.dwPictAspectRatioY = mvih->hdr.bmiHeader.biHeight; mvih->cbSequenceHeader = di->GetDataSize(); memcpy(mvih->dwSequenceHeader, di->GetData(), di->GetDataSize()); mts.Add(mt); //b_HasVideo = true; } break; case AP4_MPEG2_VISUAL_SIMPLE_OTI: case AP4_MPEG2_VISUAL_MAIN_OTI: case AP4_MPEG2_VISUAL_SNR_OTI: case AP4_MPEG2_VISUAL_SPATIAL_OTI: case AP4_MPEG2_VISUAL_HIGH_OTI: case AP4_MPEG2_VISUAL_422_OTI: mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO; { m_pFile->Seek(sample.GetOffset()); CBaseSplitterFileEx::seqhdr h; CMediaType mt2; if (m_pFile->Read(h, sample.GetSize(), &mt2)) { mt = mt2; } } mts.Add(mt); break; case AP4_MPEG1_VISUAL_OTI: // ??? mt.subtype = MEDIASUBTYPE_MPEG1Payload; { m_pFile->Seek(sample.GetOffset()); CBaseSplitterFileEx::seqhdr h; CMediaType mt2; if (m_pFile->Read(h, sample.GetSize(), &mt2)) { mt = mt2; } } mts.Add(mt); break; } if (mt.subtype == GUID_NULL) { TRACE(_T("Unknown video OBI: %02x\n"), video_desc->GetObjectTypeId()); } } else if (AP4_MpegAudioSampleDescription* audio_desc = dynamic_cast(mpeg_desc)) { const AP4_DataBuffer* di = audio_desc->GetDecoderInfo(); if (!di) { di = ∅ } const BYTE* extdata = di->GetData(); size_t extsize = di->GetDataSize(); ASSERT(extsize <= WORD_MAX); mt.majortype = MEDIATYPE_Audio; mt.formattype = FORMAT_WaveFormatEx; wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX) + di->GetDataSize()); memset(wfe, 0, mt.FormatLength()); wfe->nSamplesPerSec = audio_desc->GetSampleRate(); wfe->nAvgBytesPerSec = audio_desc->GetAvgBitrate() / 8; wfe->nChannels = audio_desc->GetChannelCount(); wfe->wBitsPerSample = audio_desc->GetSampleSize(); wfe->cbSize = (WORD)extsize; wfe->nBlockAlign = wfe->nChannels * wfe->wBitsPerSample / 8; memcpy(wfe + 1, extdata, extsize); switch (audio_desc->GetObjectTypeId()) { case AP4_MPEG4_AUDIO_OTI: case AP4_MPEG2_AAC_AUDIO_MAIN_OTI: // ??? case AP4_MPEG2_AAC_AUDIO_LC_OTI: // ??? case AP4_MPEG2_AAC_AUDIO_SSRP_OTI: // ??? if (extsize > 10) { if (*(DWORD*)(extdata + 6) == 0x00534c41) { // 'ALS\0' sync word wfe->wFormatTag = WAVE_FORMAT_UNKNOWN; mt.subtype = FOURCCMap(MAKEFOURCC('A', 'L', 'S', ' ')); // create our own GUID - {20534C41-0000-0010-8000-00AA00389B71} mts.Add(mt); break; } } mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_AAC); if (extsize >= 2 && wfe->nChannels < 8) { WORD channels = (extdata[1] >> 3) & 0xf; if (channels) { // not the best solution wfe->nChannels = channels; wfe->nBlockAlign = channels * wfe->wBitsPerSample / 8; } } mts.Add(mt); break; case AP4_MPEG2_PART3_AUDIO_OTI: // ??? case AP4_MPEG1_AUDIO_OTI: mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_MPEGLAYER3); { m_pFile->Seek(sample.GetOffset()); CBaseSplitterFileEx::mpahdr h; CMediaType mt2; if (m_pFile->Read(h, sample.GetSize(), false, &mt2)) { mt = mt2; } } mts.Add(mt); break; case AP4_DTSC_AUDIO_OTI: case AP4_DTSH_AUDIO_OTI: case AP4_DTSL_AUDIO_OTI: mt.subtype = FOURCCMap(wfe->wFormatTag = WAVE_FORMAT_DVD_DTS); { m_pFile->Seek(sample.GetOffset()); CBaseSplitterFileEx::dtshdr h; CMediaType mt2; if (m_pFile->Read(h, sample.GetSize(), &mt2)) { mt = mt2; } } mts.Add(mt); break; } if (mt.subtype == GUID_NULL) { TRACE(_T("Unknown audio OBI: %02x\n"), audio_desc->GetObjectTypeId()); } } else if (AP4_MpegSystemSampleDescription* system_desc = dynamic_cast(desc)) { const AP4_DataBuffer* di = system_desc->GetDecoderInfo(); if (!di) { di = ∅ } switch (system_desc->GetObjectTypeId()) { case AP4_NERO_VOBSUB: if (di->GetDataSize() >= 16 * 4) { CSize size(720, 576); if (AP4_TkhdAtom* tkhd = dynamic_cast(track->GetTrakAtom()->GetChild(AP4_ATOM_TYPE_TKHD))) { size.cx = tkhd->GetWidth() >> 16; size.cy = tkhd->GetHeight() >> 16; } const AP4_Byte* pal = di->GetData(); CAtlList sl; for (int i = 0; i < 16 * 4; i += 4) { BYTE y = (pal[i + 1] - 16) * 255 / 219; BYTE u = pal[i + 2]; BYTE v = pal[i + 3]; BYTE r = (BYTE)min(max(1.0 * y + 1.4022 * (v - 128), 0), 255); BYTE g = (BYTE)min(max(1.0 * y - 0.3456 * (u - 128) - 0.7145 * (v - 128), 0), 255); BYTE b = (BYTE)min(max(1.0 * y + 1.7710 * (u - 128), 0) , 255); CStringA str; str.Format("%02x%02x%02x", r, g, b); sl.AddTail(str); } CStringA hdr; hdr.Format( "# VobSub index file, v7 (do not modify this line!)\n" "size: %dx%d\n" "palette: %s\n", size.cx, size.cy, Implode(sl, ',')); mt.majortype = MEDIATYPE_Subtitle; mt.subtype = MEDIASUBTYPE_VOBSUB; mt.formattype = FORMAT_SubtitleInfo; SUBTITLEINFO* si = (SUBTITLEINFO*)mt.AllocFormatBuffer(sizeof(SUBTITLEINFO) + hdr.GetLength()); memset(si, 0, mt.FormatLength()); si->dwOffset = sizeof(SUBTITLEINFO); strcpy_s(si->IsoLang, _countof(si->IsoLang), CStringA(TrackLanguage)); wcscpy_s(si->TrackName, _countof(si->TrackName), TrackName); memcpy(si + 1, (LPCSTR)hdr, hdr.GetLength()); mts.Add(mt); } break; } if (mt.subtype == GUID_NULL) { TRACE(_T("Unknown audio OBI: %02x\n"), system_desc->GetObjectTypeId()); } } else if (AP4_UnknownSampleDescription* unknown_desc = dynamic_cast(desc)) { // TEMP AP4_SampleEntry* sample_entry = unknown_desc->GetSampleEntry(); if (dynamic_cast(sample_entry) || dynamic_cast(sample_entry)) { mt.majortype = MEDIATYPE_Subtitle; mt.subtype = MEDIASUBTYPE_ASS2; mt.formattype = FORMAT_SubtitleInfo; CStringA hdr; hdr.Format( "[Script Info]\n" "ScriptType: v4.00++\n" "ScaledBorderAndShadow: yes\n" "PlayResX: %d\n" "PlayResY: %d\n" "[V4++ Styles]\n" "Style: Text,Arial,12,&H00ffffff,&H0000ffff,&H00000000,&H80000000,0,0,0,0,100,100,0,0.00,3,0,0,2,0,0,0,0,1,1\n", // "Style: Text,Arial,12,&H00ffffff,&H0000ffff,&H00000000,&H80000000,0,0,0,0,100,100,0,0.00,1,0,0,2,0,0,0,0,1,1\n", m_framesize.cx, m_framesize.cy); SUBTITLEINFO* si = (SUBTITLEINFO*)mt.AllocFormatBuffer(sizeof(SUBTITLEINFO) + hdr.GetLength()); memset(si, 0, mt.FormatLength()); si->dwOffset = sizeof(SUBTITLEINFO); strcpy_s(si->IsoLang, _countof(si->IsoLang), CStringA(TrackLanguage)); wcscpy_s(si->TrackName, _countof(si->TrackName), TrackName); memcpy(si + 1, (LPCSTR)hdr, hdr.GetLength()); mts.Add(mt); } } } else if (AP4_Avc1SampleEntry* avc1 = dynamic_cast( track->GetTrakAtom()->FindChild("mdia/minf/stbl/stsd/avc1"))) { if (AP4_AvcCAtom* avcC = dynamic_cast(avc1->GetChild(AP4_ATOM_TYPE_AVCC))) { SetTrackName(&TrackName, _T("MPEG4 Video (H264)")); const AP4_DataBuffer* di = avcC->GetDecoderInfo(); if (!di) { di = ∅ } int num = 1; int den = 1; if (AP4_PaspAtom* pasp = dynamic_cast(avc1->GetChild(AP4_ATOM_TYPE_PASP))) { num = pasp->GetNum(); den = pasp->GetDen(); } if (num <= 0 || den <= 0) { // if bad AR num = den = 1; // then reset AR } const AP4_Byte* data = di->GetData(); AP4_Size size = di->GetDataSize(); mt.majortype = MEDIATYPE_Video; mt.subtype = FOURCCMap('1cva'); mt.formattype = FORMAT_MPEG2Video; MPEG2VIDEOINFO* mvih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + size - 7); memset(mvih, 0, mt.FormatLength()); mvih->hdr.bmiHeader.biSize = sizeof(mvih->hdr.bmiHeader); mvih->hdr.bmiHeader.biWidth = (LONG)avc1->GetWidth(); mvih->hdr.bmiHeader.biHeight = (LONG)avc1->GetHeight(); mvih->hdr.bmiHeader.biCompression = '1cva'; mvih->hdr.bmiHeader.biPlanes = 1; mvih->hdr.bmiHeader.biBitCount = 24; CSize aspect(mvih->hdr.bmiHeader.biWidth * num, mvih->hdr.bmiHeader.biHeight * den); int gcd = GCD(aspect.cx, aspect.cy); if (gcd > 1) { aspect.cx /= gcd; aspect.cy /= gcd; } mvih->hdr.dwPictAspectRatioX = aspect.cx; mvih->hdr.dwPictAspectRatioY = aspect.cy; if (item->GetData()->GetSampleCount()) { mvih->hdr.AvgTimePerFrame = item->GetData()->GetDurationMs() * 10000 / (item->GetData()->GetSampleCount()); } mvih->dwProfile = data[1]; mvih->dwLevel = data[3]; mvih->dwFlags = (data[4] & 3) + 1; mvih->cbSequenceHeader = 0; BYTE* src = (BYTE*)data + 5; BYTE* dst = (BYTE*)mvih->dwSequenceHeader; BYTE* src_end = (BYTE*)data + size; BYTE* dst_end = (BYTE*)mvih->dwSequenceHeader + size; for (int i = 0; i < 2; ++i) { for (int n = *src++ & 0x1f; n > 0; --n) { int len = ((src[0] << 8) | src[1]) + 2; if (src + len > src_end || dst + len > dst_end) { ASSERT(0); break; } memcpy(dst, src, len); src += len; dst += len; mvih->cbSequenceHeader += len; } } mts.Add(mt); mt.subtype = FOURCCMap(mvih->hdr.bmiHeader.biCompression = '1CVA'); mts.Add(mt); //b_HasVideo = true; } } else if (AP4_StsdAtom* stsd = dynamic_cast(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stsd"))) { const AP4_DataBuffer& db = stsd->GetDataBuffer(); int k = 0; for (AP4_List::Item* item = stsd->GetChildren().FirstItem(); item; item = item->GetNext(), ++k) { AP4_Atom* atom = item->GetData(); AP4_Atom::Type type = atom->GetType(); if (k == 0 && stsd->GetChildren().ItemCount() > 1 && type == AP4_ATOM_TYPE_JPEG) { continue; // Multiple fourcc, we skip first JPEG. } DWORD fourcc = ((type >> 24) & 0x000000ff) | ((type >> 8) & 0x0000ff00) | ((type << 8) & 0x00ff0000) | ((type << 24) & 0xff000000); if (AP4_VisualSampleEntry* vse = dynamic_cast(atom)) { if (type == AP4_ATOM_TYPE_MJPA || type == AP4_ATOM_TYPE_MJPB || type == AP4_ATOM_TYPE_MJPG) { SetTrackName(&TrackName, _T("M-Jpeg")); } else if (type == AP4_ATOM_TYPE_RAW) { fourcc = BI_RGB; } mt.majortype = MEDIATYPE_Video; mt.formattype = FORMAT_VideoInfo; vih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + db.GetDataSize()); memset(vih, 0, mt.FormatLength()); vih->bmiHeader.biSize = sizeof(vih->bmiHeader); vih->bmiHeader.biWidth = (LONG)vse->GetWidth(); vih->bmiHeader.biHeight = (LONG)vse->GetHeight(); vih->bmiHeader.biCompression = fourcc; vih->bmiHeader.biBitCount = (LONG)vse->GetDepth(); memcpy(vih + 1, db.GetData(), db.GetDataSize()); if (fourcc == BI_RGB) { WORD& bitcount = vih->bmiHeader.biBitCount; if (bitcount == 16) { mt.subtype = MEDIASUBTYPE_RGB555; } else if (bitcount == 24) { mt.subtype = MEDIASUBTYPE_RGB24; } else if (bitcount == 32) { mt.subtype = MEDIASUBTYPE_ARGB32; } else { break; } mts.Add(mt); break; } mt.subtype = FOURCCMap(fourcc); mts.Add(mt); char buff[5]; memcpy(buff, &fourcc, 4); buff[4] = 0; _strlwr_s(buff); AP4_Atom::Type typelwr = *(AP4_Atom::Type*)buff; if (typelwr != fourcc) { mt.subtype = FOURCCMap(vih->bmiHeader.biCompression = typelwr); mts.Add(mt); //b_HasVideo = true; } _strupr_s(buff); AP4_Atom::Type typeupr = *(AP4_Atom::Type*)buff; if (typeupr != fourcc) { mt.subtype = FOURCCMap(vih->bmiHeader.biCompression = typeupr); mts.Add(mt); //b_HasVideo = true; } break; } else if (AP4_AudioSampleEntry* ase = dynamic_cast(atom)) { DWORD samplerate = ase->GetSampleRate(); WORD channels = ase->GetChannelCount(); WORD bitspersample = ase->GetSampleSize(); // overwrite audio fourc if ((type & 0xffff0000) == AP4_ATOM_TYPE('m', 's', 0, 0)) { fourcc = type & 0xffff; } else if (type == AP4_ATOM_TYPE_ALAW) { fourcc = WAVE_FORMAT_ALAW; SetTrackName(&TrackName, _T("PCM A-law")); } else if (type == AP4_ATOM_TYPE_ULAW) { fourcc = WAVE_FORMAT_MULAW; SetTrackName(&TrackName, _T("PCM mu-law")); } else if (type == AP4_ATOM_TYPE__MP3) { SetTrackName(&TrackName, _T("MPEG Audio (MP3)")); fourcc = WAVE_FORMAT_MPEGLAYER3; } else if ((type == AP4_ATOM_TYPE__AC3) || (type == AP4_ATOM_TYPE_SAC3) || (type == AP4_ATOM_TYPE_EAC3)) { fourcc = 0x2000; if (type != AP4_ATOM_TYPE_EAC3) { SetTrackName(&TrackName, _T("AC-3 Audio")); } else { SetTrackName(&TrackName, _T("Enhanced AC-3 audio")); } } else if (type == AP4_ATOM_TYPE_MP4A) { fourcc = WAVE_FORMAT_AAC; SetTrackName(&TrackName, _T("MPEG-2 Audio AAC")); } else if (type == AP4_ATOM_TYPE_NMOS) { fourcc = MAKEFOURCC('N', 'E', 'L', 'L'); SetTrackName(&TrackName, _T("NellyMoser Audio")); } else if (type == AP4_ATOM_TYPE_ALAC) { fourcc = MAKEFOURCC('a', 'l', 'a', 'c'); SetTrackName(&TrackName, _T("ALAC Audio")); } else if ((type == AP4_ATOM_TYPE_NONE || type == AP4_ATOM_TYPE_RAW) && bitspersample == 8 || type == AP4_ATOM_TYPE_SOWT && bitspersample == 16 || (type == AP4_ATOM_TYPE_IN24 || type == AP4_ATOM_TYPE_IN32) && ase->GetEndian() == ENDIAN_LITTLE) { fourcc = type = WAVE_FORMAT_PCM; } else if ((type == AP4_ATOM_TYPE_FL32 || type == AP4_ATOM_TYPE_FL64) && ase->GetEndian() == ENDIAN_LITTLE) { fourcc = type = WAVE_FORMAT_IEEE_FLOAT; } else if (type == AP4_ATOM_TYPE_LPCM) { DWORD flags = ase->GetFormatSpecificFlags(); if (flags & 2) { // big endian if (flags & 1) { // floating point if (bitspersample == 32) { type = AP4_ATOM_TYPE_FL32; } else if (bitspersample == 64) { type = AP4_ATOM_TYPE_FL64; } } else { if (bitspersample == 16) { type = AP4_ATOM_TYPE_TWOS; } else if (bitspersample == 24) { type = AP4_ATOM_TYPE_IN24; } else if (bitspersample == 32) { type = AP4_ATOM_TYPE_IN32; } } fourcc = ((type >> 24) & 0x000000ff) | ((type >> 8) & 0x0000ff00) | ((type << 8) & 0x00ff0000) | ((type << 24) & 0xff000000); } else { // little endian if (flags & 1) { // floating point fourcc = type = WAVE_FORMAT_IEEE_FLOAT; } else { fourcc = type = WAVE_FORMAT_PCM; } } } DWORD nAvgBytesPerSec = 0; if (type == AP4_ATOM_TYPE_EAC3) { AP4_Sample sample; AP4_DataBuffer sample_data; AP4_Cardinal SampleCount = track->GetSampleCount(); if (SampleCount) { track->ReadSample(1, sample, sample_data); const AP4_Byte* data = sample_data.GetData(); AP4_Size size = sample_data.GetDataSize(); CGolombBuffer gb((BYTE*)data, size); for (; size >= 7 && gb.BitRead(16, true) != 0x0b77; --size) { gb.BitRead(8); } WORD sync = (WORD)gb.BitRead(16); if ((size >= 7) && (sync == 0x0b77)) { static DWORD freq[] = {48000, 44100, 32000, 0}; gb.BitRead(2); gb.BitRead(3); WORD frame_size = ((WORD)gb.BitRead(11) + 1) << 1; BYTE sr_code = (BYTE)gb.BitRead(2); if (sr_code == 3) { BYTE sr_code2 = (BYTE)gb.BitRead(2); samplerate = freq[sr_code2] / 2; } else { static BYTE eac3_blocks[4] = {1, 2, 3, 6}; BYTE num_blocks = eac3_blocks[gb.BitRead(2)]; samplerate = freq[sr_code]; nAvgBytesPerSec = frame_size * samplerate / (num_blocks * 256); } BYTE acmod = (BYTE)gb.BitRead(3); BYTE lfeon = (BYTE)gb.BitRead(1); static WORD channels_tbl[] = {2, 1, 2, 3, 3, 4, 4, 5}; channels = channels_tbl[acmod] + lfeon; } } } mt.majortype = MEDIATYPE_Audio; mt.formattype = FORMAT_WaveFormatEx; wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX)); memset(wfe, 0, mt.FormatLength()); if (!(fourcc & 0xffff0000)) { wfe->wFormatTag = (WORD)fourcc; } wfe->nSamplesPerSec = samplerate; wfe->nChannels = channels; wfe->wBitsPerSample = bitspersample; wfe->nBlockAlign = ase->GetBytesPerFrame(); if (nAvgBytesPerSec == 0 && ase->GetSamplesPerPacket() > 0) { wfe->nAvgBytesPerSec = wfe->nSamplesPerSec * wfe->nBlockAlign / ase->GetSamplesPerPacket(); } mt.subtype = FOURCCMap(fourcc); if (type == AP4_ATOM_TYPE('m', 's', 0x00, 0x02)) { const WORD numcoef = 7; static ADPCMCOEFSET coef[] = { {256, 0}, {512, -256}, {0, 0}, {192, 64}, {240, 0}, {460, -208}, {392, -232} }; const ULONG size = sizeof(ADPCMWAVEFORMAT) + (numcoef * sizeof(ADPCMCOEFSET)); ADPCMWAVEFORMAT* format = (ADPCMWAVEFORMAT*)mt.ReallocFormatBuffer(size); if (format != nullptr) { format->wfx.wFormatTag = WAVE_FORMAT_ADPCM; format->wfx.wBitsPerSample = 4; format->wfx.cbSize = (WORD)(size - sizeof(WAVEFORMATEX)); format->wSamplesPerBlock = format->wfx.nBlockAlign * 2 / format->wfx.nChannels - 12; format->wNumCoef = numcoef; memcpy(format->aCoef, coef, sizeof(coef)); } } else if (type == AP4_ATOM_TYPE('m', 's', 0x00, 0x11)) { IMAADPCMWAVEFORMAT* format = (IMAADPCMWAVEFORMAT*)mt.ReallocFormatBuffer(sizeof(IMAADPCMWAVEFORMAT)); if (format != nullptr) { format->wfx.wFormatTag = WAVE_FORMAT_IMA_ADPCM; format->wfx.wBitsPerSample = 4; format->wfx.cbSize = (WORD)(sizeof(IMAADPCMWAVEFORMAT) - sizeof(WAVEFORMATEX)); int X = (format->wfx.nBlockAlign - (4 * format->wfx.nChannels)) * 8; int Y = format->wfx.wBitsPerSample * format->wfx.nChannels; format->wSamplesPerBlock = (X / Y) + 1; } } else if (type == AP4_ATOM_TYPE_ALAC) { const AP4_Byte* data = db.GetData(); AP4_Size size = db.GetDataSize(); while (size >= 36) { if ((*(DWORD*)(data) == 0x24000000) && (*(DWORD*)(data + 4) == 0x63616c61)) { break; } size--; data++; } if (size >= 36) { wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + 36); wfe->cbSize = 36; memcpy(wfe + 1, data, 36); } } else if (type == WAVE_FORMAT_PCM) { mt.SetSampleSize(wfe->nBlockAlign); if (channels > 2 || bitspersample > 16) { WAVEFORMATEXTENSIBLE* wfex = (WAVEFORMATEXTENSIBLE*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEXTENSIBLE)); if (wfex != nullptr) { wfex->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfex->Format.cbSize = 22; wfex->Samples.wValidBitsPerSample = bitspersample; wfex->dwChannelMask = GetDefChannelMask(channels); // TODO: get correct channel mask from mov file wfex->SubFormat = MEDIASUBTYPE_PCM; } } } else if (type == WAVE_FORMAT_IEEE_FLOAT) { mt.SetSampleSize(wfe->nBlockAlign); if (channels > 2) { WAVEFORMATEXTENSIBLE* wfex = (WAVEFORMATEXTENSIBLE*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEXTENSIBLE)); if (wfex != nullptr) { wfex->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfex->Format.cbSize = 22; wfex->Samples.wValidBitsPerSample = bitspersample; wfex->dwChannelMask = GetDefChannelMask(channels); // TODO: get correct channel mask from mov file wfex->SubFormat = MEDIASUBTYPE_IEEE_FLOAT; } } } else if (type == AP4_ATOM_TYPE_MP4A || type == AP4_ATOM_TYPE_ALAW || type == AP4_ATOM_TYPE_ULAW) { // not need any extra data for ALAW, ULAW // also extra data is not required for IMA4, MAC3, MAC6 } else if (db.GetDataSize() > 0) { //always needed extra data for QDM2 wfe = (WAVEFORMATEX*)mt.ReallocFormatBuffer(sizeof(WAVEFORMATEX) + db.GetDataSize()); wfe->cbSize = db.GetDataSize(); memcpy(wfe + 1, db.GetData(), db.GetDataSize()); } mts.Add(mt); break; } else { TRACE(_T("Unknow MP4 Stream %x\n") , fourcc); } } } if (mts.IsEmpty()) { continue; } REFERENCE_TIME rtDuration = 10000i64 * track->GetDurationMs(); if (m_rtDuration < rtDuration) { m_rtDuration = rtDuration; } if (rtVideoDuration < rtDuration && AP4_Track::TYPE_VIDEO == track->GetType()) { rtVideoDuration = rtDuration; // get the max video duration } DWORD id = track->GetId(); CStringW name, lang; name.Format(L"Output %u", id); if (!TrackName.IsEmpty()) { name = TrackName; } if (!TrackLanguage.IsEmpty()) { if (TrackLanguage != L"und") { name += " (" + TrackLanguage + ")"; } } for (size_t i = 0, j = mts.GetCount(); i < j; ++i) { BITMAPINFOHEADER bih; if (ExtractBIH(&mts[i], &bih)) { m_framesize.cx = bih.biWidth; m_framesize.cy = abs(bih.biHeight); } } CAutoPtr pPinOut(DEBUG_NEW CMP4SplitterOutputPin(mts, name, this, this, &hr)); if (!TrackName.IsEmpty()) { pPinOut->SetProperty(L"NAME", TrackName); } if (!TrackLanguage.IsEmpty()) { pPinOut->SetProperty(L"LANG", CStringW(TrackLanguage)); } EXECUTE_ASSERT(SUCCEEDED(AddOutputPin(id, pPinOut))); m_trackpos[id] = trackpos(); if (mts.GetCount() == 1 && mts[0].subtype == MEDIASUBTYPE_ASS2) { LPCWSTR postfix = L" (plain text)"; mts[0].subtype = MEDIASUBTYPE_UTF8; SUBTITLEINFO* si = (SUBTITLEINFO*)mts[0].ReallocFormatBuffer(sizeof(SUBTITLEINFO)); wcscat_s(si->TrackName, postfix); id ^= 0x80402010; // FIXME: until fixing, let's hope there won't be another track like this... CAutoPtr pPinOut(DEBUG_NEW CMP4SplitterOutputPin(mts, name + postfix, this, this, &hr)); if (!TrackName.IsEmpty()) { pPinOut->SetProperty(L"NAME", TrackName + postfix); } if (!TrackLanguage.IsEmpty()) { pPinOut->SetProperty(L"LANG", CStringW(TrackLanguage)); } EXECUTE_ASSERT(SUCCEEDED(AddOutputPin(id, pPinOut))); } } if (AP4_ChplAtom* chpl = dynamic_cast(movie->GetMoovAtom()->FindChild("udta/chpl"))) { AP4_Array& chapters = chpl->GetChapters(); for (AP4_Cardinal i = 0; i < chapters.ItemCount(); ++i) { AP4_ChplAtom::AP4_Chapter& chapter = chapters[i]; ChapAppend(chapter.Time, UTF8To16(ConvertMBCS(chapter.Name.c_str(), ANSI_CHARSET, CP_UTF8))); // this is b0rked, thx to nero :P } ChapSort(); } if (AP4_ContainerAtom* ilst = dynamic_cast(movie->GetMoovAtom()->FindChild("udta/meta/ilst"))) { CStringW title, artist, writer, album, year, appl, desc, gen, track; for (AP4_List::Item* item = ilst->GetChildren().FirstItem(); item; item = item->GetNext()) { if (AP4_ContainerAtom* atom = dynamic_cast(item->GetData())) { if (AP4_DataAtom* data = dynamic_cast(atom->GetChild(AP4_ATOM_TYPE_DATA))) { const AP4_DataBuffer* db = data->GetData(); if (atom->GetType() == AP4_ATOM_TYPE_TRKN) { if (db->GetDataSize() >= 4) { unsigned short n = (db->GetData()[2] << 8) | db->GetData()[3]; if (n > 0 && n < 100) { track.Format(L"%02u", n); } else if (n >= 100) { track.Format(L"%u", n); } } } else { CStringW str = UTF8To16(CStringA((LPCSTR)db->GetData(), db->GetDataSize())); switch (atom->GetType()) { case AP4_ATOM_TYPE_NAM: title = str; break; case AP4_ATOM_TYPE_ART: artist = str; break; case AP4_ATOM_TYPE_WRT: writer = str; break; case AP4_ATOM_TYPE_ALB: album = str; break; case AP4_ATOM_TYPE_DAY: year = str; break; case AP4_ATOM_TYPE_TOO: appl = str; break; case AP4_ATOM_TYPE_CMT: desc = str; break; case AP4_ATOM_TYPE_GEN: gen = str; break; } } } } } if (!title.IsEmpty()) { if (!track.IsEmpty()) { title = track + L" - " + title; } if (!album.IsEmpty()) { title = album + L" - " + title; } if (!year.IsEmpty()) { title += L" - " + year; } if (!gen.IsEmpty()) { title += L" - " + gen; } SetProperty(L"TITL", title); } if (!artist.IsEmpty()) { SetProperty(L"AUTH", artist); } else if (!writer.IsEmpty()) { SetProperty(L"AUTH", writer); } if (!appl.IsEmpty()) { SetProperty(L"APPL", appl); } if (!desc.IsEmpty()) { SetProperty(L"DESC", desc); } } } if (rtVideoDuration > 0 && rtVideoDuration < m_rtDuration / 2) { m_rtDuration = rtVideoDuration; // fix incorrect duration } m_rtNewStop = m_rtStop = m_rtDuration; TRACE(_T("CMP4SplitterFilter m_pOutputs.GetCount() = %d\n") , m_pOutputs.GetCount()); return !m_pOutputs.IsEmpty() ? S_OK : E_FAIL; } bool CMP4SplitterFilter::DemuxInit() { AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie(); POSITION pos = m_trackpos.GetStartPosition(); while (pos) { CAtlMap::CPair* pPair = m_trackpos.GetNext(pos); pPair->m_value.index = 0; pPair->m_value.ts = 0; AP4_Track* track = movie->GetTrack(pPair->m_key); AP4_Sample sample; if (AP4_SUCCEEDED(track->GetSample(0, sample))) { pPair->m_value.ts = sample.GetCts(); } } return true; } void CMP4SplitterFilter::DemuxSeek(REFERENCE_TIME rt) { AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie(); POSITION pos = m_trackpos.GetStartPosition(); while (pos) { CAtlMap::CPair* pPair = m_trackpos.GetNext(pos); AP4_Track* track = movie->GetTrack(pPair->m_key); if (AP4_FAILED(track->GetSampleIndexForRefTime(rt, pPair->m_value.index))) { continue; } AP4_Sample sample; if (AP4_SUCCEEDED(track->GetSample(pPair->m_value.index, sample))) { pPair->m_value.ts = sample.GetCts(); } // FIXME: slow search & stss->m_Entries is private if (AP4_StssAtom* stss = dynamic_cast(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss"))) { if (stss->m_Entries.ItemCount() > 0) { AP4_Cardinal i = 1; while (i < stss->m_Entries.ItemCount()) { if (stss->m_Entries[i] - 1 > pPair->m_value.index) { break; } ++i; } //ASSERT(pPair->m_value.index == stss->m_Entries[i-1] - 1); // fast seek test pPair->m_value.index = stss->m_Entries[i - 1] - 1; } } } } bool CMP4SplitterFilter::DemuxLoop() { HRESULT hr = S_OK; AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie(); while (SUCCEEDED(hr) && !CheckRequest(nullptr)) { CAtlMap::CPair* pPairNext = nullptr; REFERENCE_TIME rtNext = 0; POSITION pos = m_trackpos.GetStartPosition(); while (pos) { CAtlMap::CPair* pPair = m_trackpos.GetNext(pos); AP4_Track* track = movie->GetTrack(pPair->m_key); CBaseSplitterOutputPin* pPin = GetOutputPin((DWORD)track->GetId()); if (!pPin->IsConnected()) { continue; } REFERENCE_TIME rt = (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * pPair->m_value.ts); if (pPair->m_value.index < track->GetSampleCount() && (!pPairNext || rt < rtNext)) { pPairNext = pPair; rtNext = rt; } } if (!pPairNext) { break; } AP4_Track* track = movie->GetTrack(pPairNext->m_key); CBaseSplitterOutputPin* pPin = GetOutputPin((DWORD)track->GetId()); AP4_Sample sample; AP4_DataBuffer data; if (pPin && pPin->IsConnected() && AP4_SUCCEEDED(track->ReadSample(pPairNext->m_value.index, sample, data))) { const CMediaType& mt = pPin->CurrentMediaType(); CAutoPtr p(DEBUG_NEW Packet()); p->TrackNumber = (DWORD)track->GetId(); p->rtStart = (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * sample.GetCts()); p->rtStop = p->rtStart + (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * sample.GetDuration()); p->bSyncPoint = TRUE; // FIXME: slow search & stss->m_Entries is private if (AP4_StssAtom* stss = dynamic_cast(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss"))) { if (stss->m_Entries.ItemCount() > 0) { p->bSyncPoint = FALSE; for (AP4_Cardinal i = 0; i < stss->m_Entries.ItemCount(); ++i) { if (stss->m_Entries[i] - 1 < pPairNext->m_value.index) { continue; } if (stss->m_Entries[i] - 1 == pPairNext->m_value.index) { p->bSyncPoint = TRUE; } break; } } } // if (track->GetType() == AP4_Track::TYPE_AUDIO && data.GetDataSize() >= 1 && data.GetDataSize() <= 16) { WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.Format(); int nBlockAlign; if (wfe->nBlockAlign == 0) { nBlockAlign = 1200; } else if (wfe->nBlockAlign <= 16) { // for PCM (from 8bit mono to 64bit stereo), A-Law, u-Law nBlockAlign = wfe->nBlockAlign * (wfe->nSamplesPerSec >> 4); // 1/16s=62.5ms } else { nBlockAlign = wfe->nBlockAlign; pPairNext->m_value.index -= pPairNext->m_value.index % wfe->nBlockAlign; } p->rtStop = p->rtStart; TRACE(_T("track->GetSampleCount() %d %d\n"), track->GetSampleCount(), pPairNext->m_value.index); int fFirst = true; while (AP4_SUCCEEDED(track->ReadSample(pPairNext->m_value.index, sample, data))) { AP4_Size size = data.GetDataSize(); const AP4_Byte* ptr = data.GetData(); if (fFirst) { p->SetData(ptr, size); p->rtStart = p->rtStop = (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * sample.GetCts()); fFirst = false; } else { for (int i = 0; i < size; ++i) { p->Add(ptr[i]); } } p->rtStop += (REFERENCE_TIME)(10000000.0 / track->GetMediaTimeScale() * sample.GetDuration()); if (pPairNext->m_value.index + 1 >= track->GetSampleCount() || (int)p->GetCount() >= nBlockAlign) { break; } pPairNext->m_value.index++; } } else if (track->GetType() == AP4_Track::TYPE_TEXT) { CStringA dlgln_bkg, dlgln_plaintext; const AP4_Byte* ptr = data.GetData(); AP4_Size avail = data.GetDataSize(); if (avail > 2) { AP4_UI16 size = (ptr[0] << 8) | ptr[1]; if (size <= avail - 2) { CStringA str; if (size >= 2 && ptr[2] == 0xfe && ptr[3] == 0xff) { CStringW wstr = CStringW((LPCWSTR)&ptr[2], size / 2); for (int i = 0; i < wstr.GetLength(); ++i) { wstr.SetAt(i, ((WORD)wstr[i] >> 8) | ((WORD)wstr[i] << 8)); } str = UTF16To8(wstr); } else { str = CStringA((LPCSTR)&ptr[2], size); } CStringA dlgln = str; if (mt.subtype == MEDIASUBTYPE_ASS2) { AP4_SampleDescription* desc = track->GetSampleDescription(sample.GetDescriptionIndex()); dlgln = "0,0,Text,,0000,0000,0000,0000,," + str; dlgln_plaintext = str; CPoint translation(0, 0); if (AP4_TkhdAtom* tkhd = dynamic_cast(track->GetTrakAtom()->GetChild(AP4_ATOM_TYPE_TKHD))) { AP4_Float x, y; tkhd->GetTranslation(x, y); translation.SetPoint((int)x, (int)y); } if (AP4_UnknownSampleDescription* unknown_desc = dynamic_cast(desc)) { // TEMP AP4_SampleEntry* sample_entry = unknown_desc->GetSampleEntry(); if (AP4_TextSampleEntry* text = dynamic_cast(sample_entry)) { const AP4_TextSampleEntry::AP4_TextDescription& d = text->GetDescription(); // TODO } else if (AP4_Tx3gSampleEntry* tx3g = dynamic_cast(sample_entry)) { const AP4_Tx3gSampleEntry::AP4_Tx3gDescription& desc = tx3g->GetDescription(); CStringW font = L"Arial"; if (AP4_FtabAtom* ftab = dynamic_cast(tx3g->GetChild(AP4_ATOM_TYPE_FTAB))) { AP4_String Name; if (AP4_SUCCEEDED(ftab->LookupFont(desc.Style.Font.Id, Name))) { font = Name.c_str(); } } CRect rbox; CStringW ssa = ConvertTX3GToSSA( UTF8To16(str), desc, font, ptr + (2 + size), avail - (2 + size), m_framesize, translation, (p->rtStop - p->rtStart) / 10000, rbox); dlgln = UTF16To8(ssa); const AP4_Byte* bclr = (const AP4_Byte*)&desc.BackgroundColor; if (bclr[3]) { CPoint tl = rbox.TopLeft(); rbox.OffsetRect(-tl.x, -tl.y); dlgln_bkg.Format( "0,-1,Text,,0,0,0,0,,{\\an7\\pos(%d,%d)\\1c%02x%02x%02x\\1a%02x\\bord0\\shad0}{\\p1}m %d %d l %d %d l %d %d l %d %d {\\p0}", tl.x, tl.y, bclr[2], bclr[1], bclr[0], 255 - bclr[3], rbox.left, rbox.top, rbox.right, rbox.top, rbox.right, rbox.bottom, rbox.left, rbox.bottom); } } } } dlgln.Replace("\r", ""); dlgln.Replace("\n", "\\N"); p->SetData((LPCSTR)dlgln, dlgln.GetLength()); } } if (!dlgln_bkg.IsEmpty()) { CAutoPtr p2(DEBUG_NEW Packet()); p2->TrackNumber = p->TrackNumber; p2->rtStart = p->rtStart; p2->rtStop = p->rtStop; p2->bSyncPoint = p->bSyncPoint; p2->SetData((LPCSTR)dlgln_bkg, dlgln_bkg.GetLength()); hr = DeliverPacket(p2); } if (!dlgln_plaintext.IsEmpty()) { CAutoPtr p2(DEBUG_NEW Packet()); p2->TrackNumber = p->TrackNumber ^ 0x80402010; p2->rtStart = p->rtStart; p2->rtStop = p->rtStop; p2->bSyncPoint = p->bSyncPoint; p2->SetData((LPCSTR)dlgln_plaintext, dlgln_plaintext.GetLength()); hr = DeliverPacket(p2); } } else { p->SetData(data.GetData(), data.GetDataSize()); } hr = DeliverPacket(p); } { AP4_Sample sample; if (AP4_SUCCEEDED(track->GetSample(++pPairNext->m_value.index, sample))) { pPairNext->m_value.ts = sample.GetCts(); } } } return true; } // IKeyFrameInfo STDMETHODIMP CMP4SplitterFilter::GetKeyFrameCount(UINT& nKFs) { CheckPointer(m_pFile, E_UNEXPECTED); if (!m_pFile) { return E_UNEXPECTED; } AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie(); POSITION pos = m_trackpos.GetStartPosition(); while (pos) { CAtlMap::CPair* pPair = m_trackpos.GetNext(pos); AP4_Track* track = movie->GetTrack(pPair->m_key); if (track->GetType() != AP4_Track::TYPE_VIDEO) { continue; } if (AP4_StssAtom* stss = dynamic_cast(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss"))) { nKFs = stss->m_Entries.ItemCount(); return S_OK; } } return E_FAIL; } STDMETHODIMP CMP4SplitterFilter::GetKeyFrames(const GUID* pFormat, REFERENCE_TIME* pKFs, UINT& nKFs) { CheckPointer(pFormat, E_POINTER); CheckPointer(pKFs, E_POINTER); CheckPointer(m_pFile, E_UNEXPECTED); if (*pFormat != TIME_FORMAT_MEDIA_TIME) { return E_INVALIDARG; } if (!m_pFile) { return E_UNEXPECTED; } AP4_Movie* movie = (AP4_Movie*)m_pFile->GetMovie(); POSITION pos = m_trackpos.GetStartPosition(); while (pos) { CAtlMap::CPair* pPair = m_trackpos.GetNext(pos); AP4_Track* track = movie->GetTrack(pPair->m_key); if (track->GetType() != AP4_Track::TYPE_VIDEO) { continue; } if (AP4_StssAtom* stss = dynamic_cast(track->GetTrakAtom()->FindChild("mdia/minf/stbl/stss"))) { UINT nKFsTmp = 0; AP4_UI32 mts = track->GetMediaTimeScale(); for (AP4_Cardinal i = 0; i < stss->m_Entries.ItemCount() && nKFsTmp < nKFs; ++i) { AP4_Sample sample; if (AP4_SUCCEEDED(track->GetSample(stss->m_Entries[i] - 1, sample))) { pKFs[nKFsTmp++] = REFERENCE_TIME((10000000ui64 * sample.GetCts() + mts / 2) / mts); //pKFs[nKFsTmp++] = REFERENCE_TIME(10000000.0 * sample.GetCts() / track->GetMediaTimeScale() + 0.5); } } nKFs = nKFsTmp; return S_OK; } } return E_FAIL; } // // CMP4SourceFilter // CMP4SourceFilter::CMP4SourceFilter(LPUNKNOWN pUnk, HRESULT* phr) : CMP4SplitterFilter(pUnk, phr) { m_clsid = __uuidof(this); m_pInput.Free(); } // // CMPEG4VideoSplitterFilter // CMPEG4VideoSplitterFilter::CMPEG4VideoSplitterFilter(LPUNKNOWN pUnk, HRESULT* phr) : CBaseSplitterFilter(NAME("CMPEG4VideoSplitterFilter"), pUnk, phr, __uuidof(this)) { } void CMPEG4VideoSplitterFilter::SkipUserData() { m_pFile->BitByteAlign(); while (m_pFile->BitRead(32, true) == 0x000001b2) while (m_pFile->BitRead(24, true) != 0x000001) { m_pFile->BitRead(8); } } HRESULT CMPEG4VideoSplitterFilter::CreateOutputs(IAsyncReader* pAsyncReader) { CheckPointer(pAsyncReader, E_POINTER); HRESULT hr = E_FAIL; m_pFile.Free(); m_pFile.Attach(DEBUG_NEW CBaseSplitterFileEx(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; // TODO DWORD width = 0; DWORD height = 0; BYTE parx = 1; BYTE pary = 1; REFERENCE_TIME atpf = 400000; if (m_pFile->BitRead(24, true) != 0x000001) { return E_FAIL; } BYTE id; while (m_pFile->NextMpegStartCode(id, 1024 - m_pFile->GetPos())) { if (id == 0xb5) { BYTE is_visual_object_identifier = (BYTE)m_pFile->BitRead(1); if (is_visual_object_identifier) { BYTE visual_object_verid = (BYTE)m_pFile->BitRead(4); BYTE visual_object_priority = (BYTE)m_pFile->BitRead(3); } BYTE visual_object_type = (BYTE)m_pFile->BitRead(4); if (visual_object_type == 1 || visual_object_type == 2) { BYTE video_signal_type = (BYTE)m_pFile->BitRead(1); if (video_signal_type) { BYTE video_format = (BYTE)m_pFile->BitRead(3); BYTE video_range = (BYTE)m_pFile->BitRead(1); BYTE colour_description = (BYTE)m_pFile->BitRead(1); if (colour_description) { BYTE colour_primaries = (BYTE)m_pFile->BitRead(8); BYTE transfer_characteristics = (BYTE)m_pFile->BitRead(8); BYTE matrix_coefficients = (BYTE)m_pFile->BitRead(8); } } } SkipUserData(); if (visual_object_type == 1) { if (m_pFile->BitRead(24) != 0x000001) { break; } BYTE video_object_start_code = (BYTE)m_pFile->BitRead(8); if (video_object_start_code < 0x00 || video_object_start_code > 0x1f) { break; } if (m_pFile->BitRead(24) != 0x000001) { break; } BYTE video_object_layer_start_code = (DWORD)m_pFile->BitRead(8); if (video_object_layer_start_code < 0x20 || video_object_layer_start_code > 0x2f) { break; } BYTE random_accessible_vol = (BYTE)m_pFile->BitRead(1); BYTE video_object_type_indication = (BYTE)m_pFile->BitRead(8); if (video_object_type_indication == 0x12) { // Fine Granularity Scalable break; // huh } BYTE is_object_layer_identifier = (BYTE)m_pFile->BitRead(1); BYTE video_object_layer_verid = 0; if (is_object_layer_identifier) { video_object_layer_verid = (BYTE)m_pFile->BitRead(4); BYTE video_object_layer_priority = (BYTE)m_pFile->BitRead(3); } BYTE aspect_ratio_info = (BYTE)m_pFile->BitRead(4); switch (aspect_ratio_info) { default: ASSERT(0); break; case 1: parx = 1; pary = 1; break; case 2: parx = 12; pary = 11; break; case 3: parx = 10; pary = 11; break; case 4: parx = 16; pary = 11; break; case 5: parx = 40; pary = 33; break; case 15: parx = (BYTE)m_pFile->BitRead(8); pary = (BYTE)m_pFile->BitRead(8); break; } BYTE vol_control_parameters = (BYTE)m_pFile->BitRead(1); if (vol_control_parameters) { BYTE chroma_format = (BYTE)m_pFile->BitRead(2); BYTE low_delay = (BYTE)m_pFile->BitRead(1); BYTE vbv_parameters = (BYTE)m_pFile->BitRead(1); if (vbv_parameters) { WORD first_half_bit_rate = (WORD)m_pFile->BitRead(15); if (!m_pFile->BitRead(1)) { break; } WORD latter_half_bit_rate = (WORD)m_pFile->BitRead(15); if (!m_pFile->BitRead(1)) { break; } WORD first_half_vbv_buffer_size = (WORD)m_pFile->BitRead(15); if (!m_pFile->BitRead(1)) { break; } BYTE latter_half_vbv_buffer_size = (BYTE)m_pFile->BitRead(3); WORD first_half_vbv_occupancy = (WORD)m_pFile->BitRead(11); if (!m_pFile->BitRead(1)) { break; } WORD latter_half_vbv_occupancy = (WORD)m_pFile->BitRead(15); if (!m_pFile->BitRead(1)) { break; } } } BYTE video_object_layer_shape = (BYTE)m_pFile->BitRead(2); if (video_object_layer_shape == 3 && video_object_layer_verid != 1) { BYTE video_object_layer_shape_extension = (BYTE)m_pFile->BitRead(4); } if (!m_pFile->BitRead(1)) { break; } WORD vop_time_increment_resolution = (WORD)m_pFile->BitRead(16); if (!m_pFile->BitRead(1)) { break; } BYTE fixed_vop_rate = (BYTE)m_pFile->BitRead(1); if (fixed_vop_rate) { int bits = 0; for (WORD i = vop_time_increment_resolution; i; i /= 2) { ++bits; } WORD fixed_vop_time_increment = m_pFile->BitRead(bits); if (fixed_vop_time_increment) { atpf = 10000000i64 * fixed_vop_time_increment / vop_time_increment_resolution; } } if (video_object_layer_shape != 2) { if (video_object_layer_shape == 0) { if (!m_pFile->BitRead(1)) { break; } width = (WORD)m_pFile->BitRead(13); if (!m_pFile->BitRead(1)) { break; } height = (WORD)m_pFile->BitRead(13); if (!m_pFile->BitRead(1)) { break; } } BYTE interlaced = (BYTE)m_pFile->BitRead(1); BYTE obmc_disable = (BYTE)m_pFile->BitRead(1); // ... } } } else if (id == 0xb6) { m_seqhdrsize = m_pFile->GetPos() - 4; } } if (!width || !height) { return E_FAIL; } CAtlArray mts; CMediaType mt; mt.SetSampleSize(1); mt.majortype = MEDIATYPE_Video; mt.subtype = FOURCCMap('v4pm'); mt.formattype = FORMAT_MPEG2Video; MPEG2VIDEOINFO* mvih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + m_seqhdrsize); memset(mvih, 0, mt.FormatLength()); mvih->hdr.bmiHeader.biSize = sizeof(mvih->hdr.bmiHeader); mvih->hdr.bmiHeader.biWidth = width; mvih->hdr.bmiHeader.biHeight = height; mvih->hdr.bmiHeader.biCompression = 'v4pm'; mvih->hdr.bmiHeader.biPlanes = 1; mvih->hdr.bmiHeader.biBitCount = 24; mvih->hdr.AvgTimePerFrame = atpf; mvih->hdr.dwPictAspectRatioX = width * parx; mvih->hdr.dwPictAspectRatioY = height * pary; mvih->cbSequenceHeader = m_seqhdrsize; m_pFile->Seek(0); m_pFile->ByteRead((BYTE*)mvih->dwSequenceHeader, m_seqhdrsize); mts.Add(mt); mt.subtype = FOURCCMap(mvih->hdr.bmiHeader.biCompression = 'V4PM'); mts.Add(mt); CAutoPtr pPinOut(DEBUG_NEW CBaseSplitterOutputPin(mts, L"Video", this, this, &hr)); EXECUTE_ASSERT(SUCCEEDED(AddOutputPin(0, pPinOut))); m_rtNewStop = m_rtStop = m_rtDuration; return !m_pOutputs.IsEmpty() ? S_OK : E_FAIL; } bool CMPEG4VideoSplitterFilter::DemuxInit() { return true; } void CMPEG4VideoSplitterFilter::DemuxSeek(REFERENCE_TIME rt) { ASSERT(rt == 0); m_pFile->Seek(m_seqhdrsize); } bool CMPEG4VideoSplitterFilter::DemuxLoop() { HRESULT hr = S_OK; CAutoPtr p; REFERENCE_TIME rt = 0; REFERENCE_TIME atpf = ((MPEG2VIDEOINFO*)GetOutputPin(0)->CurrentMediaType().Format())->hdr.AvgTimePerFrame; DWORD sync = ~0; while (SUCCEEDED(hr) && !CheckRequest(nullptr) && m_pFile->GetRemaining()) { for (int i = 0; i < 65536; ++i) { // don't call CheckRequest so often bool eof = !m_pFile->GetRemaining(); if (p && !p->IsEmpty() && (m_pFile->BitRead(32, true) == 0x000001b6 || eof)) { hr = DeliverPacket(p); } if (eof) { break; } if (!p) { p.Attach(DEBUG_NEW Packet()); p->SetCount(0, 1024); p->TrackNumber = 0; p->rtStart = rt; p->rtStop = rt + atpf; p->bSyncPoint = FALSE; rt += atpf; // rt = Packet::INVALID_TIME; } BYTE b; m_pFile->ByteRead(&b, 1); p->Add(b); } } return true; } // // CMPEG4VideoSourceFilter // CMPEG4VideoSourceFilter::CMPEG4VideoSourceFilter(LPUNKNOWN pUnk, HRESULT* phr) : CMPEG4VideoSplitterFilter(pUnk, phr) { m_clsid = __uuidof(this); m_pInput.Free(); } // // CMP4SplitterOutputPin // CMP4SplitterOutputPin::CMP4SplitterOutputPin(CAtlArray& mts, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : CBaseSplitterOutputPin(mts, pName, pFilter, pLock, phr) { } CMP4SplitterOutputPin::~CMP4SplitterOutputPin() { } HRESULT CMP4SplitterOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { CAutoLock cAutoLock(this); return __super::DeliverNewSegment(tStart, tStop, dRate); } HRESULT CMP4SplitterOutputPin::DeliverEndFlush() { CAutoLock cAutoLock(this); return __super::DeliverEndFlush(); } HRESULT CMP4SplitterOutputPin::DeliverPacket(CAutoPtr p) { CAutoLock cAutoLock(this); return __super::DeliverPacket(p); }