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 }

Generated on Tue Dec 13 14:47:21 2005 for guliverkli by  doxygen 1.4.5