AVI2AC3Filter.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 <atlbase.h>
00024 #include "avi2ac3filter.h"
00025 #include "..\..\..\DSUtil\DSUtil.h"
00026 
00027 #include <initguid.h>
00028 #include "..\..\..\..\include\moreuuids.h"
00029 
00030 #ifdef REGISTER_FILTER
00031 
00032 const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] =
00033 {
00034         {&MEDIATYPE_Audio, &MEDIASUBTYPE_WAVE_DOLBY_AC3},
00035         {&MEDIATYPE_Audio, &MEDIASUBTYPE_WAVE_DTS},
00036 };
00037 
00038 const AMOVIESETUP_MEDIATYPE sudPinTypesOut[] =
00039 {
00040         {&MEDIATYPE_Audio, &MEDIASUBTYPE_NULL},
00041 };
00042 
00043 const AMOVIESETUP_PIN sudpPins[] =
00044 {
00045     {L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesIn), sudPinTypesIn},
00046     {L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesOut), sudPinTypesOut}
00047 };
00048 
00049 const AMOVIESETUP_FILTER sudFilter[] =
00050 {
00051         {&__uuidof(CAVI2AC3Filter), L"AVI<->AC3/DTS", MERIT_UNLIKELY, countof(sudpPins), sudpPins}
00052 };
00053 
00054 CFactoryTemplate g_Templates[] =
00055 {
00056     {sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<CAVI2AC3Filter>, NULL, &sudFilter[0]}
00057 };
00058 
00059 int g_cTemplates = countof(g_Templates);
00060 
00061 STDAPI DllRegisterServer()
00062 {
00063         return AMovieDllRegisterServer2(TRUE);
00064 }
00065 
00066 STDAPI DllUnregisterServer()
00067 {
00068         return AMovieDllRegisterServer2(FALSE);
00069 }
00070 
00071 extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
00072 
00073 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
00074 {
00075     return DllEntryPoint((HINSTANCE)hModule, dwReason, 0); // "DllMain" of the dshow baseclasses;
00076 }
00077 
00078 #endif
00079 
00080 //
00081 // CAVI2AC3Filter
00082 //
00083 
00084 CAVI2AC3Filter::CAVI2AC3Filter(LPUNKNOWN lpunk, HRESULT* phr)
00085         : CTransformFilter(NAME("CAVI2AC3Filter"), lpunk, __uuidof(this))       
00086 {
00087         if(phr) *phr = S_OK;
00088 }
00089 
00090 CAVI2AC3Filter::~CAVI2AC3Filter()
00091 {
00092 }
00093 
00094 HRESULT CAVI2AC3Filter::Transform(IMediaSample* pSample, IMediaSample* pOutSample)
00095 {
00096         HRESULT hr;
00097 
00098         BYTE* pIn = NULL;
00099         if(FAILED(hr = pSample->GetPointer(&pIn))) return hr;
00100         BYTE* pInOrg = pIn;
00101 
00102         long len = pSample->GetActualDataLength();
00103         if(len <= 0) return S_FALSE;
00104 
00105         BYTE* pOut = NULL;
00106         if(FAILED(hr = pOutSample->GetPointer(&pOut))) return hr;
00107         BYTE* pOutOrg = pOut;
00108 
00109         int size = pOutSample->GetSize();
00110 
00111         if((CheckAC3(&m_pInput->CurrentMediaType()) || CheckDTS(&m_pInput->CurrentMediaType()))
00112         && (CheckWAVEAC3(&m_pOutput->CurrentMediaType()) || CheckWAVEDTS(&m_pOutput->CurrentMediaType())))
00113         {
00114                 if(*(DWORD*)pIn == 0xBA010000)
00115                 {
00116                         pIn += 14;
00117                 }
00118 
00119                 if(*(DWORD*)pIn == 0xBD010000)
00120                 {
00121                         pIn += 8 + 1 + pIn[8] + 1 + 3;
00122                 }
00123 
00124                 len -= (pInOrg - pIn);
00125 
00126                 if(size < len) return E_FAIL;
00127 
00128                 memcpy(pOut, pIn, len);
00129                 pOut += len;
00130         }
00131         else if((CheckWAVEAC3(&m_pInput->CurrentMediaType()) || CheckWAVEDTS(&m_pInput->CurrentMediaType()))
00132         && (CheckAC3(&m_pOutput->CurrentMediaType()) || CheckDTS(&m_pOutput->CurrentMediaType())))
00133         {
00134                 if((m_pOutput->CurrentMediaType().majortype == MEDIATYPE_DVD_ENCRYPTED_PACK
00135                         || m_pOutput->CurrentMediaType().majortype == MEDIATYPE_MPEG2_PES)
00136                 && (len + 12 + 3) >= 0x10000) // damn, this can happen if the interleave time is too big
00137                 {
00138                         REFERENCE_TIME rtStart = 0, rtStop = 1;
00139                         bool fHasTime = (S_OK == pSample->GetTime(&rtStart, &rtStop));
00140 
00141                         bool fDiscontinuity = (S_OK == pOutSample->IsDiscontinuity());
00142 
00143                         int pos = 0;
00144                         while(pos < len)
00145                         {
00146                                 int curlen = min(len - pos, 2013);
00147                                 pos += 2013;
00148 
00149                                 CComPtr<IMediaSample> pOutSample;
00150                                 hr = InitializeOutputSample(pSample, &pOutSample);
00151 
00152                                 if(fDiscontinuity)
00153                                 {
00154                                         if(fHasTime)
00155                                         {
00156                                                 rtStop = rtStart + (rtStop - rtStart) * curlen / len;
00157                                                 pOutSample->SetTime(&rtStart, &rtStop);
00158                                         }
00159 
00160                                         fDiscontinuity = false;
00161                                 }
00162                                 else
00163                                 {
00164                                         pOutSample->SetTime(NULL, NULL);
00165                                         pOutSample->SetDiscontinuity(FALSE);
00166                                 }
00167 
00168                                 BYTE* pOut = NULL;
00169                                 if(FAILED(hr = pOutSample->GetPointer(&pOut))) return hr;
00170                                 BYTE* pOutOrg = pOut;
00171 
00172                                 int size = pOutSample->GetSize();
00173 
00174                                 const GUID* majortype = &m_pOutput->CurrentMediaType().majortype;
00175                                 const GUID* subtype = &m_pOutput->CurrentMediaType().subtype;
00176 
00177                                 if(*majortype == MEDIATYPE_DVD_ENCRYPTED_PACK)
00178                                 {
00179                                         if(size < curlen + 32 + 3) return E_FAIL;
00180 
00181                                         BYTE PESHeader[] = 
00182                                         {
00183                                                 0x00,0x00,0x01,0xBA,                    // PES id
00184                                                 0x44,0x00,0x04,0x00,0x04,0x01,  // SCR (0)
00185                                                 0x01,0x89,0xC3,0xF8,                    // mux rate (1260000 bytes/sec, 22bits), marker (2bits), reserved (~0, 5bits), stuffing (0, 3bits)
00186                                         };
00187 
00188                                         memcpy(pOut, &PESHeader, sizeof(PESHeader));
00189                                         pOut += sizeof(PESHeader);
00190 
00191                                         majortype = &MEDIATYPE_MPEG2_PES;
00192                                 }
00193 
00194                                 if(*majortype == MEDIATYPE_MPEG2_PES)
00195                                 {
00196                                         if(size < curlen + 20 + 3) return E_FAIL;
00197 
00198                                         BYTE Private1Header[] = 
00199                                         {
00200                                                 0x00,0x00,0x01,0xBD,                    // private stream 1 id
00201                                                 0x07,0xEC,                                              // packet length (TODO: modify it later)
00202                                                 0x81,0x80,                                              // marker, original, PTS - flags
00203                                                 0x08,                                                   // packet data starting offset
00204                                                 0x21,0x00,0x01,0x00,0x01,               // PTS (0)
00205                                                 0xFF,0xFF,0xFF,                                 // stuffing
00206                                                 0x80,                                                   // stream id (0)
00207                                                 0x01,0x00,0x01,                                 // no idea about the first byte, the sencond+third seem to show the ac3/dts header sync offset plus one (dvd2avi doesn't output it to the ac3/dts file so we have to put it back)
00208                                         };
00209 
00210                                         int packetlen = curlen + 12 + 3;
00211                                         ASSERT(packetlen <= 0xffff);
00212                                         Private1Header[4] = (packetlen>>8)&0xff;
00213                                         Private1Header[5] = packetlen&0xff;
00214 
00215                                         if(*subtype == MEDIASUBTYPE_DTS)
00216                                         {
00217                                                 Private1Header[17] += 8;
00218                                         }
00219 
00220                                         if(*subtype == MEDIASUBTYPE_DOLBY_AC3)
00221                                         {
00222                                                 for(int i = 0; i < curlen; i++)
00223                                                 {
00224                                                         if(*(DWORD*)&pIn[i] == 0x770B)
00225                                                         {
00226                                                                 i++;
00227                                                                 Private1Header[19] = (i>>8)&0xff;
00228                                                                 Private1Header[20] = i&0xff;
00229                                                                 break;
00230                                                         }
00231                                                 }
00232                                         }
00233                                         else if(*subtype == MEDIASUBTYPE_DTS)
00234                                         {
00235                                                 for(int i = 0; i < curlen; i++)
00236                                                 {
00237                                                         if(*(DWORD*)&pIn[i] == 0x0180FE7F)
00238                                                         {
00239                                                                 i++;
00240                                                                 Private1Header[19] = (i>>8)&0xff;
00241                                                                 Private1Header[20] = i&0xff;
00242                                                                 break;
00243                                                         }
00244                                                 }
00245                                         }
00246 
00247                                         memcpy(pOut, &Private1Header, sizeof(Private1Header));
00248                                         pOut += sizeof(Private1Header);
00249 
00250                                         majortype = &MEDIATYPE_Audio;
00251                                 }
00252 
00253                                 if(*majortype == MEDIATYPE_Audio)
00254                                 {
00255                                         if(size < curlen) return E_FAIL;
00256                                         memcpy(pOut, pIn, curlen);
00257                                         pIn += curlen;
00258                                         pOut += curlen;
00259                                 }
00260 
00261                                 pOutSample->SetActualDataLength(pOut - pOutOrg);
00262 
00263                                 hr = m_pOutput->Deliver(pOutSample);
00264                         }
00265 
00266                         return S_FALSE;
00267                 }
00268                 else // phew, we can do the easier way
00269                 {
00270                         const GUID* majortype = &m_pOutput->CurrentMediaType().majortype;
00271                         const GUID* subtype = &m_pOutput->CurrentMediaType().subtype;
00272 
00273                         if(*majortype == MEDIATYPE_DVD_ENCRYPTED_PACK)
00274                         {
00275                                 if(size < len + 32 + 3) return E_FAIL;
00276 
00277                                 BYTE PESHeader[] = 
00278                                 {
00279                                         0x00,0x00,0x01,0xBA,                    // PES id
00280                                         0x44,0x00,0x04,0x00,0x04,0x01,  // SCR (0)
00281                                         0x01,0x89,0xC3,0xF8,                    // mux rate (1260000 bytes/sec, 22bits), marker (2bits), reserved (~0, 5bits), stuffing (0, 3bits)
00282                                 };
00283 
00284                                 memcpy(pOut, &PESHeader, sizeof(PESHeader));
00285                                 pOut += sizeof(PESHeader);
00286 
00287                                 majortype = &MEDIATYPE_MPEG2_PES;
00288                         }
00289 
00290                         if(*majortype == MEDIATYPE_MPEG2_PES)
00291                         {
00292                                 if(size < len + 20 + 3) return E_FAIL;
00293 
00294                                 BYTE Private1Header[] = 
00295                                 {
00296                                         0x00,0x00,0x01,0xBD,                    // private stream 1 id
00297                                         0x07,0xEC,                                              // packet length (TODO: modify it later)
00298                                         0x81,0x80,                                              // marker, original, PTS - flags
00299                                         0x08,                                                   // packet data starting offset
00300                                         0x21,0x00,0x01,0x00,0x01,               // PTS (0)
00301                                         0xFF,0xFF,0xFF,                                 // stuffing
00302                                         0x80,                                                   // stream id (0)
00303                                         0x01,0x00,0x01,                                 // no idea about the first byte, the sencond+third seem to show the ac3/dts header sync offset plus one (dvd2avi doesn't output it to the ac3/dts file so we have to put it back)
00304                                 };
00305 
00306                                 int packetlen = len + 12 + 3;
00307                                 ASSERT(packetlen <= 0xffff);
00308                                 Private1Header[4] = (packetlen>>8)&0xff;
00309                                 Private1Header[5] = packetlen&0xff;
00310 
00311                                 if(*subtype == MEDIASUBTYPE_DTS)
00312                                 {
00313                                         Private1Header[17] += 8;
00314                                 }
00315 
00316                                 memcpy(pOut, &Private1Header, sizeof(Private1Header));
00317                                 pOut += sizeof(Private1Header);
00318 
00319                                 majortype = &MEDIATYPE_Audio;
00320                         }
00321 
00322                         if(*majortype == MEDIATYPE_Audio)
00323                         {
00324                                 if(size < len) return E_FAIL;
00325 
00326                                 memcpy(pOut, pIn, len);
00327                                 pIn += len;
00328                                 pOut += len;
00329                         }
00330                 }
00331         }
00332         else
00333         {
00334                 return E_FAIL;
00335         }
00336 
00337         pOutSample->SetActualDataLength(pOut - pOutOrg);
00338 
00339         return S_OK;
00340 }
00341 
00342 bool CAVI2AC3Filter::CheckAC3(const CMediaType* pmt)
00343 {
00344         return (pmt->majortype == MEDIATYPE_Audio
00345                         || pmt->majortype == MEDIATYPE_MPEG2_PES 
00346                         || pmt->majortype == MEDIATYPE_DVD_ENCRYPTED_PACK)
00347                 && pmt->subtype == MEDIASUBTYPE_DOLBY_AC3;
00348 }
00349 
00350 bool CAVI2AC3Filter::CheckDTS(const CMediaType* pmt)
00351 {
00352         return (pmt->majortype == MEDIATYPE_Audio
00353                         || pmt->majortype == MEDIATYPE_MPEG2_PES 
00354                         || pmt->majortype == MEDIATYPE_DVD_ENCRYPTED_PACK)
00355                 && pmt->subtype == MEDIASUBTYPE_DTS;
00356 }
00357 
00358 bool CAVI2AC3Filter::CheckWAVEAC3(const CMediaType* pmt)
00359 {
00360         return pmt->majortype == MEDIATYPE_Audio
00361                 && pmt->subtype == MEDIASUBTYPE_WAVE_DOLBY_AC3
00362                 && pmt->formattype == FORMAT_WaveFormatEx
00363                 && ((WAVEFORMATEX*)pmt->pbFormat)->wFormatTag == WAVE_FORMAT_DOLBY_AC3;
00364 }
00365 
00366 bool CAVI2AC3Filter::CheckWAVEDTS(const CMediaType* pmt)
00367 {
00368         return pmt->majortype == MEDIATYPE_Audio
00369                 && pmt->subtype == MEDIASUBTYPE_WAVE_DTS
00370                 && pmt->formattype == FORMAT_WaveFormatEx
00371                 && ((WAVEFORMATEX*)pmt->pbFormat)->wFormatTag == WAVE_FORMAT_DVD_DTS;
00372 }
00373 
00374 HRESULT CAVI2AC3Filter::CheckInputType(const CMediaType* mtIn)
00375 {
00376         bool fWaveFormatEx = !!(mtIn->formattype == FORMAT_WaveFormatEx);
00377 
00378         return CheckAC3(mtIn) && fWaveFormatEx || CheckDTS(mtIn) && fWaveFormatEx
00379                 || CheckWAVEAC3(mtIn) || CheckWAVEDTS(mtIn)
00380                 ? S_OK
00381                 : VFW_E_TYPE_NOT_ACCEPTED;
00382 }
00383 
00384 HRESULT CAVI2AC3Filter::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut)
00385 {
00386         return CheckAC3(mtIn) && CheckWAVEAC3(mtOut)
00387                 || CheckWAVEAC3(mtIn) && CheckAC3(mtOut)
00388                 || CheckDTS(mtIn) && CheckWAVEDTS(mtOut)
00389                 || CheckWAVEDTS(mtIn) && CheckDTS(mtOut)
00390                 ? S_OK
00391                 : VFW_E_TYPE_NOT_ACCEPTED;
00392 }
00393 
00394 HRESULT CAVI2AC3Filter::DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties)
00395 {
00396         if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED;
00397 
00398         CComPtr<IMemAllocator> pAllocatorIn;
00399         m_pInput->GetAllocator(&pAllocatorIn);
00400         if(!pAllocatorIn) return E_UNEXPECTED;
00401 
00402         pAllocatorIn->GetProperties(pProperties);
00403 
00404         pProperties->cBuffers = 2;
00405         pProperties->cbBuffer = max(pProperties->cbBuffer, 1024*1024); // this should be enough...
00406         pProperties->cbAlign = 1;
00407         pProperties->cbPrefix = 0;
00408 
00409         HRESULT hr;
00410         ALLOCATOR_PROPERTIES Actual;
00411     if(FAILED(hr = pAllocator->SetProperties(pProperties, &Actual))) 
00412                 return hr;
00413 
00414     return(pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer
00415                 ? E_FAIL
00416                 : NOERROR);
00417 }
00418 
00419 HRESULT CAVI2AC3Filter::GetMediaType(int iPosition, CMediaType* pMediaType)
00420 {
00421     if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED;
00422 
00423         const GUID& majortype = m_pInput->CurrentMediaType().majortype;
00424         const GUID& subtype = m_pInput->CurrentMediaType().subtype;
00425         
00426         if(CheckAC3(&m_pInput->CurrentMediaType()) || CheckDTS(&m_pInput->CurrentMediaType()))
00427         {
00428                 if(iPosition < 0) return E_INVALIDARG;
00429                 if(iPosition > 0) return VFW_S_NO_MORE_ITEMS;
00430 
00431                 pMediaType->majortype = MEDIATYPE_Audio;
00432 
00433                 pMediaType->formattype = FORMAT_WaveFormatEx;
00434                 WAVEFORMATEX* wfe = (WAVEFORMATEX*)pMediaType->AllocFormatBuffer(sizeof(WAVEFORMATEX));
00435                 memset(wfe, 0, sizeof(WAVEFORMATEX));
00436                 wfe->cbSize = sizeof(WAVEFORMATEX);
00437                 wfe->nAvgBytesPerSec = ((WAVEFORMATEX*)m_pInput->CurrentMediaType().pbFormat)->nAvgBytesPerSec;
00438                 wfe->nSamplesPerSec = ((WAVEFORMATEX*)m_pInput->CurrentMediaType().pbFormat)->nSamplesPerSec;
00439                 wfe->wBitsPerSample = ((WAVEFORMATEX*)m_pInput->CurrentMediaType().pbFormat)->wBitsPerSample;
00440                 wfe->nChannels = 2;
00441                 wfe->nBlockAlign = 1;
00442 
00443                 if(subtype == MEDIASUBTYPE_DOLBY_AC3)
00444                 {
00445                         pMediaType->subtype = MEDIASUBTYPE_WAVE_DOLBY_AC3;
00446                         wfe->wFormatTag = WAVE_FORMAT_DOLBY_AC3;
00447                 }
00448                 else if(subtype == MEDIASUBTYPE_DTS)
00449                 {
00450                         pMediaType->subtype = MEDIASUBTYPE_WAVE_DTS;
00451                         wfe->wFormatTag = WAVE_FORMAT_DVD_DTS;
00452                 }
00453                 else
00454                 {
00455                         return E_INVALIDARG;
00456                 }
00457         }
00458         else if(CheckWAVEAC3(&m_pInput->CurrentMediaType()) || CheckWAVEDTS(&m_pInput->CurrentMediaType()))
00459         {
00460                 if(iPosition < 0) return E_INVALIDARG;
00461                 if(iPosition > 4) return VFW_S_NO_MORE_ITEMS;
00462 
00463                 if(subtype == MEDIASUBTYPE_WAVE_DOLBY_AC3)
00464                 {
00465                         pMediaType->subtype = MEDIASUBTYPE_DOLBY_AC3;
00466 
00467                         pMediaType->formattype = FORMAT_WaveFormatEx;
00468                         DOLBYAC3WAVEFORMAT* wfe = (DOLBYAC3WAVEFORMAT*)pMediaType->AllocFormatBuffer(sizeof(DOLBYAC3WAVEFORMAT));
00469                         memset(wfe, 0, sizeof(DOLBYAC3WAVEFORMAT));
00470                         // unfortunately we can't tell what we are going to get in transform, 
00471                         // so we just set the most common values and hope that the ac3 decoder 
00472                         // is flexible enough (it is usually :) to find out these from the bitstream
00473                         wfe->wfx.cbSize = sizeof(DOLBYAC3WAVEFORMAT) - sizeof(WAVEFORMATEX);
00474                         wfe->wfx.wFormatTag = WAVE_FORMAT_DOLBY_AC3;
00475                         wfe->wfx.nSamplesPerSec = 48000;
00476                         wfe->wfx.nChannels = 6;
00477                         wfe->bBigEndian = TRUE;
00478                 }
00479                 else if(subtype == MEDIASUBTYPE_WAVE_DTS)
00480                 {
00481                         pMediaType->subtype = MEDIASUBTYPE_DTS;
00482 
00483                         pMediaType->formattype = FORMAT_WaveFormatEx;
00484                         WAVEFORMATEX* wfe = (WAVEFORMATEX*)pMediaType->AllocFormatBuffer(sizeof(WAVEFORMATEX));
00485                         memset(wfe, 0, sizeof(WAVEFORMATEX));
00486                         // same case as with ac3, but this time we don't even know the structure
00487                         wfe->cbSize = sizeof(WAVEFORMATEX);
00488                         wfe->wFormatTag = WAVE_FORMAT_PCM;
00489                         wfe->nSamplesPerSec = 48000;
00490                         wfe->nChannels = 6;
00491                 }
00492                 else
00493                 {
00494                         return E_INVALIDARG;
00495                 }
00496 
00497                 switch(iPosition)
00498                 {
00499                 case 0:
00500                         pMediaType->majortype = MEDIATYPE_Audio;
00501                         break;
00502                 case 1:
00503                         pMediaType->ResetFormatBuffer();
00504                         pMediaType->formattype = FORMAT_None;
00505                 case 2:
00506                         pMediaType->majortype = MEDIATYPE_MPEG2_PES;
00507                         break;
00508                 case 3:
00509                         pMediaType->ResetFormatBuffer();
00510                         pMediaType->formattype = FORMAT_None;
00511                 case 4:
00512                         pMediaType->majortype = MEDIATYPE_DVD_ENCRYPTED_PACK;
00513                         break;
00514                 default:
00515                         return E_INVALIDARG;
00516                 }
00517         }
00518         else
00519         {
00520                 return VFW_S_NO_MORE_ITEMS;
00521         }
00522 
00523         return S_OK;
00524 }

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