- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# OggSplitter.cpp

    00001 /*
    00002  *      Copyright (C) 2003-2005 Gabest
    00003  *      http://www.gabest.org
    00004  *
    00005  *  This Program is free software; you can redistribute it and/or modify
    00006  *  it under the terms of the GNU General Public License as published by
    00007  *  the Free Software Foundation; either version 2, or (at your option)
    00008  *  any later version.
    00009  *
    00010  *  This Program is distributed in the hope that it will be useful,
    00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    00013  *  GNU General Public License for more details.
    00014  *
    00015  *  You should have received a copy of the GNU General Public License
    00016  *  along with GNU Make; see the file COPYING.  If not, write to
    00017  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
    00018  *  http://www.gnu.org/copyleft/gpl.html
    00019  *
    00020  */
    00021
    00022 #include "StdAfx.h"
    00023 #include "OggSplitter.h"
    00024 #include "..\..\..\DSUtil\DSUtil.h"
    00025
    00026 #include <initguid.h>
    00027 #include "..\..\..\..\include\ogg\OggDS.h"
    00028 #include "..\..\..\..\include\moreuuids.h"
    00029
    00030 #ifdef REGISTER_FILTER
    00031
    00032 const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] =
    00033 {
    00034         {&MEDIATYPE_Stream, &MEDIASUBTYPE_NULL},
    00035 };
    00036
    00037 const AMOVIESETUP_PIN sudpPins[] =
    00038 {
    00039     {L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesIn), sudPinTypesIn},
    00040     {L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, 0, NULL}
    00041 };
    00042
    00043 const AMOVIESETUP_FILTER sudFilter[] =
    00044 {
    00045         {&__uuidof(COggSplitterFilter), L"Ogg Splitter", MERIT_NORMAL+1, countof(sudpPins), sudpPins},
    00046         {&__uuidof(COggSourceFilter), L"Ogg Source", MERIT_NORMAL+1, 0, NULL},
    00047 };
    00048
    00049 CFactoryTemplate g_Templates[] =
    00050 {
    00051         {sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<COggSplitterFilter>, NULL, &sudFilter[0]},
    00052         {sudFilter[1].strName, sudFilter[1].clsID, CreateInstance<COggSourceFilter>, NULL, &sudFilter[1]},
    00053 };
    00054
    00055 int g_cTemplates = countof(g_Templates);
    00056
    00057 STDAPI DllRegisterServer()
    00058 {
    00059         RegisterSourceFilter(
    00060                 CLSID_AsyncReader,
    00061                 MEDIASUBTYPE_Ogg,
    00062                 _T("0,4,,4F676753"), // OggS
    00063                 _T(".ogg"), _T(".ogm"), NULL);
    00064
    00065         return AMovieDllRegisterServer2(TRUE);
    00066 }
    00067
    00068 STDAPI DllUnregisterServer()
    00069 {
    00070         UnRegisterSourceFilter(MEDIASUBTYPE_Ogg);
    00071
    00072         return AMovieDllRegisterServer2(FALSE);
    00073 }
    00074
    00075 extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
    00076
    00077 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
    00078 {
    00079     return DllEntryPoint((HINSTANCE)hModule, dwReason, 0); // "DllMain" of the dshow baseclasses;
    00080 }
    00081
    00082 #endif
    00083
    00084 //
    00085 // bitstream
    00086 //
    00087
    00088 class bitstream
    00089 {
    00090         BYTE* m_p;
    00091         int m_len, m_pos;
    00092 public:
    00093         bitstream(BYTE* p, int len, bool rev = false) : m_p(p), m_len(len*8) {m_pos = !rev ? 0 : len*8;}
    00094         bool hasbits(int cnt)
    00095         {
    00096                 int pos = m_pos+cnt;
    00097                 return(pos >= 0 && pos < m_len);
    00098         }
    00099         unsigned int showbits(int cnt) // a bit unclean, but works and can read backwards too! :P
    00100         {
    00101                 if(!hasbits(cnt)) {ASSERT(0); return 0;}
    00102                 unsigned int ret = 0, off = 0;
    00103                 BYTE* p = m_p;
    00104                 if(cnt < 0)
    00105                 {
    00106                         p += (m_pos+cnt)>>3;
    00107                         off = (m_pos+cnt)&7;
    00108                         cnt = abs(cnt);
    00109                         ret = (*p++&(~0<<off))>>off; off = 8 - off; cnt -= off;
    00110                 }
    00111                 else
    00112                 {
    00113                         p += m_pos>>3;
    00114                         off = m_pos&7;
    00115                         ret = (*p++>>off)&((1<<min(cnt,8))-1); off = 0; cnt -= 8 - off;
    00116                 }
    00117                 while(cnt > 0) {ret |= (*p++&((1<<min(cnt,8))-1)) << off; off += 8; cnt -= 8;}
    00118                 return ret;
    00119         }
    00120         unsigned int getbits(int cnt)
    00121         {
    00122                 unsigned int ret = showbits(cnt);
    00123                 m_pos += cnt;
    00124                 return ret;
    00125         }
    00126 };
    00127
    00128 //
    00129 // COggSplitterFilter
    00130 //
    00131
    00132 COggSplitterFilter::COggSplitterFilter(LPUNKNOWN pUnk, HRESULT* phr)
    00133         : CBaseSplitterFilter(NAME("COggSplitterFilter"), pUnk, phr, __uuidof(this))
    00134 {
    00135 }
    00136
    00137 COggSplitterFilter::~COggSplitterFilter()
    00138 {
    00139 }
    00140
    00141 HRESULT COggSplitterFilter::CreateOutputs(IAsyncReader* pAsyncReader)
    00142 {
    00143         CheckPointer(pAsyncReader, E_POINTER);
    00144
    00145         HRESULT hr = E_FAIL;
    00146
    00147         m_pFile.Free();
    00148
    00149         m_pFile.Attach(new COggFile(pAsyncReader, hr));
    00150         if(!m_pFile) return E_OUTOFMEMORY;
    00151         if(FAILED(hr)) {m_pFile.Free(); return hr;}
    00152
    00153         m_rtNewStart = m_rtCurrent = 0;
    00154         m_rtNewStop = m_rtStop = 0;
    00155
    00156         m_rtDuration = 0;
    00157
    00158         m_pFile->Seek(0);
    00159         OggPage page;
    00160         for(int i = 0, nWaitForMore = 0; m_pFile->Read(page); i++)
    00161         {
    00162                 BYTE* p = page.GetData();
    00163
    00164                 if(!(page.m_hdr.header_type_flag & OggPageHeader::continued))
    00165                 {
    00166                         BYTE type = *p++;
    00167                         if(!(type&1) && nWaitForMore == 0)
    00168                                 break;
    00169
    00170                         CStringW name;
    00171                         name.Format(L"Stream %d", i);
    00172
    00173                         HRESULT hr;
    00174
    00175                         if(type == 1 && (page.m_hdr.header_type_flag & OggPageHeader::first))
    00176                         {
    00177                                 CAutoPtr<CBaseSplitterOutputPin> pPinOut;
    00178
    00179                                 if(!memcmp(p, "vorbis", 6))
    00180                                 {
    00181                                         name.Format(L"Vorbis %d", i);
    00182                                         pPinOut.Attach(new COggVorbisOutputPin((OggVorbisIdHeader*)(p+6), name, this, this, &hr));
    00183                                         nWaitForMore++;
    00184                                 }
    00185                                 else if(!memcmp(p, "video", 5))
    00186                                 {
    00187                                         name.Format(L"Video %d", i);
    00188                                         pPinOut.Attach(new COggVideoOutputPin((OggStreamHeader*)p, name, this, this, &hr));
    00189                                 }
    00190                                 else if(!memcmp(p, "audio", 5))
    00191                                 {
    00192                                         name.Format(L"Audio %d", i);
    00193                                         pPinOut.Attach(new COggAudioOutputPin((OggStreamHeader*)p, name, this, this, &hr));
    00194                                 }
    00195                                 else if(!memcmp(p, "text", 4))
    00196                                 {
    00197                                         name.Format(L"Text %d", i);
    00198                                         pPinOut.Attach(new COggTextOutputPin((OggStreamHeader*)p, name, this, this, &hr));
    00199                                 }
    00200                                 else if(!memcmp(p, "Direct Show Samples embedded in Ogg", 35))
    00201                                 {
    00202                                         name.Format(L"DirectShow %d", i);
    00203                                         pPinOut.Attach(new COggDirectShowOutputPin((AM_MEDIA_TYPE*)(p+35+sizeof(GUID)), name, this, this, &hr));
    00204                                 }
    00205
    00206                                 AddOutputPin(page.m_hdr.bitstream_serial_number, pPinOut);
    00207                         }
    00208
    00209                         if(type == 3 && !memcmp(p, "vorbis", 6))
    00210                         {
    00211                                 if(COggSplitterOutputPin* pOggPin =
    00212                                         dynamic_cast<COggSplitterOutputPin*>(GetOutputPin(page.m_hdr.bitstream_serial_number)))
    00213                                 {
    00214                                         pOggPin->AddComment(p+6, page.GetSize()-6-1);
    00215                                 }
    00216                         }
    00217                 }
    00218
    00219                 if(COggVorbisOutputPin* pOggVorbisPin =
    00220                         dynamic_cast<COggVorbisOutputPin*>(GetOutputPin(page.m_hdr.bitstream_serial_number)))
    00221                 {
    00222                         pOggVorbisPin->UnpackInitPage(page);
    00223                         if(pOggVorbisPin->IsInitialized()) nWaitForMore--;
    00224                 }
    00225         }
    00226
    00227         if(m_pOutputs.IsEmpty())
    00228                 return E_FAIL;
    00229
    00230         m_pFile->Seek(max(m_pFile->GetLength()-65536, 0));
    00231         while(m_pFile->Read(page))
    00232         {
    00233                 COggSplitterOutputPin* pOggPin = dynamic_cast<COggSplitterOutputPin*>(GetOutputPin(page.m_hdr.bitstream_serial_number));
    00234                 if(!pOggPin) {ASSERT(0); continue;}
    00235                 if(page.m_hdr.granule_position == -1) continue;
    00236                 REFERENCE_TIME rt = pOggPin->GetRefTime(page.m_hdr.granule_position);
    00237                 m_rtDuration = max(rt, m_rtDuration);
    00238         }
    00239
    00240         m_rtNewStart = m_rtCurrent = 0;
    00241         m_rtNewStop = m_rtStop = m_rtDuration;
    00242
    00243         // comments
    00244
    00245         {
    00246                 CAtlMap<CStringW, CStringW, CStringElementTraits<CStringW> > tagmap;
    00247                 tagmap[L"TITLE"] = L"TITL";
    00248                 tagmap[L"ARTIST"] = L"AUTH"; // not quite
    00249                 tagmap[L"COPYRIGHT"] = L"CPYR";
    00250                 tagmap[L"DESCRIPTION"] = L"DESC";
    00251
    00252                 POSITION pos2 = tagmap.GetStartPosition();
    00253                 while(pos2)
    00254                 {
    00255                         CStringW oggtag, dsmtag;
    00256                         tagmap.GetNextAssoc(pos2, oggtag, dsmtag);
    00257
    00258                         POSITION pos = m_pOutputs.GetHeadPosition();
    00259                         while(pos)
    00260                         {
    00261                                 COggSplitterOutputPin* pOggPin = dynamic_cast<COggSplitterOutputPin*>((CBaseOutputPin*)m_pOutputs.GetNext(pos));
    00262                                 if(!pOggPin) continue;
    00263
    00264                                 CStringW value = pOggPin->GetComment(oggtag);
    00265                                 if(!value.IsEmpty())
    00266                                 {
    00267                                         SetProperty(dsmtag, value);
    00268                                         break;
    00269                                 }
    00270                         }
    00271                 }
    00272
    00273                 POSITION pos = m_pOutputs.GetHeadPosition();
    00274                 while(pos && !ChapGetCount())
    00275                 {
    00276                         COggSplitterOutputPin* pOggPin = dynamic_cast<COggSplitterOutputPin*>((CBaseOutputPin*)m_pOutputs.GetNext(pos));
    00277                         if(!pOggPin) continue;
    00278
    00279                         for(int i = 1; pOggPin; i++)
    00280                         {
    00281                                 CStringW key;
    00282                                 key.Format(L"CHAPTER%02d", i);
    00283                                 CStringW time = pOggPin->GetComment(key);
    00284                                 if(time.IsEmpty()) break;
    00285                                 key.Format(L"CHAPTER%02dNAME", i);
    00286                                 CStringW name = pOggPin->GetComment(key);
    00287                                 if(name.IsEmpty()) name.Format(L"Chapter %d", i);
    00288                                 int h, m, s, ms;
    00289                                 WCHAR c;
    00290                                 if(7 != swscanf(time, L"%d%c%d%c%d%c%d", &h, &c, &m, &c, &s, &c, &ms)) break;
    00291                                 REFERENCE_TIME rt = ((((REFERENCE_TIME)h*60+m)*60+s)*1000+ms)*10000;
    00292                                 ChapAppend(rt, name);
    00293                         }
    00294                 }
    00295         }
    00296
    00297         return m_pOutputs.GetCount() > 0 ? S_OK : E_FAIL;
    00298 }
    00299
    00300 bool COggSplitterFilter::DemuxInit()
    00301 {
    00302         if(!m_pFile) return(false);
    00303
    00304         return(true);
    00305 }
    00306
    00307 void COggSplitterFilter::DemuxSeek(REFERENCE_TIME rt)
    00308 {
    00309         if(rt <= 0)
    00310         {
    00311                 m_pFile->Seek(0);
    00312         }
    00313         else if(m_rtDuration > 0)
    00314         {
    00315                 // oh, the horror...
    00316 clock_t t1 = clock();
    00317                 __int64 len = m_pFile->GetLength();
    00318                 __int64 startpos = len * rt/m_rtDuration;
    00319                 __int64 diff = 0;
    00320
    00321                 REFERENCE_TIME rtMinDiff = _I64_MAX;
    00322
    00323                 while(1)
    00324                 {
    00325                         __int64 endpos = startpos;
    00326                         REFERENCE_TIME rtPos = -1;
    00327
    00328                         OggPage page;
    00329                         m_pFile->Seek(startpos);
    00330                         while(m_pFile->Read(page, false))
    00331                         {
    00332                                 if(page.m_hdr.granule_position == -1) continue;
    00333
    00334                                 COggSplitterOutputPin* pOggPin = dynamic_cast<COggSplitterOutputPin*>(GetOutputPin(page.m_hdr.bitstream_serial_number));
    00335                                 if(!pOggPin) {ASSERT(0); continue;}
    00336
    00337                                 rtPos = pOggPin->GetRefTime(page.m_hdr.granule_position);
    00338                                 endpos = m_pFile->GetPos();
    00339
    00340                                 break;
    00341                         }
    00342
    00343                         __int64 rtDiff = rtPos - rt;
    00344
    00345                         if(rtDiff < 0)
    00346                         {
    00347                                 rtDiff = -rtDiff;
    00348
    00349                                 if(rtDiff < 1000000 || rtDiff >= rtMinDiff)
    00350                                 {
    00351                                         m_pFile->Seek(startpos);
    00352                                         break;
    00353                                 }
    00354
    00355                                 rtMinDiff = rtDiff;
    00356                         }
    00357
    00358                         __int64 newpos = startpos;
    00359
    00360                         if(rtPos < rt && rtPos < m_rtDuration)
    00361                         {
    00362                                 newpos = startpos + (__int64)((1.0*(rt - rtPos)/(m_rtDuration - rtPos)) * (len - startpos)) + 1024;
    00363                     if(newpos < endpos) newpos = endpos + 1024;
    00364                         }
    00365                         else if(rtPos > rt && rtPos > 0)
    00366                         {
    00367                                 newpos = startpos - (__int64)((1.0*(rtPos - rt)/(rtPos - 0)) * (startpos - 0)) - 1024;
    00368                     if(newpos >= startpos) newpos = startpos - 1024;
    00369                         }
    00370                         else if(rtPos == rt)
    00371                         {
    00372                                 m_pFile->Seek(startpos);
    00373                                 break;
    00374                         }
    00375                         else
    00376                         {
    00377                                 ASSERT(0);
    00378                                 m_pFile->Seek(0);
    00379                                 break;
    00380                         }
    00381
    00382                         diff = newpos - startpos;
    00383
    00384                         startpos = max(min(newpos, len), 0);
    00385                 }
    00386
    00387 TRACE(_T("****** t1: %d\n"), clock() - t1);
    00388 t1 = clock();
    00389                 m_pFile->Seek(startpos);
    00390
    00391                 POSITION pos = m_pOutputs.GetHeadPosition();
    00392                 while(pos)
    00393                 {
    00394                         CBaseSplitterOutputPin* pPin = m_pOutputs.GetNext(pos);
    00395                         COggVideoOutputPin* pOggVideoPin = dynamic_cast<COggVideoOutputPin*>(pPin);
    00396                         if(!pOggVideoPin) continue;
    00397
    00398                         bool fKeyFrameFound = false, fSkipKeyFrame = true;
    00399                         __int64 endpos = _I64_MAX;
    00400
    00401                         while(!(fKeyFrameFound && !fSkipKeyFrame) && startpos > 0)
    00402                         {
    00403                                 OggPage page;
    00404                                 while(!(fKeyFrameFound && !fSkipKeyFrame) && m_pFile->GetPos() < endpos && m_pFile->Read(page))
    00405                                 {
    00406                                         if(page.m_hdr.granule_position == -1) continue;
    00407
    00408                                         COggSplitterOutputPin* pOggPin = dynamic_cast<COggSplitterOutputPin*>(GetOutputPin(page.m_hdr.bitstream_serial_number));
    00409                                         if(pOggPin != pOggVideoPin) continue;
    00410
    00411                                         REFERENCE_TIME rtPos = pOggPin->GetRefTime(page.m_hdr.granule_position);
    00412
    00413                                         if(rtPos > rt)
    00414                                                 break;
    00415
    00416                                         if(!fKeyFrameFound)
    00417                                         {
    00418                                                 pOggPin->UnpackPage(page);
    00419
    00420                                                 CAutoPtr<OggPacket> p;
    00421                                                 while(p = pOggPin->GetPacket())
    00422                                                 {
    00423                                                         if(p->bSyncPoint)
    00424                                                         {
    00425                                                                 fKeyFrameFound = true;
    00426                                                                 fSkipKeyFrame = p->fSkip;
    00427                                                         }
    00428                                                 }
    00429
    00430                                                 if(fKeyFrameFound) break;
    00431                                         }
    00432                                         else
    00433                                         {
    00434                                                 pOggPin->UnpackPage(page);
    00435
    00436                                                 CAutoPtr<OggPacket> p;
    00437                                                 while(p = pOggPin->GetPacket())
    00438                                                 {
    00439                                                         if(!p->fSkip)
    00440                                                         {
    00441                                                                 fSkipKeyFrame = false;
    00442                                                                 break;
    00443                                                         }
    00444                                                 }
    00445                                         }
    00446                                 }
    00447
    00448                                 if(!(fKeyFrameFound && !fSkipKeyFrame)) {endpos = startpos; startpos = max(startpos - 10*65536, 0);}
    00449
    00450                                 m_pFile->Seek(startpos);
    00451                         }
    00452 TRACE(_T("****** t2: %d\n"), clock() - t1);
    00453 t1 = clock();
    00454
    00455 #ifdef DEBUG
    00456                         // verify kf
    00457
    00458                         {
    00459                                 fKeyFrameFound = false;
    00460
    00461                                 OggPage page;
    00462                                 while(!fKeyFrameFound && m_pFile->Read(page))
    00463                                 {
    00464                                         if(page.m_hdr.granule_position == -1) continue;
    00465
    00466                                         COggSplitterOutputPin* pOggPin = dynamic_cast<COggSplitterOutputPin*>(GetOutputPin(page.m_hdr.bitstream_serial_number));
    00467                                         if(pOggPin != pOggVideoPin) continue;
    00468
    00469                                         REFERENCE_TIME rtPos = pOggPin->GetRefTime(page.m_hdr.granule_position);
    00470                                         if(rtPos > rt)
    00471                                                 break;
    00472
    00473                                         pOggPin->UnpackPage(page);
    00474
    00475                                         CAutoPtr<OggPacket> p;
    00476                                         while(p = pOggPin->GetPacket())
    00477                                         {
    00478                                                 if(p->bSyncPoint)
    00479                                                 {
    00480                                                         fKeyFrameFound = true;
    00481                                                         break;
    00482                                                 }
    00483                                         }
    00484                                 }
    00485
    00486                                 ASSERT(fKeyFrameFound);
    00487
    00488                                 m_pFile->Seek(startpos);
    00489                         }
    00490 TRACE(_T("****** t3: %d\n"), clock() - t1);
    00491 t1 = clock();
    00492 #endif
    00493                         break;
    00494                 }
    00495         }
    00496 }
    00497
    00498 bool COggSplitterFilter::DemuxLoop()
    00499 {
    00500         HRESULT hr = S_OK;
    00501
    00502         OggPage page;
    00503         while(SUCCEEDED(hr) && !CheckRequest(NULL) && m_pFile->Read(page, true, GetRequestHandle()))
    00504         {
    00505                 COggSplitterOutputPin* pOggPin = dynamic_cast<COggSplitterOutputPin*>(GetOutputPin(page.m_hdr.bitstream_serial_number));
    00506                 if(!pOggPin) {ASSERT(0); continue;}
    00507                 if(!pOggPin->IsConnected()) continue;
    00508                 if(FAILED(hr = pOggPin->UnpackPage(page))) {ASSERT(0); break;}
    00509                 CAutoPtr<OggPacket> p;
    00510                 while(!CheckRequest(NULL) && SUCCEEDED(hr) && (p = pOggPin->GetPacket()))
    00511                 {
    00512                         if(!p->fSkip)
    00513                                 hr = DeliverPacket(p);
    00514                 }
    00515         }
    00516
    00517         return(true);
    00518 }
    00519
    00520 //
    00521 // COggSourceFilter
    00522 //
    00523
    00524 COggSourceFilter::COggSourceFilter(LPUNKNOWN pUnk, HRESULT* phr)
    00525         : COggSplitterFilter(pUnk, phr)
    00526 {
    00527         m_clsid = __uuidof(this);
    00528         m_pInput.Free();
    00529 }
    00530
    00531 //
    00532 // COggSplitterOutputPin
    00533 //
    00534
    00535 COggSplitterOutputPin::COggSplitterOutputPin(LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
    00536         : CBaseSplitterOutputPin(pName, pFilter, pLock, phr)
    00537 {
    00538         ResetState(-1);
    00539 }
    00540
    00541 void COggSplitterOutputPin::AddComment(BYTE* p, int len)
    00542 {
    00543         bitstream bs(p, len);
    00544         bs.getbits(bs.getbits(32)*8);
    00545         for(int n = bs.getbits(32); n-- > 0; )
    00546         {
    00547                 CStringA str;
    00548                 for(int len = bs.getbits(32); len-- > 0; )
    00549                         str += (CHAR)bs.getbits(8);
    00550
    00551                 CList<CStringA> sl;
    00552                 Explode(str, sl, '=', 2);
    00553                 if(sl.GetCount() == 2)
    00554                 {
    00555                         CAutoPtr<CComment> p(new CComment(UTF8To16(sl.GetHead()), UTF8To16(sl.GetTail())));
    00556
    00557                         if(p->m_key == L"LANGUAGE")
    00558                         {
    00559                                 CString lang = ISO6392ToLanguage(sl.GetTail()), iso6392 = LanguageToISO6392(CString(p->m_value));
    00560
    00561                                 if(p->m_value.GetLength() == 3 && !lang.IsEmpty())
    00562                                 {
    00563                                         SetName(CStringW(lang));
    00564                                         SetProperty(L"LANG", p->m_value);
    00565                                 }
    00566                                 else if(!iso6392.IsEmpty())
    00567                                 {
    00568                                         SetName(p->m_value);
    00569                                         SetProperty(L"LANG", CStringW(iso6392));
    00570                                 }
    00571                                 else
    00572                                 {
    00573                                         SetName(p->m_value);
    00574                                         SetProperty(L"NAME", p->m_value);
    00575                                 }
    00576                         }
    00577
    00578                         m_pComments.AddTail(p);
    00579                 }
    00580         }
    00581         ASSERT(bs.getbits(1) == 1);
    00582 }
    00583
    00584 CStringW COggSplitterOutputPin::GetComment(CStringW key)
    00585 {
    00586         key.MakeUpper();
    00587         CList<CStringW> sl;
    00588         POSITION pos = m_pComments.GetHeadPosition();
    00589         while(pos)
    00590         {
    00591                 CComment* p = m_pComments.GetNext(pos);
    00592                 if(key == p->m_key) sl.AddTail(p->m_value);
    00593         }
    00594         return Implode(sl, ';');
    00595 }
    00596
    00597 void COggSplitterOutputPin::ResetState(DWORD seqnum)
    00598 {
    00599         CAutoLock csAutoLock(&m_csPackets);
    00600         m_packets.RemoveAll();
    00601         m_lastpacket.Free();
    00602         m_lastseqnum = seqnum;
    00603         m_rtLast = 0;
    00604         m_fSkip = true;
    00605 }
    00606
    00607 HRESULT COggSplitterOutputPin::UnpackPage(OggPage& page)
    00608 {
    00609         if(m_lastseqnum != page.m_hdr.page_sequence_number-1)
    00610         {
    00611                 ResetState(page.m_hdr.page_sequence_number);
    00612         }
    00613         else
    00614         {
    00615                 m_lastseqnum = page.m_hdr.page_sequence_number;
    00616         }
    00617
    00618         POSITION first = page.m_lens.GetHeadPosition();
    00619         while(first && page.m_lens.GetAt(first) == 255) page.m_lens.GetNext(first);
    00620         if(!first) first = page.m_lens.GetTailPosition();
    00621
    00622         POSITION last = page.m_lens.GetTailPosition();
    00623         while(last && page.m_lens.GetAt(last) == 255) page.m_lens.GetPrev(last);
    00624         if(!last) last = page.m_lens.GetTailPosition();
    00625
    00626         BYTE* pData = page.GetData();
    00627
    00628         int i = 0, j = 0, len = 0;
    00629
    00630     for(POSITION pos = page.m_lens.GetHeadPosition(); pos; page.m_lens.GetNext(pos))
    00631         {
    00632                 len = page.m_lens.GetAt(pos);
    00633                 j += len;
    00634
    00635                 if(len < 255 || pos == page.m_lens.GetTailPosition())
    00636                 {
    00637                         if(first == pos && (page.m_hdr.header_type_flag & OggPageHeader::continued))
    00638                         {
    00639 //                              ASSERT(m_lastpacket);
    00640
    00641                                 if(m_lastpacket)
    00642                                 {
    00643                                         int size = m_lastpacket->pData.GetSize();
    00644                                         m_lastpacket->pData.SetSize(size + j-i);
    00645                                         memcpy(m_lastpacket->pData.GetData() + size, pData + i, j-i);
    00646
    00647                                         CAutoLock csAutoLock(&m_csPackets);
    00648
    00649                                         if(len < 255) m_packets.AddTail(m_lastpacket);
    00650                                 }
    00651                         }
    00652                         else
    00653                         {
    00654                                 CAutoPtr<OggPacket> p(new OggPacket());
    00655
    00656                                 if(last == pos && page.m_hdr.granule_position != -1)
    00657                                 {
    00658                                         p->bDiscontinuity = m_fSkip;
    00659 REFERENCE_TIME rtLast = m_rtLast;
    00660                                         m_rtLast = GetRefTime(page.m_hdr.granule_position);
    00661 // some bad encodings have a +/-1 frame difference from the normal timeline,
    00662 // but these seem to cancel eachother out nicely so we can just ignore them
    00663 // to make it play a bit more smooth.
    00664 if(abs(rtLast - m_rtLast) == GetRefTime(1)) m_rtLast = rtLast; // FIXME
    00665                                         m_fSkip = false;
    00666                                 }
    00667
    00668                                 p->TrackNumber = page.m_hdr.bitstream_serial_number;
    00669
    00670                                 if(S_OK == UnpackPacket(p, pData + i, j-i))
    00671                                 {
    00672
    00673 if(p->TrackNumber == 0)
    00674 TRACE(_T("[%d]: %d, %I64d -> %I64d (skip=%d, disc=%d, sync=%d)\n"),
    00675                 (int)p->TrackNumber, p->pData.GetSize(), p->rtStart, p->rtStop,
    00676                 (int)m_fSkip, (int)p->bDiscontinuity, (int)p->bSyncPoint);
    00677
    00678                                         CAutoLock csAutoLock(&m_csPackets);
    00679
    00680                                         m_rtLast = p->rtStop;
    00681                                         p->fSkip = m_fSkip;
    00682
    00683                                         if(len < 255) m_packets.AddTail(p);
    00684                                         else m_lastpacket = p;
    00685                                 }
    00686                         }
    00687
    00688                         i = j;
    00689                 }
    00690         }
    00691
    00692         return S_OK;
    00693 }
    00694
    00695 CAutoPtr<OggPacket> COggSplitterOutputPin::GetPacket()
    00696 {
    00697         CAutoPtr<OggPacket> p;
    00698         CAutoLock csAutoLock(&m_csPackets);
    00699         if(m_packets.GetCount()) p = m_packets.RemoveHead();
    00700         return p;
    00701 }
    00702
    00703 HRESULT COggSplitterOutputPin::DeliverEndFlush()
    00704 {
    00705         ResetState();
    00706         return __super::DeliverEndFlush();
    00707 }
    00708
    00709 HRESULT COggSplitterOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
    00710 {
    00711         ResetState();
    00712         return __super::DeliverNewSegment(tStart, tStop, dRate);
    00713 }
    00714
    00715 //
    00716 // COggVorbisOutputPin
    00717 //
    00718
    00719 COggVorbisOutputPin::COggVorbisOutputPin(OggVorbisIdHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
    00720         : COggSplitterOutputPin(pName, pFilter, pLock, phr)
    00721 {
    00722         m_audio_sample_rate = h->audio_sample_rate;
    00723         m_blocksize[0] = 1<<h->blocksize_0;
    00724         m_blocksize[1] = 1<<h->blocksize_1;
    00725         m_lastblocksize = 0;
    00726
    00727         CMediaType mt;
    00728
    00729         mt.InitMediaType();
    00730         mt.majortype = MEDIATYPE_Audio;
    00731         mt.subtype = MEDIASUBTYPE_Vorbis;
    00732         mt.formattype = FORMAT_VorbisFormat;
    00733         VORBISFORMAT* vf = (VORBISFORMAT*)mt.AllocFormatBuffer(sizeof(VORBISFORMAT));
    00734         memset(mt.Format(), 0, mt.FormatLength());
    00735         vf->nChannels = h->audio_channels;
    00736         vf->nSamplesPerSec = h->audio_sample_rate;
    00737         vf->nAvgBitsPerSec = h->bitrate_nominal;
    00738         vf->nMinBitsPerSec = h->bitrate_minimum;
    00739         vf->nMaxBitsPerSec = h->bitrate_maximum;
    00740         vf->fQuality = -1;
    00741         mt.SetSampleSize(8192);
    00742         m_mts.Add(mt);
    00743
    00744         mt.InitMediaType();
    00745         mt.majortype = MEDIATYPE_Audio;
    00746         mt.subtype = MEDIASUBTYPE_Vorbis2;
    00747         mt.formattype = FORMAT_VorbisFormat2;
    00748         VORBISFORMAT2* vf2 = (VORBISFORMAT2*)mt.AllocFormatBuffer(sizeof(VORBISFORMAT2));
    00749         memset(mt.Format(), 0, mt.FormatLength());
    00750         vf2->Channels = h->audio_channels;
    00751         vf2->SamplesPerSec = h->audio_sample_rate;
    00752         mt.SetSampleSize(8192);
    00753         m_mts.InsertAt(0, mt);
    00754 }
    00755
    00756 REFERENCE_TIME COggVorbisOutputPin::GetRefTime(__int64 granule_position)
    00757 {
    00758         REFERENCE_TIME rt = granule_position * 10000000 / m_audio_sample_rate;
    00759         return rt;
    00760 }
    00761
    00762 HRESULT COggVorbisOutputPin::UnpackInitPage(OggPage& page)
    00763 {
    00764         HRESULT hr = __super::UnpackPage(page);
    00765
    00766         while(m_packets.GetCount())
    00767         {
    00768                 Packet* p = m_packets.GetHead();
    00769
    00770                 if(p->pData.GetCount() >= 6 && p->pData.GetData()[0] == 0x05)
    00771                 {
    00772                         // yeah, right, we are going to be parsing this backwards! :P
    00773                         bitstream bs(p->pData.GetData(), p->pData.GetCount(), true);
    00774                         while(bs.hasbits(-1) && bs.getbits(-1) != 1);
    00775                         for(int cnt = 0; bs.hasbits(-8-16-16-1-6); cnt++)
    00776                         {
    00777                                 unsigned int modes = bs.showbits(-6)+1;
    00778
    00779                                 unsigned int mapping = bs.getbits(-8);
    00780                                 unsigned int transformtype = bs.getbits(-16);
    00781                                 unsigned int windowtype = bs.getbits(-16);
    00782                                 unsigned int blockflag = bs.getbits(-1);
    00783
    00784                                 if(transformtype != 0 || windowtype != 0)
    00785                                 {
    00786                                         ASSERT(modes == cnt);
    00787                                         break;
    00788                                 }
    00789
    00790                                 m_blockflags.InsertAt(0, !!blockflag);
    00791                         }
    00792                 }
    00793
    00794                 int cnt = m_initpackets.GetCount();
    00795                 if(cnt <= 3)
    00796                 {
    00797                         ASSERT(p->pData.GetCount() >= 6 && p->pData.GetData()[0] == 1+cnt*2);
    00798                         VORBISFORMAT2* vf2 = (VORBISFORMAT2*)m_mts[0].Format();
    00799                         vf2->HeaderSize[cnt] = p->pData.GetSize();
    00800                         int len = m_mts[0].FormatLength();
    00801                         memcpy(
    00802                                 m_mts[0].ReallocFormatBuffer(len + p->pData.GetSize()) + len,
    00803                                 p->pData.GetData(), p->pData.GetSize());
    00804                 }
    00805
    00806                 m_initpackets.AddTail(m_packets.RemoveHead());
    00807         }
    00808
    00809         return hr;
    00810 }
    00811
    00812 HRESULT COggVorbisOutputPin::UnpackPacket(CAutoPtr<OggPacket>& p, BYTE* pData, int len)
    00813 {
    00814         if(len > 0 && m_blockflags.GetCount())
    00815         {
    00816                 bitstream bs(pData, len);
    00817                 if(bs.getbits(1) == 0)
    00818                 {
    00819                         int x = m_blockflags.GetCount()-1, n = 0;
    00820                         while(x) {n++; x >>= 1;}
    00821                         DWORD blocksize = m_blocksize[m_blockflags[bs.getbits(n)]?1:0];
    00822                         if(m_lastblocksize) m_rtLast += GetRefTime((m_lastblocksize + blocksize) >> 2);
    00823                         m_lastblocksize = blocksize;
    00824                 }
    00825         }
    00826
    00827         p->bSyncPoint = TRUE;
    00828         p->rtStart = m_rtLast;
    00829         p->rtStop = m_rtLast+1;
    00830         p->pData.SetSize(len);
    00831         memcpy(p->pData.GetData(), pData, len);
    00832
    00833         return S_OK;
    00834 }
    00835
    00836 HRESULT COggVorbisOutputPin::DeliverPacket(CAutoPtr<OggPacket> p)
    00837 {
    00838         if(p->pData.GetSize() > 0 && (p->pData.GetData()[0]&1))
    00839                 return S_OK;
    00840
    00841         return __super::DeliverPacket(p);
    00842 }
    00843
    00844 HRESULT COggVorbisOutputPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
    00845 {
    00846         HRESULT hr = __super::DeliverNewSegment(tStart, tStop, dRate);
    00847
    00848         m_lastblocksize = 0;
    00849
    00850         if(m_mt.subtype == MEDIASUBTYPE_Vorbis)
    00851         {
    00852                 POSITION pos = m_initpackets.GetHeadPosition();
    00853                 while(pos)
    00854                 {
    00855                         Packet* pi = m_initpackets.GetNext(pos);
    00856                         CAutoPtr<OggPacket> p(new OggPacket());
    00857                         p->TrackNumber = pi->TrackNumber;
    00858                         p->bDiscontinuity = p->bSyncPoint = TRUE;
    00859                         p->rtStart = p->rtStop = 0;
    00860                         p->pData.Copy(pi->pData);
    00861                         __super::DeliverPacket(p);
    00862                 }
    00863         }
    00864
    00865         return hr;
    00866 }
    00867
    00868 //
    00869 // COggDirectShowOutputPin
    00870 //
    00871
    00872 COggDirectShowOutputPin::COggDirectShowOutputPin(AM_MEDIA_TYPE* pmt, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
    00873         : COggSplitterOutputPin(pName, pFilter, pLock, phr)
    00874 {
    00875         CMediaType mt;
    00876         memcpy((AM_MEDIA_TYPE*)&mt, pmt, FIELD_OFFSET(AM_MEDIA_TYPE, pUnk));
    00877         mt.SetFormat((BYTE*)(pmt+1), pmt->cbFormat);
    00878         mt.SetSampleSize(1);
    00879         if(mt.majortype == MEDIATYPE_Video) // TODO: find samples for audio and find out what to return in GetRefTime...
    00880                 m_mts.Add(mt);
    00881 }
    00882
    00883 REFERENCE_TIME COggDirectShowOutputPin::GetRefTime(__int64 granule_position)
    00884 {
    00885         REFERENCE_TIME rt = 0;
    00886
    00887         if(m_mt.majortype == MEDIATYPE_Video)
    00888         {
    00889                 rt = granule_position * ((VIDEOINFOHEADER*)m_mt.Format())->AvgTimePerFrame;
    00890         }
    00891         else if(m_mt.majortype == MEDIATYPE_Audio)
    00892         {
    00893                 rt = granule_position; // ((WAVEFORMATEX*)m_mt.Format())-> // TODO
    00894         }
    00895
    00896         return rt;
    00897 }
    00898
    00899 HRESULT COggDirectShowOutputPin::UnpackPacket(CAutoPtr<OggPacket>& p, BYTE* pData, int len)
    00900 {
    00901         int i = 0;
    00902
    00903         BYTE hdr = pData[i++];
    00904
    00905         if(!(hdr&1))
    00906         {
    00907                 // TODO: verify if this was still present in the old format (haven't found one sample yet)
    00908                 BYTE nLenBytes = (hdr>>6)|((hdr&2)<<1);
    00909                 __int64 Length = 0;
    00910                 for(int j = 0; j < nLenBytes; j++)
    00911                         Length |= (__int64)pData[i++] << (j << 3);
    00912
    00913                 // TODO: if(len < i) {ASSERT(0); return E_FAIL;}
    00914
    00915                 p->bSyncPoint = !!(hdr&8);
    00916                 p->rtStart = m_rtLast;
    00917                 p->rtStop = m_rtLast + (nLenBytes ? GetRefTime(Length) : GetRefTime(1));
    00918                 p->pData.SetSize(len - i);
    00919                 memcpy(p->pData.GetData(), &pData[i], len - i);
    00920
    00921                 return S_OK;
    00922         }
    00923
    00924         return S_FALSE;
    00925 }
    00926
    00927 //
    00928 // COggStreamOutputPin
    00929 //
    00930
    00931 COggStreamOutputPin::COggStreamOutputPin(OggStreamHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
    00932         : COggSplitterOutputPin(pName, pFilter, pLock, phr)
    00933 {
    00934         m_time_unit = h->time_unit;
    00935         m_samples_per_unit = h->samples_per_unit;
    00936         m_default_len = h->default_len;
    00937 }
    00938
    00939 REFERENCE_TIME COggStreamOutputPin::GetRefTime(__int64 granule_position)
    00940 {
    00941         return granule_position * m_time_unit / m_samples_per_unit;
    00942 }
    00943
    00944 HRESULT COggStreamOutputPin::UnpackPacket(CAutoPtr<OggPacket>& p, BYTE* pData, int len)
    00945 {
    00946         int i = 0;
    00947
    00948         BYTE hdr = pData[i++];
    00949
    00950         if(!(hdr&1))
    00951         {
    00952                 BYTE nLenBytes = (hdr>>6)|((hdr&2)<<1);
    00953                 __int64 Length = 0;
    00954                 for(int j = 0; j < nLenBytes; j++)
    00955                         Length |= (__int64)pData[i++] << (j << 3);
    00956
    00957                 // TODO: if(len < i) {ASSERT(0); return E_FAIL;}
    00958
    00959                 p->bSyncPoint = !!(hdr&8);
    00960                 p->rtStart = m_rtLast;
    00961                 p->rtStop = m_rtLast + (nLenBytes ? GetRefTime(Length) : GetRefTime(m_default_len));
    00962                 p->pData.SetSize(len - i);
    00963                 memcpy(p->pData.GetData(), &pData[i], len - i);
    00964
    00965                 return S_OK;
    00966         }
    00967
    00968         return S_FALSE;
    00969 }
    00970
    00971 //
    00972 // COggVideoOutputPin
    00973 //
    00974
    00975 COggVideoOutputPin::COggVideoOutputPin(OggStreamHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
    00976         : COggStreamOutputPin(h, pName, pFilter, pLock, phr)
    00977 {
    00978         int extra = (int)h->size - sizeof(OggStreamHeader);
    00979         extra = max(extra, 0);
    00980
    00981         CMediaType mt;
    00982         mt.majortype = MEDIATYPE_Video;
    00983         mt.subtype = FOURCCMap(MAKEFOURCC(h->subtype[0],h->subtype[1],h->subtype[2],h->subtype[3]));
    00984         mt.formattype = FORMAT_VideoInfo;
    00985         VIDEOINFOHEADER* pvih = (VIDEOINFOHEADER*)mt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER) + extra);
    00986         memset(mt.Format(), 0, mt.FormatLength());
    00987         memcpy(mt.Format() + sizeof(VIDEOINFOHEADER), h+1, extra);
    00988         pvih->AvgTimePerFrame = h->time_unit / h->samples_per_unit;
    00989         pvih->bmiHeader.biWidth = h->v.w;
    00990         pvih->bmiHeader.biHeight = h->v.h;
    00991         pvih->bmiHeader.biBitCount = (WORD)h->bps;
    00992         pvih->bmiHeader.biCompression = mt.subtype.Data1;
    00993         switch(pvih->bmiHeader.biCompression)
    00994         {
    00995         case BI_RGB: case BI_BITFIELDS: mt.subtype =
    00996                 pvih->bmiHeader.biBitCount == 1 ? MEDIASUBTYPE_RGB1 :
    00997                 pvih->bmiHeader.biBitCount == 4 ? MEDIASUBTYPE_RGB4 :
    00998                 pvih->bmiHeader.biBitCount == 8 ? MEDIASUBTYPE_RGB8 :
    00999                 pvih->bmiHeader.biBitCount == 16 ? MEDIASUBTYPE_RGB565 :
    01000                 pvih->bmiHeader.biBitCount == 24 ? MEDIASUBTYPE_RGB24 :
    01001                 pvih->bmiHeader.biBitCount == 32 ? MEDIASUBTYPE_RGB32 :
    01002                 MEDIASUBTYPE_NULL;
    01003                 break;
    01004         case BI_RLE8: mt.subtype = MEDIASUBTYPE_RGB8; break;
    01005         case BI_RLE4: mt.subtype = MEDIASUBTYPE_RGB4; break;
    01006         }
    01007         mt.SetSampleSize(max(h->buffersize, 1));
    01008         m_mts.Add(mt);
    01009 }
    01010
    01011 //
    01012 // COggAudioOutputPin
    01013 //
    01014
    01015 COggAudioOutputPin::COggAudioOutputPin(OggStreamHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
    01016         : COggStreamOutputPin(h, pName, pFilter, pLock, phr)
    01017 {
    01018         int extra = (int)h->size - sizeof(OggStreamHeader);
    01019         extra = max(extra, 0);
    01020
    01021         CMediaType mt;
    01022         mt.majortype = MEDIATYPE_Audio;
    01023         mt.subtype = FOURCCMap(strtol(CStringA(h->subtype, 4), NULL, 16));
    01024         mt.formattype = FORMAT_WaveFormatEx;
    01025         WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.AllocFormatBuffer(sizeof(WAVEFORMATEX) + extra);
    01026         memset(mt.Format(), 0, mt.FormatLength());
    01027         memcpy(mt.Format() + sizeof(WAVEFORMATEX), h+1, extra);
    01028         wfe->cbSize = extra;
    01029         wfe->wFormatTag = (WORD)mt.subtype.Data1;
    01030         wfe->nChannels = h->a.nChannels;
    01031         wfe->nSamplesPerSec = (DWORD)(10000000i64 * h->samples_per_unit / h->time_unit);
    01032         wfe->wBitsPerSample = (WORD)h->bps;
    01033         wfe->nAvgBytesPerSec = h->a.nAvgBytesPerSec; // TODO: verify for PCM
    01034         wfe->nBlockAlign = h->a.nBlockAlign; // TODO: verify for PCM
    01035         mt.SetSampleSize(max(h->buffersize, 1));
    01036         m_mts.Add(mt);
    01037 }
    01038
    01039 //
    01040 // COggTextOutputPin
    01041 //
    01042
    01043 COggTextOutputPin::COggTextOutputPin(OggStreamHeader* h, LPCWSTR pName, CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr)
    01044         : COggStreamOutputPin(h, pName, pFilter, pLock, phr)
    01045 {
    01046         CMediaType mt;
    01047         mt.majortype = MEDIATYPE_Text;
    01048         mt.subtype = MEDIASUBTYPE_NULL;
    01049         mt.formattype = FORMAT_None;
    01050         mt.SetSampleSize(1);
    01051         m_mts.Add(mt);
    01052 }