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