]> git.sesse.net Git - vlc/blob - modules/access/bda/bdagraph.cpp
Merge branch 'master' into lpcm_encoder
[vlc] / modules / access / bda / bdagraph.cpp
1 /*****************************************************************************
2  * bdagraph.cpp : DirectShow BDA graph for vlc
3  *****************************************************************************
4  * Copyright( C ) 2007 the VideoLAN team
5  *
6  * Author: Ken Self <kenself(at)optusnet(dot)com(dot)au>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  *( at your option ) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #include "bdagraph.h"
27 #include <ctype.h>
28
29 /****************************************************************************
30  * Interfaces for calls from C
31  ****************************************************************************/
32 extern "C" {
33
34     void dvb_newBDAGraph( access_t* p_access )
35     {
36         p_access->p_sys->p_bda_module = new BDAGraph( p_access );
37     };
38
39     void dvb_deleteBDAGraph( access_t* p_access )
40     {
41         delete p_access->p_sys->p_bda_module;
42     };
43
44     int dvb_SubmitCQAMTuneRequest( access_t* p_access )
45     {
46         if( p_access->p_sys->p_bda_module )
47             return p_access->p_sys->p_bda_module->SubmitCQAMTuneRequest();
48         return VLC_EGENERIC;
49     };
50
51     int dvb_SubmitATSCTuneRequest( access_t* p_access )
52     {
53         if( p_access->p_sys->p_bda_module )
54             return p_access->p_sys->p_bda_module->SubmitATSCTuneRequest();
55         return VLC_EGENERIC;
56     };
57
58     int dvb_SubmitDVBTTuneRequest( access_t* p_access )
59     {
60         if( p_access->p_sys->p_bda_module )
61             return p_access->p_sys->p_bda_module->SubmitDVBTTuneRequest();
62         return VLC_EGENERIC;
63     };
64
65     int dvb_SubmitDVBCTuneRequest( access_t* p_access )
66     {
67         if( p_access->p_sys->p_bda_module )
68             return p_access->p_sys->p_bda_module->SubmitDVBCTuneRequest();
69         return VLC_EGENERIC;
70     };
71
72     int dvb_SubmitDVBSTuneRequest( access_t* p_access )
73     {
74         if( p_access->p_sys->p_bda_module )
75             return p_access->p_sys->p_bda_module->SubmitDVBSTuneRequest();
76         return VLC_EGENERIC;
77     };
78
79     block_t *dvb_Pop( access_t* p_access )
80     {
81         if( p_access->p_sys->p_bda_module )
82             return p_access->p_sys->p_bda_module->Pop();
83         return NULL;
84     };
85 };
86
87 /*****************************************************************************
88 * BDAOutput
89 *****************************************************************************/
90 BDAOutput::BDAOutput( access_t *p_access ) :
91     p_access(p_access), p_first(NULL), pp_next(&p_first)
92 {
93     vlc_mutex_init( &lock );
94     vlc_cond_init( &wait );
95 }
96 BDAOutput::~BDAOutput()
97 {
98     Empty();
99     vlc_mutex_destroy( &lock );
100     vlc_cond_destroy( &wait );
101 }
102 void BDAOutput::Push( block_t *p_block )
103 {
104     vlc_mutex_locker l( &lock );
105
106     block_ChainLastAppend( &pp_next, p_block );
107     vlc_cond_signal( &wait );
108 }
109 block_t *BDAOutput::Pop()
110 {
111     vlc_mutex_locker l( &lock );
112
113     if( !p_first )
114         vlc_cond_timedwait( &wait, &lock, mdate() + 250*1000 );
115
116     block_t *p_ret = p_first;
117
118     p_first = NULL;
119     pp_next = &p_first;
120
121     return p_ret;
122 }
123 void BDAOutput::Empty()
124 {
125     vlc_mutex_locker l( &lock );
126
127     if( p_first )
128         block_ChainRelease( p_first );
129     p_first = NULL;
130     pp_next = &p_first;
131 }
132
133 /*****************************************************************************
134 * Constructor
135 *****************************************************************************/
136 BDAGraph::BDAGraph( access_t* p_this ):
137     p_access( p_this ),
138     guid_network_type(GUID_NULL),
139     l_tuner_used(-1),
140     d_graph_register( 0 ),
141     output( p_this )
142 {
143     p_tuning_space = NULL;
144     p_tune_request = NULL;
145     p_media_control = NULL;
146     p_filter_graph = NULL;
147     p_system_dev_enum = NULL;
148     p_network_provider = p_tuner_device = p_capture_device = NULL;
149     p_sample_grabber = p_mpeg_demux = p_transport_info = NULL;
150     p_scanning_tuner = NULL;
151     p_grabber = NULL;
152
153     /* Initialize COM - MS says to use CoInitializeEx in preference to
154      * CoInitialize */
155     CoInitializeEx( 0, COINIT_APARTMENTTHREADED );
156 }
157
158 /*****************************************************************************
159 * Destructor
160 *****************************************************************************/
161 BDAGraph::~BDAGraph()
162 {
163     Destroy();
164     CoUninitialize();
165 }
166
167 /*****************************************************************************
168 * Submit an Clear QAM Tune Request (US Cable Shit)
169 *****************************************************************************/
170 int BDAGraph::SubmitCQAMTuneRequest()
171 {
172     HRESULT hr = S_OK;
173     class localComPtr
174     {
175         public:
176         IDigitalCableTuneRequest* p_cqam_tune_request;
177         IDigitalCableLocator* p_cqam_locator;
178         localComPtr(): p_cqam_tune_request(NULL), p_cqam_locator(NULL) {};
179         ~localComPtr()
180         {
181             if( p_cqam_tune_request )
182                 p_cqam_tune_request->Release();
183             if( p_cqam_locator )
184                 p_cqam_locator->Release();
185         }
186     } l;
187     long l_minor_channel, l_physical_channel, l_frequency;
188
189     l_physical_channel = var_GetInteger( p_access, "dvb-physical-channel" );
190     l_minor_channel    = var_GetInteger( p_access, "dvb-minor-channel" );
191     l_frequency        = var_GetInteger( p_access, "dvb-frequency" );
192
193     guid_network_type = CLSID_DigitalCableNetworkType;
194     hr = CreateTuneRequest();
195     if( FAILED( hr ) )
196     {
197         msg_Warn( p_access, "SubmitCQAMTuneRequest: "\
198             "Cannot create Tuning Space: hr=0x%8lx", hr );
199         return VLC_EGENERIC;
200     }
201
202     hr = p_tune_request->QueryInterface( IID_IDigitalCableTuneRequest,
203         (void**)&l.p_cqam_tune_request );
204     if( FAILED( hr ) )
205     {
206         msg_Warn( p_access, "SubmitCQAMTuneRequest: "\
207             "Cannot QI for IDigitalCableTuneRequest: hr=0x%8lx", hr );
208         return VLC_EGENERIC;
209     }
210     hr = ::CoCreateInstance( CLSID_DigitalCableLocator, 0, CLSCTX_INPROC,
211                              IID_IDigitalCableLocator, (void**)&l.p_cqam_locator );
212     if( FAILED( hr ) )
213     {
214         msg_Warn( p_access, "SubmitCQAMTuneRequest: "\
215             "Cannot create the CQAM locator: hr=0x%8lx", hr );
216         return VLC_EGENERIC;
217     }
218
219     hr = S_OK;
220     if( SUCCEEDED( hr ) && l_physical_channel > 0 )
221         hr = l.p_cqam_locator->put_PhysicalChannel( l_physical_channel );
222     if( SUCCEEDED( hr ) && l_frequency > 0 )
223         hr = l.p_cqam_locator->put_CarrierFrequency( l_frequency );
224     if( SUCCEEDED( hr ) && l_minor_channel > 0 )
225         hr = l.p_cqam_tune_request->put_MinorChannel( l_minor_channel );
226     if( FAILED( hr ) )
227     {
228         msg_Warn( p_access, "SubmitCQAMTuneRequest: "\
229             "Cannot set tuning parameters: hr=0x%8lx", hr );
230         return VLC_EGENERIC;
231     }
232
233     hr = p_tune_request->put_Locator( l.p_cqam_locator );
234     if( FAILED( hr ) )
235     {
236         msg_Warn( p_access, "SubmitCQAMTuneRequest: "\
237             "Cannot put the locator: hr=0x%8lx", hr );
238         return VLC_EGENERIC;
239     }
240
241     /* Build and Run the Graph. If a Tuner device is in use the graph will
242      * fail to run. Repeated calls to build will check successive tuner
243      * devices */
244     do
245     {
246         hr = Build();
247         if( FAILED( hr ) )
248         {
249             msg_Warn( p_access, "SubmitCQAMTuneRequest: "\
250                 "Cannot Build the Graph: hr=0x%8lx", hr );
251             return VLC_EGENERIC;
252         }
253         hr = Start();
254     }
255     while( hr != S_OK );
256
257     return VLC_SUCCESS;
258 }
259
260 /*****************************************************************************
261 * Submit an ATSC Tune Request
262 *****************************************************************************/
263 int BDAGraph::SubmitATSCTuneRequest()
264 {
265     HRESULT hr = S_OK;
266     class localComPtr
267     {
268         public:
269         IATSCChannelTuneRequest* p_atsc_tune_request;
270         IATSCLocator* p_atsc_locator;
271         localComPtr(): p_atsc_tune_request(NULL), p_atsc_locator(NULL) {};
272         ~localComPtr()
273         {
274             if( p_atsc_tune_request )
275                 p_atsc_tune_request->Release();
276             if( p_atsc_locator )
277                 p_atsc_locator->Release();
278         }
279     } l;
280     long l_major_channel, l_minor_channel, l_physical_channel;
281     long l_frequency;
282
283     l_major_channel     = var_GetInteger( p_access, "dvb-major-channel" );
284     l_minor_channel     = var_GetInteger( p_access, "dvb-minor-channel" );
285     l_physical_channel  = var_GetInteger( p_access, "dvb-physical-channel" );
286     l_frequency         = var_GetInteger( p_access, "dvb-frequency" );
287
288     guid_network_type = CLSID_ATSCNetworkProvider;
289     hr = CreateTuneRequest();
290     if( FAILED( hr ) )
291     {
292         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
293             "Cannot create Tuning Space: hr=0x%8lx", hr );
294         return VLC_EGENERIC;
295     }
296
297     hr = p_tune_request->QueryInterface( IID_IATSCChannelTuneRequest,
298         (void**)&l.p_atsc_tune_request );
299     if( FAILED( hr ) )
300     {
301         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
302             "Cannot QI for IATSCChannelTuneRequest: hr=0x%8lx", hr );
303         return VLC_EGENERIC;
304     }
305     hr = ::CoCreateInstance( CLSID_ATSCLocator, 0, CLSCTX_INPROC,
306                              IID_IATSCLocator, (void**)&l.p_atsc_locator );
307     if( FAILED( hr ) )
308     {
309         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
310             "Cannot create the ATSC locator: hr=0x%8lx", hr );
311         return VLC_EGENERIC;
312     }
313
314     hr = S_OK;
315     if( l_frequency > 0 )
316         hr = l.p_atsc_locator->put_CarrierFrequency( l_frequency );
317     if( l_major_channel > 0 )
318         hr = l.p_atsc_tune_request->put_Channel( l_major_channel );
319     if( SUCCEEDED( hr ) && l_minor_channel > 0 )
320         hr = l.p_atsc_tune_request->put_MinorChannel( l_minor_channel );
321     if( SUCCEEDED( hr ) && l_physical_channel > 0 )
322         hr = l.p_atsc_locator->put_PhysicalChannel( l_physical_channel );
323     if( FAILED( hr ) )
324     {
325         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
326             "Cannot set tuning parameters: hr=0x%8lx", hr );
327         return VLC_EGENERIC;
328     }
329
330     hr = p_tune_request->put_Locator( l.p_atsc_locator );
331     if( FAILED( hr ) )
332     {
333         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
334             "Cannot put the locator: hr=0x%8lx", hr );
335         return VLC_EGENERIC;
336     }
337
338     /* Build and Run the Graph. If a Tuner device is in use the graph will
339      * fail to run. Repeated calls to build will check successive tuner
340      * devices */
341     do
342     {
343         hr = Build();
344         if( FAILED( hr ) )
345         {
346             msg_Warn( p_access, "SubmitATSCTuneRequest: "\
347                 "Cannot Build the Graph: hr=0x%8lx", hr );
348             return VLC_EGENERIC;
349         }
350         hr = Start();
351     }
352     while( hr != S_OK );
353
354     return VLC_SUCCESS;
355 }
356
357 /*****************************************************************************
358 * Submit a DVB-T Tune Request
359 ******************************************************************************/
360 int BDAGraph::SubmitDVBTTuneRequest()
361 {
362     HRESULT hr = S_OK;
363     class localComPtr
364     {
365         public:
366         IDVBTuneRequest* p_dvbt_tune_request;
367         IDVBTLocator* p_dvbt_locator;
368         IDVBTuningSpace2* p_dvb_tuning_space;
369         localComPtr(): p_dvbt_tune_request(NULL), p_dvbt_locator(NULL),
370            p_dvb_tuning_space(NULL) {};
371         ~localComPtr()
372         {
373             if( p_dvbt_tune_request )
374                 p_dvbt_tune_request->Release();
375             if( p_dvbt_locator )
376                 p_dvbt_locator->Release();
377             if( p_dvb_tuning_space )
378                 p_dvb_tuning_space->Release();
379         }
380     } l;
381     long l_frequency, l_bandwidth, l_hp_fec, l_lp_fec, l_guard;
382     long l_transmission, l_hierarchy;
383     BinaryConvolutionCodeRate i_hp_fec, i_lp_fec;
384     GuardInterval             i_guard;
385     TransmissionMode          i_transmission;
386     HierarchyAlpha            i_hierarchy;
387
388     l_frequency    = var_GetInteger( p_access, "dvb-frequency" ) / 1000;
389     l_bandwidth    = var_GetInteger( p_access, "dvb-bandwidth" );
390     l_hp_fec       = var_GetInteger( p_access, "dvb-code-rate-hp" );
391     l_lp_fec       = var_GetInteger( p_access, "dvb-code-rate-lp" );
392     l_guard        = var_GetInteger( p_access, "dvb-guard" );
393     l_transmission = var_GetInteger( p_access, "dvb-transmission" );
394     l_hierarchy    = var_GetInteger( p_access, "dvb-hierarchy" );
395
396     switch( l_hp_fec )
397     {
398     case 1:
399         i_hp_fec = BDA_BCC_RATE_1_2; break;
400     case 2:
401         i_hp_fec = BDA_BCC_RATE_2_3; break;
402     case 3:
403         i_hp_fec = BDA_BCC_RATE_3_4; break;
404     case 4:
405         i_hp_fec = BDA_BCC_RATE_5_6; break;
406     case 5:
407         i_hp_fec = BDA_BCC_RATE_7_8;break;
408     default:
409         i_hp_fec = BDA_BCC_RATE_NOT_SET;
410     }
411
412     switch( l_lp_fec )
413     {
414     case 1:
415         i_lp_fec = BDA_BCC_RATE_1_2; break;
416     case 2:
417         i_lp_fec = BDA_BCC_RATE_2_3; break;
418     case 3:
419         i_lp_fec = BDA_BCC_RATE_3_4; break;
420     case 4:
421         i_lp_fec = BDA_BCC_RATE_5_6; break;
422     case 5:
423         i_lp_fec = BDA_BCC_RATE_7_8; break;
424     default:
425         i_lp_fec = BDA_BCC_RATE_NOT_SET;
426     }
427
428     switch( l_guard )
429     {
430     case 32:
431         i_guard = BDA_GUARD_1_32; break;
432     case 16:
433         i_guard = BDA_GUARD_1_16; break;
434     case 8:
435         i_guard = BDA_GUARD_1_8; break;
436     case 4:
437         i_guard = BDA_GUARD_1_4; break;
438     default:
439         i_guard = BDA_GUARD_NOT_SET;
440     }
441
442     switch( l_transmission )
443     {
444     case 2:
445         i_transmission = BDA_XMIT_MODE_2K; break;
446     case 8:
447         i_transmission = BDA_XMIT_MODE_8K; break;
448     default:
449         i_transmission = BDA_XMIT_MODE_NOT_SET;
450     }
451
452     switch( l_hierarchy )
453     {
454     case 1:
455         i_hierarchy = BDA_HALPHA_1; break;
456     case 2:
457         i_hierarchy = BDA_HALPHA_2; break;
458     case 4:
459         i_hierarchy = BDA_HALPHA_4; break;
460     default:
461         i_hierarchy = BDA_HALPHA_NOT_SET;
462     }
463
464     guid_network_type = CLSID_DVBTNetworkProvider;
465     hr = CreateTuneRequest();
466     if( FAILED( hr ) )
467     {
468         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
469             "Cannot create Tune Request: hr=0x%8lx", hr );
470         return VLC_EGENERIC;
471     }
472
473     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
474         (void**)&l.p_dvbt_tune_request );
475     if( FAILED( hr ) )
476     {
477         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
478             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
479         return VLC_EGENERIC;
480     }
481     l.p_dvbt_tune_request->put_ONID( -1 );
482     l.p_dvbt_tune_request->put_SID( -1 );
483     l.p_dvbt_tune_request->put_TSID( -1 );
484
485     hr = ::CoCreateInstance( CLSID_DVBTLocator, 0, CLSCTX_INPROC,
486         IID_IDVBTLocator, (void**)&l.p_dvbt_locator );
487     if( FAILED( hr ) )
488     {
489         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
490             "Cannot create the DVBT Locator: hr=0x%8lx", hr );
491         return VLC_EGENERIC;
492     }
493     hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
494         (void**)&l.p_dvb_tuning_space );
495     if( FAILED( hr ) )
496     {
497         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
498             "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
499         return VLC_EGENERIC;
500     }
501
502     hr = S_OK;
503     hr = l.p_dvb_tuning_space->put_SystemType( DVB_Terrestrial );
504
505     if( SUCCEEDED( hr ) && l_frequency > 0 )
506         hr = l.p_dvbt_locator->put_CarrierFrequency( l_frequency );
507     if( SUCCEEDED( hr ) && l_bandwidth > 0 )
508         hr = l.p_dvbt_locator->put_Bandwidth( l_bandwidth );
509     if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
510         hr = l.p_dvbt_locator->put_InnerFECRate( i_hp_fec );
511     if( SUCCEEDED( hr ) && i_lp_fec != BDA_BCC_RATE_NOT_SET )
512         hr = l.p_dvbt_locator->put_LPInnerFECRate( i_lp_fec );
513     if( SUCCEEDED( hr ) && i_guard != BDA_GUARD_NOT_SET )
514         hr = l.p_dvbt_locator->put_Guard( i_guard );
515     if( SUCCEEDED( hr ) && i_transmission != BDA_XMIT_MODE_NOT_SET )
516         hr = l.p_dvbt_locator->put_Mode( i_transmission );
517     if( SUCCEEDED( hr ) && i_hierarchy != BDA_HALPHA_NOT_SET )
518         hr = l.p_dvbt_locator->put_HAlpha( i_hierarchy );
519     if( FAILED( hr ) )
520     {
521         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
522             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
523         return VLC_EGENERIC;
524     }
525
526     hr = p_tune_request->put_Locator( l.p_dvbt_locator );
527     if( FAILED( hr ) )
528     {
529         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
530             "Cannot put the locator: hr=0x%8lx", hr );
531         return VLC_EGENERIC;
532     }
533
534     /* Build and Run the Graph. If a Tuner device is in use the graph will
535      * fail to run. Repeated calls to build will check successive tuner
536      * devices */
537     do
538     {
539         hr = Build();
540         if( FAILED( hr ) )
541         {
542             msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
543                 "Cannot Build the Graph: hr=0x%8lx", hr );
544             return VLC_EGENERIC;
545         }
546         hr = Start();
547     }
548     while( hr != S_OK );
549
550     return VLC_SUCCESS;
551 }
552
553 /*****************************************************************************
554 * Submit a DVB-C Tune Request
555 ******************************************************************************/
556 int BDAGraph::SubmitDVBCTuneRequest()
557 {
558     HRESULT hr = S_OK;
559
560     class localComPtr
561     {
562         public:
563         IDVBTuneRequest* p_dvbc_tune_request;
564         IDVBCLocator* p_dvbc_locator;
565         IDVBTuningSpace2* p_dvb_tuning_space;
566
567         localComPtr(): p_dvbc_tune_request(NULL), p_dvbc_locator(NULL),
568                        p_dvb_tuning_space(NULL) {};
569         ~localComPtr()
570         {
571             if( p_dvbc_tune_request )
572                 p_dvbc_tune_request->Release();
573             if( p_dvbc_locator )
574                 p_dvbc_locator->Release();
575             if( p_dvb_tuning_space )
576                 p_dvb_tuning_space->Release();
577         }
578     } l;
579
580     long l_frequency, l_symbolrate;
581     int  i_qam;
582     ModulationType i_qam_mod;
583
584     l_frequency  = var_GetInteger( p_access, "dvb-frequency" ) / 1000;
585     l_symbolrate = var_GetInteger( p_access, "dvb-srate" );
586     i_qam        = var_GetInteger( p_access, "dvb-modulation" );
587
588     switch( i_qam )
589     {
590     case 16:
591         i_qam_mod = BDA_MOD_16QAM; break;
592     case 32:
593         i_qam_mod = BDA_MOD_32QAM; break;
594     case 64:
595         i_qam_mod = BDA_MOD_64QAM; break;
596     case 128:
597         i_qam_mod = BDA_MOD_128QAM; break;
598     case 256:
599         i_qam_mod = BDA_MOD_256QAM; break;
600     default:
601         i_qam_mod = BDA_MOD_NOT_SET;
602     }
603
604     guid_network_type = CLSID_DVBCNetworkProvider;
605     hr = CreateTuneRequest();
606     if( FAILED( hr ) )
607     {
608         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
609             "Cannot create Tune Request: hr=0x%8lx", hr );
610         return VLC_EGENERIC;
611     }
612
613     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
614         (void**)&l.p_dvbc_tune_request );
615     if( FAILED( hr ) )
616     {
617         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
618             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
619         return VLC_EGENERIC;
620     }
621     l.p_dvbc_tune_request->put_ONID( -1 );
622     l.p_dvbc_tune_request->put_SID( -1 );
623     l.p_dvbc_tune_request->put_TSID( -1 );
624
625     hr = ::CoCreateInstance( CLSID_DVBCLocator, 0, CLSCTX_INPROC,
626         IID_IDVBCLocator, (void**)&l.p_dvbc_locator );
627     if( FAILED( hr ) )
628     {
629         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
630             "Cannot create the DVB-C Locator: hr=0x%8lx", hr );
631         return VLC_EGENERIC;
632     }
633     hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
634         (void**)&l.p_dvb_tuning_space );
635     if( FAILED( hr ) )
636     {
637         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
638             "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
639         return VLC_EGENERIC;
640     }
641
642     hr = S_OK;
643     hr = l.p_dvb_tuning_space->put_SystemType( DVB_Cable );
644
645     if( SUCCEEDED( hr ) && l_frequency > 0 )
646         hr = l.p_dvbc_locator->put_CarrierFrequency( l_frequency );
647     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
648         hr = l.p_dvbc_locator->put_SymbolRate( l_symbolrate );
649     if( SUCCEEDED( hr ) && i_qam_mod != BDA_MOD_NOT_SET )
650         hr = l.p_dvbc_locator->put_Modulation( i_qam_mod );
651
652     if( FAILED( hr ) )
653     {
654         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
655             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
656         return VLC_EGENERIC;
657     }
658
659     hr = p_tune_request->put_Locator( l.p_dvbc_locator );
660     if( FAILED( hr ) )
661     {
662         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
663             "Cannot put the locator: hr=0x%8lx", hr );
664         return VLC_EGENERIC;
665     }
666
667     /* Build and Run the Graph. If a Tuner device is in use the graph will
668      * fail to run. Repeated calls to build will check successive tuner
669      * devices */
670     do
671     {
672         hr = Build();
673         if( FAILED( hr ) )
674         {
675             msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
676                 "Cannot Build the Graph: hr=0x%8lx", hr );
677             return VLC_EGENERIC;
678         }
679         hr = Start();
680     }
681     while( hr != S_OK );
682
683     return VLC_SUCCESS;
684 }
685
686 /*****************************************************************************
687 * Submit a DVB-S Tune Request
688 ******************************************************************************/
689 int BDAGraph::SubmitDVBSTuneRequest()
690 {
691     HRESULT hr = S_OK;
692
693     class localComPtr
694     {
695         public:
696         IDVBTuneRequest* p_dvbs_tune_request;
697         IDVBSLocator* p_dvbs_locator;
698         IDVBSTuningSpace* p_dvbs_tuning_space;
699         char* psz_polarisation;
700         char* psz_input_range;
701         BSTR bstr_input_range;
702         WCHAR* pwsz_input_range;
703         int i_range_len;
704         localComPtr(): p_dvbs_tune_request(NULL), p_dvbs_locator(NULL),
705             p_dvbs_tuning_space(NULL), bstr_input_range(NULL),
706             pwsz_input_range(NULL), i_range_len(0), psz_polarisation(NULL),
707             psz_input_range(NULL) {};
708         ~localComPtr()
709         {
710             if( p_dvbs_tuning_space )
711                 p_dvbs_tuning_space->Release();
712             if( p_dvbs_tune_request )
713                 p_dvbs_tune_request->Release();
714             if( p_dvbs_locator )
715                 p_dvbs_locator->Release();
716             SysFreeString( bstr_input_range );
717             delete pwsz_input_range;
718             free( psz_input_range );
719             free( psz_polarisation );
720         }
721     } l;
722     long l_frequency, l_symbolrate, l_azimuth, l_elevation, l_longitude;
723     long l_lnb_lof1, l_lnb_lof2, l_lnb_slof, l_inversion, l_network_id;
724     long l_hp_fec;
725     int  i_mod;
726     Polarisation i_polar;
727     SpectralInversion i_inversion;
728     VARIANT_BOOL b_west;
729     BinaryConvolutionCodeRate i_hp_fec;
730     ModulationType i_mod_typ;
731
732     l_frequency        = var_GetInteger( p_access, "dvb-frequency" );
733     l_symbolrate       = var_GetInteger( p_access, "dvb-srate" );
734     l_azimuth          = var_GetInteger( p_access, "dvb-azimuth" );
735     l_elevation        = var_GetInteger( p_access, "dvb-elevation" );
736     l_longitude        = var_GetInteger( p_access, "dvb-longitude" );
737     l_lnb_lof1         = var_GetInteger( p_access, "dvb-lnb-lof1" );
738     l_lnb_lof2         = var_GetInteger( p_access, "dvb-lnb-lof2" );
739     l_lnb_slof         = var_GetInteger( p_access, "dvb-lnb-slof" );
740     i_mod              = var_GetInteger( p_access, "dvb-modulation" );
741     l_hp_fec           = var_GetInteger( p_access, "dvb-code-rate-hp" );
742     l_inversion        = var_GetInteger( p_access, "dvb-inversion" );
743     l_network_id       = var_GetInteger( p_access, "dvb-network-id" );
744
745     l.psz_input_range  = var_GetNonEmptyString( p_access, "dvb-range" );
746     l.psz_polarisation = var_GetNonEmptyString( p_access, "dvb-polarisation" );
747
748     b_west = ( l_longitude < 0 );
749
750     i_polar = BDA_POLARISATION_NOT_SET;
751     if( l.psz_polarisation != NULL )
752     {
753         switch( toupper( l.psz_polarisation[0] ) )
754         {
755         case 'H':
756             i_polar = BDA_POLARISATION_LINEAR_H;
757             break;
758         case 'V':
759             i_polar = BDA_POLARISATION_LINEAR_V;
760             break;
761         case 'L':
762             i_polar = BDA_POLARISATION_CIRCULAR_L;
763             break;
764         case 'R':
765             i_polar = BDA_POLARISATION_CIRCULAR_R;
766             break;
767         }
768     }
769
770     switch( l_inversion )
771     {
772     case 0:
773         i_inversion = BDA_SPECTRAL_INVERSION_NORMAL; break;
774     case 1:
775         i_inversion = BDA_SPECTRAL_INVERSION_INVERTED; break;
776     case 2:
777         i_inversion = BDA_SPECTRAL_INVERSION_AUTOMATIC; break;
778     default:
779         i_inversion = BDA_SPECTRAL_INVERSION_NOT_SET;
780     }
781
782     switch( i_mod )
783     {
784     case 16:
785         i_mod_typ = BDA_MOD_16QAM; break;
786     case 128:
787         i_mod_typ = BDA_MOD_128QAM; break;
788     case 256:
789         i_mod_typ = BDA_MOD_256QAM; break;
790     case 10004:
791         i_mod_typ = BDA_MOD_QPSK; break;
792     default:
793         i_mod_typ = BDA_MOD_NOT_SET;
794     }
795
796     switch( l_hp_fec )
797     {
798     case 1:
799         i_hp_fec = BDA_BCC_RATE_1_2; break;
800     case 2:
801         i_hp_fec = BDA_BCC_RATE_2_3; break;
802     case 3:
803         i_hp_fec = BDA_BCC_RATE_3_4; break;
804     case 4:
805         i_hp_fec = BDA_BCC_RATE_5_6; break;
806     case 5:
807         i_hp_fec = BDA_BCC_RATE_7_8; break;
808     default:
809         i_hp_fec = BDA_BCC_RATE_NOT_SET;
810     }
811
812     l.i_range_len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
813         l.psz_input_range, -1, l.pwsz_input_range, 0 );
814     if( l.i_range_len > 0 )
815     {
816         l.pwsz_input_range = new WCHAR[l.i_range_len];
817         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
818             l.psz_input_range, -1, l.pwsz_input_range, l.i_range_len );
819         l.bstr_input_range=SysAllocString( l.pwsz_input_range );
820     }
821
822     guid_network_type = CLSID_DVBSNetworkProvider;
823     hr = CreateTuneRequest();
824     if( FAILED( hr ) )
825     {
826         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
827             "Cannot create Tune Request: hr=0x%8lx", hr );
828         return VLC_EGENERIC;
829     }
830
831     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
832         (void**)&l.p_dvbs_tune_request );
833     if( FAILED( hr ) )
834     {
835         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
836             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
837         return VLC_EGENERIC;
838     }
839     l.p_dvbs_tune_request->put_ONID( -1 );
840     l.p_dvbs_tune_request->put_SID( -1 );
841     l.p_dvbs_tune_request->put_TSID( -1 );
842
843     hr = ::CoCreateInstance( CLSID_DVBSLocator, 0, CLSCTX_INPROC,
844         IID_IDVBSLocator, (void**)&l.p_dvbs_locator );
845     if( FAILED( hr ) )
846     {
847         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
848             "Cannot create the DVBS Locator: hr=0x%8lx", hr );
849         return VLC_EGENERIC;
850     }
851
852     hr = p_tuning_space->QueryInterface( IID_IDVBSTuningSpace,
853         (void**)&l.p_dvbs_tuning_space );
854     if( FAILED( hr ) )
855     {
856         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
857             "Cannot QI for IDVBSTuningSpace: hr=0x%8lx", hr );
858         return VLC_EGENERIC;
859     }
860
861     hr = S_OK;
862     hr = l.p_dvbs_tuning_space->put_SystemType( DVB_Satellite );
863     if( SUCCEEDED( hr ) && l_lnb_lof1 > 0 )
864         hr = l.p_dvbs_tuning_space->put_LowOscillator( l_lnb_lof1 );
865     if( SUCCEEDED( hr ) && l_lnb_slof > 0 )
866         hr = l.p_dvbs_tuning_space->put_LNBSwitch( l_lnb_slof );
867     if( SUCCEEDED( hr ) && l_lnb_lof2 > 0 )
868         hr = l.p_dvbs_tuning_space->put_HighOscillator( l_lnb_lof2 );
869     if( SUCCEEDED( hr ) && i_inversion != BDA_SPECTRAL_INVERSION_NOT_SET )
870         hr = l.p_dvbs_tuning_space->put_SpectralInversion( i_inversion );
871     if( SUCCEEDED( hr ) && l_network_id > 0 )
872         hr = l.p_dvbs_tuning_space->put_NetworkID( l_network_id );
873     if( SUCCEEDED( hr ) && l.i_range_len > 0 )
874         hr = l.p_dvbs_tuning_space->put_InputRange( l.bstr_input_range );
875
876     if( SUCCEEDED( hr ) && l_frequency > 0 )
877         hr = l.p_dvbs_locator->put_CarrierFrequency( l_frequency );
878     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
879         hr = l.p_dvbs_locator->put_SymbolRate( l_symbolrate );
880     if( SUCCEEDED( hr ) && i_polar != BDA_POLARISATION_NOT_SET )
881         hr = l.p_dvbs_locator->put_SignalPolarisation( i_polar );
882     if( SUCCEEDED( hr ) && i_mod_typ != BDA_MOD_NOT_SET )
883         hr = l.p_dvbs_locator->put_Modulation( i_mod_typ );
884     if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
885         hr = l.p_dvbs_locator->put_InnerFECRate( i_hp_fec );
886
887     if( SUCCEEDED( hr ) && l_azimuth > 0 )
888         hr = l.p_dvbs_locator->put_Azimuth( l_azimuth );
889     if( SUCCEEDED( hr ) && l_elevation > 0 )
890         hr = l.p_dvbs_locator->put_Elevation( l_elevation );
891     if( SUCCEEDED( hr ) )
892         hr = l.p_dvbs_locator->put_WestPosition( b_west );
893     if( SUCCEEDED( hr ) )
894         hr = l.p_dvbs_locator->put_OrbitalPosition( labs( l_longitude ) );
895     if( FAILED( hr ) )
896     {
897         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
898             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
899         return VLC_EGENERIC;
900     }
901
902     hr = p_tune_request->put_Locator( l.p_dvbs_locator );
903     if( FAILED( hr ) )
904     {
905         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
906             "Cannot put the locator: hr=0x%8lx", hr );
907         return VLC_EGENERIC;
908     }
909
910     /* Build and Run the Graph. If a Tuner device is in use the graph will
911      * fail to run. Repeated calls to build will check successive tuner
912      * devices */
913     do
914     {
915         hr = Build();
916         if( FAILED( hr ) )
917         {
918             msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
919                 "Cannot Build the Graph: hr=0x%8lx", hr );
920             return VLC_EGENERIC;
921         }
922         hr = Start();
923     }
924     while( hr != S_OK );
925
926     return VLC_SUCCESS;
927 }
928
929 /*****************************************************************************
930 * Load the Tuning Space from System Tuning Spaces according to the
931 * Network Type requested
932 ******************************************************************************/
933 HRESULT BDAGraph::CreateTuneRequest()
934 {
935     HRESULT hr = S_OK;
936     GUID guid_this_network_type;
937     class localComPtr
938     {
939         public:
940         ITuningSpaceContainer*  p_tuning_space_container;
941         IEnumTuningSpaces*      p_tuning_space_enum;
942         ITuningSpace*           p_this_tuning_space;
943         IDVBTuningSpace2*       p_dvb_tuning_space;
944         BSTR                    bstr_name;
945         char * psz_network_name;
946         char * psz_create_name;
947         char * psz_bstr_name;
948         WCHAR * wpsz_create_name;
949         int i_name_len;
950         localComPtr(): p_tuning_space_container(NULL),
951             p_tuning_space_enum(NULL), p_this_tuning_space(NULL),
952             p_dvb_tuning_space(NULL),
953             i_name_len(0), psz_network_name(NULL), wpsz_create_name(NULL),
954             psz_create_name(NULL), bstr_name(NULL), psz_bstr_name(NULL) {};
955         ~localComPtr()
956         {
957             if( p_tuning_space_enum )
958                 p_tuning_space_enum->Release();
959             if( p_tuning_space_container )
960                 p_tuning_space_container->Release();
961             if( p_this_tuning_space )
962                 p_this_tuning_space->Release();
963             if( p_dvb_tuning_space )
964                 p_dvb_tuning_space->Release();
965             SysFreeString( bstr_name );
966             delete[] psz_bstr_name;
967             delete[] wpsz_create_name;
968             free( psz_network_name );
969             free( psz_create_name );
970         }
971     } l;
972
973     /* We shall test for a specific Tuning space name supplied on the command
974      * line as dvb-networkname=xxx.
975      * For some users with multiple cards and/or multiple networks this could
976      * be useful. This allows us to reasonably safely apply updates to the
977      * System Tuning Space in the registry without disrupting other streams. */
978     l.psz_network_name = var_GetNonEmptyString( p_access, "dvb-network-name" );
979     if( l.psz_network_name )
980     {
981         msg_Dbg( p_access, "CreateTuneRequest: Find Tuning Space: %s",
982             l.psz_network_name );
983     }
984     else
985     {
986         l.psz_network_name = new char[1];
987         *l.psz_network_name = '\0';
988     }
989
990     /* A Tuning Space may already have been set up. If it is for the same
991      * network type then all is well. Otherwise, reset the Tuning Space and get
992      * a new one */
993     if( p_tuning_space )
994     {
995         hr = p_tuning_space->get__NetworkType( &guid_this_network_type );
996         if( FAILED( hr ) ) guid_this_network_type = GUID_NULL;
997         if( guid_this_network_type == guid_network_type )
998         {
999             hr = p_tuning_space->get_UniqueName( &l.bstr_name );
1000             if( FAILED( hr ) )
1001             {
1002                 msg_Warn( p_access, "CreateTuneRequest: "\
1003                     "Cannot get UniqueName for Tuning Space: hr=0x%8lx", hr );
1004                 return hr;
1005             }
1006             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
1007                 l.psz_bstr_name, 0, NULL, NULL );
1008             l.psz_bstr_name = new char[ l.i_name_len ];
1009             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
1010                 l.psz_bstr_name, l.i_name_len, NULL, NULL );
1011
1012             /* Test for a specific Tuning space name supplied on the command
1013              * line as dvb-networkname=xxx */
1014             if( *l.psz_network_name == '\0' ||
1015                 strcmp( l.psz_network_name, l.psz_bstr_name ) == 0 )
1016             {
1017                 msg_Dbg( p_access, "CreateTuneRequest: Using Tuning Space: %s",
1018                     l.psz_network_name );
1019                 return S_OK;
1020             }
1021         }
1022         /* else different guid_network_type */
1023         if( p_tuning_space )
1024             p_tuning_space->Release();
1025         if( p_tune_request )
1026             p_tune_request->Release();
1027         p_tuning_space = NULL;
1028         p_tune_request = NULL;
1029     }
1030
1031     /* Force use of the first available Tuner Device during Build */
1032     l_tuner_used = -1;
1033
1034     /* Get the SystemTuningSpaces container to enumerate through all the
1035      * defined tuning spaces.
1036      * l.p_tuning_space_container->Refcount = 1  */
1037     hr = ::CoCreateInstance( CLSID_SystemTuningSpaces, 0, CLSCTX_INPROC,
1038         IID_ITuningSpaceContainer, (void**)&l.p_tuning_space_container );
1039     if( FAILED( hr ) )
1040     {
1041         msg_Warn( p_access, "CreateTuneRequest: "\
1042             "Cannot CoCreate SystemTuningSpaces: hr=0x%8lx", hr );
1043         return hr;
1044     }
1045
1046     /* Get the SystemTuningSpaces container to enumerate through all the
1047      * defined tuning spaces.
1048      * l.p_tuning_space_container->Refcount = 2
1049      * l.p_tuning_space_enum->Refcount = 1  */
1050     hr = l.p_tuning_space_container->get_EnumTuningSpaces(
1051          &l.p_tuning_space_enum );
1052     if( FAILED( hr ) )
1053     {
1054         msg_Warn( p_access, "CreateTuneRequest: "\
1055             "Cannot create SystemTuningSpaces Enumerator: hr=0x%8lx", hr );
1056         return hr;
1057     }
1058
1059     do
1060     {
1061         /* l.p_this_tuning_space->RefCount = 1 after the first pass
1062          * Release before overwriting with Next */
1063         if( l.p_this_tuning_space )
1064             l.p_this_tuning_space->Release();
1065         l.p_this_tuning_space = NULL;
1066         SysFreeString( l.bstr_name );
1067
1068         hr = l.p_tuning_space_enum->Next( 1, &l.p_this_tuning_space, NULL );
1069         if( hr != S_OK ) break;
1070
1071         hr = l.p_this_tuning_space->get__NetworkType( &guid_this_network_type );
1072
1073         /* GUID_NULL means a non-BDA network was found e.g analog
1074          * Ignore failures and non-BDA networks and keep looking */
1075         if( FAILED( hr ) ) guid_this_network_type == GUID_NULL;
1076
1077         if( guid_this_network_type == guid_network_type )
1078         {
1079             /* QueryInterface to clone l.p_this_tuning_space
1080              * l.p_this_tuning_space->RefCount = 2 */
1081             hr = l.p_this_tuning_space->Clone( &p_tuning_space );
1082             if( FAILED( hr ) )
1083             {
1084                 msg_Warn( p_access, "CreateTuneRequest: "\
1085                     "Cannot QI Tuning Space: hr=0x%8lx", hr );
1086                 return hr;
1087             }
1088             hr = p_tuning_space->get_UniqueName( &l.bstr_name );
1089             if( FAILED( hr ) )
1090             {
1091                 msg_Warn( p_access, "CreateTuneRequest: "\
1092                     "Cannot get UniqueName for Tuning Space: hr=0x%8lx", hr );
1093                 return hr;
1094             }
1095
1096             /* Test for a specific Tuning space name supplied on the command
1097              * line as dvb-networkname=xxx */
1098             delete[] l.psz_bstr_name;
1099             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
1100                 l.psz_bstr_name, 0, NULL, NULL );
1101             l.psz_bstr_name = new char[ l.i_name_len ];
1102             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
1103                 l.psz_bstr_name, l.i_name_len, NULL, NULL );
1104             if( *l.psz_network_name == '\0' ||
1105                 strcmp( l.psz_network_name, l.psz_bstr_name ) == 0 )
1106             {
1107                 msg_Dbg( p_access, "CreateTuneRequest: Using Tuning Space: %s",
1108                     l.psz_bstr_name );
1109
1110             /* CreateTuneRequest adds TuneRequest to p_tuning_space
1111              * p_tune_request->RefCount = 1 */
1112                 hr = p_tuning_space->CreateTuneRequest( &p_tune_request );
1113                 if( FAILED( hr ) )
1114                     msg_Warn( p_access, "CreateTuneRequest: "\
1115                         "Cannot Create Tune Request: hr=0x%8lx", hr );
1116                 return hr;
1117             }
1118             if( p_tuning_space )
1119                 p_tuning_space->Release();
1120             p_tuning_space = NULL;
1121         }
1122     }
1123     while( true );
1124
1125     /* No tuning space was found. If the create-name parameter was set then
1126      * create a tuning space. By rights should use the same name used in
1127      * network-name
1128      * Also would be nice to copy a tuning space but we only come here if we do
1129      * not find any. */
1130     l.psz_create_name = var_GetNonEmptyString( p_access, "dvb-create-name" );
1131     if( !l.psz_create_name || strlen( l.psz_create_name ) <= 0 )
1132     {
1133         hr = E_FAIL;
1134         msg_Warn( p_access, "CreateTuneRequest: "\
1135             "Cannot find a suitable System Tuning Space: hr=0x%8lx", hr );
1136         return hr;
1137     }
1138     if( strcmp( l.psz_create_name, l.psz_network_name ) )
1139     {
1140         hr = E_FAIL;
1141         msg_Warn( p_access, "CreateTuneRequest: "\
1142             "dvb-create-name %s must match dvb-network-name %s",
1143             l.psz_create_name, l.psz_network_name );
1144         return hr;
1145     }
1146
1147     /* Need to use DVBSTuningSpace for DVB-S and ATSCTuningSpace for ATSC */
1148     VARIANT var_id;
1149     CLSID cls_tuning_space;
1150
1151     if( IsEqualCLSID( guid_network_type, CLSID_ATSCNetworkProvider ) )
1152         cls_tuning_space = CLSID_ATSCTuningSpace;
1153     if( IsEqualCLSID( guid_network_type, CLSID_DVBTNetworkProvider ) )
1154         cls_tuning_space = CLSID_DVBTuningSpace;
1155     if( IsEqualCLSID( guid_network_type, CLSID_DVBCNetworkProvider ) )
1156         cls_tuning_space = CLSID_DVBTuningSpace;
1157     if( IsEqualCLSID( guid_network_type, CLSID_DVBSNetworkProvider ) )
1158         cls_tuning_space = CLSID_DVBSTuningSpace;
1159
1160     l.i_name_len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
1161         l.psz_create_name, -1, l.wpsz_create_name, 0 );
1162     if( l.i_name_len <= 0 )
1163     {
1164         hr = E_FAIL;
1165         msg_Warn( p_access, "CreateTuneRequest: "\
1166             "Cannot convert zero length dvb-create-name %s",
1167             l.psz_create_name );
1168         return hr;
1169     }
1170     l.wpsz_create_name = new WCHAR[l.i_name_len];
1171     MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, l.psz_create_name, -1,
1172             l.wpsz_create_name, l.i_name_len );
1173     if( l.bstr_name )
1174         SysFreeString( l.bstr_name );
1175     l.bstr_name = SysAllocString( l.wpsz_create_name );
1176
1177     msg_Dbg( p_access, "CreateTuneRequest: Create Tuning Space: %s",
1178         l.psz_create_name );
1179
1180     hr = ::CoCreateInstance( cls_tuning_space, 0, CLSCTX_INPROC,
1181          IID_ITuningSpace, (void**)&p_tuning_space );
1182
1183     if( FAILED( hr ) )
1184         msg_Warn( p_access, "CreateTuneRequest: "\
1185             "Cannot CoCreate new TuningSpace: hr=0x%8lx", hr );
1186     if( SUCCEEDED( hr ) )
1187         hr = p_tuning_space->put__NetworkType( guid_network_type );
1188     if( FAILED( hr ) )
1189         msg_Warn( p_access, "CreateTuneRequest: "\
1190             "Cannot Put Network Type: hr=0x%8lx", hr );
1191     if( SUCCEEDED( hr ) )
1192         hr = p_tuning_space->put_UniqueName( l.bstr_name );
1193     if( FAILED( hr ) )
1194         msg_Warn( p_access, "CreateTuneRequest: "\
1195             "Cannot Put Unique Name: hr=0x%8lx", hr );
1196     if( SUCCEEDED( hr ) )
1197         hr = p_tuning_space->put_FriendlyName( l.bstr_name );
1198     if( FAILED( hr ) )
1199         msg_Warn( p_access, "CreateTuneRequest: "\
1200             "Cannot Put Friendly Name: hr=0x%8lx", hr );
1201     if( guid_network_type == CLSID_DVBTNetworkProvider ||
1202         guid_network_type == CLSID_DVBCNetworkProvider ||
1203         guid_network_type == CLSID_DVBSNetworkProvider )
1204     {
1205         hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
1206             (void**)&l.p_dvb_tuning_space );
1207         if( FAILED( hr ) )
1208         {
1209             msg_Warn( p_access, "CreateTuneRequest: "\
1210                 "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
1211             return hr;
1212         }
1213         if( guid_network_type == CLSID_DVBTNetworkProvider )
1214             hr = l.p_dvb_tuning_space->put_SystemType( DVB_Terrestrial );
1215         if( guid_network_type == CLSID_DVBCNetworkProvider )
1216             hr = l.p_dvb_tuning_space->put_SystemType( DVB_Cable );
1217         if( guid_network_type == CLSID_DVBSNetworkProvider )
1218             hr = l.p_dvb_tuning_space->put_SystemType( DVB_Satellite );
1219     }
1220
1221     if( SUCCEEDED( hr ) )
1222         hr = l.p_tuning_space_container->Add( p_tuning_space, &var_id );
1223
1224     if( FAILED( hr ) )
1225     {
1226         msg_Warn( p_access, "CreateTuneRequest: "\
1227             "Cannot Create new TuningSpace: hr=0x%8lx", hr );
1228         return hr;
1229     }
1230
1231     msg_Dbg( p_access, "CreateTuneRequest: Tuning Space: %s created",
1232          l.psz_create_name );
1233
1234     hr = p_tuning_space->CreateTuneRequest( &p_tune_request );
1235     if( FAILED( hr ) )
1236         msg_Warn( p_access, "CreateTuneRequest: "\
1237             "Cannot Create Tune Request: hr=0x%8lx", hr );
1238
1239     return hr;
1240 }
1241
1242 /******************************************************************************
1243 * Build
1244 * Step 4: Build the Filter Graph
1245 * Build sets up devices, adds and connects filters
1246 ******************************************************************************/
1247 HRESULT BDAGraph::Build()
1248 {
1249     HRESULT hr = S_OK;
1250     long l_capture_used, l_tif_used;
1251     VARIANT l_tuning_space_id;
1252     AM_MEDIA_TYPE grabber_media_type;
1253     class localComPtr
1254     {
1255         public:
1256         ITuningSpaceContainer*  p_tuning_space_container;
1257         localComPtr(): p_tuning_space_container(NULL) {};
1258         ~localComPtr()
1259         {
1260             if( p_tuning_space_container )
1261                 p_tuning_space_container->Release();
1262         }
1263     } l;
1264
1265     /* Get the SystemTuningSpaces container to save the Tuning space */
1266     l_tuning_space_id.vt = VT_I4;
1267     l_tuning_space_id.lVal = 0L;
1268     hr = ::CoCreateInstance( CLSID_SystemTuningSpaces, 0, CLSCTX_INPROC,
1269         IID_ITuningSpaceContainer, (void**)&l.p_tuning_space_container );
1270     if( FAILED( hr ) )
1271     {
1272         msg_Warn( p_access, "Build: "\
1273             "Cannot CoCreate SystemTuningSpaces: hr=0x%8lx", hr );
1274         return hr;
1275     }
1276     hr = l.p_tuning_space_container->FindID( p_tuning_space,
1277         &l_tuning_space_id.lVal );
1278     if( FAILED( hr ) )
1279     {
1280         msg_Warn( p_access, "Build: "\
1281             "Cannot Find Tuning Space ID: hr=0x%8lx", hr );
1282         return hr;
1283     }
1284     msg_Dbg( p_access, "Build: Using Tuning Space ID %d",
1285         l_tuning_space_id.lVal );
1286     hr = l.p_tuning_space_container->put_Item( l_tuning_space_id,
1287         p_tuning_space );
1288     if( FAILED( hr ) )
1289     {
1290         msg_Warn( p_access, "Build: "\
1291             "Cannot save Tuning Space: hr=0x%8lx (ignored)", hr );
1292     }
1293
1294     /* If we have already have a filter graph, rebuild it*/
1295     Destroy();
1296
1297     hr = ::CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
1298         IID_IGraphBuilder, (void**)&p_filter_graph );
1299     if( FAILED( hr ) )
1300     {
1301         msg_Warn( p_access, "Build: "\
1302             "Cannot CoCreate IFilterGraph: hr=0x%8lx", hr );
1303         return hr;
1304     }
1305
1306     /* First filter in the graph is the Network Provider and
1307      * its Scanning Tuner which takes the Tune Request
1308      * Try to build the Win 7 Universal Network Provider first*/
1309     hr = ::CoCreateInstance( CLSID_NetworkProvider, NULL, CLSCTX_INPROC_SERVER,
1310         IID_IBaseFilter, (void**)&p_network_provider);
1311     if( FAILED( hr ) )
1312     {
1313         msg_Warn( p_access, "Build: "\
1314             "Cannot CoCreate the Universal Network Provider, trying the old way...");
1315         hr = ::CoCreateInstance( guid_network_type, NULL, CLSCTX_INPROC_SERVER,
1316             IID_IBaseFilter, (void**)&p_network_provider);
1317         if( FAILED( hr ) )
1318         {
1319             msg_Warn( p_access, "Build: "\
1320                 "Cannot CoCreate Network Provider: hr=0x%8lx", hr );
1321             return hr;
1322         }
1323     }
1324     hr = p_filter_graph->AddFilter( p_network_provider, L"Network Provider" );
1325     if( FAILED( hr ) )
1326     {
1327         msg_Warn( p_access, "Build: "\
1328             "Cannot load network provider: hr=0x%8lx", hr );
1329         return hr;
1330     }
1331
1332     /* Add the Network Tuner to the Network Provider. On subsequent calls,
1333      * l_tuner_used will cause a different tuner to be selected
1334      * To select a specific device first get the parameter that nominates the
1335      * device (dvb-adapter) and use the value to initialise l_tuner_used.
1336      * When FindFilter returns check the contents of l_tuner_used.
1337      * If it is not what was selected then the requested device was not
1338      * available so return with an error. */
1339
1340     long l_adapter = -1;
1341     l_adapter = var_GetInteger( p_access, "dvb-adapter" );
1342     if( l_tuner_used < 0 && l_adapter >= 0 )
1343         l_tuner_used = l_adapter - 1;
1344
1345     hr = FindFilter( KSCATEGORY_BDA_NETWORK_TUNER, &l_tuner_used,
1346         p_network_provider, &p_tuner_device );
1347     if( FAILED( hr ) )
1348     {
1349         msg_Warn( p_access, "Build: "\
1350             "Cannot load tuner device and connect network provider: "\
1351             "hr=0x%8lx", hr );
1352         return hr;
1353     }
1354     if( l_adapter > 0 && l_tuner_used != l_adapter )
1355     {
1356          msg_Warn( p_access, "Build: "\
1357              "Tuner device %d is not available", l_adapter );
1358         return E_FAIL;
1359     }
1360     msg_Dbg( p_access, "BDAGraph: Using adapter %d", l_tuner_used );
1361
1362 /* VLC 1.0 works reliably up this point then crashes
1363  * Obvious candidate is FindFilter */
1364     /* Always look for all capture devices to match the Network Tuner */
1365     l_capture_used = -1;
1366     hr = FindFilter( KSCATEGORY_BDA_RECEIVER_COMPONENT, &l_capture_used,
1367         p_tuner_device, &p_capture_device );
1368     if( FAILED( hr ) )
1369     {
1370         /* Some BDA drivers do not provide a Capture Device Filter so force
1371          * the Sample Grabber to connect directly to the Tuner Device */
1372         p_capture_device = p_tuner_device;
1373         p_tuner_device = NULL;
1374         msg_Warn( p_access, "Build: "\
1375             "Cannot find Capture device. Connecting to tuner: hr=0x%8lx", hr );
1376     }
1377
1378     hr = p_network_provider->QueryInterface( IID_IScanningTuner,
1379         (void**)&p_scanning_tuner );
1380     if( FAILED( hr ) )
1381     {
1382         msg_Warn( p_access, "Build: "\
1383             "Cannot QI Network Provider for Scanning Tuner: hr=0x%8lx", hr );
1384         return hr;
1385     }
1386
1387     hr = p_scanning_tuner->Validate( p_tune_request );
1388     if( FAILED( hr ) )
1389     {
1390         msg_Warn( p_access, "Build: "\
1391             "Tune Request is invalid: hr=0x%8lx", hr );
1392         //return hr; it is not mandatory to validate. Validate fails, but the request is successfully accepted
1393     }
1394     hr = p_scanning_tuner->put_TuneRequest( p_tune_request );
1395     if( FAILED( hr ) )
1396     {
1397         msg_Warn( p_access, "Build: "\
1398             "Cannot submit the tune request: hr=0x%8lx", hr );
1399         return hr;
1400     }
1401
1402     if( p_sample_grabber )
1403          p_sample_grabber->Release();
1404     p_sample_grabber = NULL;
1405     /* Insert the Sample Grabber to tap into the Transport Stream. */
1406     hr = ::CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
1407         IID_IBaseFilter, (void**)&p_sample_grabber );
1408     if( FAILED( hr ) )
1409     {
1410         msg_Warn( p_access, "Build: "\
1411             "Cannot load Sample Grabber Filter: hr=0x%8lx", hr );
1412         return hr;
1413     }
1414     hr = p_filter_graph->AddFilter( p_sample_grabber, L"Sample Grabber" );
1415     if( FAILED( hr ) )
1416     {
1417         msg_Warn( p_access, "Build: "\
1418             "Cannot add Sample Grabber Filter to graph: hr=0x%8lx", hr );
1419         return hr;
1420     }
1421
1422     if( p_grabber )
1423         p_grabber->Release();
1424     p_grabber = NULL;
1425     hr = p_sample_grabber->QueryInterface( IID_ISampleGrabber,
1426         (void**)&p_grabber );
1427     if( FAILED( hr ) )
1428     {
1429         msg_Warn( p_access, "Build: "\
1430             "Cannot QI Sample Grabber Filter: hr=0x%8lx", hr );
1431         return hr;
1432     }
1433
1434     /* Try the possible stream type */
1435     hr = E_FAIL;
1436     for( int i = 0; i < 2; i++ )
1437     {
1438         ZeroMemory( &grabber_media_type, sizeof( AM_MEDIA_TYPE ) );
1439         grabber_media_type.majortype = MEDIATYPE_Stream;
1440         grabber_media_type.subtype   =  i == 0 ? MEDIASUBTYPE_MPEG2_TRANSPORT : KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT;
1441         msg_Dbg( p_access, "Build: "
1442                            "Trying connecting with subtype %s",
1443                            i == 0 ? "MEDIASUBTYPE_MPEG2_TRANSPORT" : "KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT" );
1444         hr = p_grabber->SetMediaType( &grabber_media_type );
1445         if( SUCCEEDED( hr ) )
1446         {
1447             hr = Connect( p_capture_device, p_sample_grabber );
1448             if( SUCCEEDED( hr ) )
1449                 break;
1450             msg_Warn( p_access, "Build: "\
1451                 "Cannot connect Sample Grabber to Capture device: hr=0x%8lx (try %d/2)", hr, 1+i );
1452         }
1453         else
1454         {
1455             msg_Warn( p_access, "Build: "\
1456                 "Cannot set media type on grabber filter: hr=0x%8lx (try %d/2", hr, 1+i );
1457         }
1458     }
1459     if( hr )
1460         return hr;
1461
1462     /* We need the MPEG2 Demultiplexer even though we are going to use the VLC
1463      * TS demuxer. The TIF filter connects to the MPEG2 demux and works with
1464      * the Network Provider filter to set up the stream */
1465     hr = ::CoCreateInstance( CLSID_MPEG2Demultiplexer, NULL,
1466         CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&p_mpeg_demux );
1467     if( FAILED( hr ) )
1468     {
1469         msg_Warn( p_access, "Build: "\
1470             "Cannot CoCreateInstance MPEG2 Demultiplexer: hr=0x%8lx", hr );
1471         return hr;
1472     }
1473     hr = p_filter_graph->AddFilter( p_mpeg_demux, L"Demux" );
1474     if( FAILED( hr ) )
1475     {
1476         msg_Warn( p_access, "Build: "\
1477             "Cannot add demux filter to graph: hr=0x%8lx", hr );
1478         return hr;
1479     }
1480     hr = Connect( p_sample_grabber, p_mpeg_demux );
1481     if( FAILED( hr ) )
1482     {
1483         msg_Warn( p_access, "Build: "\
1484             "Cannot connect demux to grabber: hr=0x%8lx", hr );
1485         return hr;
1486     }
1487
1488     /* Always look for the Transform Information Filter from the start
1489      * of the collection*/
1490     l_tif_used = -1;
1491     hr = FindFilter( KSCATEGORY_BDA_TRANSPORT_INFORMATION, &l_tif_used,
1492         p_mpeg_demux, &p_transport_info );
1493     if( FAILED( hr ) )
1494     {
1495         msg_Warn( p_access, "Build: "\
1496             "Cannot load TIF onto demux: hr=0x%8lx", hr );
1497         return hr;
1498     }
1499     /* Configure the Sample Grabber to buffer the samples continuously */
1500     hr = p_grabber->SetBufferSamples( true );
1501     if( FAILED( hr ) )
1502     {
1503         msg_Warn( p_access, "Build: "\
1504             "Cannot set Sample Grabber to buffering: hr=0x%8lx", hr );
1505         return hr;
1506     }
1507     hr = p_grabber->SetOneShot( false );
1508     if( FAILED( hr ) )
1509     {
1510         msg_Warn( p_access, "Build: "\
1511             "Cannot set Sample Grabber to multi shot: hr=0x%8lx", hr );
1512         return hr;
1513     }
1514     hr = p_grabber->SetCallback( this, 0 );
1515     if( FAILED( hr ) )
1516     {
1517         msg_Warn( p_access, "Build: "\
1518             "Cannot set SampleGrabber Callback: hr=0x%8lx", hr );
1519         return hr;
1520     }
1521
1522     hr = Register();
1523     if( FAILED( hr ) )
1524     {
1525         d_graph_register = 0;
1526     }
1527
1528     /* The Media Control is used to Run and Stop the Graph */
1529     if( p_media_control )
1530         p_media_control->Release();
1531     p_media_control = NULL;
1532     hr = p_filter_graph->QueryInterface( IID_IMediaControl,
1533         (void**)&p_media_control );
1534     if( FAILED( hr ) )
1535     {
1536         msg_Warn( p_access, "Build: "\
1537             "Cannot QI Media Control: hr=0x%8lx", hr );
1538         return hr;
1539     }
1540
1541     return hr;
1542
1543 }
1544
1545 /******************************************************************************
1546 * FindFilter
1547 * Looks up all filters in a category and connects to the upstream filter until
1548 * a successful match is found. The index of the connected filter is returned.
1549 * On subsequent calls, this can be used to start from that point to find
1550 * another match.
1551 * This is used when the graph does not run because a tuner device is in use so
1552 * another one needs to be selected.
1553 ******************************************************************************/
1554 HRESULT BDAGraph::FindFilter( REFCLSID clsid, long* i_moniker_used,
1555     IBaseFilter* p_upstream, IBaseFilter** p_p_downstream )
1556 {
1557     HRESULT                 hr = S_OK;
1558     int                     i_moniker_index = -1;
1559     class localComPtr
1560     {
1561         public:
1562         IMoniker*      p_moniker;
1563         IEnumMoniker*  p_moniker_enum;
1564         IBaseFilter*   p_filter;
1565         IPropertyBag*  p_property_bag;
1566         VARIANT        var_bstr;
1567         char *         psz_bstr;
1568         int            i_bstr_len;
1569         localComPtr():
1570             p_moniker(NULL),
1571             p_moniker_enum(NULL),
1572             p_filter(NULL),
1573             p_property_bag(NULL),
1574             psz_bstr( NULL )
1575             { ::VariantInit(&var_bstr); };
1576         ~localComPtr()
1577         {
1578             if( p_property_bag )
1579                 p_property_bag->Release();
1580             if( p_filter )
1581                 p_filter->Release();
1582             if( p_moniker )
1583                 p_moniker->Release();
1584             if( p_moniker_enum )
1585                 p_moniker_enum->Release();
1586             ::VariantClear(&var_bstr);
1587             delete[] psz_bstr;
1588         }
1589     } l;
1590
1591     if( !p_system_dev_enum )
1592     {
1593         hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
1594             IID_ICreateDevEnum, (void**)&p_system_dev_enum );
1595         if( FAILED( hr ) )
1596         {
1597             msg_Warn( p_access, "FindFilter: "\
1598                 "Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
1599             return hr;
1600         }
1601     }
1602
1603     hr = p_system_dev_enum->CreateClassEnumerator( clsid,
1604         &l.p_moniker_enum, 0 );
1605     if( hr != S_OK )
1606     {
1607         msg_Warn( p_access, "FindFilter: "\
1608             "Cannot CreateClassEnumerator: hr=0x%8lx", hr );
1609         return E_FAIL;
1610     }
1611
1612     do
1613     {
1614         /* We are overwriting l.p_moniker so we should Release and nullify
1615          * It is important that p_moniker and p_property_bag are fully released
1616          * l.p_filter may not be dereferenced so we could force to NULL */
1617         if( l.p_property_bag )
1618             l.p_property_bag->Release();
1619         l.p_property_bag = NULL;
1620         if( l.p_filter )
1621             l.p_filter->Release();
1622         l.p_filter = NULL;
1623         if( l.p_moniker )
1624             l.p_moniker->Release();
1625          l.p_moniker = NULL;
1626
1627         hr = l.p_moniker_enum->Next( 1, &l.p_moniker, 0 );
1628         if( hr != S_OK ) break;
1629         i_moniker_index++;
1630
1631         /* Skip over devices already found on previous calls */
1632         if( i_moniker_index <= *i_moniker_used ) continue;
1633         *i_moniker_used = i_moniker_index;
1634
1635         /* l.p_filter is Released at the top of the loop */
1636         hr = l.p_moniker->BindToObject( NULL, NULL, IID_IBaseFilter,
1637             (void**)&l.p_filter );
1638         if( FAILED( hr ) )
1639         {
1640             continue;
1641         }
1642         /* l.p_property_bag is released at the top of the loop */
1643         hr = l.p_moniker->BindToStorage( NULL, NULL, IID_IPropertyBag,
1644             (void**)&l.p_property_bag );
1645         if( FAILED( hr ) )
1646         {
1647             msg_Warn( p_access, "FindFilter: "\
1648                 "Cannot Bind to Property Bag: hr=0x%8lx", hr );
1649             return hr;
1650         }
1651         hr = l.p_property_bag->Read( L"FriendlyName", &l.var_bstr, NULL );
1652         if( FAILED( hr ) )
1653         {
1654             msg_Warn( p_access, "FindFilter: "\
1655                 "Cannot read filter friendly name: hr=0x%8lx", hr );
1656             return hr;
1657         }
1658
1659         hr = p_filter_graph->AddFilter( l.p_filter, l.var_bstr.bstrVal );
1660         if( FAILED( hr ) )
1661         {
1662             msg_Warn( p_access, "FindFilter: "\
1663                 "Cannot add filter: hr=0x%8lx", hr );
1664             return hr;
1665         }
1666         hr = Connect( p_upstream, l.p_filter );
1667         if( SUCCEEDED( hr ) )
1668         {
1669             /* p_p_downstream has not been touched yet so no release needed */
1670             delete[] l.psz_bstr;
1671             l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0,
1672                 l.var_bstr.bstrVal, -1, l.psz_bstr, 0, NULL, NULL );
1673             l.psz_bstr = new char[l.i_bstr_len];
1674             l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0,
1675                 l.var_bstr.bstrVal, -1, l.psz_bstr, l.i_bstr_len, NULL, NULL );
1676             msg_Dbg( p_access, "FindFilter: Connected %s", l.psz_bstr );
1677             l.p_filter->QueryInterface( IID_IBaseFilter,
1678                 (void**)p_p_downstream );
1679             return S_OK;
1680         }
1681         /* Not the filter we want so unload and try the next one */
1682         hr = p_filter_graph->RemoveFilter( l.p_filter );
1683         if( FAILED( hr ) )
1684         {
1685             msg_Warn( p_access, "FindFilter: "\
1686                 "Failed unloading Filter: hr=0x%8lx", hr );
1687             return hr;
1688         }
1689
1690     }
1691     while( true );
1692
1693     hr = E_FAIL;
1694     msg_Warn( p_access, "FindFilter: No filter connected: hr=0x%8lx", hr );
1695     return hr;
1696 }
1697
1698 /*****************************************************************************
1699 * Connect is called from Build to enumerate and connect pins
1700 *****************************************************************************/
1701 HRESULT BDAGraph::Connect( IBaseFilter* p_upstream, IBaseFilter* p_downstream )
1702 {
1703     HRESULT             hr = E_FAIL;
1704     class localComPtr
1705     {
1706         public:
1707         IPin*      p_pin_upstream;
1708         IPin*      p_pin_downstream;
1709         IEnumPins* p_pin_upstream_enum;
1710         IEnumPins* p_pin_downstream_enum;
1711         IPin*      p_pin_temp;
1712         localComPtr(): p_pin_upstream(NULL), p_pin_downstream(NULL),
1713             p_pin_upstream_enum(NULL), p_pin_downstream_enum(NULL),
1714             p_pin_temp(NULL) { };
1715         ~localComPtr()
1716         {
1717             if( p_pin_temp )
1718                 p_pin_temp->Release();
1719             if( p_pin_downstream )
1720                 p_pin_downstream->Release();
1721             if( p_pin_upstream )
1722                 p_pin_upstream->Release();
1723             if( p_pin_downstream_enum )
1724                 p_pin_downstream_enum->Release();
1725             if( p_pin_upstream_enum )
1726                 p_pin_upstream_enum->Release();
1727         }
1728     } l;
1729
1730     PIN_DIRECTION pin_dir;
1731
1732     hr = p_upstream->EnumPins( &l.p_pin_upstream_enum );
1733     if( FAILED( hr ) )
1734     {
1735         msg_Warn( p_access, "Connect: "\
1736             "Cannot get upstream filter enumerator: hr=0x%8lx", hr );
1737         return hr;
1738     }
1739
1740     do
1741     {
1742         /* Release l.p_pin_upstream before next iteration */
1743         if( l.p_pin_upstream  )
1744             l.p_pin_upstream ->Release();
1745         l.p_pin_upstream = NULL;
1746         hr = l.p_pin_upstream_enum->Next( 1, &l.p_pin_upstream, 0 );
1747         if( hr != S_OK ) break;
1748
1749         hr = l.p_pin_upstream->QueryDirection( &pin_dir );
1750         if( FAILED( hr ) )
1751         {
1752             msg_Warn( p_access, "Connect: "\
1753                 "Cannot get upstream filter pin direction: hr=0x%8lx", hr );
1754             return hr;
1755         }
1756         hr = l.p_pin_upstream->ConnectedTo( &l.p_pin_downstream );
1757         if( SUCCEEDED( hr ) )
1758         {
1759             l.p_pin_downstream->Release();
1760             l.p_pin_downstream = NULL;
1761         }
1762         if( FAILED( hr ) && hr != VFW_E_NOT_CONNECTED )
1763         {
1764             msg_Warn( p_access, "Connect: "\
1765                 "Cannot check upstream filter connection: hr=0x%8lx", hr );
1766             return hr;
1767         }
1768         if( ( pin_dir == PINDIR_OUTPUT ) && ( hr == VFW_E_NOT_CONNECTED ) )
1769         {
1770             /* The upstream pin is not yet connected so check each pin on the
1771              * downstream filter */
1772             hr = p_downstream->EnumPins( &l.p_pin_downstream_enum );
1773             if( FAILED( hr ) )
1774             {
1775                 msg_Warn( p_access, "Connect: Cannot get "\
1776                     "downstream filter enumerator: hr=0x%8lx", hr );
1777                 return hr;
1778             }
1779             do
1780             {
1781                 /* Release l.p_pin_downstream before next iteration */
1782                 if( l.p_pin_downstream  )
1783                     l.p_pin_downstream ->Release();
1784                 l.p_pin_downstream = NULL;
1785
1786                 hr = l.p_pin_downstream_enum->Next( 1, &l.p_pin_downstream, 0 );
1787                 if( hr != S_OK ) break;
1788
1789                 hr = l.p_pin_downstream->QueryDirection( &pin_dir );
1790                 if( FAILED( hr ) )
1791                 {
1792                     msg_Warn( p_access, "Connect: Cannot get "\
1793                         "downstream filter pin direction: hr=0x%8lx", hr );
1794                     return hr;
1795                 }
1796
1797                 /* Looking for a free Pin to connect to
1798                  * A connected Pin may have an reference count > 1
1799                  * so Release and nullify the pointer */
1800                 hr = l.p_pin_downstream->ConnectedTo( &l.p_pin_temp );
1801                 if( SUCCEEDED( hr ) )
1802                 {
1803                     l.p_pin_temp->Release();
1804                     l.p_pin_temp = NULL;
1805                 }
1806                 if( hr != VFW_E_NOT_CONNECTED )
1807                 {
1808                     if( FAILED( hr ) )
1809                     {
1810                         msg_Warn( p_access, "Connect: Cannot check "\
1811                             "downstream filter connection: hr=0x%8lx", hr );
1812                         return hr;
1813                     }
1814                 }
1815                 if( ( pin_dir == PINDIR_INPUT ) &&
1816                     ( hr == VFW_E_NOT_CONNECTED ) )
1817                 {
1818                     hr = p_filter_graph->ConnectDirect( l.p_pin_upstream,
1819                         l.p_pin_downstream, NULL );
1820                     if( SUCCEEDED( hr ) )
1821                     {
1822                         /* If we arrive here then we have a matching pair of
1823                          * pins. */
1824                         return S_OK;
1825                     }
1826                 }
1827                 /* If we arrive here it means this downstream pin is not
1828                  * suitable so try the next downstream pin.
1829                  * l.p_pin_downstream is released at the top of the loop */
1830             }
1831             while( true );
1832             /* If we arrive here then we ran out of pins before we found a
1833              * suitable one. Release outstanding refcounts */
1834             if( l.p_pin_downstream_enum )
1835                 l.p_pin_downstream_enum->Release();
1836             l.p_pin_downstream_enum = NULL;
1837             if( l.p_pin_downstream )
1838                 l.p_pin_downstream->Release();
1839             l.p_pin_downstream = NULL;
1840         }
1841         /* If we arrive here it means this upstream pin is not suitable
1842          * so try the next upstream pin
1843          * l.p_pin_upstream is released at the top of the loop */
1844     }
1845     while( true );
1846     /* If we arrive here it means we did not find any pair of suitable pins
1847      * Outstanding refcounts are released in the destructor */
1848     return E_FAIL;
1849 }
1850
1851 /*****************************************************************************
1852 * Start uses MediaControl to start the graph
1853 *****************************************************************************/
1854 HRESULT BDAGraph::Start()
1855 {
1856     HRESULT hr = S_OK;
1857     OAFilterState i_state; /* State_Stopped, State_Paused, State_Running */
1858
1859     if( !p_media_control )
1860     {
1861         msg_Dbg( p_access, "Start: Media Control has not been created" );
1862         return E_FAIL;
1863     }
1864     hr = p_media_control->Run();
1865     msg_Dbg( p_access, "Graph started hr=0x%lx", hr );
1866     if( hr == S_OK )
1867         return hr;
1868
1869     /* Query the state of the graph - timeout after 100 milliseconds */
1870     while( hr = p_media_control->GetState( 100, &i_state ) != S_OK )
1871     {
1872         if( FAILED( hr ) )
1873         {
1874             msg_Warn( p_access,
1875                 "Start: Cannot get Graph state: hr=0x%8lx", hr );
1876             return hr;
1877         }
1878     }
1879     if( i_state == State_Running )
1880         return hr;
1881
1882     /* The Graph is not running so stop it and return an error */
1883     msg_Warn( p_access, "Start: Graph not started: %d", i_state );
1884     hr = p_media_control->StopWhenReady(); /* Instead of Stop() */
1885     if( FAILED( hr ) )
1886     {
1887         msg_Warn( p_access,
1888             "Start: Cannot stop Graph after Run failed: hr=0x%8lx", hr );
1889         return hr;
1890     }
1891     return E_FAIL;
1892 }
1893
1894 /*****************************************************************************
1895 * Pop the stream of data
1896 *****************************************************************************/
1897 block_t *BDAGraph::Pop()
1898 {
1899     return output.Pop();
1900 }
1901
1902 /******************************************************************************
1903 * SampleCB - Callback when the Sample Grabber has a sample
1904 ******************************************************************************/
1905 STDMETHODIMP BDAGraph::SampleCB( double d_time, IMediaSample *p_sample )
1906 {
1907     if( p_sample->IsDiscontinuity() == S_OK )
1908         msg_Warn( p_access, "BDA SampleCB: Sample Discontinuity.");
1909
1910     const size_t i_sample_size = p_sample->GetActualDataLength();
1911     BYTE *p_sample_data;
1912     p_sample->GetPointer( &p_sample_data );
1913
1914     if( i_sample_size > 0 && p_sample_data )
1915     {
1916         block_t *p_block = block_New( p_access, i_sample_size );
1917
1918         if( p_block )
1919         {
1920             memcpy( p_block->p_buffer, p_sample_data, i_sample_size );
1921             output.Push( p_block );
1922         }
1923      }
1924      return S_OK;
1925 }
1926
1927 STDMETHODIMP BDAGraph::BufferCB( double d_time, BYTE* p_buffer,
1928     long l_buffer_len )
1929 {
1930     return E_FAIL;
1931 }
1932
1933 /******************************************************************************
1934 * removes each filter from the graph
1935 ******************************************************************************/
1936 HRESULT BDAGraph::Destroy()
1937 {
1938     HRESULT hr = S_OK;
1939     ULONG ul_refcount = 0;
1940
1941     if( p_media_control )
1942         hr = p_media_control->StopWhenReady(); /* Instead of Stop() */
1943
1944     if( d_graph_register )
1945     {
1946         Deregister();
1947     }
1948
1949     output.Empty();
1950
1951     if( p_grabber )
1952     {
1953         p_grabber->Release();
1954         p_grabber = NULL;
1955     }
1956
1957     if( p_transport_info )
1958     {
1959         p_filter_graph->RemoveFilter( p_transport_info );
1960         p_transport_info->Release();
1961         p_transport_info = NULL;
1962     }
1963     if( p_mpeg_demux )
1964     {
1965         p_filter_graph->RemoveFilter( p_mpeg_demux );
1966         p_mpeg_demux->Release();
1967         p_mpeg_demux = NULL;
1968     }
1969     if( p_sample_grabber )
1970     {
1971         p_filter_graph->RemoveFilter( p_sample_grabber );
1972         p_sample_grabber->Release();
1973         p_sample_grabber = NULL;
1974     }
1975     if( p_capture_device )
1976     {
1977         p_filter_graph->RemoveFilter( p_capture_device );
1978         p_capture_device->Release();
1979         p_capture_device = NULL;
1980     }
1981     if( p_tuner_device )
1982     {
1983         p_filter_graph->RemoveFilter( p_tuner_device );
1984         p_tuner_device->Release();
1985         p_tuner_device = NULL;
1986     }
1987     if( p_scanning_tuner )
1988     {
1989         p_scanning_tuner->Release();
1990         p_scanning_tuner = NULL;
1991     }
1992     if( p_network_provider )
1993     {
1994         p_filter_graph->RemoveFilter( p_network_provider );
1995         p_network_provider->Release();
1996         p_network_provider = NULL;
1997     }
1998
1999     if( p_media_control )
2000     {
2001         p_media_control->Release();
2002         p_media_control = NULL;
2003     }
2004     if( p_filter_graph )
2005     {
2006         p_filter_graph->Release();
2007         p_filter_graph = NULL;
2008     }
2009     if( p_system_dev_enum )
2010     {
2011         p_system_dev_enum->Release();
2012         p_system_dev_enum = NULL;
2013     }
2014
2015     return S_OK;
2016 }
2017
2018 /*****************************************************************************
2019 * Add/Remove a DirectShow filter graph to/from the Running Object Table.
2020 * Allows GraphEdit to "spy" on a remote filter graph.
2021 ******************************************************************************/
2022 HRESULT BDAGraph::Register()
2023 {
2024     class localComPtr
2025     {
2026         public:
2027         IMoniker*             p_moniker;
2028         IRunningObjectTable*  p_ro_table;
2029         localComPtr(): p_moniker(NULL), p_ro_table(NULL) {};
2030         ~localComPtr()
2031         {
2032             if( p_moniker )
2033                 p_moniker->Release();
2034             if( p_ro_table )
2035                 p_ro_table->Release();
2036         }
2037     } l;
2038     WCHAR     psz_w_graph_name[128];
2039     HRESULT   hr;
2040
2041     hr = ::GetRunningObjectTable( 0, &l.p_ro_table );
2042     if( FAILED( hr ) )
2043     {
2044         msg_Warn( p_access, "Register: Cannot get ROT: hr=0x%8lx", hr );
2045         return hr;
2046     }
2047
2048     wsprintfW( psz_w_graph_name, L"VLC BDA Graph %08x Pid %08x",
2049         (DWORD_PTR) p_filter_graph, ::GetCurrentProcessId() );
2050     hr = CreateItemMoniker( L"!", psz_w_graph_name, &l.p_moniker );
2051     if( FAILED( hr ) )
2052     {
2053         msg_Warn( p_access, "Register: Cannot Create Moniker: hr=0x%8lx", hr );
2054         return hr;
2055     }
2056     hr = l.p_ro_table->Register( ROTFLAGS_REGISTRATIONKEEPSALIVE,
2057         p_filter_graph, l.p_moniker, &d_graph_register );
2058     if( FAILED( hr ) )
2059     {
2060         msg_Warn( p_access, "Register: Cannot Register Graph: hr=0x%8lx", hr );
2061         return hr;
2062     }
2063 //    msg_Dbg( p_access, "Register: registered Graph: %S", psz_w_graph_name );
2064     return hr;
2065 }
2066
2067 void BDAGraph::Deregister()
2068 {
2069     HRESULT   hr;
2070     IRunningObjectTable* p_ro_table;
2071     hr = ::GetRunningObjectTable( 0, &p_ro_table );
2072     if( SUCCEEDED( hr ) )
2073         p_ro_table->Revoke( d_graph_register );
2074     d_graph_register = 0;
2075     p_ro_table->Release();
2076 }