]> git.vomp.tv Git - vompclient.git/blob - dssourcepin.cc
Fixes for w2k
[vompclient.git] / dssourcepin.cc
1 /*\r
2     Copyright 2004-2005 Chris Tallon\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 */\r
20 #include "dssourcepin.h"\r
21 #include "dssourcefilter.h"\r
22 #include <Dvdmedia.h>\r
23 #include <mmreg.h>\r
24 \r
25 \r
26 class DsSFEnumMediaTypes: public IEnumMediaTypes {
27 public:
28   DsSFEnumMediaTypes(DsSourcePin* papa,ULONG pos=0);
29   virtual ~DsSFEnumMediaTypes();
30   virtual HRESULT STDMETHODCALLTYPE Next(ULONG nummedia,  AM_MEDIA_TYPE **pins,ULONG *fetched);\r
31   virtual HRESULT STDMETHODCALLTYPE Skip(ULONG numpin);\r
32   virtual HRESULT STDMETHODCALLTYPE Reset();\r
33   virtual HRESULT STDMETHODCALLTYPE Clone(IEnumMediaTypes **enuma);\r
34   virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id,void ** object);
35   virtual ULONG STDMETHODCALLTYPE AddRef();\r
36     virtual ULONG STDMETHODCALLTYPE Release();\r
37 protected:\r
38   DsSourcePin* parent;\r
39   ULONG curpos;
40   volatile long refs;\r
41 };\r
42 \r
43 DsSFEnumMediaTypes::DsSFEnumMediaTypes(DsSourcePin* papa,ULONG pos){\r
44   parent=papa;\r
45   curpos=pos;\r
46   parent->AddRef();\r
47   refs=0;\r
48 }\r
49 \r
50 DsSFEnumMediaTypes::~DsSFEnumMediaTypes(){
51   parent->Release();
52 }
53 \r
54 HRESULT STDMETHODCALLTYPE DsSFEnumMediaTypes::Next(ULONG numpin, AM_MEDIA_TYPE **pins,ULONG *fetched) {
55   int i;
56
57   if (pins==NULL) return E_POINTER;
58   if (numpin!=1 && fetched==NULL) return E_INVALIDARG;
59   *fetched=0;
60
61   for (i=0;(i<numpin);i++) {
62     pins[i]=(AM_MEDIA_TYPE*)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
63     if (parent->GetMediaType(curpos+i,pins[i])!=S_OK) return S_FALSE;
64     curpos++;
65     if (fetched!=NULL)  (*fetched)++;
66   }
67   return S_OK;
68 }
69 \r
70 HRESULT STDMETHODCALLTYPE   DsSFEnumMediaTypes::Skip(ULONG numpin){\r
71   curpos+=numpin;\r
72   if (curpos>1) return S_FALSE;\r
73   return S_OK;\r
74 }\r
75 HRESULT STDMETHODCALLTYPE DsSFEnumMediaTypes::Reset(){\r
76   curpos=0;\r
77   return S_OK;\r
78 }\r
79 HRESULT STDMETHODCALLTYPE DsSFEnumMediaTypes::Clone(IEnumMediaTypes **enuma){\r
80   if (enuma==NULL) return E_POINTER;\r
81   *enuma=new DsSFEnumMediaTypes(parent,curpos);\r
82   (*enuma)->AddRef();\r
83   return S_OK;\r
84 }\r
85 \r
86 HRESULT STDMETHODCALLTYPE  DsSFEnumMediaTypes::QueryInterface(REFIID id,void ** object){
87   if (object==NULL) return E_POINTER;
88   if (id==IID_IUnknown ||id == IID_IEnumMediaTypes) {
89     *object=(IUnknown*)this;
90     ((IUnknown*)object)->AddRef();
91     return NOERROR;
92   } else {
93     *object=NULL;
94     return E_NOINTERFACE;
95   }
96 }
97
98
99 \r
100 ULONG STDMETHODCALLTYPE DsSFEnumMediaTypes::AddRef(){\r
101   InterlockedIncrement(&refs);\r
102   long tempref=refs;\r
103   if (tempref>1) return tempref;\r
104   else return 1;\r
105 }\r
106 \r
107 ULONG STDMETHODCALLTYPE DsSFEnumMediaTypes::Release(){\r
108   long tempref=InterlockedDecrement(&refs);\r
109 \r
110   if (tempref==0) {\r
111     refs++;\r
112     delete this;\r
113     return NULL;\r
114   } else {\r
115     if (tempref>1) return tempref;\r
116     else return 1;\r
117   }\r
118 }\r
119 \r
120 \r
121 void CopyMType(AM_MEDIA_TYPE* dest,const AM_MEDIA_TYPE*source) {\r
122   memcpy(dest,source,sizeof(AM_MEDIA_TYPE));\r
123   if (source->pbFormat!=NULL) {\r
124     dest->pbFormat=(BYTE*)CoTaskMemAlloc(dest->cbFormat);\r
125     memcpy(dest->pbFormat,source->pbFormat,dest->cbFormat);\r
126   }\r
127 }\r
128 void ReleaseMType(AM_MEDIA_TYPE* free) {\r
129   if (free->cbFormat!=NULL) CoTaskMemFree(free->pbFormat);\r
130   free->pbFormat=NULL;\r
131 }\r
132 \r
133 DsSourcePin::DsSourcePin(DsSourceFilter *pFilter,\r
134              HRESULT *phr,LPCWSTR pName,bool audio)\r
135 {\r
136   isaudiopin=audio;\r
137   m_pFilter=pFilter;\r
138   connected=NULL;\r
139   connectedinput=NULL;\r
140   allocator=NULL;\r
141 \r
142 \r
143 \r
144 }\r
145 \r
146 DsSourcePin::~DsSourcePin()\r
147 {\r
148 \r
149 \r
150 \r
151 }\r
152 \r
153 HRESULT STDMETHODCALLTYPE DsSourcePin::QueryInterface(REFIID id,void ** object){
154   if (object==NULL) return E_POINTER;
155   if (id==IID_IUnknown) {
156     *object=(IUnknown*)this;
157     ((IUnknown*)object)->AddRef();
158     return NOERROR;
159   } else if (id==IID_IPin) {
160     *object=(IPin*)this;
161     ((IPin*)object)->AddRef();
162     return NOERROR;
163   } else {
164     *object=NULL;
165     return E_NOINTERFACE;
166   }
167 }
168
169 ULONG STDMETHODCALLTYPE DsSourcePin::AddRef(){\r
170   return m_pFilter->AddRef();\r
171 \r
172 }\r
173 ULONG STDMETHODCALLTYPE DsSourcePin::Release(){\r
174   return m_pFilter->Release();\r
175 }\r
176 \r
177 \r
178 /*IPin*/\r
179 HRESULT STDMETHODCALLTYPE DsSourcePin::Connect(IPin *pinempf,const AM_MEDIA_TYPE *mtype) {\r
180   if (pinempf==NULL) return E_POINTER;\r
181   EnterCriticalSection(&m_pFilter->filterlock);\r
182 \r
183   if (connected!=NULL) {LeaveCriticalSection(&m_pFilter->filterlock);return VFW_E_ALREADY_CONNECTED;}\r
184   if (m_pFilter->mystate!=State_Stopped) {LeaveCriticalSection(&m_pFilter->filterlock);return VFW_E_NOT_STOPPED;}\r
185 \r
186 \r
187   bool gotmt=false;\r
188 \r
189   if (mtype!=NULL) {\r
190     if (CheckMediaType(mtype)==S_OK){\r
191       pinempf->AddRef();\r
192       if (pinempf->ReceiveConnection((IPin*)this,mtype)==S_OK) {\r
193         CopyMType(&medtype,mtype);\r
194         LeaveCriticalSection(&m_pFilter->filterlock);\r
195       } else {\r
196         LeaveCriticalSection(&m_pFilter->filterlock);\r
197         CoTaskMemFree(mtype->pbFormat);\r
198         pinempf->Release();\r
199         return VFW_E_TYPE_NOT_ACCEPTED;\r
200       }\r
201 \r
202     } else {\r
203       LeaveCriticalSection(&m_pFilter->filterlock);\r
204       CoTaskMemFree(mtype->pbFormat);\r
205       return VFW_E_TYPE_NOT_ACCEPTED;\r
206     }\r
207     CoTaskMemFree(mtype->pbFormat);\r
208   }else {\r
209     IEnumMediaTypes * emt;\r
210     EnumMediaTypes(&emt);\r
211     AM_MEDIA_TYPE emtype_np;\r
212     AM_MEDIA_TYPE  * emtype;\r
213     emtype=&emtype_np;\r
214     ULONG fetched=0;\r
215     pinempf->AddRef();\r
216     while (emt->Next(1,&emtype,&fetched)==S_OK) {\r
217       if (CheckMediaType(emtype)==S_OK){\r
218         if (pinempf->ReceiveConnection((IPin*)this,emtype)==S_OK) {\r
219           connected=pinempf;\r
220           CopyMType(&medtype,emtype);\r
221           CoTaskMemFree(emtype->pbFormat);\r
222           gotmt=true;\r
223           break;\r
224         }\r
225 \r
226       }\r
227       CoTaskMemFree(emtype->pbFormat);\r
228     }\r
229     emt->Release();\r
230     if (gotmt==false) {\r
231       pinempf->EnumMediaTypes(&emt);\r
232       while (emt->Next(1,&emtype,&fetched)==S_OK) {\r
233         if (CheckMediaType(emtype)==S_OK){\r
234           if (pinempf->ReceiveConnection((IPin*)this,emtype)==S_OK) {\r
235             connected=pinempf;\r
236             CopyMType(&medtype,emtype);\r
237             CoTaskMemFree(emtype->pbFormat);\r
238             gotmt=true;\r
239             break;\r
240           }\r
241 \r
242         }\r
243         CoTaskMemFree(emtype->pbFormat);\r
244       }\r
245       emt->Release();\r
246       if (gotmt==false) {\r
247         pinempf->Release();\r
248         LeaveCriticalSection(&m_pFilter->filterlock);\r
249         return VFW_E_NO_ACCEPTABLE_TYPES;\r
250       }\r
251     }\r
252   }\r
253 \r
254   if (pinempf->QueryInterface(IID_IMemInputPin,(void**)&connectedinput)!=S_OK) {\r
255     LeaveCriticalSection(&m_pFilter->filterlock);\r
256     connected->Release();\r
257     connected=NULL;\r
258   /*  connectedinput->Release();\r
259     connectedinput=NULL;*/\r
260     return VFW_E_NO_TRANSPORT;\r
261   }\r
262   ALLOCATOR_PROPERTIES eigenall;\r
263   ZeroMemory(&eigenall,sizeof(eigenall));\r
264   connectedinput->GetAllocatorRequirements(&eigenall);\r
265   if (eigenall.cbAlign==0) eigenall.cbAlign=1;\r
266   connectedinput->GetAllocator(&allocator);\r
267   if (DecideBufferSize(allocator,&eigenall)==S_OK) {\r
268     if (connectedinput->NotifyAllocator(allocator,FALSE)==S_OK){\r
269       connected=pinempf;\r
270       LeaveCriticalSection(&m_pFilter->filterlock);\r
271       return S_OK;\r
272     }\r
273   }\r
274   if (allocator!=NULL) allocator->Release();\r
275   allocator=NULL;\r
276 \r
277   if (CoCreateInstance(CLSID_MemoryAllocator,0,CLSCTX_INPROC_SERVER,\r
278     IID_IMemAllocator,(void **)allocator)==S_OK) {\r
279       if (DecideBufferSize(allocator,&eigenall)==S_OK) {\r
280         if (connectedinput->NotifyAllocator(allocator,FALSE)==S_OK){\r
281           connected=pinempf;\r
282           LeaveCriticalSection(&m_pFilter->filterlock);\r
283         return S_OK;\r
284       }\r
285     }\r
286   }\r
287   if (allocator!=NULL) allocator->Release();\r
288   allocator=NULL;\r
289   connected->Release();\r
290   connected=NULL;\r
291   connectedinput->Release();\r
292   connectedinput=NULL;\r
293   LeaveCriticalSection(&m_pFilter->filterlock);\r
294   return VFW_E_NO_TRANSPORT;\r
295 \r
296 \r
297 \r
298 \r
299 }\r
300 HRESULT STDMETHODCALLTYPE DsSourcePin::ReceiveConnection(IPin *connect,\r
301                              const AM_MEDIA_TYPE *mtype){\r
302      return VFW_E_TYPE_NOT_ACCEPTED; //We have only output pins\r
303 }\r
304  HRESULT STDMETHODCALLTYPE DsSourcePin::Disconnect() {\r
305    EnterCriticalSection(&m_pFilter->filterlock);\r
306    if (connected!=NULL) {\r
307      if (m_pFilter->mystate!=State_Stopped) {LeaveCriticalSection(&m_pFilter->filterlock);return VFW_E_NOT_STOPPED;}\r
308      /*TODO: Decommit allocator*/\r
309      allocator->Decommit();\r
310      allocator->Release();\r
311      allocator=NULL;\r
312      ReleaseMType(&medtype);\r
313      connectedinput->Release();\r
314      connectedinput=NULL;\r
315      connected->Release();\r
316      connected=NULL;\r
317      LeaveCriticalSection(&m_pFilter->filterlock);\r
318      return S_OK;\r
319    }\r
320    LeaveCriticalSection(&m_pFilter->filterlock);\r
321    return S_FALSE;\r
322  }\r
323 \r
324  HRESULT STDMETHODCALLTYPE DsSourcePin::ConnectedTo(IPin **pin){\r
325    if (pin==NULL) return E_POINTER;\r
326    IPin* pinn=connected;\r
327    *pin=pinn;\r
328    if (pinn!=NULL) {\r
329      pinn->AddRef();\r
330      return S_OK;\r
331    } else {\r
332      return VFW_E_NOT_CONNECTED;\r
333    }\r
334  }\r
335  HRESULT STDMETHODCALLTYPE  DsSourcePin::ConnectionMediaType(AM_MEDIA_TYPE *mtype){\r
336    if (mtype==NULL) return E_POINTER;\r
337    if (connected!=NULL){\r
338     CopyMType(mtype,&medtype);\r
339     return S_OK;\r
340    } else {\r
341      ZeroMemory(mtype,sizeof(mtype));\r
342      return VFW_E_NOT_CONNECTED;\r
343    }\r
344  }\r
345  HRESULT STDMETHODCALLTYPE DsSourcePin::QueryPinInfo(PIN_INFO *info){\r
346    if (info==NULL) return E_POINTER;\r
347    info->dir=PINDIR_OUTPUT;\r
348    info->pFilter=(IBaseFilter*)m_pFilter;\r
349    if (m_pFilter) m_pFilter->AddRef();\r
350    if (isaudiopin) wcscpy(info->achName,L"Audio");\r
351    else wcscpy(info->achName,L"Video");\r
352    return S_OK;\r
353  }\r
354 \r
355  HRESULT STDMETHODCALLTYPE  DsSourcePin::QueryDirection(PIN_DIRECTION *dir){\r
356    if (dir==NULL) return E_POINTER;\r
357    *dir=PINDIR_OUTPUT;\r
358    return S_OK;\r
359  }\r
360  HRESULT STDMETHODCALLTYPE DsSourcePin::QueryId(LPWSTR *id){\r
361    if (id==NULL) return E_POINTER;\r
362    *id=(LPWSTR)CoTaskMemAlloc(12);\r
363    if (*id==NULL) return E_OUTOFMEMORY;\r
364 \r
365 \r
366    if (isaudiopin) wcscpy(*id,L"Audio");\r
367     else  wcscpy(*id, L"Video");\r
368     return S_OK;\r
369  }\r
370  HRESULT STDMETHODCALLTYPE  DsSourcePin::QueryAccept(const AM_MEDIA_TYPE *mtype) {\r
371    if (mtype==NULL) return S_FALSE;\r
372    if (CheckMediaType(mtype)==S_OK) return S_OK;\r
373    else return S_FALSE;\r
374  }\r
375  HRESULT STDMETHODCALLTYPE DsSourcePin::EnumMediaTypes(IEnumMediaTypes **enuma){\r
376 if (enuma==NULL) return E_POINTER;
377   *enuma=new DsSFEnumMediaTypes( this);
378   (*enuma)->AddRef();
379   return S_OK;
380 }\r
381 \r
382 HRESULT STDMETHODCALLTYPE DsSourcePin::QueryInternalConnections(IPin **pin,ULONG *numpin){\r
383   return E_NOTIMPL;\r
384 }\r
385 HRESULT STDMETHODCALLTYPE DsSourcePin::EndOfStream(){\r
386   return E_UNEXPECTED; //we are a output pin!\r
387 }\r
388 \r
389 HRESULT STDMETHODCALLTYPE DsSourcePin::NewSegment(REFERENCE_TIME start,REFERENCE_TIME stop,double rate){\r
390   return E_UNEXPECTED;//we are a output pin!\r
391 }\r
392 \r
393 HRESULT DsSourcePin::getCurrentMediaSample(IMediaSample**ms){\r
394   if (allocator!=NULL) return allocator->GetBuffer(ms,NULL,NULL,0);\r
395   else return E_NOINTERFACE;\r
396 }\r
397 \r
398 HRESULT DsSourcePin::deliver(IMediaSample * ms){\r
399   //EnterCriticalSection(&m_pFilter->filterlock);\r
400   HRESULT hres;\r
401   if (connectedinput!=NULL)hres= connectedinput->Receive(ms);\r
402   else hres= VFW_E_NOT_CONNECTED;\r
403   //LeaveCriticalSection(&m_pFilter->filterlock);\r
404   return hres;\r
405 \r
406 }\r
407 \r
408 HRESULT DsSourcePin::GetMediaType(int iPosition, AM_MEDIA_TYPE *pmt)\r
409 {\r
410   HRESULT hr;\r
411 \r
412   if (isaudiopin){\r
413     if (iPosition==0) {\r
414       ZeroMemory(pmt,sizeof(*pmt));\r
415       pmt->lSampleSize = 1;\r
416       pmt->bFixedSizeSamples = TRUE;\r
417       pmt->majortype=MEDIATYPE_Audio;\r
418       MPEG1WAVEFORMAT wfe;\r
419       ZeroMemory(&wfe,sizeof(wfe));\r
420       wfe.wfx.cbSize=22;\r
421       wfe.wfx.nSamplesPerSec=48000;\r
422       wfe.wfx.nChannels=2;\r
423       wfe.wfx.nAvgBytesPerSec=32000;\r
424       wfe.wfx.nBlockAlign=768;\r
425       wfe.wfx.wFormatTag=WAVE_FORMAT_MPEG;\r
426       wfe.fwHeadLayer=2;\r
427       wfe.dwHeadBitrate=256000;\r
428       wfe.fwHeadMode=ACM_MPEG_STEREO;\r
429       wfe.fwHeadModeExt=1;\r
430       wfe.wHeadEmphasis=1;\r
431       wfe.fwHeadFlags=ACM_MPEG_ID_MPEG1 |ACM_MPEG_ORIGINALHOME | ACM_MPEG_PROTECTIONBIT;\r
432       pmt->subtype=MEDIASUBTYPE_MPEG2_AUDIO;\r
433       pmt->formattype=FORMAT_WaveFormatEx;\r
434       pmt->cbFormat=sizeof(wfe);\r
435       pmt->pbFormat=(BYTE*)CoTaskMemAlloc(sizeof(wfe));\r
436       memcpy(pmt->pbFormat,&wfe,sizeof(wfe));\r
437       pmt->lSampleSize=0;\r
438       hr=S_OK;\r
439 \r
440 \r
441         } else  {\r
442       hr=VFW_S_NO_MORE_ITEMS ;\r
443     }\r
444   } else {\r
445     if (iPosition == 0) {\r
446       ZeroMemory(pmt,sizeof(*pmt));\r
447       pmt->lSampleSize = 1;\r
448       pmt->bFixedSizeSamples = TRUE;\r
449       pmt->majortype=MEDIATYPE_Video;\r
450       hr=S_OK;\r
451       pmt->subtype=MEDIASUBTYPE_MPEG2_VIDEO;\r
452             pmt->formattype=FORMAT_MPEG2Video;\r
453 \r
454             MPEG2VIDEOINFO hdr;\r
455             ZeroMemory(&hdr,sizeof(hdr));\r
456             hdr.dwProfile=AM_MPEG2Profile_Main;\r
457             hdr.dwLevel=AM_MPEG2Level_Main;\r
458             hdr.hdr.bmiHeader.biSize = sizeof(hdr.hdr.bmiHeader);\r
459             hdr.hdr.bmiHeader.biWidth = 720;\r
460             hdr.hdr.bmiHeader.biHeight = 568;\r
461       pmt->cbFormat=sizeof(hdr);\r
462       pmt->pbFormat=(BYTE*)CoTaskMemAlloc(sizeof(hdr));\r
463       memcpy(pmt->pbFormat,&hdr,sizeof(hdr));\r
464 \r
465     } else {\r
466       hr=VFW_S_NO_MORE_ITEMS;\r
467     }\r
468   }\r
469   return hr ;\r
470 }\r
471 \r
472 HRESULT DsSourcePin::Inactive() {\r
473   if (allocator!=NULL) return allocator->Decommit();\r
474   return VFW_E_NO_ALLOCATOR;\r
475 }\r
476 \r
477 HRESULT DsSourcePin::Active() {\r
478   if (allocator!=NULL) return allocator->Commit();\r
479   return VFW_E_NO_ALLOCATOR;\r
480 }\r
481 \r
482
483 HRESULT DsSourcePin::Run(REFERENCE_TIME reftime){\r
484   return NOERROR;\r
485 }\r
486 \r
487 // No description\r
488 HRESULT DsSourcePin::CheckMediaType(const AM_MEDIA_TYPE *pmt)\r
489 {\r
490     HRESULT res;\r
491 \r
492     if (isaudiopin) {\r
493         bool subtype=false;\r
494 #if 0 /* For future demands ac3 */\r
495     subtype=pmt->subtype==(MEDIASUBTYPE_DOLBY_AC3);\r
496 #endif\r
497     subtype=(pmt->subtype==(MEDIASUBTYPE_MPEG2_AUDIO));\r
498         if (pmt->majortype==MEDIATYPE_Audio && subtype) {\r
499       res = S_OK ;\r
500         } else {\r
501             res = S_FALSE ;\r
502         }\r
503     } else {\r
504         if (pmt->majortype==MEDIATYPE_Video &&\r
505                   pmt-> subtype==MEDIASUBTYPE_MPEG2_VIDEO) {\r
506       res = S_OK ;\r
507         } else {\r
508             res = S_FALSE ;\r
509         }\r
510     }\r
511     return res;\r
512 }\r
513 \r
514 HRESULT DsSourcePin::DecideBufferSize(IMemAllocator *pa,ALLOCATOR_PROPERTIES *all_pp){\r
515   HRESULT hr;\r
516 \r
517     if (pa==NULL)return E_POINTER;\r
518     if (all_pp==NULL) return E_POINTER;\r
519   if (isaudiopin) {\r
520     if (all_pp->cBuffers*all_pp->cbBuffer < 300*64*1024)\r
521     {\r
522       //all_pp->cBuffers = 300;//old\r
523       all_pp->cBuffers = 10;\r
524       all_pp->cbBuffer = 64*1024;\r
525     }\r
526   } else {\r
527     if (all_pp->cBuffers*all_pp->cbBuffer < 300*64*1024)\r
528     {\r
529       //all_pp->cBuffers = 300;//old\r
530       all_pp->cBuffers = 30;\r
531       all_pp->cbBuffer = 64*1024;\r
532     }\r
533   }\r
534 \r
535     ALLOCATOR_PROPERTIES all_pp_cur;\r
536     hr =pa->SetProperties(all_pp,&all_pp_cur);\r
537     if (FAILED(hr))\r
538     {\r
539         return hr;\r
540     }\r
541     if (all_pp_cur.cbBuffer*all_pp_cur.cBuffers < all_pp->cBuffers*all_pp->cbBuffer)\r
542     {\r
543         return E_FAIL;\r
544     }\r
545 \r
546     return S_OK;\r
547 }\r
548 \r