]> git.sesse.net Git - vlc/blob - modules/access/bda/bdagraph.cpp
1655c2394f5670dbeca4e5bb3aea1f6cf95aaa7a
[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 <kens@campoz.fslife.co.uk>
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
28 /****************************************************************************
29  * Interfaces for calls from C
30  ****************************************************************************/
31 extern "C" {
32
33     void dvb_newBDAGraph( access_t* p_access )
34     {
35         p_access->p_sys->p_bda_module = new BDAGraph( p_access );
36     };
37
38     void dvb_deleteBDAGraph( access_t* p_access )
39     {
40         if( p_access->p_sys->p_bda_module )
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     long dvb_GetBufferSize( access_t* p_access )
73     {
74         if( p_access->p_sys->p_bda_module )
75             return p_access->p_sys->p_bda_module->GetBufferSize();
76         return -1;
77     };
78
79     long dvb_ReadBuffer( access_t* p_access, long* l_buffer_len, BYTE* p_buff )
80     {
81         if( p_access->p_sys->p_bda_module )
82             return p_access->p_sys->p_bda_module->ReadBuffer( l_buffer_len, 
83                 p_buff );
84         return -1;
85     };
86 };
87
88 /*****************************************************************************
89 * Constructor
90 *****************************************************************************/
91 BDAGraph::BDAGraph( access_t* p_this ):
92     p_access( p_this ),
93     guid_network_type(GUID_NULL),
94     l_tuner_used(-1),
95     d_graph_register( 0 )
96 {
97     b_ready = FALSE;
98     p_tuning_space = NULL;
99     p_tune_request = NULL;
100     p_media_control = NULL;
101     p_filter_graph = NULL;
102     p_system_dev_enum = NULL;
103     p_network_provider = p_tuner_device = p_capture_device = NULL;
104     p_sample_grabber = p_mpeg_demux = p_transport_info = NULL;
105     p_scanning_tuner = NULL;
106     p_grabber = NULL;
107
108     /* Initialize COM - MS says to use CoInitializeEx in preference to
109      * CoInitialize */
110     CoInitializeEx( 0, COINIT_APARTMENTTHREADED );
111 }
112
113 /*****************************************************************************
114 * Destructor
115 *****************************************************************************/
116 BDAGraph::~BDAGraph()
117 {
118     Destroy();
119     CoUninitialize();
120 }
121
122 /*****************************************************************************
123 * Submit an ATSC Tune Request
124 *****************************************************************************/
125 int BDAGraph::SubmitATSCTuneRequest()
126 {
127     HRESULT hr = S_OK;
128     class localComPtr
129     {
130         public:
131         IATSCChannelTuneRequest* p_atsc_tune_request;
132         IATSCLocator* p_atsc_locator;
133         localComPtr(): p_atsc_tune_request(NULL), p_atsc_locator(NULL) {};
134         ~localComPtr()
135         {
136             if( p_atsc_tune_request )
137                 p_atsc_tune_request->Release();
138             if( p_atsc_locator )
139                 p_atsc_locator->Release();
140         }
141     } l;
142     long l_major_channel, l_minor_channel, l_physical_channel;
143
144     l_major_channel = l_minor_channel = l_physical_channel = -1;
145 /*
146     l_major_channel = var_GetInteger( p_access, "dvb-major-channel" );
147     l_minor_channel = var_GetInteger( p_access, "dvb-minor-channel" );
148     l_physical_channel = var_GetInteger( p_access, "dvb-physical-channel" );
149 */
150
151     guid_network_type = CLSID_ATSCNetworkProvider;
152     hr = CreateTuneRequest();
153     if( FAILED( hr ) )
154     {
155         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
156             "Cannot create Tuning Space: hr=0x%8lx", hr );
157         return VLC_EGENERIC;
158     }
159
160     hr = p_tune_request->QueryInterface( IID_IATSCChannelTuneRequest,
161         (void**)&l.p_atsc_tune_request );
162     if( FAILED( hr ) )
163     {
164         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
165             "Cannot QI for IATSCChannelTuneRequest: hr=0x%8lx", hr );
166         return VLC_EGENERIC;
167     }
168     hr = ::CoCreateInstance( CLSID_ATSCLocator, 0, CLSCTX_INPROC,
169                              IID_IATSCLocator, (void**)&l.p_atsc_locator );
170     if( FAILED( hr ) )
171     {
172         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
173             "Cannot create the ATSC locator: hr=0x%8lx", hr );
174         return VLC_EGENERIC;
175     }
176
177     hr = S_OK;
178     if( l_major_channel > 0 )
179         hr = l.p_atsc_tune_request->put_Channel( l_major_channel );
180     if( SUCCEEDED( hr ) && l_minor_channel > 0 )
181         hr = l.p_atsc_tune_request->put_MinorChannel( l_minor_channel );
182     if( SUCCEEDED( hr ) && l_physical_channel > 0 )
183         hr = l.p_atsc_locator->put_PhysicalChannel( l_physical_channel );
184     if( FAILED( hr ) )
185     {
186         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
187             "Cannot set tuning parameters: hr=0x%8lx", hr );
188         return VLC_EGENERIC;
189     }
190
191     hr = p_tune_request->put_Locator( l.p_atsc_locator );
192     if( FAILED( hr ) )
193     {
194         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
195             "Cannot put the locator: hr=0x%8lx", hr );
196         return VLC_EGENERIC;
197     }
198
199     /* Build and Run the Graph. If a Tuner device is in use the graph will
200      * fail to run. Repeated calls to build will check successive tuner
201      * devices */
202     do
203     {
204         hr = Build();
205         if( FAILED( hr ) )
206         {
207             msg_Warn( p_access, "SubmitATSCTuneRequest: "\
208                 "Cannot Build the Graph: hr=0x%8lx", hr );
209             return VLC_EGENERIC;
210         }
211         hr = Start();
212     }
213     while( hr != S_OK );
214
215     return VLC_SUCCESS;
216 }
217
218 /*****************************************************************************
219 * Submit a DVB-T Tune Request
220 ******************************************************************************/
221 int BDAGraph::SubmitDVBTTuneRequest()
222 {
223     HRESULT hr = S_OK;
224     class localComPtr
225     {
226         public:
227         IDVBTuneRequest* p_dvbt_tune_request;
228         IDVBTLocator* p_dvbt_locator;
229         localComPtr(): p_dvbt_tune_request(NULL), p_dvbt_locator(NULL) {};
230         ~localComPtr()
231         {
232             if( p_dvbt_tune_request )
233                 p_dvbt_tune_request->Release();
234             if( p_dvbt_locator )
235                 p_dvbt_locator->Release();
236         }
237     } l;
238     long l_frequency, l_bandwidth, l_hp_fec, l_lp_fec, l_guard;
239     long l_transmission, l_hierarchy;
240     BinaryConvolutionCodeRate i_hp_fec, i_lp_fec;
241     GuardInterval             i_guard;
242     TransmissionMode          i_transmission;
243     HierarchyAlpha            i_hierarchy;
244
245     l_frequency = l_bandwidth = l_hp_fec = l_lp_fec = l_guard = -1;
246     l_transmission = l_hierarchy = -1;
247     l_frequency = var_GetInteger( p_access, "dvb-frequency" );
248     l_bandwidth = var_GetInteger( p_access, "dvb-bandwidth" );
249     l_hp_fec = var_GetInteger( p_access, "dvb-code-rate-hp" );
250     l_lp_fec = var_GetInteger( p_access, "dvb-code-rate-lp" );
251     l_guard = var_GetInteger( p_access, "dvb-guard" );
252     l_transmission = var_GetInteger( p_access, "dvb-transmission" );
253     l_hierarchy = var_GetInteger( p_access, "dvb-hierarchy" );
254
255     i_hp_fec = BDA_BCC_RATE_NOT_SET;
256     if( l_hp_fec == 1 )
257         i_hp_fec = BDA_BCC_RATE_1_2;
258     if( l_hp_fec == 2 )
259         i_hp_fec = BDA_BCC_RATE_2_3;
260     if( l_hp_fec == 3 )
261         i_hp_fec = BDA_BCC_RATE_3_4;
262     if( l_hp_fec == 4 )
263         i_hp_fec = BDA_BCC_RATE_5_6;
264     if( l_hp_fec == 5 )
265         i_hp_fec = BDA_BCC_RATE_7_8;
266
267     i_lp_fec = BDA_BCC_RATE_NOT_SET;
268     if( l_lp_fec == 1 )
269         i_lp_fec = BDA_BCC_RATE_1_2;
270     if( l_lp_fec == 2 )
271         i_lp_fec = BDA_BCC_RATE_2_3;
272     if( l_lp_fec == 3 )
273         i_lp_fec = BDA_BCC_RATE_3_4;
274     if( l_lp_fec == 4 )
275         i_lp_fec = BDA_BCC_RATE_5_6;
276     if( l_lp_fec == 5 )
277         i_lp_fec = BDA_BCC_RATE_7_8;
278
279     i_guard = BDA_GUARD_NOT_SET;
280     if( l_guard == 32 )
281         i_guard = BDA_GUARD_1_32;
282     if( l_guard == 16 )
283         i_guard = BDA_GUARD_1_16;
284     if( l_guard == 8 )
285         i_guard = BDA_GUARD_1_8;
286     if( l_guard == 4 )
287         i_guard = BDA_GUARD_1_4;
288
289     i_transmission = BDA_XMIT_MODE_NOT_SET;
290     if( l_transmission == 2 )
291         i_transmission = BDA_XMIT_MODE_2K;
292     if( l_transmission == 8 )
293         i_transmission = BDA_XMIT_MODE_8K;
294
295     i_hierarchy = BDA_HALPHA_NOT_SET;
296     if( l_hierarchy == 1 )
297         i_hierarchy = BDA_HALPHA_1;
298     if( l_hierarchy == 2 )
299         i_hierarchy = BDA_HALPHA_2;
300     if( l_hierarchy == 4 )
301         i_hierarchy = BDA_HALPHA_4;
302
303     guid_network_type = CLSID_DVBTNetworkProvider;
304     hr = CreateTuneRequest();
305     if( FAILED( hr ) )
306     {
307         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
308             "Cannot create Tune Request: hr=0x%8lx", hr );
309         return VLC_EGENERIC;
310     }
311
312     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
313         (void**)&l.p_dvbt_tune_request );
314     if( FAILED( hr ) )
315     {
316         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
317             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
318         return VLC_EGENERIC;
319     }
320     l.p_dvbt_tune_request->put_ONID( -1 );
321     l.p_dvbt_tune_request->put_SID( -1 );
322     l.p_dvbt_tune_request->put_TSID( -1 );
323
324     hr = ::CoCreateInstance( CLSID_DVBTLocator, 0, CLSCTX_INPROC,
325         IID_IDVBTLocator, (void**)&l.p_dvbt_locator );
326     if( FAILED( hr ) )
327     {
328         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
329             "Cannot create the DVBT Locator: hr=0x%8lx", hr );
330         return VLC_EGENERIC;
331     }
332
333     hr = S_OK;
334     if( l_frequency > 0 )
335         hr = l.p_dvbt_locator->put_CarrierFrequency( l_frequency );
336     if( SUCCEEDED( hr ) && l_bandwidth > 0 )
337         hr = l.p_dvbt_locator->put_Bandwidth( l_bandwidth );
338     if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
339         hr = l.p_dvbt_locator->put_InnerFECRate( i_hp_fec );
340     if( SUCCEEDED( hr ) && i_lp_fec != BDA_BCC_RATE_NOT_SET )
341         hr = l.p_dvbt_locator->put_LPInnerFECRate( i_lp_fec );
342     if( SUCCEEDED( hr ) && i_guard != BDA_GUARD_NOT_SET )
343         hr = l.p_dvbt_locator->put_Guard( i_guard );
344     if( SUCCEEDED( hr ) && i_transmission != BDA_XMIT_MODE_NOT_SET )
345         hr = l.p_dvbt_locator->put_Mode( i_transmission );
346     if( SUCCEEDED( hr ) && i_hierarchy != BDA_HALPHA_NOT_SET )
347         hr = l.p_dvbt_locator->put_HAlpha( i_hierarchy );
348     if( FAILED( hr ) )
349     {
350         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
351             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
352         return VLC_EGENERIC;
353     }
354
355     hr = p_tune_request->put_Locator( l.p_dvbt_locator );
356     if( FAILED( hr ) )
357     {
358         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
359             "Cannot put the locator: hr=0x%8lx", hr );
360         return VLC_EGENERIC;
361     }
362
363     /* Build and Run the Graph. If a Tuner device is in use the graph will
364      * fail to run. Repeated calls to build will check successive tuner
365      * devices */
366     do
367     {
368         hr = Build();
369         if( FAILED( hr ) )
370         {
371             msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
372                 "Cannot Build the Graph: hr=0x%8lx", hr );
373             return VLC_EGENERIC;
374         }
375         hr = Start();
376     }
377     while( hr != S_OK );
378
379     return VLC_SUCCESS;
380 }
381
382 /*****************************************************************************
383 * Submit a DVB-C Tune Request
384 ******************************************************************************/
385 int BDAGraph::SubmitDVBCTuneRequest()
386 {
387     HRESULT hr = S_OK;
388
389     class localComPtr
390     {
391         public:
392         IDVBTuneRequest* p_dvbc_tune_request;
393         IDVBCLocator* p_dvbc_locator;
394         localComPtr(): p_dvbc_tune_request(NULL), p_dvbc_locator(NULL) {};
395         ~localComPtr()
396         {
397             if( p_dvbc_tune_request )
398                 p_dvbc_tune_request->Release();
399             if( p_dvbc_locator )
400                 p_dvbc_locator->Release();
401         }
402     } l;
403
404     long l_frequency, l_symbolrate;
405     int  i_qam;
406     ModulationType i_qam_mod;
407
408     l_frequency = l_symbolrate = i_qam = -1;
409     l_frequency = var_GetInteger( p_access, "dvb-frequency" );
410     l_symbolrate = var_GetInteger( p_access, "dvb-srate" );
411     i_qam = var_GetInteger( p_access, "dvb-modulation" );
412     i_qam_mod = BDA_MOD_NOT_SET;
413     if( i_qam == 16 )
414         i_qam_mod = BDA_MOD_16QAM;
415     if( i_qam == 32 )
416         i_qam_mod = BDA_MOD_32QAM;
417     if( i_qam == 64 )
418         i_qam_mod = BDA_MOD_64QAM;
419     if( i_qam == 128 )
420         i_qam_mod = BDA_MOD_128QAM;
421     if( i_qam == 256 )
422         i_qam_mod = BDA_MOD_256QAM;
423
424     guid_network_type = CLSID_DVBCNetworkProvider;
425     hr = CreateTuneRequest();
426     if( FAILED( hr ) )
427     {
428         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
429             "Cannot create Tune Request: hr=0x%8lx", hr );
430         return VLC_EGENERIC;
431     }
432
433     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
434         (void**)&l.p_dvbc_tune_request );
435     if( FAILED( hr ) )
436     {
437         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
438             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
439         return VLC_EGENERIC;
440     }
441     l.p_dvbc_tune_request->put_ONID( -1 );
442     l.p_dvbc_tune_request->put_SID( -1 );
443     l.p_dvbc_tune_request->put_TSID( -1 );
444
445     hr = ::CoCreateInstance( CLSID_DVBCLocator, 0, CLSCTX_INPROC,
446         IID_IDVBCLocator, (void**)&l.p_dvbc_locator );
447     if( FAILED( hr ) )
448     {
449         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
450             "Cannot create the DVB-C Locator: hr=0x%8lx", hr );
451         return VLC_EGENERIC;
452     }
453
454     hr = S_OK;
455     if( l_frequency > 0 )
456         hr = l.p_dvbc_locator->put_CarrierFrequency( l_frequency );
457     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
458         hr = l.p_dvbc_locator->put_SymbolRate( l_symbolrate );
459     if( SUCCEEDED( hr ) && i_qam_mod != BDA_MOD_NOT_SET )
460         hr = l.p_dvbc_locator->put_Modulation( i_qam_mod );
461     if( FAILED( hr ) )
462     {
463         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
464             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
465         return VLC_EGENERIC;
466     }
467
468     hr = p_tune_request->put_Locator( l.p_dvbc_locator );
469     if( FAILED( hr ) )
470     {
471         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
472             "Cannot put the locator: hr=0x%8lx", hr );
473         return VLC_EGENERIC;
474     }
475
476     /* Build and Run the Graph. If a Tuner device is in use the graph will
477      * fail to run. Repeated calls to build will check successive tuner
478      * devices */
479     do
480     {
481         hr = Build();
482         if( FAILED( hr ) )
483         {
484             msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
485                 "Cannot Build the Graph: hr=0x%8lx", hr );
486             return VLC_EGENERIC;
487         }
488         hr = Start();
489     }
490     while( hr != S_OK );
491
492     return VLC_SUCCESS;
493 }
494
495 /*****************************************************************************
496 * Submit a DVB-S Tune Request
497 ******************************************************************************/
498 int BDAGraph::SubmitDVBSTuneRequest()
499 {
500     HRESULT hr = S_OK;
501
502     class localComPtr
503     {
504         public:
505         IDVBTuneRequest* p_dvbs_tune_request;
506         IDVBSLocator* p_dvbs_locator;
507         IDVBSTuningSpace* p_dvbs_tuning_space;
508         localComPtr(): p_dvbs_tune_request(NULL), p_dvbs_locator(NULL),
509             p_dvbs_tuning_space(NULL) {};
510         ~localComPtr()
511         {
512             if( p_dvbs_tuning_space )
513                 p_dvbs_tuning_space->Release();
514             if( p_dvbs_tune_request )
515                 p_dvbs_tune_request->Release();
516             if( p_dvbs_locator )
517                 p_dvbs_locator->Release();
518         }
519     } l;
520     long l_frequency, l_symbolrate, l_azimuth, l_elevation, l_longitude;
521     long l_lnb_lof1, l_lnb_lof2, l_lnb_slof, l_inversion, l_network_id;
522     char* psz_polarisation;
523     Polarisation i_polar;
524     SpectralInversion i_inversion;
525     VARIANT_BOOL b_west;
526
527     l_frequency = l_symbolrate = l_azimuth = l_elevation = l_longitude = -1;
528     l_lnb_lof1 = l_lnb_lof2 = l_lnb_slof = l_inversion = l_network_id = -1;
529     l_frequency = var_GetInteger( p_access, "dvb-frequency" );
530     l_symbolrate = var_GetInteger( p_access, "dvb-srate" );
531     l_azimuth = var_GetInteger( p_access, "dvb-azimuth" );
532     l_elevation = var_GetInteger( p_access, "dvb-elevation" );
533     l_longitude = var_GetInteger( p_access, "dvb-longitude" );
534     l_lnb_lof1 = var_GetInteger( p_access, "dvb-lnb-lof1" );
535     l_lnb_lof2 = var_GetInteger( p_access, "dvb-lnb-lof2" ); 
536     l_lnb_slof = var_GetInteger( p_access, "dvb-lnb-slof" );
537     psz_polarisation = var_GetString( p_access, "dvb-polarisation" );
538     l_inversion = var_GetInteger( p_access, "dvb-inversion" );
539     l_network_id = var_GetInteger( p_access, "dvb-network_id" );
540
541     b_west = ( l_longitude < 0 ) ? TRUE : FALSE;
542
543     i_polar = BDA_POLARISATION_NOT_SET;
544     if( *psz_polarisation == 'H' || *psz_polarisation == 'h' )
545         i_polar = BDA_POLARISATION_LINEAR_H;
546     if( *psz_polarisation == 'V' || *psz_polarisation == 'v' )
547         i_polar = BDA_POLARISATION_LINEAR_V;
548     if( *psz_polarisation == 'L' || *psz_polarisation == 'l' )
549         i_polar = BDA_POLARISATION_CIRCULAR_L;
550     if( *psz_polarisation == 'R' || *psz_polarisation == 'r' )
551         i_polar = BDA_POLARISATION_CIRCULAR_R;
552
553     i_inversion = BDA_SPECTRAL_INVERSION_NOT_SET;
554     if( l_inversion == 0 )
555         i_inversion = BDA_SPECTRAL_INVERSION_NORMAL;
556     if( l_inversion == 1 )
557         i_inversion = BDA_SPECTRAL_INVERSION_INVERTED;
558     if( l_inversion == 2 )
559         i_inversion = BDA_SPECTRAL_INVERSION_AUTOMATIC;
560
561     guid_network_type = CLSID_DVBSNetworkProvider;
562     hr = CreateTuneRequest();
563     if( FAILED( hr ) )
564     {
565         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
566             "Cannot create Tune Request: hr=0x%8lx", hr );
567         return VLC_EGENERIC;
568     }
569
570     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
571         (void**)&l.p_dvbs_tune_request );
572     if( FAILED( hr ) )
573     {
574         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
575             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
576         return VLC_EGENERIC;
577     }
578     l.p_dvbs_tune_request->put_ONID( -1 );
579     l.p_dvbs_tune_request->put_SID( -1 );
580     l.p_dvbs_tune_request->put_TSID( -1 );
581
582     hr = ::CoCreateInstance( CLSID_DVBSLocator, 0, CLSCTX_INPROC,
583         IID_IDVBSLocator, (void**)&l.p_dvbs_locator );
584     if( FAILED( hr ) )
585     {
586         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
587             "Cannot create the DVBS Locator: hr=0x%8lx", hr );
588         return VLC_EGENERIC;
589     }
590
591     hr = p_tuning_space->QueryInterface( IID_IDVBSTuningSpace,
592         (void**)&l.p_dvbs_tuning_space );
593     if( FAILED( hr ) )
594     {
595         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
596             "Cannot QI for IDVBSTuningSpace: hr=0x%8lx", hr );
597         return VLC_EGENERIC;
598     }
599
600     hr = S_OK;
601     if( l_frequency > 0 )
602         hr = l.p_dvbs_locator->put_CarrierFrequency( l_frequency );
603     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
604         hr = l.p_dvbs_locator->put_SymbolRate( l_symbolrate );
605     if( SUCCEEDED( hr ) && l_azimuth > 0 )
606         hr = l.p_dvbs_locator->put_Azimuth( l_azimuth );
607     if( SUCCEEDED( hr ) && l_elevation > 0 )
608         hr = l.p_dvbs_locator->put_Elevation( l_elevation );
609     if( SUCCEEDED( hr ) )
610         hr = l.p_dvbs_locator->put_OrbitalPosition( labs( l_longitude ) );
611     if( SUCCEEDED( hr ) )
612         hr = l.p_dvbs_locator->put_WestPosition( b_west );
613     if( SUCCEEDED( hr ) && i_polar != BDA_POLARISATION_NOT_SET )
614         hr = l.p_dvbs_locator->put_SignalPolarisation( i_polar );
615     if( SUCCEEDED( hr ) && l_lnb_lof1 > 0 )
616         hr = l.p_dvbs_tuning_space->put_LowOscillator( l_lnb_lof1 );
617     if( SUCCEEDED( hr ) && l_lnb_lof2 > 0 )
618         hr = l.p_dvbs_tuning_space->put_HighOscillator( l_lnb_lof2 );
619     if( SUCCEEDED( hr ) && l_lnb_slof > 0 )
620         hr = l.p_dvbs_tuning_space->put_LNBSwitch( l_lnb_slof );
621     if( SUCCEEDED( hr ) && i_inversion != BDA_SPECTRAL_INVERSION_NOT_SET )
622         hr = l.p_dvbs_tuning_space->put_SpectralInversion( i_inversion );
623     if( SUCCEEDED( hr ) && l_network_id > 0 )
624         hr = l.p_dvbs_tuning_space->put_NetworkID( l_network_id );
625     if( FAILED( hr ) )
626     {
627         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
628             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
629         return VLC_EGENERIC;
630     }
631
632     hr = p_tune_request->put_Locator( l.p_dvbs_locator );
633     if( FAILED( hr ) )
634     {
635         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
636             "Cannot put the locator: hr=0x%8lx", hr );
637         return VLC_EGENERIC;
638     }
639
640     /* Build and Run the Graph. If a Tuner device is in use the graph will
641      * fail to run. Repeated calls to build will check successive tuner
642      * devices */
643     do
644     {
645         hr = Build();
646         if( FAILED( hr ) )
647         {
648             msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
649                 "Cannot Build the Graph: hr=0x%8lx", hr );
650             return VLC_EGENERIC;
651         }
652         hr = Start();
653     }
654     while( hr != S_OK );
655
656     return VLC_SUCCESS;
657 }
658
659 /*****************************************************************************
660 * Load the Tuning Space from System Tuning Spaces according to the
661 * Network Type requested
662 ******************************************************************************/
663 HRESULT BDAGraph::CreateTuneRequest()
664 {
665     HRESULT hr = S_OK;
666     GUID guid_this_network_type;
667     class localComPtr
668     {
669         public:
670         ITuningSpaceContainer*  p_tuning_space_container;
671         IEnumTuningSpaces*      p_tuning_space_enum;
672         ITuningSpace*           p_this_tuning_space;
673         localComPtr(): p_tuning_space_container(NULL),
674             p_tuning_space_enum(NULL), p_this_tuning_space(NULL) {};
675         ~localComPtr()
676         {
677             if( p_tuning_space_container )
678                 p_tuning_space_container->Release();
679             if( p_tuning_space_enum )
680                 p_tuning_space_enum->Release();
681             if( p_this_tuning_space )
682                 p_this_tuning_space->Release();
683         }
684     } l;
685
686     /* A Tuning Space may already have been set up. If it is for the same
687      * network type then all is well. Otherwise, reset the Tuning Space and get
688      * a new one */
689     if( p_tuning_space )
690     {
691         hr = p_tuning_space->get__NetworkType( &guid_this_network_type );
692         if( FAILED( hr ) ) guid_this_network_type = GUID_NULL;
693         if( guid_this_network_type == guid_network_type )
694         {
695             return S_OK;
696         }
697         else
698         {
699             p_tuning_space->Release();
700             p_tuning_space = NULL;
701         }
702     }
703
704     /* Force use of the first available Tuner Device during Build */
705     l_tuner_used = -1;
706
707     /* Get the SystemTuningSpaces container to enumerate through all the
708      * defined tuning spaces */
709     hr = ::CoCreateInstance( CLSID_SystemTuningSpaces, 0, CLSCTX_INPROC,
710         IID_ITuningSpaceContainer, (void**)&l.p_tuning_space_container );
711     if( FAILED( hr ) )
712     {
713         msg_Warn( p_access, "CreateTuneRequest: "\
714             "Cannot CoCreate SystemTuningSpaces: hr=0x%8lx", hr );
715         return hr;
716     }
717
718     hr = l.p_tuning_space_container->get_EnumTuningSpaces(
719          &l.p_tuning_space_enum );
720     if( FAILED( hr ) )
721     {
722         msg_Warn( p_access, "CreateTuneRequest: "\
723             "Cannot create SystemTuningSpaces Enumerator: hr=0x%8lx", hr );
724         return hr;
725     }
726
727     while( l.p_tuning_space_enum->Next( 1, &l.p_this_tuning_space, NULL ) ==
728         S_OK )
729     {
730        hr = l.p_this_tuning_space->get__NetworkType( &guid_this_network_type );
731
732         /* GUID_NULL means a non-BDA network was found e.g analog
733          * Ignore failures and non-BDA networks and keep looking */
734         if( FAILED( hr ) ) guid_this_network_type == GUID_NULL;
735
736         if( guid_this_network_type == guid_network_type )
737         {
738             hr = l.p_this_tuning_space->QueryInterface( IID_ITuningSpace,
739                 (void**)&p_tuning_space );
740             if( FAILED( hr ) )
741             {
742                 msg_Warn( p_access, "CreateTuneRequest: "\
743                     "Cannot QI Tuning Space: hr=0x%8lx", hr );
744                 return hr;
745             }
746             hr = p_tuning_space->CreateTuneRequest( &p_tune_request );
747             if( FAILED( hr ) )
748             {
749                 msg_Warn( p_access, "CreateTuneRequest: "\
750                     "Cannot Create Tune Request: hr=0x%8lx", hr );
751                 return hr;
752             }
753             return hr;
754         }
755     }
756     hr = E_FAIL;
757     if( FAILED( hr ) )
758     {
759         msg_Warn( p_access, "CreateTuneRequest: "\
760             "Cannot find a suitable System Tuning Space: hr=0x%8lx", hr );
761         return hr;
762     }
763     return hr;
764 }
765
766 /******************************************************************************
767 * Build
768 * Step 4: Build the Filter Graph
769 * Build sets up devices, adds and connects filters
770 ******************************************************************************/
771 HRESULT BDAGraph::Build()
772 {
773     HRESULT hr = S_OK;
774     long l_capture_used, l_tif_used;
775     AM_MEDIA_TYPE grabber_media_type;
776
777     /* If we have already have a filter graph, rebuild it*/
778     Destroy();
779
780     hr = ::CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
781         IID_IGraphBuilder, (void**)&p_filter_graph );
782     if( FAILED( hr ) )
783     {
784         msg_Warn( p_access, "Build: "\
785             "Cannot CoCreate IFilterGraph: hr=0x%8lx", hr );
786         return hr;
787     }
788
789     /* First filter in the graph is the Network Provider and
790      * its Scanning Tuner which takes the Tune Request*/
791     hr = ::CoCreateInstance( guid_network_type, NULL, CLSCTX_INPROC_SERVER,
792         IID_IBaseFilter, (void**)&p_network_provider);
793     if( FAILED( hr ) )
794     {
795         msg_Warn( p_access, "Build: "\
796             "Cannot CoCreate Network Provider: hr=0x%8lx", hr );
797         return hr;
798     }
799     hr = p_filter_graph->AddFilter( p_network_provider, L"Network Provider" );
800     if( FAILED( hr ) )
801     {
802         msg_Warn( p_access, "Build: "\
803             "Cannot load network provider: hr=0x%8lx", hr );
804         return hr;
805     }
806
807     hr = p_network_provider->QueryInterface( IID_IScanningTuner,
808         (void**)&p_scanning_tuner );
809     if( FAILED( hr ) )
810     {
811         msg_Warn( p_access, "Build: "\
812             "Cannot QI Network Provider for Scanning Tuner: hr=0x%8lx", hr );
813         return hr;
814     }
815
816     hr = p_scanning_tuner->Validate( p_tune_request );
817     if( FAILED( hr ) )
818     {
819         msg_Warn( p_access, "Build: "\
820             "Tune Request is invalid: hr=0x%8lx", hr );
821         return hr;
822     }
823     hr = p_scanning_tuner->put_TuneRequest( p_tune_request );
824     if( FAILED( hr ) )
825     {
826         msg_Warn( p_access, "Build: "\
827             "Cannot submit the tune request: hr=0x%8lx", hr );
828         return hr;
829     }
830
831     /* Add the Network Tuner to the Network Provider. On subsequent calls,
832      * l_tuner_used will cause a different tuner to be selected */
833     hr = FindFilter( KSCATEGORY_BDA_NETWORK_TUNER, &l_tuner_used,
834         p_network_provider, &p_tuner_device );
835     if( FAILED( hr ) )
836     {
837         msg_Warn( p_access, "Build: "\
838             "Cannot load tuner device and connect network provider: "\
839             "hr=0x%8lx", hr );
840         return hr;
841     }
842
843     /* Always look for all capture devices to match the Network Tuner */
844     l_capture_used = -1;
845     hr = FindFilter( KSCATEGORY_BDA_RECEIVER_COMPONENT, &l_capture_used,
846         p_tuner_device, &p_capture_device );
847     if( FAILED( hr ) )
848     {
849         /* Some BDA drivers do not provide a Capture Device Filter so force
850          * the Sample Grabber to connect directly to the Tuner Device */
851         p_capture_device = p_tuner_device;
852         p_tuner_device = NULL;
853         msg_Warn( p_access, "Build: "\
854             "Cannot find Capture device. Connecting to tuner: hr=0x%8lx", hr );
855     }
856
857     /* Insert the Sample Grabber to tap into the Transport Stream. */
858     hr = ::CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
859         IID_IBaseFilter, (void**)&p_sample_grabber );
860     if( FAILED( hr ) )
861     {
862         msg_Warn( p_access, "Build: "\
863             "Cannot load Sample Grabber Filter: hr=0x%8lx", hr );
864         return hr;
865     }
866     hr = p_filter_graph->AddFilter( p_sample_grabber, L"Sample Grabber" );
867     if( FAILED( hr ) )
868     {
869         msg_Warn( p_access, "Build: "\
870             "Cannot add Sample Grabber Filter to graph: hr=0x%8lx", hr );
871         return hr;
872     }
873
874     hr = p_sample_grabber->QueryInterface( IID_ISampleGrabber,
875         (void**)&p_grabber );
876     if( FAILED( hr ) )
877     {
878         msg_Warn( p_access, "Build: "\
879             "Cannot QI Sample Grabber Filter: hr=0x%8lx", hr );
880         return hr;
881     }
882
883     ZeroMemory( &grabber_media_type, sizeof( AM_MEDIA_TYPE ) );
884     grabber_media_type.majortype == MEDIATYPE_Stream;
885     grabber_media_type.subtype == MEDIASUBTYPE_MPEG2_TRANSPORT;
886     hr = p_grabber->SetMediaType( &grabber_media_type );
887     if( FAILED( hr ) )
888     {
889         msg_Warn( p_access, "Build: "\
890             "Cannot set media type on grabber filter: hr=0x%8lx", hr );
891         return hr;
892     }
893     hr = Connect( p_capture_device, p_sample_grabber );
894     if( FAILED( hr ) )
895     {
896         msg_Warn( p_access, "Build: "\
897             "Cannot connect Sample Grabber to Capture device: hr=0x%8lx", hr );
898         return hr;
899     }
900
901     /* We need the MPEG2 Demultiplexer even though we are going to use the VLC
902      * TS demuxer. The TIF filter connects to the MPEG2 demux and works with
903      * the Network Provider filter to set up the stream */
904     hr = ::CoCreateInstance( CLSID_MPEG2Demultiplexer, NULL,
905         CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&p_mpeg_demux );
906     if( FAILED( hr ) )
907     {
908         msg_Warn( p_access, "Build: "\
909             "Cannot CoCreateInstance MPEG2 Demultiplexer: hr=0x%8lx", hr );
910         return hr;
911     }
912     hr = p_filter_graph->AddFilter( p_mpeg_demux, L"Demux" );
913     if( FAILED( hr ) )
914     {
915         msg_Warn( p_access, "Build: "\
916             "Cannot add demux filter to graph: hr=0x%8lx", hr );
917         return hr;
918     }
919
920     hr = Connect( p_sample_grabber, p_mpeg_demux );
921     if( FAILED( hr ) )
922     {
923         msg_Warn( p_access, "Build: "\
924             "Cannot connect demux to grabber: hr=0x%8lx", hr );
925         return hr;
926     }
927
928     /* Always look for the Transform Information Filter from the start
929      * of the collection*/
930     l_tif_used = -1;
931     hr = FindFilter( KSCATEGORY_BDA_TRANSPORT_INFORMATION, &l_tif_used,
932         p_mpeg_demux, &p_transport_info );
933     if( FAILED( hr ) )
934     {
935         msg_Warn( p_access, "Build: "\
936             "Cannot load TIF onto demux: hr=0x%8lx", hr );
937         return hr;
938     }
939
940     /* Configure the Sample Grabber to buffer the samples continuously */
941     hr = p_grabber->SetBufferSamples( TRUE );
942     if( FAILED( hr ) )
943     {
944         msg_Warn( p_access, "Build: "\
945             "Cannot set Sample Grabber to buffering: hr=0x%8lx", hr );
946         return hr;
947     }
948     hr = p_grabber->SetOneShot( FALSE );
949     if( FAILED( hr ) )
950     {
951         msg_Warn( p_access, "Build: "\
952             "Cannot set Sample Grabber to multi shot: hr=0x%8lx", hr );
953         return hr;
954     }
955     hr = p_grabber->SetCallback( this, 0 );
956     if( FAILED( hr ) )
957     {
958         msg_Warn( p_access, "Build: "\
959             "Cannot set SampleGrabber Callback: hr=0x%8lx", hr );
960         return hr;
961     }
962
963     hr = Register();
964     if( FAILED( hr ) )
965     {
966         d_graph_register = 0;
967     }
968
969     /* The Media Control is used to Run and Stop the Graph */
970     hr = p_filter_graph->QueryInterface( IID_IMediaControl,
971         (void**)&p_media_control );
972     if( FAILED( hr ) )
973     {
974         msg_Warn( p_access, "Build: "\
975             "Cannot QI Media Control: hr=0x%8lx", hr );
976         return hr;
977     }
978     return hr;
979 }
980
981 /******************************************************************************
982 * FindFilter
983 * Looks up all filters in a category and connects to the upstream filter until
984 * a successful match is found. The index of the connected filter is returned.
985 * On subsequent calls, this can be used to start from that point to find
986 * another match.
987 * This is used when the graph does not run because a tuner device is in use so
988 * another one needs to be slected.
989 ******************************************************************************/
990 HRESULT BDAGraph::FindFilter( REFCLSID clsid, long* i_moniker_used,
991     IBaseFilter* p_upstream, IBaseFilter** p_p_downstream )
992 {
993     HRESULT                 hr = S_OK;
994     int                     i_moniker_index = -1;
995     class localComPtr
996     {
997         public:
998         IMoniker*      p_moniker;
999         IEnumMoniker*  p_moniker_enum;
1000         IBaseFilter*   p_filter;
1001         IPropertyBag*  p_property_bag;
1002         VARIANT        var_bstr;
1003         localComPtr():
1004             p_moniker(NULL),
1005             p_moniker_enum(NULL),
1006             p_filter(NULL),
1007             p_property_bag(NULL)
1008             { ::VariantInit(&var_bstr); };
1009         ~localComPtr()
1010         {
1011             if( p_moniker )
1012                 p_moniker->Release();
1013             if( p_moniker_enum )
1014                 p_moniker_enum->Release();
1015             if( p_filter )
1016                 p_filter->Release();
1017             if( p_property_bag )
1018                 p_property_bag->Release();
1019             ::VariantClear(&var_bstr);
1020         }
1021     } l;
1022
1023     if( !p_system_dev_enum )
1024     {
1025         hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
1026             IID_ICreateDevEnum, (void**)&p_system_dev_enum );
1027         if( FAILED( hr ) )
1028         {
1029             msg_Warn( p_access, "FindFilter: "\
1030                 "Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
1031             return hr;
1032         }
1033     }
1034
1035     hr = p_system_dev_enum->CreateClassEnumerator( clsid,
1036         &l.p_moniker_enum, 0 );
1037     if( hr != S_OK )
1038     {
1039         msg_Warn( p_access, "FindFilter: "\
1040             "Cannot CreateClassEnumerator: hr=0x%8lx", hr );
1041         return E_FAIL;
1042     }
1043     while( l.p_moniker_enum->Next( 1, &l.p_moniker, 0 ) == S_OK )
1044     {
1045         i_moniker_index++;
1046
1047         /* Skip over devices already found on previous calls */
1048         if( i_moniker_index <= *i_moniker_used ) continue;
1049         *i_moniker_used = i_moniker_index;
1050
1051         hr = l.p_moniker->BindToObject( NULL, NULL, IID_IBaseFilter,
1052             (void**)&l.p_filter );
1053         if( FAILED( hr ) )
1054         {
1055             if( l.p_moniker )
1056                 l.p_moniker->Release();
1057             l.p_moniker = NULL;
1058             if( l.p_filter)
1059                  l.p_filter->Release();
1060             l.p_filter = NULL;
1061             continue;
1062         }
1063
1064         hr = l.p_moniker->BindToStorage( NULL, NULL, IID_IPropertyBag,
1065             (void**)&l.p_property_bag );
1066         if( FAILED( hr ) )
1067         {
1068             msg_Warn( p_access, "FindFilter: "\
1069                 "Cannot Bind to Property Bag: hr=0x%8lx", hr );
1070             return hr;
1071         }
1072
1073         hr = l.p_property_bag->Read( L"FriendlyName", &l.var_bstr, NULL );
1074         if( FAILED( hr ) )
1075         {
1076             msg_Warn( p_access, "FindFilter: "\
1077                 "Cannot read filter friendly name: hr=0x%8lx", hr );
1078             return hr;
1079         }
1080
1081         hr = p_filter_graph->AddFilter( l.p_filter, l.var_bstr.bstrVal );
1082         if( FAILED( hr ) )
1083         {
1084             msg_Warn( p_access, "FindFilter: "\
1085                 "Cannot add filter: hr=0x%8lx", hr );
1086             return hr;
1087         }
1088
1089         hr = Connect( p_upstream, l.p_filter );
1090         if( SUCCEEDED( hr ) )
1091         {
1092             msg_Dbg( p_access, "FindFilter: Connected %S", l.var_bstr.bstrVal );
1093             l.p_filter->QueryInterface( IID_IBaseFilter,
1094                 (void**)p_p_downstream );
1095             return S_OK;
1096         }
1097         /* Not the filter we want so unload and try the next one */
1098         hr = p_filter_graph->RemoveFilter( l.p_filter );
1099         if( FAILED( hr ) )
1100         {
1101             msg_Warn( p_access, "FindFilter: "\
1102                 "Failed unloading Filter: hr=0x%8lx", hr );
1103             return hr;
1104         }
1105
1106         if( l.p_moniker )
1107             l.p_moniker->Release();
1108         l.p_moniker = NULL;
1109         if( l.p_filter)
1110             l.p_filter->Release();
1111         l.p_filter = NULL;
1112     }
1113
1114     hr = E_FAIL;
1115     msg_Warn( p_access, "FindFilter: No filter connected: hr=0x%8lx", hr );
1116     return hr;
1117 }
1118
1119 /*****************************************************************************
1120 * Connect is called from Build to enumerate and connect pins
1121 *****************************************************************************/
1122 HRESULT BDAGraph::Connect( IBaseFilter* p_upstream, IBaseFilter* p_downstream )
1123 {
1124     HRESULT             hr = E_FAIL;
1125     class localComPtr
1126     {
1127         public:
1128         IPin*      p_pin_upstream;
1129         IPin*      p_pin_downstream;
1130         IEnumPins* p_pin_upstream_enum;
1131         IEnumPins* p_pin_downstream_enum;
1132         IPin*      p_pin_temp;
1133         localComPtr(): p_pin_upstream(NULL), p_pin_downstream(NULL),
1134             p_pin_upstream_enum(NULL), p_pin_downstream_enum(NULL),
1135             p_pin_temp(NULL) { };
1136         ~localComPtr()
1137         {
1138             if( p_pin_upstream )
1139                 p_pin_upstream->Release();
1140             if( p_pin_downstream )
1141                 p_pin_downstream->Release();
1142             if( p_pin_upstream_enum )
1143                 p_pin_upstream_enum->Release();
1144             if( p_pin_downstream_enum )
1145                 p_pin_downstream_enum->Release();
1146             if( p_pin_temp )
1147                 p_pin_temp->Release();
1148         }
1149     } l;
1150
1151     PIN_INFO            pin_info_upstream;
1152     PIN_INFO            pin_info_downstream;
1153
1154     hr = p_upstream->EnumPins( &l.p_pin_upstream_enum );
1155     if( FAILED( hr ) )
1156     {
1157         msg_Warn( p_access, "Connect: "\
1158             "Cannot get upstream filter enumerator: hr=0x%8lx", hr );
1159         return hr;
1160     }
1161     while( l.p_pin_upstream_enum->Next( 1, &l.p_pin_upstream, 0 ) == S_OK )
1162     {
1163         hr = l.p_pin_upstream->QueryPinInfo( &pin_info_upstream );
1164         if( FAILED( hr ) )
1165         {
1166             msg_Warn( p_access, "Connect: "\
1167                 "Cannot get upstream filter pin information: hr=0x%8lx", hr );
1168             return hr;
1169         }
1170         hr = l.p_pin_upstream->ConnectedTo( &l.p_pin_downstream );
1171         if( hr == S_OK )
1172             l.p_pin_downstream->Release();
1173         if(FAILED( hr ) && hr != VFW_E_NOT_CONNECTED )
1174         {
1175             msg_Warn( p_access, "Connect: "\
1176                 "Cannot check upstream filter connection: hr=0x%8lx", hr );
1177             return hr;
1178         }
1179         if(( pin_info_upstream.dir == PINDIR_OUTPUT ) &&
1180            ( hr == VFW_E_NOT_CONNECTED ) )
1181         {
1182             /* The upstream pin is not yet connected so check each pin on the
1183              * downstream filter */
1184             hr = p_downstream->EnumPins( &l.p_pin_downstream_enum );
1185             if( FAILED( hr ) )
1186             {
1187                 msg_Warn( p_access, "Connect: Cannot get "\
1188                     "downstream filter enumerator: hr=0x%8lx", hr );
1189                 return hr;
1190             }
1191             while( l.p_pin_downstream_enum->Next( 1, &l.p_pin_downstream, 0 )
1192                 == S_OK )
1193             {
1194                 hr = l.p_pin_downstream->QueryPinInfo( &pin_info_downstream );
1195                 if( FAILED( hr ) )
1196                 {
1197                     msg_Warn( p_access, "Connect: Cannot get "\
1198                         "downstream filter pin information: hr=0x%8lx", hr );
1199                     return hr;
1200                 }
1201
1202                 hr = l.p_pin_downstream->ConnectedTo( &l.p_pin_temp );
1203                 if( hr == S_OK ) l.p_pin_temp->Release();
1204                 if( hr != VFW_E_NOT_CONNECTED )
1205                 {
1206                     if( FAILED( hr ) )
1207                     {
1208                         msg_Warn( p_access, "Connect: Cannot check "\
1209                             "downstream filter connection: hr=0x%8lx", hr );
1210                         return hr;
1211                     }
1212                 }
1213                 if(( pin_info_downstream.dir == PINDIR_INPUT ) &&
1214                    ( hr == VFW_E_NOT_CONNECTED ) )
1215                 {
1216                     hr = p_filter_graph->ConnectDirect( l.p_pin_upstream,
1217                         l.p_pin_downstream, NULL );
1218                     if( SUCCEEDED( hr ) )
1219                     {
1220                         pin_info_downstream.pFilter->Release();
1221                         pin_info_upstream.pFilter->Release();
1222                         return S_OK;
1223                     }
1224                 }
1225                 /* If we fall out here it means this downstream pin was not
1226                  * suitable so try the next downstream pin */
1227                 l.p_pin_downstream = NULL;
1228                 pin_info_downstream.pFilter->Release();
1229             }
1230         }
1231
1232         /* If we fall out here it means we did not find any suitable downstream
1233          * pin so try the next upstream pin */
1234         l.p_pin_upstream = NULL;
1235         pin_info_upstream.pFilter->Release();
1236     }
1237
1238     /* If we fall out here it means we did not find any pair of suitable pins */
1239     return E_FAIL;
1240 }
1241
1242 /*****************************************************************************
1243 * Start uses MediaControl to start the graph
1244 *****************************************************************************/
1245 HRESULT BDAGraph::Start()
1246 {
1247     HRESULT hr = S_OK;
1248     OAFilterState i_state; /* State_Stopped, State_Paused, State_Running */
1249
1250     if( !p_media_control )
1251     {
1252         msg_Dbg( p_access, "Start: Media Control has not been created" );
1253         return E_FAIL;
1254     }
1255     hr = p_media_control->Run();
1256     if( hr == S_OK )
1257         return hr;
1258
1259     /* Query the state of the graph - timeout after 100 milliseconds */
1260     while( hr = p_media_control->GetState( 100, &i_state ) != S_OK )
1261     {
1262         if( FAILED( hr ) )
1263         {
1264             msg_Warn( p_access,
1265                 "Start: Cannot get Graph state: hr=0x%8lx", hr );
1266             return hr;
1267         }
1268     }
1269     if( i_state == State_Running )
1270         return hr;
1271
1272     /* The Graph is not running so stop it and return an error */
1273     msg_Warn( p_access, "Start: Graph not started: %d", i_state );
1274     hr = p_media_control->Stop();
1275     if( FAILED( hr ) )
1276     {
1277         msg_Warn( p_access,
1278             "Start: Cannot stop Graph after Run failed: hr=0x%8lx", hr );
1279         return hr;
1280     }
1281     return E_FAIL;
1282 }
1283
1284 /*****************************************************************************
1285 * Read the stream of data - query the buffer size required
1286 *****************************************************************************/
1287 long BDAGraph::GetBufferSize()
1288 {
1289     long l_buffer_size = 0;
1290     long l_queue_size;
1291
1292     b_ready = true;
1293
1294     for( int i_timer = 0; queue_sample.empty() && i_timer < 200; i_timer++ )
1295         Sleep( 10 );
1296
1297     l_queue_size = queue_sample.size();
1298     if( l_queue_size <= 0 )
1299     {
1300         msg_Warn( p_access, "BDA GetBufferSize: Timed Out waiting for sample" );
1301         return -1;
1302     }
1303
1304     /* Establish the length of the queue as it grows quickly. If the queue
1305      * size is checked dynamically there is a risk of not exiting the loop */
1306     for( long l_queue_count=0; l_queue_count < l_queue_size; l_queue_count++ )
1307     {
1308         l_buffer_size += queue_sample.front()->GetActualDataLength();
1309         queue_buffer.push( queue_sample.front() );
1310         queue_sample.pop();
1311     }
1312     return l_buffer_size;
1313 }
1314
1315 /*****************************************************************************
1316 * Read the stream of data - Retrieve from the buffer queue
1317 ******************************************************************************/
1318 long BDAGraph::ReadBuffer( long* pl_buffer_len, BYTE* p_buffer )
1319 {
1320     HRESULT hr = S_OK;
1321
1322     *pl_buffer_len = 0;
1323     BYTE *p_buff_temp;
1324
1325     while( !queue_buffer.empty() )
1326     {
1327         queue_buffer.front()->GetPointer( &p_buff_temp );
1328         hr = queue_buffer.front()->IsDiscontinuity();
1329         if( hr == S_OK )
1330             msg_Warn( p_access,
1331                 "BDA ReadBuffer: Sample Discontinuity. 0x%8lx", hr );
1332         memcpy( p_buffer + *pl_buffer_len, p_buff_temp,
1333             queue_buffer.front()->GetActualDataLength() );
1334         *pl_buffer_len += queue_buffer.front()->GetActualDataLength();
1335         queue_buffer.front()->Release();
1336         queue_buffer.pop();
1337     }
1338
1339     return *pl_buffer_len;
1340 }
1341
1342 /******************************************************************************
1343 * SampleCB - Callback when the Sample Grabber has a sample
1344 ******************************************************************************/
1345 STDMETHODIMP BDAGraph::SampleCB( double d_time, IMediaSample *p_sample )
1346 {
1347     if( b_ready )
1348     {
1349         p_sample->AddRef();
1350         queue_sample.push( p_sample );
1351     }
1352     else
1353     {
1354         msg_Warn( p_access, "BDA SampleCB: Not ready - dropped sample" );
1355     }
1356     return S_OK;
1357 }
1358
1359 STDMETHODIMP BDAGraph::BufferCB( double d_time, BYTE* p_buffer,
1360     long l_buffer_len )
1361 {
1362     return E_FAIL;
1363 }
1364
1365 /******************************************************************************
1366 * removes each filter from the graph
1367 ******************************************************************************/
1368 HRESULT BDAGraph::Destroy()
1369 {
1370     HRESULT hr = S_OK;
1371
1372     if( p_media_control )
1373         hr = p_media_control->Stop();
1374
1375     if( p_transport_info )
1376     {
1377         p_filter_graph->RemoveFilter( p_transport_info );
1378         p_transport_info->Release();
1379         p_transport_info = NULL;
1380     }
1381     if( p_mpeg_demux )
1382     {
1383         p_filter_graph->RemoveFilter( p_mpeg_demux );
1384         p_mpeg_demux->Release();
1385         p_mpeg_demux = NULL;
1386     }
1387     if( p_sample_grabber )
1388     {
1389         p_filter_graph->RemoveFilter( p_sample_grabber );
1390         p_sample_grabber->Release();
1391         p_sample_grabber = NULL;
1392     }
1393     if( p_capture_device )
1394     {
1395         p_filter_graph->RemoveFilter( p_capture_device );
1396         p_capture_device->Release();
1397         p_capture_device = NULL;
1398     }
1399     if( p_tuner_device )
1400     {
1401         p_filter_graph->RemoveFilter( p_tuner_device );
1402         p_tuner_device->Release();
1403         p_tuner_device = NULL;
1404     }
1405     if( p_network_provider )
1406     {
1407         p_filter_graph->RemoveFilter( p_network_provider );
1408         p_network_provider->Release();
1409         p_network_provider = NULL;
1410     }
1411
1412     if( p_scanning_tuner )
1413     {
1414         p_scanning_tuner->Release();
1415         p_scanning_tuner = NULL;
1416     }
1417     if( p_media_control )
1418     {
1419         p_media_control->Release();
1420         p_media_control = NULL;
1421     }
1422     if( p_filter_graph )
1423     {
1424         p_filter_graph->Release();
1425         p_filter_graph = NULL;
1426     }
1427
1428     if( d_graph_register )
1429     {
1430         Deregister();
1431     }
1432
1433     return S_OK;
1434 }
1435
1436 /*****************************************************************************
1437 * Add/Remove a DirectShow filter graph to/from the Running Object Table.
1438 * Allows GraphEdit to "spy" on a remote filter graph.
1439 ******************************************************************************/
1440 HRESULT BDAGraph::Register()
1441 {
1442     class localComPtr
1443     {
1444         public:
1445         IMoniker*             p_moniker;
1446         IRunningObjectTable*  p_ro_table;
1447         localComPtr(): p_moniker(NULL), p_ro_table(NULL) {};
1448         ~localComPtr()
1449         {
1450             if( p_moniker )
1451                 p_moniker->Release();
1452             if( p_ro_table )
1453                 p_ro_table->Release();
1454         }
1455     } l;
1456     WCHAR     psz_w_graph_name[128];
1457     HRESULT   hr;
1458
1459     hr = ::GetRunningObjectTable( 0, &l.p_ro_table );
1460     if( FAILED( hr ) )
1461     {
1462         msg_Warn( p_access, "Register: Cannot get ROT: hr=0x%8lx", hr );
1463         return hr;
1464     }
1465
1466     wsprintfW( psz_w_graph_name, L"VLC BDA Graph %08x Pid %08x",
1467         (DWORD_PTR) p_filter_graph, ::GetCurrentProcessId() );
1468     hr = CreateItemMoniker( L"!", psz_w_graph_name, &l.p_moniker );
1469     if( FAILED( hr ) )
1470     {
1471         msg_Warn( p_access, "Register: Cannot Create Moniker: hr=0x%8lx", hr );
1472         return hr;
1473     }
1474     hr = l.p_ro_table->Register( ROTFLAGS_REGISTRATIONKEEPSALIVE,
1475         p_filter_graph, l.p_moniker, &d_graph_register );
1476     if( FAILED( hr ) )
1477     {
1478         msg_Warn( p_access, "Register: Cannot Register Graph: hr=0x%8lx", hr );
1479         return hr;
1480     }
1481     msg_Dbg( p_access, "Register: registered Graph: %S", psz_w_graph_name );
1482     return hr;
1483 }
1484
1485 void BDAGraph::Deregister()
1486 {
1487     HRESULT   hr;
1488     IRunningObjectTable* p_ro_table;
1489     hr = ::GetRunningObjectTable( 0, &p_ro_table );
1490     if( SUCCEEDED( hr ) )
1491         p_ro_table->Revoke( d_graph_register );
1492     d_graph_register = 0;
1493     p_ro_table->Release();
1494 }