/* * Copyright (C) 2003-2006 Gabest * http://www.gabest.org * * This Program 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 2, or (at your option) * any later version. * * This Program 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "OggSplitter.h" #include "../../../DSUtil/DSUtil.h" #include "../../../DSUtil/GolombBuffer.h" #include #include #define MAKE32BITS(x) (x[0]<<24 | x[1]<<16 | x[2]<<8 | x[3]) #define MAKE24BITS(x) (x[0]<<16 | x[1]<<8 | x[2]) #define MAKE16BITS(x) (x[0]<<8 | x[1]) # define TH_VERSION_CHECK(version_major,version_minor,version_subminor,_maj,_min,_sub) \ (version_major>(_maj)||version_major==(_maj)&& \ (version_minor>(_min)||version_minor==(_min)&& \ version_subminor>=(_sub))) #ifdef REGISTER_FILTER const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] = { {&MEDIATYPE_Stream, &MEDIASUBTYPE_NULL}, }; const AMOVIESETUP_PIN sudpPins[] = { {L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesIn), sudPinTypesIn}, {L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, 0, NULL} }; const AMOVIESETUP_FILTER sudFilter[] = { {&__uuidof(COggSplitterFilter), L"MPC - Ogg Splitter", MERIT_NORMAL+1, countof(sudpPins), sudpPins, CLSID_LegacyAmFilterCategory}, {&__uuidof(COggSourceFilter), L"MPC - Ogg Source", MERIT_NORMAL+1, 0, NULL, CLSID_LegacyAmFilterCategory}, }; CFactoryTemplate g_Templates[] = { {sudFilter[0].strName, sudFilter[0].clsID, CreateInstance, NULL, &sudFilter[0]}, {sudFilter[1].strName, sudFilter[1].clsID, CreateInstance, NULL, &sudFilter[1]}, }; int g_cTemplates = countof(g_Templates); STDAPI DllRegisterServer() { RegisterSourceFilter( CLSID_AsyncReader, MEDIASUBTYPE_Ogg, _T("0,4,,4F676753"), // OggS _T(".ogg"), _T(".ogm"), NULL); return AMovieDllRegisterServer2(TRUE); } STDAPI DllUnregisterServer() { UnRegisterSourceFilter(MEDIASUBTYPE_Ogg); return AMovieDllRegisterServer2(FALSE); } #include "../../FilterApp.h" CFilterApp theApp; #endif // // bitstream // class bitstream { BYTE* m_p; int m_len, m_pos; public: bitstream(BYTE* p, int len, bool rev = false) : m_p(p), m_len(len*8) {m_pos = !rev ? 0 : len*8;} bool hasbits(int cnt) { int pos = m_pos+cnt; return(pos >= 0 && pos < m_len); } unsigned int showbits(int cnt) // a bit unclean, but works and can read backwards too! :P { if(!hasbits(cnt)) {ASSERT(0); return 0;} unsigned int ret = 0, off = 0; BYTE* p = m_p; if(cnt < 0) { p += (m_pos+cnt)>>3; off = (m_pos+cnt)&7; cnt = abs(cnt); ret = (*p++&(~0<>off; off = 8 - off; cnt -= off; } else { p += m_pos>>3; off = m_pos&7; ret = (*p++>>off)&((1< 0) {ret |= (*p++&((1<Seek(0); OggPage page; for(int i = 0, nWaitForMore = 0; m_pFile->Read(page); i++) { BYTE* p = page.GetData(); if(!(page.m_hdr.header_type_flag & OggPageHeader::continued)) { BYTE type = *p++; CStringW name; name.Format(L"Stream %d", i); HRESULT hr; if(type >= 0x80 && type <= 0x82 && !memcmp(p, "theora", 6)) { if(type == 0x80) { name.Format(L"Theora %d", i); CAutoPtr pPinOut; pPinOut.Attach(DNew COggTheoraOutputPin(page.GetData(), name, this, this, &hr)); AddOutputPin(page.m_hdr.bitstream_serial_number, pPinOut); nWaitForMore++; } } else if(type == 1 && (page.m_hdr.header_type_flag & OggPageHeader::first)) { CAutoPtr pPinOut; if(!memcmp(p, "vorbis", 6)) { name.Format(L"Vorbis %d", i); pPinOut.Attach(DNew COggVorbisOutputPin((OggVorbisIdHeader*)(p+6), name, this, this, &hr)); nWaitForMore++; } else if(!memcmp(p, "video", 5)) { name.Format(L"Video %d", i); pPinOut.Attach(DNew COggVideoOutputPin((OggStreamHeader*)p, name, this, this, &hr)); } else if(!memcmp(p, "audio", 5)) { name.Format(L"Audio %d", i); pPinOut.Attach(DNew COggAudioOutputPin((OggStreamHeader*)p, name, this, this, &hr)); } else if(!memcmp(p, "text", 4)) { name.Format(L"Text %d", i); pPinOut.Attach(DNew COggTextOutputPin((OggStreamHeader*)p, name, this, this, &hr)); } else if(!memcmp(p, "Direct Show Samples embedded in Ogg", 35)) { name.Format(L"DirectShow %d", i); pPinOut.Attach(DNew COggDirectShowOutputPin((AM_MEDIA_TYPE*)(p+35+sizeof(GUID)), name, this, this, &hr)); } AddOutputPin(page.m_hdr.bitstream_serial_number, pPinOut); } else if(type == 3 && !memcmp(p, "vorbis", 6)) { if(COggSplitterOutputPin* pOggPin = dynamic_cast(GetOutputPin(page.m_hdr.bitstream_serial_number))) { pOggPin->AddComment(p+6, page.GetCount()-6-1); } } else if(type == 0x7F && page.GetCount()>12 && *(long*)(p+8) == 0x43614C66) // Flac { // Ogg Flac : method 1 CAutoPtr pPinOut; name.Format(L"Flac %d", i); pPinOut.Attach(DNew COggFlacOutputPin(p+12, page.GetCount()-14, name, this, this, &hr)); AddOutputPin(page.m_hdr.bitstream_serial_number, pPinOut); } else if (*(long*)(p-1) == 0x43614C66) { //bFlac = true; //nWaitForMore++; if (m_pFile->Read(page)) { CAutoPtr pPinOut; name.Format(L"Flac %d", i); p = page.GetData(); pPinOut.Attach(DNew COggFlacOutputPin(p, page.GetCount(), name, this, this, &hr)); AddOutputPin(page.m_hdr.bitstream_serial_number, pPinOut); } } else if(!(type&1) && nWaitForMore == 0) { break; } } if(COggTheoraOutputPin* p = dynamic_cast(GetOutputPin(page.m_hdr.bitstream_serial_number))) { p->UnpackInitPage(page); if(p->IsInitialized()) nWaitForMore--; } if(COggVorbisOutputPin* p = dynamic_cast(GetOutputPin(page.m_hdr.bitstream_serial_number))) { p->UnpackInitPage(page); if(p->IsInitialized()) nWaitForMore--; } } if(m_pOutputs.IsEmpty()) return E_FAIL; if(m_pFile->IsRandomAccess()) { m_pFile->Seek(max(m_pFile->GetLength()-65536, 0)); OggPage page; while(m_pFile->Read(page)) { COggSplitterOutputPin* pOggPin = dynamic_cast(GetOutputPin(page.m_hdr.bitstream_serial_number)); if(!pOggPin || page.m_hdr.granule_position == -1) continue; REFERENCE_TIME rt = pOggPin->GetRefTime(page.m_hdr.granule_position); m_rtDuration = max(rt, m_rtDuration); } } m_rtNewStart = m_rtCurrent = 0; m_rtNewStop = m_rtStop = m_rtDuration; // comments { CAtlMap > tagmap; tagmap[L"TITLE"] = L"TITL"; tagmap[L"ARTIST"] = L"AUTH"; // not quite tagmap[L"COPYRIGHT"] = L"CPYR"; tagmap[L"DESCRIPTION"] = L"DESC"; POSITION pos2 = tagmap.GetStartPosition(); while(pos2) { CStringW oggtag, dsmtag; tagmap.GetNextAssoc(pos2, oggtag, dsmtag); POSITION pos = m_pOutputs.GetHeadPosition(); while(pos) { COggSplitterOutputPin* pOggPin = dynamic_cast((CBaseOutputPin*)m_pOutputs.GetNext(pos)); if(!pOggPin) continue; CStringW value = pOggPin->GetComment(oggtag); if(!value.IsEmpty()) { SetProperty(dsmtag, value); break; } } } POSITION pos = m_pOutputs.GetHeadPosition(); while(pos && !ChapGetCount()) { COggSplitterOutputPin* pOggPin = dynamic_cast((CBaseOutputPin*)m_pOutputs.GetNext(pos)); if(!pOggPin) continue; for(int i = 1; pOggPin; i++) { CStringW key; key.Format(L"CHAPTER%02d", i); CStringW time = pOggPin->GetComment(key); if(time.IsEmpty()) break; key.Format(L"CHAPTER%02dNAME", i); CStringW name = pOggPin->GetComment(key); if(name.IsEmpty()) name.Format(L"Chapter %d", i); int h, m, s, ms; WCHAR c; if(7 != swscanf(time, L"%d%c%d%c%d%c%d", &h, &c, &m, &c, &s, &c, &ms)) break; REFERENCE_TIME rt = ((((REFERENCE_TIME)h*60+m)*60+s)*1000+ms)*10000; ChapAppend(rt, name); } } } return m_pOutputs.GetCount() > 0 ? S_OK : E_FAIL; } bool COggSplitterFilter::DemuxInit() { SetThreadName((DWORD)-1, "COggSplitterFilter"); if(!m_pFile) return(false); return(true); } void COggSplitterFilter::DemuxSeek(REFERENCE_TIME rt) { if(rt <= 0 ) { m_pFile->Seek(0); } else if(m_rtDuration > 0) { // oh, the horror... __int64 len = m_pFile->GetLength(); __int64 startpos = len * rt/m_rtDuration; __int64 diff = 0; REFERENCE_TIME rtMinDiff = _I64_MAX; while(1) { __int64 endpos = startpos; REFERENCE_TIME rtPos = -1; OggPage page; m_pFile->Seek(startpos); while(m_pFile->Read(page, false)) { if(page.m_hdr.granule_position == -1) continue; COggSplitterOutputPin* pOggPin = dynamic_cast(GetOutputPin(page.m_hdr.bitstream_serial_number)); if(!pOggPin) {ASSERT(0); continue;} rtPos = pOggPin->GetRefTime(page.m_hdr.granule_position); endpos = m_pFile->GetPos(); break; } __int64 rtDiff = rtPos - rt; if(rtDiff < 0) { rtDiff = -rtDiff; if(rtDiff < 1000000 || rtDiff >= rtMinDiff) { m_pFile->Seek(startpos); break; } rtMinDiff = rtDiff; } __int64 newpos = startpos; if(rtPos < rt && rtPos < m_rtDuration) { newpos = startpos + (__int64)((1.0*(rt - rtPos)/(m_rtDuration - rtPos)) * (len - startpos)) + 1024; if(newpos < endpos) newpos = endpos + 1024; } else if(rtPos > rt && rtPos > 0) { newpos = startpos - (__int64)((1.0*(rtPos - rt)/(rtPos - 0)) * (startpos - 0)) - 1024; if(newpos >= startpos) newpos = startpos - 1024; } else if(rtPos == rt) { m_pFile->Seek(startpos); break; } else { ASSERT(0); m_pFile->Seek(0); break; } diff = newpos - startpos; startpos = max(min(newpos, len), 0); } m_pFile->Seek(startpos); POSITION pos = m_pOutputs.GetHeadPosition(); while(pos) { COggSplitterOutputPin* pPin = dynamic_cast(static_cast(m_pOutputs.GetNext(pos))); if(!dynamic_cast(pPin) && !dynamic_cast(pPin)) continue; bool fKeyFrameFound = false, fSkipKeyFrame = true; __int64 endpos = _I64_MAX; while(!(fKeyFrameFound && !fSkipKeyFrame) && startpos > 0) { OggPage page; while(!(fKeyFrameFound && !fSkipKeyFrame) && m_pFile->GetPos() < endpos && m_pFile->Read(page)) { if(page.m_hdr.granule_position == -1) continue; if(pPin != dynamic_cast(GetOutputPin(page.m_hdr.bitstream_serial_number))) continue; if(pPin->GetRefTime(page.m_hdr.granule_position) > rt) break; if(!fKeyFrameFound) { pPin->UnpackPage(page); CAutoPtr p; while(p = pPin->GetPacket()) { if(p->bSyncPoint) { fKeyFrameFound = true; fSkipKeyFrame = p->fSkip; } } if(fKeyFrameFound) break; } else { pPin->UnpackPage(page); CAutoPtr p; while(p = pPin->GetPacket()) { if(!p->fSkip) { fSkipKeyFrame = false; break; } } } } if(!(fKeyFrameFound && !fSkipKeyFrame)) {endpos = startpos; startpos = max(startpos - 10*65536, 0);} m_pFile->Seek(startpos); } #ifdef DEBUG // verify kf { fKeyFrameFound = false; OggPage page; while(!fKeyFrameFound && m_pFile->Read(page)) { if(page.m_hdr.granule_position == -1) continue; if(pPin != dynamic_cast(GetOutputPin(page.m_hdr.bitstream_serial_number))) continue; REFERENCE_TIME rtPos = pPin->GetRefTime(page.m_hdr.granule_position); if(rtPos > rt) break; pPin->UnpackPage(page); CAutoPtr p; while(p = pPin->GetPacket()) { if(p->bSyncPoint) { fKeyFrameFound = true; break; } } } ASSERT(fKeyFrameFound); m_pFile->Seek(startpos); } #endif break; } } } bool COggSplitterFilter::DemuxLoop() { HRESULT hr = S_OK; OggPage page; while(SUCCEEDED(hr) && !CheckRequest(NULL) && m_pFile->Read(page, true, GetRequestHandle())) { COggSplitterOutputPin* pOggPin = dynamic_cast(GetOutputPin(page.m_hdr.bitstream_serial_number)); if(!pOggPin) {ASSERT(0); continue;} if(!pOggPin->IsConnected()) continue; if(FAILED(hr = pOggPin->UnpackPage(page))) {ASSERT(0); break;} CAutoPtr p; while(!CheckRequest(NULL) && SUCCEEDED(hr) && (p = pOggPin->GetPacket())) { if(!p->fSkip) hr = DeliverPacket(p); } } return(true); } // // COggSourceFilter // COggSourceFilter::COggSourceFilter(LPUNKNOWN pUnk, HRESULT* phr) : COggSplitterFilter(pUnk, phr) { m_clsid = __uuidof(this); m_pInput.Free(); } // // COggSplitterOutputPin // COggSplitterOutputPin::COggSplitterOutputPin(LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : CBaseSplitterOutputPin(pName, pFilter, pLock, phr) { ResetState((DWORD)-1); } void COggSplitterOutputPin::AddComment(BYTE* p, int len) { bitstream bs(p, len); bs.getbits(bs.getbits(32)*8); for(int n = bs.getbits(32); n-- > 0; ) { CStringA str; for(int len = bs.getbits(32); len-- > 0; ) str += (CHAR)bs.getbits(8); CAtlList sl; Explode(str, sl, '=', 2); if(sl.GetCount() == 2) { CAutoPtr p(DNew CComment(UTF8To16(sl.GetHead()), UTF8To16(sl.GetTail()))); if(p->m_key == L"LANGUAGE") { CString lang = ISO6392ToLanguage(sl.GetTail()), iso6392 = LanguageToISO6392(CString(p->m_value)); if(p->m_value.GetLength() == 3 && !lang.IsEmpty()) { SetName(CStringW(lang)); SetProperty(L"LANG", p->m_value); } else if(!iso6392.IsEmpty()) { SetName(p->m_value); SetProperty(L"LANG", CStringW(iso6392)); } else { SetName(p->m_value); SetProperty(L"NAME", p->m_value); } } m_pComments.AddTail(p); } } ASSERT(bs.getbits(1) == 1); } CStringW COggSplitterOutputPin::GetComment(CStringW key) { key.MakeUpper(); CAtlList sl; POSITION pos = m_pComments.GetHeadPosition(); while(pos) { CComment* p = m_pComments.GetNext(pos); if(key == p->m_key) sl.AddTail(p->m_value); } return Implode(sl, ';'); } void COggSplitterOutputPin::ResetState(DWORD seqnum) { CAutoLock csAutoLock(&m_csPackets); m_packets.RemoveAll(); m_lastpacket.Free(); m_lastseqnum = seqnum; m_rtLast = 0; m_fSkip = true; } HRESULT COggSplitterOutputPin::UnpackPage(OggPage& page) { if(m_lastseqnum != page.m_hdr.page_sequence_number-1) { ResetState(page.m_hdr.page_sequence_number); return S_FALSE; // FIXME } else { m_lastseqnum = page.m_hdr.page_sequence_number; } POSITION first = page.m_lens.GetHeadPosition(); while(first && page.m_lens.GetAt(first) == 255) page.m_lens.GetNext(first); if(!first) first = page.m_lens.GetTailPosition(); POSITION last = page.m_lens.GetTailPosition(); while(last && page.m_lens.GetAt(last) == 255) page.m_lens.GetPrev(last); if(!last) last = page.m_lens.GetTailPosition(); BYTE* pData = page.GetData(); int i = 0, j = 0, len = 0; for(POSITION pos = page.m_lens.GetHeadPosition(); pos; page.m_lens.GetNext(pos)) { len = page.m_lens.GetAt(pos); j += len; if(len < 255 || pos == page.m_lens.GetTailPosition()) { if(first == pos && (page.m_hdr.header_type_flag & OggPageHeader::continued)) { // ASSERT(m_lastpacket); if(m_lastpacket) { int size = m_lastpacket->GetCount(); m_lastpacket->SetCount(size + j-i); memcpy(m_lastpacket->GetData() + size, pData + i, j-i); CAutoLock csAutoLock(&m_csPackets); if(len < 255) m_packets.AddTail(m_lastpacket); } } else { CAutoPtr p(DNew OggPacket()); if(last == pos && page.m_hdr.granule_position != -1) { p->bDiscontinuity = m_fSkip; REFERENCE_TIME rtLast = m_rtLast; m_rtLast = GetRefTime(page.m_hdr.granule_position); // some bad encodings have a +/-1 frame difference from the normal timeline, // but these seem to cancel eachother out nicely so we can just ignore them // to make it play a bit more smooth. if(abs(rtLast - m_rtLast) == GetRefTime(1)) m_rtLast = rtLast; // FIXME m_fSkip = false; } p->TrackNumber = page.m_hdr.bitstream_serial_number; if(S_OK == UnpackPacket(p, pData + i, j-i)) { //if(p->TrackNumber == 1) //TRACE(_T("[%d]: %d, %I64d -> %I64d (skip=%d, disc=%d, sync=%d)\n"), // (int)p->TrackNumber, p->GetCount(), p->rtStart, p->rtStop, // (int)m_fSkip, (int)p->bDiscontinuity, (int)p->bSyncPoint); if(p->rtStart <= p->rtStop && p->rtStop <= p->rtStart + 10000000i64*60) { CAutoLock csAutoLock(&m_csPackets); m_rtLast = p->rtStop; p->fSkip = m_fSkip; if(len < 255) m_packets.AddTail(p); else m_lastpacket = p; } else { ASSERT(0); } } } i = j; } } return S_OK; } CAutoPtr COggSplitterOutputPin::GetPacket() { CAutoPtr p; CAutoLock csAutoLock(&m_csPackets); if(m_packets.GetCount()) p = m_packets.RemoveHead(); return p; } HRESULT COggSplitterOutputPin::DeliverEndFlush() { ResetState(); return __super::DeliverEndFlush(); } HRESULT COggSplitterOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { ResetState(); return __super::DeliverNewSegment(tStart, tStop, dRate); } // // COggVorbisOutputPin // COggVorbisOutputPin::COggVorbisOutputPin(OggVorbisIdHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : COggSplitterOutputPin(pName, pFilter, pLock, phr) { m_audio_sample_rate = h->audio_sample_rate; m_blocksize[0] = 1<blocksize_0; m_blocksize[1] = 1<blocksize_1; m_lastblocksize = 0; CMediaType mt; mt.InitMediaType(); mt.majortype = MEDIATYPE_Audio; mt.subtype = MEDIASUBTYPE_Vorbis; mt.formattype = FORMAT_VorbisFormat; VORBISFORMAT* vf = (VORBISFORMAT*)mt.AllocFormatBuffer(sizeof(VORBISFORMAT)); memset(mt.Format(), 0, mt.FormatLength()); vf->nChannels = h->audio_channels; vf->nSamplesPerSec = h->audio_sample_rate; vf->nAvgBitsPerSec = h->bitrate_nominal; vf->nMinBitsPerSec = h->bitrate_minimum; vf->nMaxBitsPerSec = h->bitrate_maximum; vf->fQuality = -1; mt.SetSampleSize(8192); m_mts.Add(mt); mt.InitMediaType(); mt.majortype = MEDIATYPE_Audio; mt.subtype = MEDIASUBTYPE_Vorbis2; mt.formattype = FORMAT_VorbisFormat2; VORBISFORMAT2* vf2 = (VORBISFORMAT2*)mt.AllocFormatBuffer(sizeof(VORBISFORMAT2)); memset(mt.Format(), 0, mt.FormatLength()); vf2->Channels = h->audio_channels; vf2->SamplesPerSec = h->audio_sample_rate; mt.SetSampleSize(8192); m_mts.InsertAt(0, mt); } HRESULT COggVorbisOutputPin::UnpackInitPage(OggPage& page) { HRESULT hr = __super::UnpackPage(page); while(m_packets.GetCount()) { Packet* p = m_packets.GetHead(); if(p->GetCount() >= 6 && p->GetAt(0) == 0x05) { // yeah, right, we are going to be parsing this backwards! :P bitstream bs(p->GetData(), p->GetCount(), true); while(bs.hasbits(-1) && bs.getbits(-1) != 1); for(int cnt = 0; bs.hasbits(-8-16-16-1-6); cnt++) { unsigned int modes = bs.showbits(-6)+1; unsigned int mapping = bs.getbits(-8); unsigned int transformtype = bs.getbits(-16); unsigned int windowtype = bs.getbits(-16); unsigned int blockflag = bs.getbits(-1); UNUSED_ALWAYS(mapping); if(transformtype != 0 || windowtype != 0) { ASSERT(modes == cnt); break; } m_blockflags.InsertAt(0, !!blockflag); } } int cnt = m_initpackets.GetCount(); if(cnt <= 3) { ASSERT(p->GetCount() >= 6 && p->GetAt(0) == 1+cnt*2); VORBISFORMAT2* vf2 = (VORBISFORMAT2*)m_mts[0].Format(); vf2->HeaderSize[cnt] = p->GetCount(); int len = m_mts[0].FormatLength(); memcpy( m_mts[0].ReallocFormatBuffer(len + p->GetCount()) + len, p->GetData(), p->GetCount()); } m_initpackets.AddTail(m_packets.RemoveHead()); } return hr; } REFERENCE_TIME COggVorbisOutputPin::GetRefTime(__int64 granule_position) { REFERENCE_TIME rt = granule_position * 10000000 / m_audio_sample_rate; return rt; } HRESULT COggVorbisOutputPin::UnpackPacket(CAutoPtr& p, BYTE* pData, int len) { if(len > 0 && m_blockflags.GetCount()) { bitstream bs(pData, len); if(bs.getbits(1) == 0) { int x = m_blockflags.GetCount()-1, n = 0; while(x) {n++; x >>= 1;} DWORD blocksize = m_blocksize[m_blockflags[bs.getbits(n)]?1:0]; if(m_lastblocksize) m_rtLast += GetRefTime((m_lastblocksize + blocksize) >> 2); m_lastblocksize = blocksize; } } p->bSyncPoint = TRUE; p->rtStart = m_rtLast; p->rtStop = m_rtLast+1; p->SetData(pData, len); return S_OK; } HRESULT COggVorbisOutputPin::DeliverPacket(CAutoPtr p) { if(p->GetCount() > 0 && (p->GetAt(0)&1)) return S_OK; return __super::DeliverPacket(p); } HRESULT COggVorbisOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { HRESULT hr = __super::DeliverNewSegment(tStart, tStop, dRate); m_lastblocksize = 0; if(m_mt.subtype == MEDIASUBTYPE_Vorbis) { POSITION pos = m_initpackets.GetHeadPosition(); while(pos) { Packet* pi = m_initpackets.GetNext(pos); CAutoPtr p(DNew OggPacket()); p->TrackNumber = pi->TrackNumber; p->bDiscontinuity = p->bSyncPoint = FALSE;//TRUE; p->rtStart = p->rtStop = 0; p->Copy(*pi); __super::DeliverPacket(p); } } return hr; } // // COggFlacOutputPin // COggFlacOutputPin::COggFlacOutputPin(BYTE* h, int nCount, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : COggSplitterOutputPin(pName, pFilter, pLock, phr) { CGolombBuffer Buffer(h, nCount); Buffer.BitRead(1); // Last-metadata-block flag if (Buffer.BitRead(7) != 0) // Should be a STREAMINFO block { if(phr) *phr = VFW_E_INVALID_FILE_FORMAT; return; } Buffer.BitRead(24); // Length (in bytes) of metadata to follow Buffer.ReadShort(); // m_nMinBlocksize Buffer.ReadShort(); // m_nMaxBlocksize Buffer.BitRead(24); // m_nMinFrameSize Buffer.BitRead(24); // m_nMaxFrameSize m_nSamplesPerSec = (int)Buffer.BitRead(20); m_nChannels = (int)Buffer.BitRead(3) + 1; m_wBitsPerSample = (WORD)Buffer.BitRead(5) + 1; Buffer.BitRead(36); // m_i64TotalNumSamples m_nAvgBytesPerSec = (m_nChannels * (m_wBitsPerSample >> 3)) * m_nSamplesPerSec; CMediaType mt; mt.majortype = MEDIATYPE_Audio; mt.subtype = MEDIASUBTYPE_FLAC_FRAMED; mt.formattype = FORMAT_WaveFormatEx; WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX)); memset(wfe, 0, sizeof(WAVEFORMATEX)); wfe->cbSize = sizeof(WAVEFORMATEX); wfe->wFormatTag = WAVE_FORMAT_FLAC; wfe->nSamplesPerSec = m_nSamplesPerSec; wfe->nAvgBytesPerSec = m_nAvgBytesPerSec; wfe->nChannels = m_nChannels; wfe->nBlockAlign = 1; wfe->wBitsPerSample = m_wBitsPerSample; m_mts.InsertAt(0, mt); *phr = S_OK; } REFERENCE_TIME COggFlacOutputPin::GetRefTime(__int64 granule_position) { REFERENCE_TIME rt = (granule_position * UNITS) / m_nSamplesPerSec; return rt; } HRESULT COggFlacOutputPin::UnpackPacket(CAutoPtr& p, BYTE* pData, int len) { if (pData[0] != 0xFF || (pData[1] & 0xFE) != 0xF8) { return S_FALSE; } p->bSyncPoint = TRUE; p->rtStart = m_rtLast; p->rtStop = m_rtLast+1; // TODO : find packet duration ! p->SetData(pData, len); return S_OK; } HRESULT COggFlacOutputPin::DeliverPacket(CAutoPtr p) { if(p->GetCount() > 0 && (p->GetAt(0)&1)) return S_OK; return __super::DeliverPacket(p); } HRESULT COggFlacOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { HRESULT hr = __super::DeliverNewSegment(tStart, tStop, dRate); m_lastblocksize = 0; if(m_mt.subtype == MEDIASUBTYPE_FLAC_FRAMED) { POSITION pos = m_initpackets.GetHeadPosition(); while(pos) { Packet* pi = m_initpackets.GetNext(pos); CAutoPtr p(DNew OggPacket()); p->TrackNumber = pi->TrackNumber; p->bDiscontinuity = p->bSyncPoint = FALSE;//TRUE; p->rtStart = p->rtStop = 0; p->Copy(*pi); __super::DeliverPacket(p); } } return hr; } // // COggDirectShowOutputPin // COggDirectShowOutputPin::COggDirectShowOutputPin(AM_MEDIA_TYPE* pmt, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : COggSplitterOutputPin(pName, pFilter, pLock, phr) { CMediaType mt; memcpy((AM_MEDIA_TYPE*)&mt, pmt, FIELD_OFFSET(AM_MEDIA_TYPE, pUnk)); mt.SetFormat((BYTE*)(pmt+1), pmt->cbFormat); mt.SetSampleSize(1); if(mt.majortype == MEDIATYPE_Video) // TODO: find samples for audio and find out what to return in GetRefTime... m_mts.Add(mt); } REFERENCE_TIME COggDirectShowOutputPin::GetRefTime(__int64 granule_position) { REFERENCE_TIME rt = 0; if(m_mt.majortype == MEDIATYPE_Video) { rt = granule_position * ((VIDEOINFOHEADER*)m_mt.Format())->AvgTimePerFrame; } else if(m_mt.majortype == MEDIATYPE_Audio) { rt = granule_position; // ((WAVEFORMATEX*)m_mt.Format())-> // TODO } return rt; } HRESULT COggDirectShowOutputPin::UnpackPacket(CAutoPtr& p, BYTE* pData, int len) { int i = 0; BYTE hdr = pData[i++]; if(!(hdr&1)) { // TODO: verify if this was still present in the old format (haven't found one sample yet) BYTE nLenBytes = (hdr>>6)|((hdr&2)<<1); __int64 Length = 0; for(int j = 0; j < nLenBytes; j++) Length |= (__int64)pData[i++] << (j << 3); if(len < i) {ASSERT(0); return E_FAIL;} p->bSyncPoint = !!(hdr&8); p->rtStart = m_rtLast; p->rtStop = m_rtLast + (nLenBytes ? GetRefTime(Length) : GetRefTime(1)); p->SetData(&pData[i], len - i); return S_OK; } return S_FALSE; } // // COggStreamOutputPin // COggStreamOutputPin::COggStreamOutputPin(OggStreamHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : COggSplitterOutputPin(pName, pFilter, pLock, phr) { m_time_unit = h->time_unit; m_samples_per_unit = h->samples_per_unit; m_default_len = h->default_len; } REFERENCE_TIME COggStreamOutputPin::GetRefTime(__int64 granule_position) { return granule_position * m_time_unit / m_samples_per_unit; } HRESULT COggStreamOutputPin::UnpackPacket(CAutoPtr& p, BYTE* pData, int len) { int i = 0; BYTE hdr = pData[i++]; if(!(hdr&1)) { BYTE nLenBytes = (hdr>>6)|((hdr&2)<<1); __int64 Length = 0; for(int j = 0; j < nLenBytes; j++) Length |= (__int64)pData[i++] << (j << 3); if(len < i) {ASSERT(0); return E_FAIL;} p->bSyncPoint = !!(hdr&8); p->rtStart = m_rtLast; p->rtStop = m_rtLast + (nLenBytes ? GetRefTime(Length) : GetRefTime(m_default_len)); p->SetData(&pData[i], len - i); return S_OK; } return S_FALSE; } // // COggVideoOutputPin // COggVideoOutputPin::COggVideoOutputPin(OggStreamHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : COggStreamOutputPin(h, pName, pFilter, pLock, phr) { int extra = (int)h->size - sizeof(OggStreamHeader); extra = max(extra, 0); CMediaType mt; mt.majortype = MEDIATYPE_Video; mt.subtype = FOURCCMap(MAKEFOURCC(h->subtype[0],h->subtype[1],h->subtype[2],h->subtype[3])); mt.formattype = FORMAT_VideoInfo; VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + extra); memset(mt.Format(), 0, mt.FormatLength()); memcpy(mt.Format() + sizeof(VIDEOINFOHEADER), h+1, extra); pvih->AvgTimePerFrame = h->time_unit / h->samples_per_unit; pvih->bmiHeader.biWidth = h->v.w; pvih->bmiHeader.biHeight = h->v.h; pvih->bmiHeader.biBitCount = (WORD)h->bps; pvih->bmiHeader.biCompression = mt.subtype.Data1; 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_RGB32 : MEDIASUBTYPE_NULL; break; case BI_RLE8: mt.subtype = MEDIASUBTYPE_RGB8; break; case BI_RLE4: mt.subtype = MEDIASUBTYPE_RGB4; break; } mt.SetSampleSize(max(h->buffersize, 1)); m_mts.Add(mt); } // // COggAudioOutputPin // COggAudioOutputPin::COggAudioOutputPin(OggStreamHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : COggStreamOutputPin(h, pName, pFilter, pLock, phr) { int extra = (int)h->size - sizeof(OggStreamHeader); extra = max(extra, 0); CMediaType mt; mt.majortype = MEDIATYPE_Audio; mt.subtype = FOURCCMap(strtol(CStringA(h->subtype, 4), NULL, 16)); mt.formattype = FORMAT_WaveFormatEx; WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX) + extra); memset(mt.Format(), 0, mt.FormatLength()); memcpy(mt.Format() + sizeof(WAVEFORMATEX), h+1, extra); wfe->cbSize = extra; wfe->wFormatTag = (WORD)mt.subtype.Data1; wfe->nChannels = h->a.nChannels; wfe->nSamplesPerSec = (DWORD)(10000000i64 * h->samples_per_unit / h->time_unit); wfe->wBitsPerSample = (WORD)h->bps; wfe->nAvgBytesPerSec = h->a.nAvgBytesPerSec; // TODO: verify for PCM wfe->nBlockAlign = h->a.nBlockAlign; // TODO: verify for PCM mt.SetSampleSize(max(h->buffersize, 1)); m_mts.Add(mt); } // // COggTextOutputPin // COggTextOutputPin::COggTextOutputPin(OggStreamHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : COggStreamOutputPin(h, pName, pFilter, pLock, phr) { CMediaType mt; mt.majortype = MEDIATYPE_Text; mt.subtype = MEDIASUBTYPE_NULL; mt.formattype = FORMAT_None; mt.SetSampleSize(1); m_mts.Add(mt); } // COggTheoraOutputPin COggTheoraOutputPin::COggTheoraOutputPin(BYTE* p, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) : COggSplitterOutputPin(pName, pFilter, pLock, phr) { CMediaType mt; mt.majortype = MEDIATYPE_Video; mt.subtype = FOURCCMap('OEHT'); mt.formattype = FORMAT_MPEG2_VIDEO; MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(sizeof(MPEG2VIDEOINFO)); memset(mt.Format(), 0, mt.FormatLength()); vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader); vih->hdr.bmiHeader.biWidth = *(WORD*)&p[10] >> 4; vih->hdr.bmiHeader.biHeight = *(WORD*)&p[12] >> 4; vih->hdr.bmiHeader.biCompression = 'OEHT'; vih->hdr.bmiHeader.biPlanes = 1; vih->hdr.bmiHeader.biBitCount = 24; m_nFpsNum = (p[22]<<24)|(p[23]<<16)|(p[24]<<8)|p[25]; m_nFpsDenum = (p[26]<<24)|(p[27]<<16)|(p[28]<<8)|p[29]; if(m_nFpsNum) { m_rtAvgTimePerFrame = (REFERENCE_TIME)(10000000.0 * m_nFpsDenum / m_nFpsNum); vih->hdr.AvgTimePerFrame = m_rtAvgTimePerFrame; } vih->hdr.dwPictAspectRatioX = (p[14]<<16)|(p[15]<<8)|p[16]; vih->hdr.dwPictAspectRatioY = (p[17]<<16)|(p[18]<<8)|p[19]; m_KfgShift = (((p[40]<<8)+p[41]) &0x3E0) >> 5; m_nIndexOffset = TH_VERSION_CHECK(p[7],p[8],p[9],3,2,1); if (m_KfgShift == 0) m_KfgShift = 6; // Is it really default value ? mt.bFixedSizeSamples = 0; m_mts.Add(mt); } HRESULT COggTheoraOutputPin::UnpackInitPage(OggPage& page) { HRESULT hr = __super::UnpackPage(page); while(m_packets.GetCount()) { Packet* p = m_packets.GetHead(); CMediaType& mt = m_mts[0]; int size = p->GetCount(); ASSERT(size <= 0xffff); MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.ReallocFormatBuffer( FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + ((MPEG2VIDEOINFO*)mt.Format())->cbSequenceHeader + 2 + size); *(WORD*)((BYTE*)vih->dwSequenceHeader + vih->cbSequenceHeader) = (size>>8)|(size<<8); memcpy((BYTE*)vih->dwSequenceHeader + vih->cbSequenceHeader + 2, p->GetData(), size); vih->cbSequenceHeader += 2 + size; m_initpackets.AddTail(m_packets.RemoveHead()); } return hr; } REFERENCE_TIME COggTheoraOutputPin::GetRefTime(__int64 granule_position) { LONGLONG iframe; LONGLONG pframe; iframe=granule_position>>m_KfgShift; pframe=granule_position-(iframe<& p, BYTE* pData, int len) { p->bSyncPoint = len > 0 ? !(*pData & 0x40) : TRUE; p->rtStart = m_rtLast; p->rtStop = m_rtLast+1; p->SetData(pData, len); if(!(*pData & 0x80) && m_mt.majortype == MEDIATYPE_Video) p->rtStop = p->rtStart + ((MPEG2VIDEOINFO*)m_mt.Format())->hdr.AvgTimePerFrame; return S_OK; }