]> git.sesse.net Git - vlc/blob - modules/access/bda/bdagraph.cpp
"strlen( psz ) == 0" => "*psz == '\0'"
[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     hr = ::CoCreateInstance( guid_network_type, NULL, CLSCTX_INPROC_SERVER,
1209         IID_IBaseFilter, (void**)&p_network_provider);
1210     if( FAILED( hr ) )
1211     {
1212         msg_Warn( p_access, "Build: "\
1213             "Cannot CoCreate Network Provider: hr=0x%8lx", hr );
1214         return hr;
1215     }
1216     hr = p_filter_graph->AddFilter( p_network_provider, L"Network Provider" );
1217     if( FAILED( hr ) )
1218     {
1219         msg_Warn( p_access, "Build: "\
1220             "Cannot load network provider: hr=0x%8lx", hr );
1221         return hr;
1222     }
1223
1224     hr = p_network_provider->QueryInterface( IID_IScanningTuner,
1225         (void**)&p_scanning_tuner );
1226     if( FAILED( hr ) )
1227     {
1228         msg_Warn( p_access, "Build: "\
1229             "Cannot QI Network Provider for Scanning Tuner: hr=0x%8lx", hr );
1230         return hr;
1231     }
1232
1233     hr = p_scanning_tuner->Validate( p_tune_request );
1234     if( FAILED( hr ) )
1235     {
1236         msg_Warn( p_access, "Build: "\
1237             "Tune Request is invalid: hr=0x%8lx", hr );
1238         return hr;
1239     }
1240     hr = p_scanning_tuner->put_TuneRequest( p_tune_request );
1241     if( FAILED( hr ) )
1242     {
1243         msg_Warn( p_access, "Build: "\
1244             "Cannot submit the tune request: hr=0x%8lx", hr );
1245         return hr;
1246     }
1247
1248     /* Add the Network Tuner to the Network Provider. On subsequent calls,
1249      * l_tuner_used will cause a different tuner to be selected
1250      * To select a specific device first get the parameter that nominates the
1251      * device (dvb-adapter) and use the value to initialise l_tuner_used.
1252      * When FindFilter returns check the contents of l_tuner_used.
1253      * If it is not what was selected then the requested device was not
1254      * available so return with an error. */
1255
1256     long l_adapter = -1;
1257     l_adapter = var_GetInteger( p_access, "dvb-adapter" );
1258     if( l_tuner_used < 0 && l_adapter >= 0 )
1259         l_tuner_used = l_adapter - 1;
1260
1261     hr = FindFilter( KSCATEGORY_BDA_NETWORK_TUNER, &l_tuner_used,
1262         p_network_provider, &p_tuner_device );
1263     if( FAILED( hr ) )
1264     {
1265         msg_Warn( p_access, "Build: "\
1266             "Cannot load tuner device and connect network provider: "\
1267             "hr=0x%8lx", hr );
1268         return hr;
1269     }
1270     if( l_adapter > 0 && l_tuner_used != l_adapter )
1271     {
1272          msg_Warn( p_access, "Build: "\
1273              "Tuner device %d is not available", l_adapter );
1274         return E_FAIL;
1275     }
1276     msg_Dbg( p_access, "BDAGraph: Using adapter %d", l_tuner_used );
1277
1278 /* VLC 1.0 works reliably up this point then crashes
1279  * Obvious candidate is FindFilter */
1280     /* Always look for all capture devices to match the Network Tuner */
1281     l_capture_used = -1;
1282     hr = FindFilter( KSCATEGORY_BDA_RECEIVER_COMPONENT, &l_capture_used,
1283         p_tuner_device, &p_capture_device );
1284     if( FAILED( hr ) )
1285     {
1286         /* Some BDA drivers do not provide a Capture Device Filter so force
1287          * the Sample Grabber to connect directly to the Tuner Device */
1288         p_capture_device = p_tuner_device;
1289         p_tuner_device = NULL;
1290         msg_Warn( p_access, "Build: "\
1291             "Cannot find Capture device. Connecting to tuner: hr=0x%8lx", hr );
1292     }
1293     if( p_sample_grabber )
1294          p_sample_grabber->Release();
1295     p_sample_grabber = NULL;
1296     /* Insert the Sample Grabber to tap into the Transport Stream. */
1297     hr = ::CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
1298         IID_IBaseFilter, (void**)&p_sample_grabber );
1299     if( FAILED( hr ) )
1300     {
1301         msg_Warn( p_access, "Build: "\
1302             "Cannot load Sample Grabber Filter: hr=0x%8lx", hr );
1303         return hr;
1304     }
1305     hr = p_filter_graph->AddFilter( p_sample_grabber, L"Sample Grabber" );
1306     if( FAILED( hr ) )
1307     {
1308         msg_Warn( p_access, "Build: "\
1309             "Cannot add Sample Grabber Filter to graph: hr=0x%8lx", hr );
1310         return hr;
1311     }
1312
1313     if( p_grabber )
1314         p_grabber->Release();
1315     p_grabber = NULL;
1316     hr = p_sample_grabber->QueryInterface( IID_ISampleGrabber,
1317         (void**)&p_grabber );
1318     if( FAILED( hr ) )
1319     {
1320         msg_Warn( p_access, "Build: "\
1321             "Cannot QI Sample Grabber Filter: hr=0x%8lx", hr );
1322         return hr;
1323     }
1324
1325     /* Try the possible stream type */
1326     hr = E_FAIL;
1327     for( int i = 0; i < 2; i++ )
1328     {
1329         ZeroMemory( &grabber_media_type, sizeof( AM_MEDIA_TYPE ) );
1330         grabber_media_type.majortype = MEDIATYPE_Stream;
1331         grabber_media_type.subtype   =  i == 0 ? MEDIASUBTYPE_MPEG2_TRANSPORT : KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT;
1332         msg_Dbg( p_access, "Build: "
1333                            "Trying connecting with subtype %s",
1334                            i == 0 ? "MEDIASUBTYPE_MPEG2_TRANSPORT" : "KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT" );
1335         hr = p_grabber->SetMediaType( &grabber_media_type );
1336         if( SUCCEEDED( hr ) )
1337         {
1338             hr = Connect( p_capture_device, p_sample_grabber );
1339             if( SUCCEEDED( hr ) )
1340                 break;
1341             msg_Warn( p_access, "Build: "\
1342                 "Cannot connect Sample Grabber to Capture device: hr=0x%8lx (try %d/2)", hr, 1+i );
1343         }
1344         else
1345         {
1346             msg_Warn( p_access, "Build: "\
1347                 "Cannot set media type on grabber filter: hr=0x%8lx (try %d/2", hr, 1+i );
1348         }
1349     }
1350     if( hr )
1351         return hr;
1352
1353     /* We need the MPEG2 Demultiplexer even though we are going to use the VLC
1354      * TS demuxer. The TIF filter connects to the MPEG2 demux and works with
1355      * the Network Provider filter to set up the stream */
1356     hr = ::CoCreateInstance( CLSID_MPEG2Demultiplexer, NULL,
1357         CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&p_mpeg_demux );
1358     if( FAILED( hr ) )
1359     {
1360         msg_Warn( p_access, "Build: "\
1361             "Cannot CoCreateInstance MPEG2 Demultiplexer: hr=0x%8lx", hr );
1362         return hr;
1363     }
1364     hr = p_filter_graph->AddFilter( p_mpeg_demux, L"Demux" );
1365     if( FAILED( hr ) )
1366     {
1367         msg_Warn( p_access, "Build: "\
1368             "Cannot add demux filter to graph: hr=0x%8lx", hr );
1369         return hr;
1370     }
1371     hr = Connect( p_sample_grabber, p_mpeg_demux );
1372     if( FAILED( hr ) )
1373     {
1374         msg_Warn( p_access, "Build: "\
1375             "Cannot connect demux to grabber: hr=0x%8lx", hr );
1376         return hr;
1377     }
1378
1379     /* Always look for the Transform Information Filter from the start
1380      * of the collection*/
1381     l_tif_used = -1;
1382     hr = FindFilter( KSCATEGORY_BDA_TRANSPORT_INFORMATION, &l_tif_used,
1383         p_mpeg_demux, &p_transport_info );
1384     if( FAILED( hr ) )
1385     {
1386         msg_Warn( p_access, "Build: "\
1387             "Cannot load TIF onto demux: hr=0x%8lx", hr );
1388         return hr;
1389     }
1390     /* Configure the Sample Grabber to buffer the samples continuously */
1391     hr = p_grabber->SetBufferSamples( true );
1392     if( FAILED( hr ) )
1393     {
1394         msg_Warn( p_access, "Build: "\
1395             "Cannot set Sample Grabber to buffering: hr=0x%8lx", hr );
1396         return hr;
1397     }
1398     hr = p_grabber->SetOneShot( false );
1399     if( FAILED( hr ) )
1400     {
1401         msg_Warn( p_access, "Build: "\
1402             "Cannot set Sample Grabber to multi shot: hr=0x%8lx", hr );
1403         return hr;
1404     }
1405     hr = p_grabber->SetCallback( this, 0 );
1406     if( FAILED( hr ) )
1407     {
1408         msg_Warn( p_access, "Build: "\
1409             "Cannot set SampleGrabber Callback: hr=0x%8lx", hr );
1410         return hr;
1411     }
1412
1413     hr = Register();
1414     if( FAILED( hr ) )
1415     {
1416         d_graph_register = 0;
1417     }
1418
1419     /* The Media Control is used to Run and Stop the Graph */
1420     if( p_media_control )
1421         p_media_control->Release();
1422     p_media_control = NULL;
1423     hr = p_filter_graph->QueryInterface( IID_IMediaControl,
1424         (void**)&p_media_control );
1425     if( FAILED( hr ) )
1426     {
1427         msg_Warn( p_access, "Build: "\
1428             "Cannot QI Media Control: hr=0x%8lx", hr );
1429         return hr;
1430     }
1431
1432     return hr;
1433
1434 }
1435
1436 /******************************************************************************
1437 * FindFilter
1438 * Looks up all filters in a category and connects to the upstream filter until
1439 * a successful match is found. The index of the connected filter is returned.
1440 * On subsequent calls, this can be used to start from that point to find
1441 * another match.
1442 * This is used when the graph does not run because a tuner device is in use so
1443 * another one needs to be selected.
1444 ******************************************************************************/
1445 HRESULT BDAGraph::FindFilter( REFCLSID clsid, long* i_moniker_used,
1446     IBaseFilter* p_upstream, IBaseFilter** p_p_downstream )
1447 {
1448     HRESULT                 hr = S_OK;
1449     int                     i_moniker_index = -1;
1450     class localComPtr
1451     {
1452         public:
1453         IMoniker*      p_moniker;
1454         IEnumMoniker*  p_moniker_enum;
1455         IBaseFilter*   p_filter;
1456         IPropertyBag*  p_property_bag;
1457         VARIANT        var_bstr;
1458         char *         psz_bstr;
1459         int            i_bstr_len;
1460         localComPtr():
1461             p_moniker(NULL),
1462             p_moniker_enum(NULL),
1463             p_filter(NULL),
1464             p_property_bag(NULL),
1465             psz_bstr( NULL )
1466             { ::VariantInit(&var_bstr); };
1467         ~localComPtr()
1468         {
1469             if( p_property_bag )
1470                 p_property_bag->Release();
1471             if( p_filter )
1472                 p_filter->Release();
1473             if( p_moniker )
1474                 p_moniker->Release();
1475             if( p_moniker_enum )
1476                 p_moniker_enum->Release();
1477             ::VariantClear(&var_bstr);
1478             delete[] psz_bstr;
1479         }
1480     } l;
1481
1482     if( !p_system_dev_enum )
1483     {
1484         hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
1485             IID_ICreateDevEnum, (void**)&p_system_dev_enum );
1486         if( FAILED( hr ) )
1487         {
1488             msg_Warn( p_access, "FindFilter: "\
1489                 "Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
1490             return hr;
1491         }
1492     }
1493
1494     hr = p_system_dev_enum->CreateClassEnumerator( clsid,
1495         &l.p_moniker_enum, 0 );
1496     if( hr != S_OK )
1497     {
1498         msg_Warn( p_access, "FindFilter: "\
1499             "Cannot CreateClassEnumerator: hr=0x%8lx", hr );
1500         return E_FAIL;
1501     }
1502
1503     do
1504     {
1505         /* We are overwriting l.p_moniker so we should Release and nullify
1506          * It is important that p_moniker and p_property_bag are fully released
1507          * l.p_filter may not be dereferenced so we could force to NULL */
1508         if( l.p_property_bag )
1509             l.p_property_bag->Release();
1510         l.p_property_bag = NULL;
1511         if( l.p_filter )
1512             l.p_filter->Release();
1513         l.p_filter = NULL;
1514         if( l.p_moniker )
1515             l.p_moniker->Release();
1516          l.p_moniker = NULL;
1517
1518         hr = l.p_moniker_enum->Next( 1, &l.p_moniker, 0 );
1519         if( hr != S_OK ) break;
1520         i_moniker_index++;
1521
1522         /* Skip over devices already found on previous calls */
1523         if( i_moniker_index <= *i_moniker_used ) continue;
1524         *i_moniker_used = i_moniker_index;
1525
1526         /* l.p_filter is Released at the top of the loop */
1527         hr = l.p_moniker->BindToObject( NULL, NULL, IID_IBaseFilter,
1528             (void**)&l.p_filter );
1529         if( FAILED( hr ) )
1530         {
1531             continue;
1532         }
1533         /* l.p_property_bag is released at the top of the loop */
1534         hr = l.p_moniker->BindToStorage( NULL, NULL, IID_IPropertyBag,
1535             (void**)&l.p_property_bag );
1536         if( FAILED( hr ) )
1537         {
1538             msg_Warn( p_access, "FindFilter: "\
1539                 "Cannot Bind to Property Bag: hr=0x%8lx", hr );
1540             return hr;
1541         }
1542         hr = l.p_property_bag->Read( L"FriendlyName", &l.var_bstr, NULL );
1543         if( FAILED( hr ) )
1544         {
1545             msg_Warn( p_access, "FindFilter: "\
1546                 "Cannot read filter friendly name: hr=0x%8lx", hr );
1547             return hr;
1548         }
1549
1550         hr = p_filter_graph->AddFilter( l.p_filter, l.var_bstr.bstrVal );
1551         if( FAILED( hr ) )
1552         {
1553             msg_Warn( p_access, "FindFilter: "\
1554                 "Cannot add filter: hr=0x%8lx", hr );
1555             return hr;
1556         }
1557         hr = Connect( p_upstream, l.p_filter );
1558         if( SUCCEEDED( hr ) )
1559         {
1560             /* p_p_downstream has not been touched yet so no release needed */
1561             delete[] l.psz_bstr;
1562             l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0,
1563                 l.var_bstr.bstrVal, -1, l.psz_bstr, 0, NULL, NULL );
1564             l.psz_bstr = new char[l.i_bstr_len];
1565             l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0,
1566                 l.var_bstr.bstrVal, -1, l.psz_bstr, l.i_bstr_len, NULL, NULL );
1567             msg_Dbg( p_access, "FindFilter: Connected %s", l.psz_bstr );
1568             l.p_filter->QueryInterface( IID_IBaseFilter,
1569                 (void**)p_p_downstream );
1570             return S_OK;
1571         }
1572         /* Not the filter we want so unload and try the next one */
1573         hr = p_filter_graph->RemoveFilter( l.p_filter );
1574         if( FAILED( hr ) )
1575         {
1576             msg_Warn( p_access, "FindFilter: "\
1577                 "Failed unloading Filter: hr=0x%8lx", hr );
1578             return hr;
1579         }
1580
1581     }
1582     while( true );
1583
1584     hr = E_FAIL;
1585     msg_Warn( p_access, "FindFilter: No filter connected: hr=0x%8lx", hr );
1586     return hr;
1587 }
1588
1589 /*****************************************************************************
1590 * Connect is called from Build to enumerate and connect pins
1591 *****************************************************************************/
1592 HRESULT BDAGraph::Connect( IBaseFilter* p_upstream, IBaseFilter* p_downstream )
1593 {
1594     HRESULT             hr = E_FAIL;
1595     class localComPtr
1596     {
1597         public:
1598         IPin*      p_pin_upstream;
1599         IPin*      p_pin_downstream;
1600         IEnumPins* p_pin_upstream_enum;
1601         IEnumPins* p_pin_downstream_enum;
1602         IPin*      p_pin_temp;
1603         localComPtr(): p_pin_upstream(NULL), p_pin_downstream(NULL),
1604             p_pin_upstream_enum(NULL), p_pin_downstream_enum(NULL),
1605             p_pin_temp(NULL) { };
1606         ~localComPtr()
1607         {
1608             if( p_pin_temp )
1609                 p_pin_temp->Release();
1610             if( p_pin_downstream )
1611                 p_pin_downstream->Release();
1612             if( p_pin_upstream )
1613                 p_pin_upstream->Release();
1614             if( p_pin_downstream_enum )
1615                 p_pin_downstream_enum->Release();
1616             if( p_pin_upstream_enum )
1617                 p_pin_upstream_enum->Release();
1618         }
1619     } l;
1620
1621     PIN_DIRECTION pin_dir;
1622
1623     hr = p_upstream->EnumPins( &l.p_pin_upstream_enum );
1624     if( FAILED( hr ) )
1625     {
1626         msg_Warn( p_access, "Connect: "\
1627             "Cannot get upstream filter enumerator: hr=0x%8lx", hr );
1628         return hr;
1629     }
1630
1631     do
1632     {
1633         /* Release l.p_pin_upstream before next iteration */
1634         if( l.p_pin_upstream  )
1635             l.p_pin_upstream ->Release();
1636         l.p_pin_upstream = NULL;
1637         hr = l.p_pin_upstream_enum->Next( 1, &l.p_pin_upstream, 0 );
1638         if( hr != S_OK ) break;
1639
1640         hr = l.p_pin_upstream->QueryDirection( &pin_dir );
1641         if( FAILED( hr ) )
1642         {
1643             msg_Warn( p_access, "Connect: "\
1644                 "Cannot get upstream filter pin direction: hr=0x%8lx", hr );
1645             return hr;
1646         }
1647         hr = l.p_pin_upstream->ConnectedTo( &l.p_pin_downstream );
1648         if( SUCCEEDED( hr ) )
1649         {
1650             l.p_pin_downstream->Release();
1651             l.p_pin_downstream = NULL;
1652         }
1653         if( FAILED( hr ) && hr != VFW_E_NOT_CONNECTED )
1654         {
1655             msg_Warn( p_access, "Connect: "\
1656                 "Cannot check upstream filter connection: hr=0x%8lx", hr );
1657             return hr;
1658         }
1659         if( ( pin_dir == PINDIR_OUTPUT ) && ( hr == VFW_E_NOT_CONNECTED ) )
1660         {
1661             /* The upstream pin is not yet connected so check each pin on the
1662              * downstream filter */
1663             hr = p_downstream->EnumPins( &l.p_pin_downstream_enum );
1664             if( FAILED( hr ) )
1665             {
1666                 msg_Warn( p_access, "Connect: Cannot get "\
1667                     "downstream filter enumerator: hr=0x%8lx", hr );
1668                 return hr;
1669             }
1670             do
1671             {
1672                 /* Release l.p_pin_downstream before next iteration */
1673                 if( l.p_pin_downstream  )
1674                     l.p_pin_downstream ->Release();
1675                 l.p_pin_downstream = NULL;
1676
1677                 hr = l.p_pin_downstream_enum->Next( 1, &l.p_pin_downstream, 0 );
1678                 if( hr != S_OK ) break;
1679
1680                 hr = l.p_pin_downstream->QueryDirection( &pin_dir );
1681                 if( FAILED( hr ) )
1682                 {
1683                     msg_Warn( p_access, "Connect: Cannot get "\
1684                         "downstream filter pin direction: hr=0x%8lx", hr );
1685                     return hr;
1686                 }
1687
1688                 /* Looking for a free Pin to connect to
1689                  * A connected Pin may have an reference count > 1
1690                  * so Release and nullify the pointer */
1691                 hr = l.p_pin_downstream->ConnectedTo( &l.p_pin_temp );
1692                 if( SUCCEEDED( hr ) )
1693                 {
1694                     l.p_pin_temp->Release();
1695                     l.p_pin_temp = NULL;
1696                 }
1697                 if( hr != VFW_E_NOT_CONNECTED )
1698                 {
1699                     if( FAILED( hr ) )
1700                     {
1701                         msg_Warn( p_access, "Connect: Cannot check "\
1702                             "downstream filter connection: hr=0x%8lx", hr );
1703                         return hr;
1704                     }
1705                 }
1706                 if( ( pin_dir == PINDIR_INPUT ) &&
1707                     ( hr == VFW_E_NOT_CONNECTED ) )
1708                 {
1709                     hr = p_filter_graph->ConnectDirect( l.p_pin_upstream,
1710                         l.p_pin_downstream, NULL );
1711                     if( SUCCEEDED( hr ) )
1712                     {
1713                         /* If we arrive here then we have a matching pair of
1714                          * pins. */
1715                         return S_OK;
1716                     }
1717                 }
1718                 /* If we arrive here it means this downstream pin is not
1719                  * suitable so try the next downstream pin.
1720                  * l.p_pin_downstream is released at the top of the loop */
1721             }
1722             while( true );
1723             /* If we arrive here then we ran out of pins before we found a
1724              * suitable one. Release outstanding refcounts */
1725             if( l.p_pin_downstream_enum )
1726                 l.p_pin_downstream_enum->Release();
1727             l.p_pin_downstream_enum = NULL;
1728             if( l.p_pin_downstream )
1729                 l.p_pin_downstream->Release();
1730             l.p_pin_downstream = NULL;
1731         }
1732         /* If we arrive here it means this upstream pin is not suitable
1733          * so try the next upstream pin
1734          * l.p_pin_upstream is released at the top of the loop */
1735     }
1736     while( true );
1737     /* If we arrive here it means we did not find any pair of suitable pins
1738      * Outstanding refcounts are released in the destructor */
1739     return E_FAIL;
1740 }
1741
1742 /*****************************************************************************
1743 * Start uses MediaControl to start the graph
1744 *****************************************************************************/
1745 HRESULT BDAGraph::Start()
1746 {
1747     HRESULT hr = S_OK;
1748     OAFilterState i_state; /* State_Stopped, State_Paused, State_Running */
1749
1750     if( !p_media_control )
1751     {
1752         msg_Dbg( p_access, "Start: Media Control has not been created" );
1753         return E_FAIL;
1754     }
1755     hr = p_media_control->Run();
1756     msg_Dbg( p_access, "Graph started hr=0x%lx", hr );
1757     if( hr == S_OK )
1758         return hr;
1759
1760     /* Query the state of the graph - timeout after 100 milliseconds */
1761     while( hr = p_media_control->GetState( 100, &i_state ) != S_OK )
1762     {
1763         if( FAILED( hr ) )
1764         {
1765             msg_Warn( p_access,
1766                 "Start: Cannot get Graph state: hr=0x%8lx", hr );
1767             return hr;
1768         }
1769     }
1770     if( i_state == State_Running )
1771         return hr;
1772
1773     /* The Graph is not running so stop it and return an error */
1774     msg_Warn( p_access, "Start: Graph not started: %d", i_state );
1775     hr = p_media_control->StopWhenReady(); /* Instead of Stop() */
1776     if( FAILED( hr ) )
1777     {
1778         msg_Warn( p_access,
1779             "Start: Cannot stop Graph after Run failed: hr=0x%8lx", hr );
1780         return hr;
1781     }
1782     return E_FAIL;
1783 }
1784
1785 /*****************************************************************************
1786 * Pop the stream of data
1787 *****************************************************************************/
1788 block_t *BDAGraph::Pop()
1789 {
1790     return output.Pop();
1791 }
1792
1793 /******************************************************************************
1794 * SampleCB - Callback when the Sample Grabber has a sample
1795 ******************************************************************************/
1796 STDMETHODIMP BDAGraph::SampleCB( double d_time, IMediaSample *p_sample )
1797 {
1798     if( p_sample->IsDiscontinuity() == S_OK )
1799         msg_Warn( p_access, "BDA SampleCB: Sample Discontinuity.");
1800
1801     const size_t i_sample_size = p_sample->GetActualDataLength();
1802     BYTE *p_sample_data;
1803     p_sample->GetPointer( &p_sample_data );
1804
1805     if( i_sample_size > 0 && p_sample_data )
1806     {
1807         block_t *p_block = block_New( p_access, i_sample_size );
1808
1809         if( p_block )
1810         {
1811             memcpy( p_block->p_buffer, p_sample_data, i_sample_size );
1812             output.Push( p_block );
1813         }
1814      }
1815      return S_OK;
1816 }
1817
1818 STDMETHODIMP BDAGraph::BufferCB( double d_time, BYTE* p_buffer,
1819     long l_buffer_len )
1820 {
1821     return E_FAIL;
1822 }
1823
1824 /******************************************************************************
1825 * removes each filter from the graph
1826 ******************************************************************************/
1827 HRESULT BDAGraph::Destroy()
1828 {
1829     HRESULT hr = S_OK;
1830     ULONG ul_refcount = 0;
1831
1832     if( p_media_control )
1833         hr = p_media_control->StopWhenReady(); /* Instead of Stop() */
1834
1835     if( d_graph_register )
1836     {
1837         Deregister();
1838     }
1839
1840     output.Empty();
1841
1842     if( p_grabber )
1843     {
1844         p_grabber->Release();
1845         p_grabber = NULL;
1846     }
1847
1848     if( p_transport_info )
1849     {
1850         p_filter_graph->RemoveFilter( p_transport_info );
1851         p_transport_info->Release();
1852         p_transport_info = NULL;
1853     }
1854     if( p_mpeg_demux )
1855     {
1856         p_filter_graph->RemoveFilter( p_mpeg_demux );
1857         p_mpeg_demux->Release();
1858         p_mpeg_demux = NULL;
1859     }
1860     if( p_sample_grabber )
1861     {
1862         p_filter_graph->RemoveFilter( p_sample_grabber );
1863         p_sample_grabber->Release();
1864         p_sample_grabber = NULL;
1865     }
1866     if( p_capture_device )
1867     {
1868         p_filter_graph->RemoveFilter( p_capture_device );
1869         p_capture_device->Release();
1870         p_capture_device = NULL;
1871     }
1872     if( p_tuner_device )
1873     {
1874         p_filter_graph->RemoveFilter( p_tuner_device );
1875         p_tuner_device->Release();
1876         p_tuner_device = NULL;
1877     }
1878     if( p_scanning_tuner )
1879     {
1880         p_scanning_tuner->Release();
1881         p_scanning_tuner = NULL;
1882     }
1883     if( p_network_provider )
1884     {
1885         p_filter_graph->RemoveFilter( p_network_provider );
1886         p_network_provider->Release();
1887         p_network_provider = NULL;
1888     }
1889
1890     if( p_media_control )
1891     {
1892         p_media_control->Release();
1893         p_media_control = NULL;
1894     }
1895     if( p_filter_graph )
1896     {
1897         p_filter_graph->Release();
1898         p_filter_graph = NULL;
1899     }
1900     if( p_system_dev_enum )
1901     {
1902         p_system_dev_enum->Release();
1903         p_system_dev_enum = NULL;
1904     }
1905
1906     return S_OK;
1907 }
1908
1909 /*****************************************************************************
1910 * Add/Remove a DirectShow filter graph to/from the Running Object Table.
1911 * Allows GraphEdit to "spy" on a remote filter graph.
1912 ******************************************************************************/
1913 HRESULT BDAGraph::Register()
1914 {
1915     class localComPtr
1916     {
1917         public:
1918         IMoniker*             p_moniker;
1919         IRunningObjectTable*  p_ro_table;
1920         localComPtr(): p_moniker(NULL), p_ro_table(NULL) {};
1921         ~localComPtr()
1922         {
1923             if( p_moniker )
1924                 p_moniker->Release();
1925             if( p_ro_table )
1926                 p_ro_table->Release();
1927         }
1928     } l;
1929     WCHAR     psz_w_graph_name[128];
1930     HRESULT   hr;
1931
1932     hr = ::GetRunningObjectTable( 0, &l.p_ro_table );
1933     if( FAILED( hr ) )
1934     {
1935         msg_Warn( p_access, "Register: Cannot get ROT: hr=0x%8lx", hr );
1936         return hr;
1937     }
1938
1939     wsprintfW( psz_w_graph_name, L"VLC BDA Graph %08x Pid %08x",
1940         (DWORD_PTR) p_filter_graph, ::GetCurrentProcessId() );
1941     hr = CreateItemMoniker( L"!", psz_w_graph_name, &l.p_moniker );
1942     if( FAILED( hr ) )
1943     {
1944         msg_Warn( p_access, "Register: Cannot Create Moniker: hr=0x%8lx", hr );
1945         return hr;
1946     }
1947     hr = l.p_ro_table->Register( ROTFLAGS_REGISTRATIONKEEPSALIVE,
1948         p_filter_graph, l.p_moniker, &d_graph_register );
1949     if( FAILED( hr ) )
1950     {
1951         msg_Warn( p_access, "Register: Cannot Register Graph: hr=0x%8lx", hr );
1952         return hr;
1953     }
1954 //    msg_Dbg( p_access, "Register: registered Graph: %S", psz_w_graph_name );
1955     return hr;
1956 }
1957
1958 void BDAGraph::Deregister()
1959 {
1960     HRESULT   hr;
1961     IRunningObjectTable* p_ro_table;
1962     hr = ::GetRunningObjectTable( 0, &p_ro_table );
1963     if( SUCCEEDED( hr ) )
1964         p_ro_table->Revoke( d_graph_register );
1965     d_graph_register = 0;
1966     p_ro_table->Release();
1967 }