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 }

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