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