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