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