- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# DSMMuxer.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 "DSMMuxer.h"
    00024 #include "..\..\..\DSUtil\DSUtil.h"
    00025
    00026 #include <initguid.h>
    00027 #include <qnetwork.h>
    00028 #include "..\..\..\..\include\moreuuids.h"
    00029
    00030 #ifdef REGISTER_FILTER
    00031
    00032 const AMOVIESETUP_MEDIATYPE sudPinTypesOut[] =
    00033 {
    00034         {&MEDIATYPE_Stream, &MEDIASUBTYPE_DirectShowMedia}
    00035 };
    00036
    00037 const AMOVIESETUP_PIN sudpPins[] =
    00038 {
    00039     {L"Input", FALSE, FALSE, FALSE, TRUE, &CLSID_NULL, NULL, 0, NULL},
    00040     {L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesOut), sudPinTypesOut}
    00041 };
    00042
    00043 const AMOVIESETUP_FILTER sudFilter[] =
    00044 {
    00045         {&__uuidof(CDSMMuxerFilter), L"DSM Muxer", MERIT_DO_NOT_USE, countof(sudpPins), sudpPins}
    00046 };
    00047
    00048 CFactoryTemplate g_Templates[] =
    00049 {
    00050         {sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<CDSMMuxerFilter>, NULL, &sudFilter[0]}
    00051 };
    00052
    00053 int g_cTemplates = countof(g_Templates);
    00054
    00055 STDAPI DllRegisterServer()
    00056 {
    00057         return AMovieDllRegisterServer2(TRUE);
    00058 }
    00059
    00060 STDAPI DllUnregisterServer()
    00061 {
    00062         return AMovieDllRegisterServer2(FALSE);
    00063 }
    00064
    00065 extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
    00066
    00067 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
    00068 {
    00069     return DllEntryPoint((HINSTANCE)hModule, dwReason, 0); // "DllMain" of the dshow baseclasses;
    00070 }
    00071
    00072 #endif
    00073
    00074 template<typename T> static T myabs(T n) {return n >= 0 ? n : -n;}
    00075
    00076 static int GetByteLength(UINT64 data, int min = 0)
    00077 {
    00078         int i = 7;
    00079         while(i >= min && ((BYTE*)&data)[i] == 0) i--;
    00080         return ++i;
    00081 }
    00082
    00083 //
    00084 // CDSMMuxerFilter
    00085 //
    00086
    00087 CDSMMuxerFilter::CDSMMuxerFilter(LPUNKNOWN pUnk, HRESULT* phr, bool fAutoChap, bool fAutoRes)
    00088         : CBaseMuxerFilter(pUnk, phr, __uuidof(this))
    00089         , m_fAutoChap(fAutoChap)
    00090         , m_fAutoRes(fAutoRes)
    00091 {
    00092         if(phr) *phr = S_OK;
    00093 }
    00094
    00095 CDSMMuxerFilter::~CDSMMuxerFilter()
    00096 {
    00097 }
    00098
    00099 void CDSMMuxerFilter::MuxPacketHeader(IBitStream* pBS, dsmp_t type, UINT64 len)
    00100 {
    00101         ASSERT(type < 32);
    00102
    00103         int i = GetByteLength(len, 1);
    00104
    00105         pBS->BitWrite(DSMSW, DSMSW_SIZE<<3);
    00106         pBS->BitWrite(type, 5);
    00107         pBS->BitWrite(i-1, 3);
    00108         pBS->BitWrite(len, i<<3);
    00109 }
    00110
    00111 void CDSMMuxerFilter::MuxFileInfo(IBitStream* pBS)
    00112 {
    00113         int len = 1;
    00114         CSimpleMap<CStringA, CStringA> si;
    00115
    00116         for(int i = 0; i < GetSize(); i++)
    00117         {
    00118                 CStringA key = CStringA(CString(GetKeyAt(i))), value = UTF16To8(GetValueAt(i));
    00119                 if(key.GetLength() != 4) continue;
    00120                 si.Add(key, value);
    00121                 len += 4 + value.GetLength() + 1;
    00122         }
    00123
    00124         MuxPacketHeader(pBS, DSMP_FILEINFO, len);
    00125         pBS->BitWrite(DSMF_VERSION, 8);
    00126         for(int i = 0; i < si.GetSize(); i++)
    00127         {
    00128                 CStringA key = si.GetKeyAt(i), value = si.GetValueAt(i);
    00129                 pBS->ByteWrite((LPCSTR)key, 4);
    00130                 pBS->ByteWrite((LPCSTR)value, value.GetLength()+1);
    00131         }
    00132
    00133 }
    00134
    00135 void CDSMMuxerFilter::MuxStreamInfo(IBitStream* pBS, CBaseMuxerInputPin* pPin)
    00136 {
    00137         int len = 1;
    00138         CSimpleMap<CStringA, CStringA> si;
    00139
    00140         for(int i = 0; i < pPin->GetSize(); i++)
    00141         {
    00142                 CStringA key = CStringA(CString(pPin->GetKeyAt(i))), value = UTF16To8(pPin->GetValueAt(i));
    00143                 if(key.GetLength() != 4) continue;
    00144                 si.Add(key, value);
    00145                 len += 4 + value.GetLength() + 1;
    00146         }
    00147
    00148         if(len > 1)
    00149         {
    00150                 MuxPacketHeader(pBS, DSMP_STREAMINFO, len);
    00151                 pBS->BitWrite(pPin->GetID(), 8);
    00152                 for(int i = 0; i < si.GetSize(); i++)
    00153                 {
    00154                         CStringA key = si.GetKeyAt(i), value = si.GetValueAt(i);
    00155                         pBS->ByteWrite((LPCSTR)key, 4);
    00156                         pBS->ByteWrite((LPCSTR)value, value.GetLength()+1);
    00157                 }
    00158         }
    00159 }
    00160
    00161 void CDSMMuxerFilter::MuxInit()
    00162 {
    00163         m_sps.RemoveAll();
    00164         m_isps.RemoveAll();
    00165         m_rtPrevSyncPoint = _I64_MIN;
    00166 }
    00167
    00168 void CDSMMuxerFilter::MuxHeader(IBitStream* pBS)
    00169 {
    00170         CString muxer;
    00171         muxer.Format(_T("DSM Muxer (%s)"), CString(__TIMESTAMP__));
    00172
    00173         SetProperty(L"MUXR", CStringW(muxer));
    00174         SetProperty(L"DATE", CStringW(CTime::GetCurrentTime().FormatGmt(_T("%Y-%m-%d %H:%M:%S"))));
    00175
    00176         MuxFileInfo(pBS);
    00177
    00178         POSITION pos = m_pPins.GetHeadPosition();
    00179         while(pos)
    00180         {
    00181                 CBaseMuxerInputPin* pPin = m_pPins.GetNext(pos);
    00182                 const CMediaType& mt = pPin->CurrentMediaType();
    00183
    00184                 ASSERT((mt.lSampleSize >> 30) == 0); // you don't need >1GB samples, do you?
    00185
    00186                 MuxPacketHeader(pBS, DSMP_MEDIATYPE, 5 + sizeof(GUID)*3 + mt.FormatLength());
    00187                 pBS->BitWrite(pPin->GetID(), 8);
    00188                 pBS->ByteWrite(&mt.majortype, sizeof(mt.majortype));
    00189                 pBS->ByteWrite(&mt.subtype, sizeof(mt.subtype));
    00190                 pBS->BitWrite(mt.bFixedSizeSamples, 1);
    00191                 pBS->BitWrite(mt.bTemporalCompression, 1);
    00192                 pBS->BitWrite(mt.lSampleSize, 30);
    00193                 pBS->ByteWrite(&mt.formattype, sizeof(mt.formattype));
    00194                 pBS->ByteWrite(mt.Format(), mt.FormatLength());
    00195
    00196                 MuxStreamInfo(pBS, pPin);
    00197         }
    00198
    00199         // resources & chapters
    00200
    00201         CInterfaceList<IDSMResourceBag> pRBs;
    00202         pRBs.AddTail(this);
    00203
    00204         CComQIPtr<IDSMChapterBag> pCB = (IUnknown*)(INonDelegatingUnknown*)this;
    00205
    00206         pos = m_pPins.GetHeadPosition();
    00207         while(pos)
    00208         {
    00209                 for(CComPtr<IPin> pPin = m_pPins.GetNext(pos)->GetConnected(); pPin; pPin = GetUpStreamPin(GetFilterFromPin(pPin)))
    00210                 {
    00211                         if(m_fAutoRes)
    00212                         {
    00213                                 CComQIPtr<IDSMResourceBag> pPB = GetFilterFromPin(pPin);
    00214                                 if(pPB && !pRBs.Find(pPB)) pRBs.AddTail(pPB);
    00215                         }
    00216
    00217                         if(m_fAutoChap)
    00218                         {
    00219                                 if(!pCB || pCB->ChapGetCount() == 0) pCB = GetFilterFromPin(pPin);
    00220                         }
    00221                 }
    00222         }
    00223
    00224         // resources
    00225
    00226         pos = pRBs.GetHeadPosition();
    00227         while(pos)
    00228         {
    00229                 IDSMResourceBag* pRB = pRBs.GetNext(pos);
    00230
    00231                 for(DWORD i = 0, j = pRB->ResGetCount(); i < j; i++)
    00232                 {
    00233                         CComBSTR name, desc, mime;
    00234                         BYTE* pData = NULL;
    00235                         DWORD len = 0;
    00236                         if(SUCCEEDED(pRB->ResGet(i, &name, &desc, &mime, &pData, &len, NULL)))
    00237                         {
    00238                                 CStringA utf8_name = UTF16To8(name);
    00239                                 CStringA utf8_desc = UTF16To8(desc);
    00240                                 CStringA utf8_mime = UTF16To8(mime);
    00241
    00242                                 MuxPacketHeader(pBS, DSMP_RESOURCE,
    00243                                         1 +
    00244                                         utf8_name.GetLength()+1 +
    00245                                         utf8_desc.GetLength()+1 +
    00246                                         utf8_mime.GetLength()+1 +
    00247                                         len);
    00248
    00249                                 pBS->BitWrite(0, 2);
    00250                                 pBS->BitWrite(0, 6); // reserved
    00251                                 pBS->ByteWrite(utf8_name, utf8_name.GetLength()+1);
    00252                                 pBS->ByteWrite(utf8_desc, utf8_desc.GetLength()+1);
    00253                                 pBS->ByteWrite(utf8_mime, utf8_mime.GetLength()+1);
    00254                                 pBS->ByteWrite(pData, len);
    00255
    00256                                 CoTaskMemFree(pData);
    00257                         }
    00258                 }
    00259         }
    00260
    00261         // chapters
    00262
    00263         if(pCB)
    00264         {
    00265                 CList<CDSMChapter> chapters;
    00266                 REFERENCE_TIME rtPrev = 0;
    00267                 int len = 0;
    00268
    00269                 pCB->ChapSort();
    00270
    00271                 for(DWORD i = 0; i < pCB->ChapGetCount(); i++)
    00272                 {
    00273                         CDSMChapter c;
    00274                         CComBSTR name;
    00275                         if(SUCCEEDED(pCB->ChapGet(i, &c.rt, &name)))
    00276                         {
    00277                                 REFERENCE_TIME rtDiff = c.rt - rtPrev; rtPrev = c.rt; c.rt = rtDiff;
    00278                                 c.name = name;
    00279                                 len += 1 + GetByteLength(myabs(c.rt)) + UTF16To8(c.name).GetLength()+1;
    00280                                 chapters.AddTail(c);
    00281                         }
    00282                 }
    00283
    00284                 if(chapters.GetCount())
    00285                 {
    00286                         MuxPacketHeader(pBS, DSMP_CHAPTERS, len);
    00287
    00288                         pos = chapters.GetHeadPosition();
    00289                         while(pos)
    00290                         {
    00291                                 CDSMChapter& c = chapters.GetNext(pos);
    00292                                 CStringA name = UTF16To8(c.name);
    00293                                 int irt = GetByteLength(myabs(c.rt));
    00294                                 pBS->BitWrite(c.rt < 0, 1);
    00295                                 pBS->BitWrite(irt, 3);
    00296                                 pBS->BitWrite(0, 4);
    00297                                 pBS->BitWrite(myabs(c.rt), irt<<3);
    00298                                 pBS->ByteWrite((LPCSTR)name, name.GetLength()+1);
    00299                         }
    00300                 }
    00301         }
    00302 }
    00303
    00304 void CDSMMuxerFilter::MuxPacket(IBitStream* pBS, const MuxerPacket* pPacket)
    00305 {
    00306         if(pPacket->IsEOS())
    00307                 return;
    00308
    00309         if(pPacket->pPin->CurrentMediaType().majortype == MEDIATYPE_Text)
    00310         {
    00311                 CStringA str((char*)pPacket->pData.GetData(), pPacket->pData.GetCount());
    00312                 str.Replace("\xff", " ");
    00313                 str.Replace("&nbsp;", " ");
    00314                 str.Replace("&nbsp", " ");
    00315                 str.Trim();
    00316                 if(str.IsEmpty())
    00317                         return;
    00318         }
    00319
    00320         ASSERT(!pPacket->IsSyncPoint() || pPacket->IsTimeValid());
    00321
    00322         REFERENCE_TIME rtTimeStamp = _I64_MIN, rtDuration = 0;
    00323         int iTimeStamp = 0, iDuration = 0;
    00324
    00325         if(pPacket->IsTimeValid())
    00326         {
    00327                 rtTimeStamp = pPacket->rtStart;
    00328                 rtDuration = max(pPacket->rtStop - pPacket->rtStart, 0);
    00329
    00330                 iTimeStamp = GetByteLength(myabs(rtTimeStamp));
    00331                 ASSERT(iTimeStamp <= 7);
    00332
    00333                 iDuration = GetByteLength(rtDuration);
    00334                 ASSERT(iDuration <= 7);
    00335
    00336                 IndexSyncPoint(pPacket, pBS->GetPos());
    00337         }
    00338
    00339         int len = 2 + iTimeStamp + iDuration + pPacket->pData.GetCount(); // id + flags + data
    00340
    00341         MuxPacketHeader(pBS, DSMP_SAMPLE, len);
    00342         pBS->BitWrite(pPacket->pPin->GetID(), 8);
    00343         pBS->BitWrite(pPacket->IsSyncPoint(), 1);
    00344         pBS->BitWrite(rtTimeStamp < 0, 1);
    00345         pBS->BitWrite(iTimeStamp, 3);
    00346         pBS->BitWrite(iDuration, 3);
    00347         pBS->BitWrite(myabs(rtTimeStamp), iTimeStamp<<3);
    00348         pBS->BitWrite(rtDuration, iDuration<<3);
    00349         pBS->ByteWrite(pPacket->pData.GetData(), pPacket->pData.GetCount());
    00350 }
    00351
    00352 void CDSMMuxerFilter::MuxFooter(IBitStream* pBS)
    00353 {
    00354         // syncpoints
    00355
    00356         int len = 0;
    00357         CList<IndexedSyncPoint> isps;
    00358         REFERENCE_TIME rtPrev = 0, rt;
    00359         UINT64 fpPrev = 0, fp;
    00360
    00361         POSITION pos = m_isps.GetHeadPosition();
    00362         while(pos)
    00363         {
    00364                 IndexedSyncPoint& isp = m_isps.GetNext(pos);
    00365                 TRACE(_T("sp[%d]: %I64d %I64x\n"), isp.id, isp.rt, isp.fp);
    00366
    00367                 rt = isp.rt - rtPrev; rtPrev = isp.rt;
    00368                 fp = isp.fp - fpPrev; fpPrev = isp.fp;
    00369
    00370                 IndexedSyncPoint isp2;
    00371                 isp2.fp = fp;
    00372                 isp2.rt = rt;
    00373                 isps.AddTail(isp2);
    00374
    00375                 len += 1 + GetByteLength(myabs(rt)) + GetByteLength(fp); // flags + rt + fp
    00376         }
    00377
    00378         MuxPacketHeader(pBS, DSMP_SYNCPOINTS, len);
    00379
    00380         pos = isps.GetHeadPosition();
    00381         while(pos)
    00382         {
    00383                 IndexedSyncPoint& isp = isps.GetNext(pos);
    00384
    00385                 int irt = GetByteLength(myabs(isp.rt));
    00386                 int ifp = GetByteLength(isp.fp);
    00387
    00388                 pBS->BitWrite(isp.rt < 0, 1);
    00389                 pBS->BitWrite(irt, 3);
    00390                 pBS->BitWrite(ifp, 3);
    00391                 pBS->BitWrite(0, 1); // reserved
    00392                 pBS->BitWrite(myabs(isp.rt), irt<<3);
    00393                 pBS->BitWrite(isp.fp, ifp<<3);
    00394         }
    00395 }
    00396
    00397 void CDSMMuxerFilter::IndexSyncPoint(const MuxerPacket* p, __int64 fp)
    00398 {
    00399         // Yes, this is as complicated as it looks.
    00400         // Rule #1: don't write this packet if you can't do it reliably.
    00401         // (think about overlapped subtitles, line1: 0->10, line2: 1->9)
    00402
    00403         // FIXME: the very last syncpoints won't get moved to m_isps because there are no more syncpoints to trigger it!
    00404
    00405         if(fp < 0 || !p || !p->IsTimeValid() || !p->IsSyncPoint())
    00406                 return;
    00407
    00408         ASSERT(p->rtStart >= m_rtPrevSyncPoint);
    00409         m_rtPrevSyncPoint = p->rtStart;
    00410
    00411         SyncPoint sp;
    00412         sp.id = p->pPin->GetID();
    00413         sp.rtStart = p->rtStart;
    00414         sp.rtStop = p->pPin->IsSubtitleStream() ? p->rtStop : _I64_MAX;
    00415         sp.fp = fp;
    00416
    00417         {
    00418                 SyncPoint& head = !m_sps.IsEmpty() ? m_sps.GetHead() : sp;
    00419                 SyncPoint& tail = !m_sps.IsEmpty() ? m_sps.GetTail() : sp;
    00420                 REFERENCE_TIME rtfp = !m_isps.IsEmpty() ? m_isps.GetTail().rtfp : _I64_MIN;
    00421
    00422                 if(head.rtStart > rtfp + 1000000) // 100ms limit, just in case every stream had only keyframes, then sycnpoints would be too frequent
    00423                 {
    00424                         IndexedSyncPoint isp;
    00425                         isp.id = head.id;
    00426                         isp.rt = tail.rtStart;
    00427                         isp.rtfp = head.rtStart;
    00428                         isp.fp = head.fp;
    00429                         m_isps.AddTail(isp);
    00430                 }
    00431         }
    00432
    00433         POSITION pos = m_sps.GetHeadPosition();
    00434         while(pos)
    00435         {
    00436                 POSITION cur = pos;
    00437                 SyncPoint& sp2 = m_sps.GetNext(pos);
    00438                 if(sp2.id == sp.id && sp2.rtStop <= sp.rtStop || sp2.rtStop <= sp.rtStart)
    00439                         m_sps.RemoveAt(cur);
    00440         }
    00441
    00442         m_sps.AddTail(sp);
    00443 }