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