]> git.sesse.net Git - vlc/blob - modules/access/bda/bdagraph.cpp
5fa59d75b4009a08816d8b9d240e052bfde22146
[vlc] / modules / access / bda / bdagraph.cpp
1 /*****************************************************************************
2  * bdagraph.cpp : DirectShow BDA graph for vlc
3  *****************************************************************************
4  * Copyright( C ) 2007 the VideoLAN team
5  *
6  * Author: Ken Self <kenself(at)optusnet(dot)com(dot)au>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  *( at your option ) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #include "bdagraph.h"
27 #include <ctype.h>
28
29 /****************************************************************************
30  * Interfaces for calls from C
31  ****************************************************************************/
32 extern "C" {
33
34     void dvb_newBDAGraph( access_t* p_access )
35     {
36         p_access->p_sys->p_bda_module = new BDAGraph( p_access );
37     };
38
39     void dvb_deleteBDAGraph( access_t* p_access )
40     {
41         delete p_access->p_sys->p_bda_module;
42     };
43
44     int dvb_SubmitATSCTuneRequest( access_t* p_access )
45     {
46         if( p_access->p_sys->p_bda_module )
47             return p_access->p_sys->p_bda_module->SubmitATSCTuneRequest();
48         return VLC_EGENERIC;
49     };
50
51     int dvb_SubmitDVBTTuneRequest( access_t* p_access )
52     {
53         if( p_access->p_sys->p_bda_module )
54             return p_access->p_sys->p_bda_module->SubmitDVBTTuneRequest();
55         return VLC_EGENERIC;
56     };
57
58     int dvb_SubmitDVBCTuneRequest( access_t* p_access )
59     {
60         if( p_access->p_sys->p_bda_module )
61             return p_access->p_sys->p_bda_module->SubmitDVBCTuneRequest();
62         return VLC_EGENERIC;
63     };
64
65     int dvb_SubmitDVBSTuneRequest( access_t* p_access )
66     {
67         if( p_access->p_sys->p_bda_module )
68             return p_access->p_sys->p_bda_module->SubmitDVBSTuneRequest();
69         return VLC_EGENERIC;
70     };
71
72     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         IDVBTuningSpace2* p_dvb_tuning_space;
231         localComPtr(): p_dvbt_tune_request(NULL), p_dvbt_locator(NULL),
232            p_dvb_tuning_space(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             if( p_dvb_tuning_space )
240                 p_dvb_tuning_space->Release();
241         }
242     } l;
243     long l_frequency, l_bandwidth, l_hp_fec, l_lp_fec, l_guard;
244     long l_transmission, l_hierarchy;
245     BinaryConvolutionCodeRate i_hp_fec, i_lp_fec;
246     GuardInterval             i_guard;
247     TransmissionMode          i_transmission;
248     HierarchyAlpha            i_hierarchy;
249
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     switch( l_hp_fec )
259     {
260     case 1:
261         i_hp_fec = BDA_BCC_RATE_1_2; break;
262     case 2:
263         i_hp_fec = BDA_BCC_RATE_2_3; break;
264     case 3:
265         i_hp_fec = BDA_BCC_RATE_3_4; break;
266     case 4:
267         i_hp_fec = BDA_BCC_RATE_5_6; break;
268     case 5:
269         i_hp_fec = BDA_BCC_RATE_7_8;break;
270     default:
271         i_hp_fec = BDA_BCC_RATE_NOT_SET;
272     }
273
274     switch( l_lp_fec )
275     {
276     case 1:
277         i_lp_fec = BDA_BCC_RATE_1_2; break;
278     case 2:
279         i_lp_fec = BDA_BCC_RATE_2_3; break;
280     case 3:
281         i_lp_fec = BDA_BCC_RATE_3_4; break;
282     case 4:
283         i_lp_fec = BDA_BCC_RATE_5_6; break;
284     case 5:
285         i_lp_fec = BDA_BCC_RATE_7_8; break;
286     default:
287         i_lp_fec = BDA_BCC_RATE_NOT_SET;
288     }
289
290     switch( l_guard )
291     {
292     case 32:
293         i_guard = BDA_GUARD_1_32; break;
294     case 16:
295         i_guard = BDA_GUARD_1_16; break;
296     case 8:
297         i_guard = BDA_GUARD_1_8; break;
298     case 4:
299         i_guard = BDA_GUARD_1_4; break;
300     default:
301         i_guard = BDA_GUARD_NOT_SET;
302     }
303
304     switch( l_transmission )
305     {
306     case 2:
307         i_transmission = BDA_XMIT_MODE_2K; break;
308     case 8:
309         i_transmission = BDA_XMIT_MODE_8K; break;
310     default:
311         i_transmission = BDA_XMIT_MODE_NOT_SET;
312     }
313
314     switch( l_hierarchy )
315     {
316     case 1:
317         i_hierarchy = BDA_HALPHA_1; break;
318     case 2:
319         i_hierarchy = BDA_HALPHA_2; break;
320     case 4:
321         i_hierarchy = BDA_HALPHA_4; break;
322     default:
323         i_hierarchy = BDA_HALPHA_NOT_SET;
324     }
325
326     guid_network_type = CLSID_DVBTNetworkProvider;
327     hr = CreateTuneRequest();
328     if( FAILED( hr ) )
329     {
330         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
331             "Cannot create Tune Request: hr=0x%8lx", hr );
332         return VLC_EGENERIC;
333     }
334
335     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
336         (void**)&l.p_dvbt_tune_request );
337     if( FAILED( hr ) )
338     {
339         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
340             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
341         return VLC_EGENERIC;
342     }
343     l.p_dvbt_tune_request->put_ONID( -1 );
344     l.p_dvbt_tune_request->put_SID( -1 );
345     l.p_dvbt_tune_request->put_TSID( -1 );
346
347     hr = ::CoCreateInstance( CLSID_DVBTLocator, 0, CLSCTX_INPROC,
348         IID_IDVBTLocator, (void**)&l.p_dvbt_locator );
349     if( FAILED( hr ) )
350     {
351         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
352             "Cannot create the DVBT Locator: hr=0x%8lx", hr );
353         return VLC_EGENERIC;
354     }
355     hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
356         (void**)&l.p_dvb_tuning_space );
357     if( FAILED( hr ) )
358     {
359         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
360             "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
361         return VLC_EGENERIC;
362     }
363
364     hr = S_OK;
365     hr = l.p_dvb_tuning_space->put_SystemType( DVB_Terrestrial );
366
367     if( SUCCEEDED( hr ) && l_frequency > 0 )
368         hr = l.p_dvbt_locator->put_CarrierFrequency( l_frequency );
369     if( SUCCEEDED( hr ) && l_bandwidth > 0 )
370         hr = l.p_dvbt_locator->put_Bandwidth( l_bandwidth );
371     if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
372         hr = l.p_dvbt_locator->put_InnerFECRate( i_hp_fec );
373     if( SUCCEEDED( hr ) && i_lp_fec != BDA_BCC_RATE_NOT_SET )
374         hr = l.p_dvbt_locator->put_LPInnerFECRate( i_lp_fec );
375     if( SUCCEEDED( hr ) && i_guard != BDA_GUARD_NOT_SET )
376         hr = l.p_dvbt_locator->put_Guard( i_guard );
377     if( SUCCEEDED( hr ) && i_transmission != BDA_XMIT_MODE_NOT_SET )
378         hr = l.p_dvbt_locator->put_Mode( i_transmission );
379     if( SUCCEEDED( hr ) && i_hierarchy != BDA_HALPHA_NOT_SET )
380         hr = l.p_dvbt_locator->put_HAlpha( i_hierarchy );
381     if( FAILED( hr ) )
382     {
383         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
384             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
385         return VLC_EGENERIC;
386     }
387
388     hr = p_tune_request->put_Locator( l.p_dvbt_locator );
389     if( FAILED( hr ) )
390     {
391         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
392             "Cannot put the locator: hr=0x%8lx", hr );
393         return VLC_EGENERIC;
394     }
395
396     /* Build and Run the Graph. If a Tuner device is in use the graph will
397      * fail to run. Repeated calls to build will check successive tuner
398      * devices */
399     do
400     {
401         hr = Build();
402         if( FAILED( hr ) )
403         {
404             msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
405                 "Cannot Build the Graph: hr=0x%8lx", hr );
406             return VLC_EGENERIC;
407         }
408         hr = Start();
409     }
410     while( hr != S_OK );
411
412     return VLC_SUCCESS;
413 }
414
415 /*****************************************************************************
416 * Submit a DVB-C Tune Request
417 ******************************************************************************/
418 int BDAGraph::SubmitDVBCTuneRequest()
419 {
420     HRESULT hr = S_OK;
421
422     class localComPtr
423     {
424         public:
425         IDVBTuneRequest* p_dvbc_tune_request;
426         IDVBCLocator* p_dvbc_locator;
427         IDVBTuningSpace2* p_dvb_tuning_space;
428
429         localComPtr(): p_dvbc_tune_request(NULL), p_dvbc_locator(NULL),
430                        p_dvb_tuning_space(NULL) {};
431         ~localComPtr()
432         {
433             if( p_dvbc_tune_request )
434                 p_dvbc_tune_request->Release();
435             if( p_dvbc_locator )
436                 p_dvbc_locator->Release();
437             if( p_dvb_tuning_space )
438                 p_dvb_tuning_space->Release();
439         }
440     } l;
441
442     long l_frequency, l_symbolrate;
443     int  i_qam;
444     ModulationType i_qam_mod;
445
446     l_frequency  = var_GetInteger( p_access, "dvb-frequency" );
447     l_symbolrate = var_GetInteger( p_access, "dvb-srate" );
448     i_qam        = var_GetInteger( p_access, "dvb-modulation" );
449
450     switch( i_qam )
451     {
452     case 16:
453         i_qam_mod = BDA_MOD_16QAM; break;
454     case 32:
455         i_qam_mod = BDA_MOD_32QAM; break;
456     case 64:
457         i_qam_mod = BDA_MOD_64QAM; break;
458     case 128:
459         i_qam_mod = BDA_MOD_128QAM; break;
460     case 256:
461         i_qam_mod = BDA_MOD_256QAM; break;
462     default:
463         i_qam_mod = BDA_MOD_NOT_SET;
464     }
465
466     guid_network_type = CLSID_DVBCNetworkProvider;
467     hr = CreateTuneRequest();
468     if( FAILED( hr ) )
469     {
470         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
471             "Cannot create Tune Request: hr=0x%8lx", hr );
472         return VLC_EGENERIC;
473     }
474
475     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
476         (void**)&l.p_dvbc_tune_request );
477     if( FAILED( hr ) )
478     {
479         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
480             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
481         return VLC_EGENERIC;
482     }
483     l.p_dvbc_tune_request->put_ONID( -1 );
484     l.p_dvbc_tune_request->put_SID( -1 );
485     l.p_dvbc_tune_request->put_TSID( -1 );
486
487     hr = ::CoCreateInstance( CLSID_DVBCLocator, 0, CLSCTX_INPROC,
488         IID_IDVBCLocator, (void**)&l.p_dvbc_locator );
489     if( FAILED( hr ) )
490     {
491         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
492             "Cannot create the DVB-C Locator: hr=0x%8lx", hr );
493         return VLC_EGENERIC;
494     }
495     hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
496         (void**)&l.p_dvb_tuning_space );
497     if( FAILED( hr ) )
498     {
499         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
500             "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
501         return VLC_EGENERIC;
502     }
503
504     hr = S_OK;
505     hr = l.p_dvb_tuning_space->put_SystemType( DVB_Cable );
506
507     if( SUCCEEDED( hr ) && l_frequency > 0 )
508         hr = l.p_dvbc_locator->put_CarrierFrequency( l_frequency );
509     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
510         hr = l.p_dvbc_locator->put_SymbolRate( l_symbolrate );
511     if( SUCCEEDED( hr ) && i_qam_mod != BDA_MOD_NOT_SET )
512         hr = l.p_dvbc_locator->put_Modulation( i_qam_mod );
513
514     if( FAILED( hr ) )
515     {
516         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
517             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
518         return VLC_EGENERIC;
519     }
520
521     hr = p_tune_request->put_Locator( l.p_dvbc_locator );
522     if( FAILED( hr ) )
523     {
524         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
525             "Cannot put the locator: hr=0x%8lx", hr );
526         return VLC_EGENERIC;
527     }
528
529     /* Build and Run the Graph. If a Tuner device is in use the graph will
530      * fail to run. Repeated calls to build will check successive tuner
531      * devices */
532     do
533     {
534         hr = Build();
535         if( FAILED( hr ) )
536         {
537             msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
538                 "Cannot Build the Graph: hr=0x%8lx", hr );
539             return VLC_EGENERIC;
540         }
541         hr = Start();
542     }
543     while( hr != S_OK );
544
545     return VLC_SUCCESS;
546 }
547
548 /*****************************************************************************
549 * Submit a DVB-S Tune Request
550 ******************************************************************************/
551 int BDAGraph::SubmitDVBSTuneRequest()
552 {
553     HRESULT hr = S_OK;
554
555     class localComPtr
556     {
557         public:
558         IDVBTuneRequest* p_dvbs_tune_request;
559         IDVBSLocator* p_dvbs_locator;
560         IDVBSTuningSpace* p_dvbs_tuning_space;
561         char* psz_polarisation;
562         char* psz_input_range;
563         BSTR bstr_input_range;
564         WCHAR* pwsz_input_range;
565         int i_range_len;
566         localComPtr(): p_dvbs_tune_request(NULL), p_dvbs_locator(NULL),
567             p_dvbs_tuning_space(NULL), bstr_input_range(NULL),
568             pwsz_input_range(NULL), i_range_len(0), psz_polarisation(NULL),
569             psz_input_range(NULL) {};
570         ~localComPtr()
571         {
572             if( p_dvbs_tuning_space )
573                 p_dvbs_tuning_space->Release();
574             if( p_dvbs_tune_request )
575                 p_dvbs_tune_request->Release();
576             if( p_dvbs_locator )
577                 p_dvbs_locator->Release();
578             SysFreeString( bstr_input_range );
579             delete pwsz_input_range;
580             free( psz_input_range );
581             free( psz_polarisation );
582         }
583     } l;
584     long l_frequency, l_symbolrate, l_azimuth, l_elevation, l_longitude;
585     long l_lnb_lof1, l_lnb_lof2, l_lnb_slof, l_inversion, l_network_id;
586     long l_hp_fec;
587     int  i_mod;
588     Polarisation i_polar;
589     SpectralInversion i_inversion;
590     VARIANT_BOOL b_west;
591     BinaryConvolutionCodeRate i_hp_fec;
592     ModulationType i_mod_typ;
593
594     l_frequency        = var_GetInteger( p_access, "dvb-frequency" );
595     l_symbolrate       = var_GetInteger( p_access, "dvb-srate" );
596     l_azimuth          = var_GetInteger( p_access, "dvb-azimuth" );
597     l_elevation        = var_GetInteger( p_access, "dvb-elevation" );
598     l_longitude        = var_GetInteger( p_access, "dvb-longitude" );
599     l_lnb_lof1         = var_GetInteger( p_access, "dvb-lnb-lof1" );
600     l_lnb_lof2         = var_GetInteger( p_access, "dvb-lnb-lof2" );
601     l_lnb_slof         = var_GetInteger( p_access, "dvb-lnb-slof" );
602     i_mod              = var_GetInteger( p_access, "dvb-modulation" );
603     l_hp_fec           = var_GetInteger( p_access, "dvb-code-rate-hp" );
604     l_inversion        = var_GetInteger( p_access, "dvb-inversion" );
605     l_network_id       = var_GetInteger( p_access, "dvb-network-id" );
606
607     l.psz_input_range  = var_GetNonEmptyString( p_access, "dvb-range" );
608     l.psz_polarisation = var_GetNonEmptyString( p_access, "dvb-polarisation" );
609
610     b_west = ( l_longitude < 0 );
611
612     i_polar = BDA_POLARISATION_NOT_SET;
613     if( l.psz_polarisation != NULL )
614     {
615         switch( toupper( l.psz_polarisation[0] ) )
616         {
617         case 'H':
618             i_polar = BDA_POLARISATION_LINEAR_H;
619             break;
620         case 'V':
621             i_polar = BDA_POLARISATION_LINEAR_V;
622             break;
623         case 'L':
624             i_polar = BDA_POLARISATION_CIRCULAR_L;
625             break;
626         case 'R':
627             i_polar = BDA_POLARISATION_CIRCULAR_R;
628             break;
629         }
630     }
631
632     switch( l_inversion )
633     {
634     case 0:
635         i_inversion = BDA_SPECTRAL_INVERSION_NORMAL; break;
636     case 1:
637         i_inversion = BDA_SPECTRAL_INVERSION_INVERTED; break;
638     case 2:
639         i_inversion = BDA_SPECTRAL_INVERSION_AUTOMATIC; break;
640     default:
641         i_inversion = BDA_SPECTRAL_INVERSION_NOT_SET;
642     }
643
644     switch( i_mod )
645     {
646     case 16:
647         i_mod_typ = BDA_MOD_16QAM; break;
648     case 128:
649         i_mod_typ = BDA_MOD_128QAM; break;
650     case 256:
651         i_mod_typ = BDA_MOD_256QAM; break;
652     case 10004:
653         i_mod_typ = BDA_MOD_QPSK; break;
654     default:
655         i_mod_typ = BDA_MOD_NOT_SET;
656     }
657
658     switch( l_hp_fec )
659     {
660     case 1:
661         i_hp_fec = BDA_BCC_RATE_1_2; break;
662     case 2:
663         i_hp_fec = BDA_BCC_RATE_2_3; break;
664     case 3:
665         i_hp_fec = BDA_BCC_RATE_3_4; break;
666     case 4:
667         i_hp_fec = BDA_BCC_RATE_5_6; break;
668     case 5:
669         i_hp_fec = BDA_BCC_RATE_7_8; break;
670     default:
671         i_hp_fec = BDA_BCC_RATE_NOT_SET;
672     }
673
674     l.i_range_len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
675         l.psz_input_range, -1, l.pwsz_input_range, 0 );
676     if( l.i_range_len > 0 )
677     {
678         l.pwsz_input_range = new WCHAR[l.i_range_len];
679         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
680             l.psz_input_range, -1, l.pwsz_input_range, l.i_range_len );
681         l.bstr_input_range=SysAllocString( l.pwsz_input_range );
682     }
683
684     guid_network_type = CLSID_DVBSNetworkProvider;
685     hr = CreateTuneRequest();
686     if( FAILED( hr ) )
687     {
688         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
689             "Cannot create Tune Request: hr=0x%8lx", hr );
690         return VLC_EGENERIC;
691     }
692
693     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
694         (void**)&l.p_dvbs_tune_request );
695     if( FAILED( hr ) )
696     {
697         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
698             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
699         return VLC_EGENERIC;
700     }
701     l.p_dvbs_tune_request->put_ONID( -1 );
702     l.p_dvbs_tune_request->put_SID( -1 );
703     l.p_dvbs_tune_request->put_TSID( -1 );
704
705     hr = ::CoCreateInstance( CLSID_DVBSLocator, 0, CLSCTX_INPROC,
706         IID_IDVBSLocator, (void**)&l.p_dvbs_locator );
707     if( FAILED( hr ) )
708     {
709         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
710             "Cannot create the DVBS Locator: hr=0x%8lx", hr );
711         return VLC_EGENERIC;
712     }
713
714     hr = p_tuning_space->QueryInterface( IID_IDVBSTuningSpace,
715         (void**)&l.p_dvbs_tuning_space );
716     if( FAILED( hr ) )
717     {
718         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
719             "Cannot QI for IDVBSTuningSpace: hr=0x%8lx", hr );
720         return VLC_EGENERIC;
721     }
722
723     hr = S_OK;
724     hr = l.p_dvbs_tuning_space->put_SystemType( DVB_Satellite );
725     if( SUCCEEDED( hr ) && l_lnb_lof1 > 0 )
726         hr = l.p_dvbs_tuning_space->put_LowOscillator( l_lnb_lof1 );
727     if( SUCCEEDED( hr ) && l_lnb_slof > 0 )
728         hr = l.p_dvbs_tuning_space->put_LNBSwitch( l_lnb_slof );
729     if( SUCCEEDED( hr ) && l_lnb_lof2 > 0 )
730         hr = l.p_dvbs_tuning_space->put_HighOscillator( l_lnb_lof2 );
731     if( SUCCEEDED( hr ) && i_inversion != BDA_SPECTRAL_INVERSION_NOT_SET )
732         hr = l.p_dvbs_tuning_space->put_SpectralInversion( i_inversion );
733     if( SUCCEEDED( hr ) && l_network_id > 0 )
734         hr = l.p_dvbs_tuning_space->put_NetworkID( l_network_id );
735     if( SUCCEEDED( hr ) && l.i_range_len > 0 )
736         hr = l.p_dvbs_tuning_space->put_InputRange( l.bstr_input_range );
737
738     if( SUCCEEDED( hr ) && l_frequency > 0 )
739         hr = l.p_dvbs_locator->put_CarrierFrequency( l_frequency );
740     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
741         hr = l.p_dvbs_locator->put_SymbolRate( l_symbolrate );
742     if( SUCCEEDED( hr ) && i_polar != BDA_POLARISATION_NOT_SET )
743         hr = l.p_dvbs_locator->put_SignalPolarisation( i_polar );
744     if( SUCCEEDED( hr ) && i_mod_typ != BDA_MOD_NOT_SET )
745         hr = l.p_dvbs_locator->put_Modulation( i_mod_typ );
746     if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
747         hr = l.p_dvbs_locator->put_InnerFECRate( i_hp_fec );
748
749     if( SUCCEEDED( hr ) && l_azimuth > 0 )
750         hr = l.p_dvbs_locator->put_Azimuth( l_azimuth );
751     if( SUCCEEDED( hr ) && l_elevation > 0 )
752         hr = l.p_dvbs_locator->put_Elevation( l_elevation );
753     if( SUCCEEDED( hr ) )
754         hr = l.p_dvbs_locator->put_WestPosition( b_west );
755     if( SUCCEEDED( hr ) )
756         hr = l.p_dvbs_locator->put_OrbitalPosition( labs( l_longitude ) );
757     if( FAILED( hr ) )
758     {
759         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
760             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
761         return VLC_EGENERIC;
762     }
763
764     hr = p_tune_request->put_Locator( l.p_dvbs_locator );
765     if( FAILED( hr ) )
766     {
767         msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
768             "Cannot put the locator: hr=0x%8lx", hr );
769         return VLC_EGENERIC;
770     }
771
772     /* Build and Run the Graph. If a Tuner device is in use the graph will
773      * fail to run. Repeated calls to build will check successive tuner
774      * devices */
775     do
776     {
777         hr = Build();
778         if( FAILED( hr ) )
779         {
780             msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
781                 "Cannot Build the Graph: hr=0x%8lx", hr );
782             return VLC_EGENERIC;
783         }
784         hr = Start();
785     }
786     while( hr != S_OK );
787
788     return VLC_SUCCESS;
789 }
790
791 /*****************************************************************************
792 * Load the Tuning Space from System Tuning Spaces according to the
793 * Network Type requested
794 ******************************************************************************/
795 HRESULT BDAGraph::CreateTuneRequest()
796 {
797     HRESULT hr = S_OK;
798     GUID guid_this_network_type;
799     class localComPtr
800     {
801         public:
802         ITuningSpaceContainer*  p_tuning_space_container;
803         IEnumTuningSpaces*      p_tuning_space_enum;
804         ITuningSpace*           p_this_tuning_space;
805         IDVBTuningSpace2*       p_dvb_tuning_space;
806         BSTR                    bstr_name;
807         char * psz_network_name;
808         char * psz_create_name;
809         char * psz_bstr_name;
810         WCHAR * wpsz_create_name;
811         int i_name_len;
812         localComPtr(): p_tuning_space_container(NULL),
813             p_tuning_space_enum(NULL), p_this_tuning_space(NULL),
814             p_dvb_tuning_space(NULL),
815             i_name_len(0), psz_network_name(NULL), wpsz_create_name(NULL),
816             psz_create_name(NULL), bstr_name(NULL), psz_bstr_name(NULL) {};
817         ~localComPtr()
818         {
819             if( p_tuning_space_enum )
820                 p_tuning_space_enum->Release();
821             if( p_tuning_space_container )
822                 p_tuning_space_container->Release();
823             if( p_this_tuning_space )
824                 p_this_tuning_space->Release();
825             if( p_dvb_tuning_space )
826                 p_dvb_tuning_space->Release();
827             SysFreeString( bstr_name );
828             delete[] psz_bstr_name;
829             delete[] wpsz_create_name;
830             free( psz_network_name );
831             free( psz_create_name );
832         }
833     } l;
834
835     /* We shall test for a specific Tuning space name supplied on the command
836      * line as dvb-networkname=xxx.
837      * For some users with multiple cards and/or multiple networks this could
838      * be useful. This allows us to reasonably safely apply updates to the
839      * System Tuning Space in the registry without disrupting other streams. */
840     l.psz_network_name = var_GetNonEmptyString( p_access, "dvb-network-name" );
841     if( l.psz_network_name )
842     {
843         msg_Dbg( p_access, "CreateTuneRequest: Find Tuning Space: %s",
844             l.psz_network_name );
845     }
846     else
847     {
848         l.psz_network_name = new char[1];
849         *l.psz_network_name = '\0';
850     }
851
852     /* A Tuning Space may already have been set up. If it is for the same
853      * network type then all is well. Otherwise, reset the Tuning Space and get
854      * a new one */
855     if( p_tuning_space )
856     {
857         hr = p_tuning_space->get__NetworkType( &guid_this_network_type );
858         if( FAILED( hr ) ) guid_this_network_type = GUID_NULL;
859         if( guid_this_network_type == guid_network_type )
860         {
861             hr = p_tuning_space->get_UniqueName( &l.bstr_name );
862             if( FAILED( hr ) )
863             {
864                 msg_Warn( p_access, "CreateTuneRequest: "\
865                     "Cannot get UniqueName for Tuning Space: hr=0x%8lx", hr );
866                 return hr;
867             }
868             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
869                 l.psz_bstr_name, 0, NULL, NULL );
870             l.psz_bstr_name = new char[ l.i_name_len ];
871             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
872                 l.psz_bstr_name, l.i_name_len, NULL, NULL );
873
874             /* Test for a specific Tuning space name supplied on the command
875              * line as dvb-networkname=xxx */
876             if( strlen( l.psz_network_name ) == 0 ||
877                 strcmp( l.psz_network_name, l.psz_bstr_name ) == 0 )
878             {
879                 msg_Dbg( p_access, "CreateTuneRequest: Using Tuning Space: %s",
880                     l.psz_network_name );
881                 return S_OK;
882             }
883         }
884         /* else different guid_network_type */
885         if( p_tuning_space )
886             p_tuning_space->Release();
887         if( p_tune_request )
888             p_tune_request->Release();
889         p_tuning_space = NULL;
890         p_tune_request = NULL;
891     }
892
893     /* Force use of the first available Tuner Device during Build */
894     l_tuner_used = -1;
895
896     /* Get the SystemTuningSpaces container to enumerate through all the
897      * defined tuning spaces.
898      * l.p_tuning_space_container->Refcount = 1  */
899     hr = ::CoCreateInstance( CLSID_SystemTuningSpaces, 0, CLSCTX_INPROC,
900         IID_ITuningSpaceContainer, (void**)&l.p_tuning_space_container );
901     if( FAILED( hr ) )
902     {
903         msg_Warn( p_access, "CreateTuneRequest: "\
904             "Cannot CoCreate SystemTuningSpaces: hr=0x%8lx", hr );
905         return hr;
906     }
907
908     /* Get the SystemTuningSpaces container to enumerate through all the
909      * defined tuning spaces.
910      * l.p_tuning_space_container->Refcount = 2
911      * l.p_tuning_space_enum->Refcount = 1  */
912     hr = l.p_tuning_space_container->get_EnumTuningSpaces(
913          &l.p_tuning_space_enum );
914     if( FAILED( hr ) )
915     {
916         msg_Warn( p_access, "CreateTuneRequest: "\
917             "Cannot create SystemTuningSpaces Enumerator: hr=0x%8lx", hr );
918         return hr;
919     }
920
921     do
922     {
923         /* l.p_this_tuning_space->RefCount = 1 after the first pass
924          * Release before overwriting with Next */
925         if( l.p_this_tuning_space )
926             l.p_this_tuning_space->Release();
927         l.p_this_tuning_space = NULL;
928         SysFreeString( l.bstr_name );
929
930         hr = l.p_tuning_space_enum->Next( 1, &l.p_this_tuning_space, NULL );
931         if( hr != S_OK ) break;
932
933         hr = l.p_this_tuning_space->get__NetworkType( &guid_this_network_type );
934
935         /* GUID_NULL means a non-BDA network was found e.g analog
936          * Ignore failures and non-BDA networks and keep looking */
937         if( FAILED( hr ) ) guid_this_network_type == GUID_NULL;
938
939         if( guid_this_network_type == guid_network_type )
940         {
941             /* QueryInterface to clone l.p_this_tuning_space
942              * l.p_this_tuning_space->RefCount = 2 */
943             hr = l.p_this_tuning_space->Clone( &p_tuning_space );
944             if( FAILED( hr ) )
945             {
946                 msg_Warn( p_access, "CreateTuneRequest: "\
947                     "Cannot QI Tuning Space: hr=0x%8lx", hr );
948                 return hr;
949             }
950             hr = p_tuning_space->get_UniqueName( &l.bstr_name );
951             if( FAILED( hr ) )
952             {
953                 msg_Warn( p_access, "CreateTuneRequest: "\
954                     "Cannot get UniqueName for Tuning Space: hr=0x%8lx", hr );
955                 return hr;
956             }
957
958             /* Test for a specific Tuning space name supplied on the command
959              * line as dvb-networkname=xxx */
960             delete[] l.psz_bstr_name;
961             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
962                 l.psz_bstr_name, 0, NULL, NULL );
963             l.psz_bstr_name = new char[ l.i_name_len ];
964             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
965                 l.psz_bstr_name, l.i_name_len, NULL, NULL );
966             if( strlen( l.psz_network_name ) == 0 ||
967                 strcmp( l.psz_network_name, l.psz_bstr_name ) == 0 )
968             {
969                 msg_Dbg( p_access, "CreateTuneRequest: Using Tuning Space: %s",
970                     l.psz_bstr_name );
971
972             /* CreateTuneRequest adds TuneRequest to p_tuning_space
973              * p_tune_request->RefCount = 1 */
974                 hr = p_tuning_space->CreateTuneRequest( &p_tune_request );
975                 if( FAILED( hr ) )
976                     msg_Warn( p_access, "CreateTuneRequest: "\
977                         "Cannot Create Tune Request: hr=0x%8lx", hr );
978                 return hr;
979             }
980             if( p_tuning_space )
981                 p_tuning_space->Release();
982             p_tuning_space = NULL;
983         }
984     }
985     while( true );
986
987     /* No tuning space was found. If the create-name parameter was set then
988      * create a tuning space. By rights should use the same name used in
989      * network-name
990      * Also would be nice to copy a tuning space but we only come here if we do
991      * not find any. */
992     l.psz_create_name = var_GetNonEmptyString( p_access, "dvb-create-name" );
993     if( !l.psz_create_name || strlen( l.psz_create_name ) <= 0 )
994     {
995         hr = E_FAIL;
996         msg_Warn( p_access, "CreateTuneRequest: "\
997             "Cannot find a suitable System Tuning Space: hr=0x%8lx", hr );
998         return hr;
999     }
1000     if( strcmp( l.psz_create_name, l.psz_network_name ) )
1001     {
1002         hr = E_FAIL;
1003         msg_Warn( p_access, "CreateTuneRequest: "\
1004             "dvb-create-name %s must match dvb-network-name %s",
1005             l.psz_create_name, l.psz_network_name );
1006         return hr;
1007     }
1008
1009     /* Need to use DVBSTuningSpace for DVB-S and ATSCTuningSpace for ATSC */
1010     VARIANT var_id;
1011     CLSID cls_tuning_space;
1012
1013     if( IsEqualCLSID( guid_network_type, CLSID_ATSCNetworkProvider ) )
1014         cls_tuning_space = CLSID_ATSCTuningSpace;
1015     if( IsEqualCLSID( guid_network_type, CLSID_DVBTNetworkProvider ) )
1016         cls_tuning_space = CLSID_DVBTuningSpace;
1017     if( IsEqualCLSID( guid_network_type, CLSID_DVBCNetworkProvider ) )
1018         cls_tuning_space = CLSID_DVBTuningSpace;
1019     if( IsEqualCLSID( guid_network_type, CLSID_DVBSNetworkProvider ) )
1020         cls_tuning_space = CLSID_DVBSTuningSpace;
1021
1022     l.i_name_len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
1023         l.psz_create_name, -1, l.wpsz_create_name, 0 );
1024     if( l.i_name_len <= 0 )
1025     {
1026         hr = E_FAIL;
1027         msg_Warn( p_access, "CreateTuneRequest: "\
1028             "Cannot convert zero length dvb-create-name %s",
1029             l.psz_create_name );
1030         return hr;
1031     }
1032     l.wpsz_create_name = new WCHAR[l.i_name_len];
1033     MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, l.psz_create_name, -1,
1034             l.wpsz_create_name, l.i_name_len );
1035     if( l.bstr_name )
1036         SysFreeString( l.bstr_name );
1037     l.bstr_name = SysAllocString( l.wpsz_create_name );
1038
1039     msg_Dbg( p_access, "CreateTuneRequest: Create Tuning Space: %s",
1040         l.psz_create_name );
1041
1042     hr = ::CoCreateInstance( cls_tuning_space, 0, CLSCTX_INPROC,
1043          IID_ITuningSpace, (void**)&p_tuning_space );
1044
1045     if( FAILED( hr ) )
1046         msg_Warn( p_access, "CreateTuneRequest: "\
1047             "Cannot CoCreate new TuningSpace: hr=0x%8lx", hr );
1048     if( SUCCEEDED( hr ) )
1049         hr = p_tuning_space->put__NetworkType( guid_network_type );
1050     if( FAILED( hr ) )
1051         msg_Warn( p_access, "CreateTuneRequest: "\
1052             "Cannot Put Network Type: hr=0x%8lx", hr );
1053     if( SUCCEEDED( hr ) )
1054         hr = p_tuning_space->put_UniqueName( l.bstr_name );
1055     if( FAILED( hr ) )
1056         msg_Warn( p_access, "CreateTuneRequest: "\
1057             "Cannot Put Unique Name: hr=0x%8lx", hr );
1058     if( SUCCEEDED( hr ) )
1059         hr = p_tuning_space->put_FriendlyName( l.bstr_name );
1060     if( FAILED( hr ) )
1061         msg_Warn( p_access, "CreateTuneRequest: "\
1062             "Cannot Put Friendly Name: hr=0x%8lx", hr );
1063     if( guid_network_type == CLSID_DVBTNetworkProvider ||
1064         guid_network_type == CLSID_DVBCNetworkProvider ||
1065         guid_network_type == CLSID_DVBSNetworkProvider )
1066     {
1067         hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
1068             (void**)&l.p_dvb_tuning_space );
1069         if( FAILED( hr ) )
1070         {
1071             msg_Warn( p_access, "CreateTuneRequest: "\
1072                 "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
1073             return hr;
1074         }
1075         if( guid_network_type == CLSID_DVBTNetworkProvider )
1076             hr = l.p_dvb_tuning_space->put_SystemType( DVB_Terrestrial );
1077         if( guid_network_type == CLSID_DVBCNetworkProvider )
1078             hr = l.p_dvb_tuning_space->put_SystemType( DVB_Cable );
1079         if( guid_network_type == CLSID_DVBSNetworkProvider )
1080             hr = l.p_dvb_tuning_space->put_SystemType( DVB_Satellite );
1081     }
1082
1083     if( SUCCEEDED( hr ) )
1084         hr = l.p_tuning_space_container->Add( p_tuning_space, &var_id );
1085
1086     if( FAILED( hr ) )
1087     {
1088         msg_Warn( p_access, "CreateTuneRequest: "\
1089             "Cannot Create new TuningSpace: hr=0x%8lx", hr );
1090         return hr;
1091     }
1092
1093     msg_Dbg( p_access, "CreateTuneRequest: Tuning Space: %s created",
1094          l.psz_create_name );
1095
1096     hr = p_tuning_space->CreateTuneRequest( &p_tune_request );
1097     if( FAILED( hr ) )
1098         msg_Warn( p_access, "CreateTuneRequest: "\
1099             "Cannot Create Tune Request: hr=0x%8lx", hr );
1100
1101     return hr;
1102 }
1103
1104 /******************************************************************************
1105 * Build
1106 * Step 4: Build the Filter Graph
1107 * Build sets up devices, adds and connects filters
1108 ******************************************************************************/
1109 HRESULT BDAGraph::Build()
1110 {
1111     HRESULT hr = S_OK;
1112     long l_capture_used, l_tif_used;
1113     VARIANT l_tuning_space_id;
1114     AM_MEDIA_TYPE grabber_media_type;
1115     class localComPtr
1116     {
1117         public:
1118         ITuningSpaceContainer*  p_tuning_space_container;
1119         localComPtr(): p_tuning_space_container(NULL) {};
1120         ~localComPtr()
1121         {
1122             if( p_tuning_space_container )
1123                 p_tuning_space_container->Release();
1124         }
1125     } l;
1126
1127     /* Get the SystemTuningSpaces container to save the Tuning space */
1128     l_tuning_space_id.vt = VT_I4;
1129     l_tuning_space_id.lVal = 0L;
1130     hr = ::CoCreateInstance( CLSID_SystemTuningSpaces, 0, CLSCTX_INPROC,
1131         IID_ITuningSpaceContainer, (void**)&l.p_tuning_space_container );
1132     if( FAILED( hr ) )
1133     {
1134         msg_Warn( p_access, "Build: "\
1135             "Cannot CoCreate SystemTuningSpaces: hr=0x%8lx", hr );
1136         return hr;
1137     }
1138     hr = l.p_tuning_space_container->FindID( p_tuning_space,
1139         &l_tuning_space_id.lVal );
1140     if( FAILED( hr ) )
1141     {
1142         msg_Warn( p_access, "Build: "\
1143             "Cannot Find Tuning Space ID: hr=0x%8lx", hr );
1144         return hr;
1145     }
1146     msg_Dbg( p_access, "Build: Using Tuning Space ID %d",
1147         l_tuning_space_id.lVal );
1148     hr = l.p_tuning_space_container->put_Item( l_tuning_space_id,
1149         p_tuning_space );
1150     if( FAILED( hr ) )
1151     {
1152         msg_Warn( p_access, "Build: "\
1153             "Cannot save Tuning Space: hr=0x%8lx", hr );
1154         return hr;
1155     }
1156
1157     /* If we have already have a filter graph, rebuild it*/
1158     Destroy();
1159
1160     hr = ::CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
1161         IID_IGraphBuilder, (void**)&p_filter_graph );
1162     if( FAILED( hr ) )
1163     {
1164         msg_Warn( p_access, "Build: "\
1165             "Cannot CoCreate IFilterGraph: hr=0x%8lx", hr );
1166         return hr;
1167     }
1168
1169     /* First filter in the graph is the Network Provider and
1170      * its Scanning Tuner which takes the Tune Request*/
1171     hr = ::CoCreateInstance( guid_network_type, NULL, CLSCTX_INPROC_SERVER,
1172         IID_IBaseFilter, (void**)&p_network_provider);
1173     if( FAILED( hr ) )
1174     {
1175         msg_Warn( p_access, "Build: "\
1176             "Cannot CoCreate Network Provider: hr=0x%8lx", hr );
1177         return hr;
1178     }
1179     hr = p_filter_graph->AddFilter( p_network_provider, L"Network Provider" );
1180     if( FAILED( hr ) )
1181     {
1182         msg_Warn( p_access, "Build: "\
1183             "Cannot load network provider: hr=0x%8lx", hr );
1184         return hr;
1185     }
1186
1187     hr = p_network_provider->QueryInterface( IID_IScanningTuner,
1188         (void**)&p_scanning_tuner );
1189     if( FAILED( hr ) )
1190     {
1191         msg_Warn( p_access, "Build: "\
1192             "Cannot QI Network Provider for Scanning Tuner: hr=0x%8lx", hr );
1193         return hr;
1194     }
1195
1196     hr = p_scanning_tuner->Validate( p_tune_request );
1197     if( FAILED( hr ) )
1198     {
1199         msg_Warn( p_access, "Build: "\
1200             "Tune Request is invalid: hr=0x%8lx", hr );
1201         return hr;
1202     }
1203     hr = p_scanning_tuner->put_TuneRequest( p_tune_request );
1204     if( FAILED( hr ) )
1205     {
1206         msg_Warn( p_access, "Build: "\
1207             "Cannot submit the tune request: hr=0x%8lx", hr );
1208         return hr;
1209     }
1210
1211     /* Add the Network Tuner to the Network Provider. On subsequent calls,
1212      * l_tuner_used will cause a different tuner to be selected
1213      * To select a specific device first get the parameter that nominates the
1214      * device (dvb-adapter) and use the value to initialise l_tuner_used.
1215      * When FindFilter returns check the contents of l_tuner_used.
1216      * If it is not what was selected then the requested device was not
1217      * available so return with an error. */
1218
1219     long l_adapter = -1;
1220     l_adapter = var_GetInteger( p_access, "dvb-adapter" );
1221     if( l_tuner_used < 0 && l_adapter >= 0 )
1222         l_tuner_used = l_adapter - 1;
1223
1224     hr = FindFilter( KSCATEGORY_BDA_NETWORK_TUNER, &l_tuner_used,
1225         p_network_provider, &p_tuner_device );
1226     if( FAILED( hr ) )
1227     {
1228         msg_Warn( p_access, "Build: "\
1229             "Cannot load tuner device and connect network provider: "\
1230             "hr=0x%8lx", hr );
1231         return hr;
1232     }
1233     if( l_adapter > 0 && l_tuner_used != l_adapter )
1234     {
1235          msg_Warn( p_access, "Build: "\
1236              "Tuner device %d is not available", l_adapter );
1237         return E_FAIL;
1238     }
1239     msg_Dbg( p_access, "BDAGraph: Using adapter %d", l_tuner_used );
1240
1241 /* VLC 1.0 works reliably up this point then crashes
1242  * Obvious candidate is FindFilter */
1243     /* Always look for all capture devices to match the Network Tuner */
1244     l_capture_used = -1;
1245     hr = FindFilter( KSCATEGORY_BDA_RECEIVER_COMPONENT, &l_capture_used,
1246         p_tuner_device, &p_capture_device );
1247     if( FAILED( hr ) )
1248     {
1249         /* Some BDA drivers do not provide a Capture Device Filter so force
1250          * the Sample Grabber to connect directly to the Tuner Device */
1251         p_capture_device = p_tuner_device;
1252         p_tuner_device = NULL;
1253         msg_Warn( p_access, "Build: "\
1254             "Cannot find Capture device. Connecting to tuner: hr=0x%8lx", hr );
1255     }
1256     if( p_sample_grabber )
1257          p_sample_grabber->Release();
1258     p_sample_grabber = NULL;
1259     /* Insert the Sample Grabber to tap into the Transport Stream. */
1260     hr = ::CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
1261         IID_IBaseFilter, (void**)&p_sample_grabber );
1262     if( FAILED( hr ) )
1263     {
1264         msg_Warn( p_access, "Build: "\
1265             "Cannot load Sample Grabber Filter: hr=0x%8lx", hr );
1266         return hr;
1267     }
1268     hr = p_filter_graph->AddFilter( p_sample_grabber, L"Sample Grabber" );
1269     if( FAILED( hr ) )
1270     {
1271         msg_Warn( p_access, "Build: "\
1272             "Cannot add Sample Grabber Filter to graph: hr=0x%8lx", hr );
1273         return hr;
1274     }
1275
1276     if( p_grabber )
1277         p_grabber->Release();
1278     p_grabber = NULL;
1279     hr = p_sample_grabber->QueryInterface( IID_ISampleGrabber,
1280         (void**)&p_grabber );
1281     if( FAILED( hr ) )
1282     {
1283         msg_Warn( p_access, "Build: "\
1284             "Cannot QI Sample Grabber Filter: hr=0x%8lx", hr );
1285         return hr;
1286     }
1287
1288     /* Try the possible stream type */
1289     hr = E_FAIL;
1290     for( int i = 0; i < 2; i++ )
1291     {
1292         ZeroMemory( &grabber_media_type, sizeof( AM_MEDIA_TYPE ) );
1293         grabber_media_type.majortype = MEDIATYPE_Stream;
1294         grabber_media_type.subtype   =  i == 0 ? MEDIASUBTYPE_MPEG2_TRANSPORT : KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT;
1295         msg_Dbg( p_access, "Build: "
1296                            "Trying connecting with subtype %s",
1297                            i == 0 ? "MEDIASUBTYPE_MPEG2_TRANSPORT" : "KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT" );
1298         hr = p_grabber->SetMediaType( &grabber_media_type );
1299         if( SUCCEEDED( hr ) )
1300         {
1301             hr = Connect( p_capture_device, p_sample_grabber );
1302             if( SUCCEEDED( hr ) )
1303                 break;
1304             msg_Warn( p_access, "Build: "\
1305                 "Cannot connect Sample Grabber to Capture device: hr=0x%8lx (try %d/2)", hr, 1+i );
1306         }
1307         else
1308         {
1309             msg_Warn( p_access, "Build: "\
1310                 "Cannot set media type on grabber filter: hr=0x%8lx (try %d/2", hr, 1+i );
1311         }
1312     }
1313     if( hr )
1314         return hr;
1315
1316     /* We need the MPEG2 Demultiplexer even though we are going to use the VLC
1317      * TS demuxer. The TIF filter connects to the MPEG2 demux and works with
1318      * the Network Provider filter to set up the stream */
1319     hr = ::CoCreateInstance( CLSID_MPEG2Demultiplexer, NULL,
1320         CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&p_mpeg_demux );
1321     if( FAILED( hr ) )
1322     {
1323         msg_Warn( p_access, "Build: "\
1324             "Cannot CoCreateInstance MPEG2 Demultiplexer: hr=0x%8lx", hr );
1325         return hr;
1326     }
1327     hr = p_filter_graph->AddFilter( p_mpeg_demux, L"Demux" );
1328     if( FAILED( hr ) )
1329     {
1330         msg_Warn( p_access, "Build: "\
1331             "Cannot add demux filter to graph: hr=0x%8lx", hr );
1332         return hr;
1333     }
1334     hr = Connect( p_sample_grabber, p_mpeg_demux );
1335     if( FAILED( hr ) )
1336     {
1337         msg_Warn( p_access, "Build: "\
1338             "Cannot connect demux to grabber: hr=0x%8lx", hr );
1339         return hr;
1340     }
1341
1342     /* Always look for the Transform Information Filter from the start
1343      * of the collection*/
1344     l_tif_used = -1;
1345     hr = FindFilter( KSCATEGORY_BDA_TRANSPORT_INFORMATION, &l_tif_used,
1346         p_mpeg_demux, &p_transport_info );
1347     if( FAILED( hr ) )
1348     {
1349         msg_Warn( p_access, "Build: "\
1350             "Cannot load TIF onto demux: hr=0x%8lx", hr );
1351         return hr;
1352     }
1353     /* Configure the Sample Grabber to buffer the samples continuously */
1354     hr = p_grabber->SetBufferSamples( true );
1355     if( FAILED( hr ) )
1356     {
1357         msg_Warn( p_access, "Build: "\
1358             "Cannot set Sample Grabber to buffering: hr=0x%8lx", hr );
1359         return hr;
1360     }
1361     hr = p_grabber->SetOneShot( false );
1362     if( FAILED( hr ) )
1363     {
1364         msg_Warn( p_access, "Build: "\
1365             "Cannot set Sample Grabber to multi shot: hr=0x%8lx", hr );
1366         return hr;
1367     }
1368     hr = p_grabber->SetCallback( this, 0 );
1369     if( FAILED( hr ) )
1370     {
1371         msg_Warn( p_access, "Build: "\
1372             "Cannot set SampleGrabber Callback: hr=0x%8lx", hr );
1373         return hr;
1374     }
1375
1376     hr = Register();
1377     if( FAILED( hr ) )
1378     {
1379         d_graph_register = 0;
1380     }
1381
1382     /* The Media Control is used to Run and Stop the Graph */
1383     if( p_media_control )
1384         p_media_control->Release();
1385     p_media_control = NULL;
1386     hr = p_filter_graph->QueryInterface( IID_IMediaControl,
1387         (void**)&p_media_control );
1388     if( FAILED( hr ) )
1389     {
1390         msg_Warn( p_access, "Build: "\
1391             "Cannot QI Media Control: hr=0x%8lx", hr );
1392         return hr;
1393     }
1394
1395     return hr;
1396
1397 }
1398
1399 /******************************************************************************
1400 * FindFilter
1401 * Looks up all filters in a category and connects to the upstream filter until
1402 * a successful match is found. The index of the connected filter is returned.
1403 * On subsequent calls, this can be used to start from that point to find
1404 * another match.
1405 * This is used when the graph does not run because a tuner device is in use so
1406 * another one needs to be selected.
1407 ******************************************************************************/
1408 HRESULT BDAGraph::FindFilter( REFCLSID clsid, long* i_moniker_used,
1409     IBaseFilter* p_upstream, IBaseFilter** p_p_downstream )
1410 {
1411     HRESULT                 hr = S_OK;
1412     int                     i_moniker_index = -1;
1413     class localComPtr
1414     {
1415         public:
1416         IMoniker*      p_moniker;
1417         IEnumMoniker*  p_moniker_enum;
1418         IBaseFilter*   p_filter;
1419         IPropertyBag*  p_property_bag;
1420         VARIANT        var_bstr;
1421         char *         psz_bstr;
1422         int            i_bstr_len;
1423         localComPtr():
1424             p_moniker(NULL),
1425             p_moniker_enum(NULL),
1426             p_filter(NULL),
1427             p_property_bag(NULL),
1428             psz_bstr( NULL )
1429             { ::VariantInit(&var_bstr); };
1430         ~localComPtr()
1431         {
1432             if( p_property_bag )
1433                 p_property_bag->Release();
1434             if( p_filter )
1435                 p_filter->Release();
1436             if( p_moniker )
1437                 p_moniker->Release();
1438             if( p_moniker_enum )
1439                 p_moniker_enum->Release();
1440             ::VariantClear(&var_bstr);
1441             delete[] psz_bstr;
1442         }
1443     } l;
1444
1445     if( !p_system_dev_enum )
1446     {
1447         hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
1448             IID_ICreateDevEnum, (void**)&p_system_dev_enum );
1449         if( FAILED( hr ) )
1450         {
1451             msg_Warn( p_access, "FindFilter: "\
1452                 "Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
1453             return hr;
1454         }
1455     }
1456
1457     hr = p_system_dev_enum->CreateClassEnumerator( clsid,
1458         &l.p_moniker_enum, 0 );
1459     if( hr != S_OK )
1460     {
1461         msg_Warn( p_access, "FindFilter: "\
1462             "Cannot CreateClassEnumerator: hr=0x%8lx", hr );
1463         return E_FAIL;
1464     }
1465
1466     do
1467     {
1468         /* We are overwriting l.p_moniker so we should Release and nullify
1469          * It is important that p_moniker and p_property_bag are fully released
1470          * l.p_filter may not be dereferenced so we could force to NULL */
1471         if( l.p_property_bag )
1472             l.p_property_bag->Release();
1473         l.p_property_bag = NULL;
1474         if( l.p_filter )
1475             l.p_filter->Release();
1476         l.p_filter = NULL;
1477         if( l.p_moniker )
1478             l.p_moniker->Release();
1479          l.p_moniker = NULL;
1480
1481         hr = l.p_moniker_enum->Next( 1, &l.p_moniker, 0 );
1482         if( hr != S_OK ) break;
1483         i_moniker_index++;
1484
1485         /* Skip over devices already found on previous calls */
1486         if( i_moniker_index <= *i_moniker_used ) continue;
1487         *i_moniker_used = i_moniker_index;
1488
1489         /* l.p_filter is Released at the top of the loop */
1490         hr = l.p_moniker->BindToObject( NULL, NULL, IID_IBaseFilter,
1491             (void**)&l.p_filter );
1492         if( FAILED( hr ) )
1493         {
1494             continue;
1495         }
1496         /* l.p_property_bag is released at the top of the loop */
1497         hr = l.p_moniker->BindToStorage( NULL, NULL, IID_IPropertyBag,
1498             (void**)&l.p_property_bag );
1499         if( FAILED( hr ) )
1500         {
1501             msg_Warn( p_access, "FindFilter: "\
1502                 "Cannot Bind to Property Bag: hr=0x%8lx", hr );
1503             return hr;
1504         }
1505         hr = l.p_property_bag->Read( L"FriendlyName", &l.var_bstr, NULL );
1506         if( FAILED( hr ) )
1507         {
1508             msg_Warn( p_access, "FindFilter: "\
1509                 "Cannot read filter friendly name: hr=0x%8lx", hr );
1510             return hr;
1511         }
1512
1513         hr = p_filter_graph->AddFilter( l.p_filter, l.var_bstr.bstrVal );
1514         if( FAILED( hr ) )
1515         {
1516             msg_Warn( p_access, "FindFilter: "\
1517                 "Cannot add filter: hr=0x%8lx", hr );
1518             return hr;
1519         }
1520         hr = Connect( p_upstream, l.p_filter );
1521         if( SUCCEEDED( hr ) )
1522         {
1523             /* p_p_downstream has not been touched yet so no release needed */
1524             delete[] l.psz_bstr;
1525             l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0,
1526                 l.var_bstr.bstrVal, -1, l.psz_bstr, 0, NULL, NULL );
1527             l.psz_bstr = new char[l.i_bstr_len];
1528             l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0,
1529                 l.var_bstr.bstrVal, -1, l.psz_bstr, l.i_bstr_len, NULL, NULL );
1530             msg_Dbg( p_access, "FindFilter: Connected %s", l.psz_bstr );
1531             l.p_filter->QueryInterface( IID_IBaseFilter,
1532                 (void**)p_p_downstream );
1533             return S_OK;
1534         }
1535         /* Not the filter we want so unload and try the next one */
1536         hr = p_filter_graph->RemoveFilter( l.p_filter );
1537         if( FAILED( hr ) )
1538         {
1539             msg_Warn( p_access, "FindFilter: "\
1540                 "Failed unloading Filter: hr=0x%8lx", hr );
1541             return hr;
1542         }
1543
1544     }
1545     while( true );
1546
1547     hr = E_FAIL;
1548     msg_Warn( p_access, "FindFilter: No filter connected: hr=0x%8lx", hr );
1549     return hr;
1550 }
1551
1552 /*****************************************************************************
1553 * Connect is called from Build to enumerate and connect pins
1554 *****************************************************************************/
1555 HRESULT BDAGraph::Connect( IBaseFilter* p_upstream, IBaseFilter* p_downstream )
1556 {
1557     HRESULT             hr = E_FAIL;
1558     class localComPtr
1559     {
1560         public:
1561         IPin*      p_pin_upstream;
1562         IPin*      p_pin_downstream;
1563         IEnumPins* p_pin_upstream_enum;
1564         IEnumPins* p_pin_downstream_enum;
1565         IPin*      p_pin_temp;
1566         localComPtr(): p_pin_upstream(NULL), p_pin_downstream(NULL),
1567             p_pin_upstream_enum(NULL), p_pin_downstream_enum(NULL),
1568             p_pin_temp(NULL) { };
1569         ~localComPtr()
1570         {
1571             if( p_pin_temp )
1572                 p_pin_temp->Release();
1573             if( p_pin_downstream )
1574                 p_pin_downstream->Release();
1575             if( p_pin_upstream )
1576                 p_pin_upstream->Release();
1577             if( p_pin_downstream_enum )
1578                 p_pin_downstream_enum->Release();
1579             if( p_pin_upstream_enum )
1580                 p_pin_upstream_enum->Release();
1581         }
1582     } l;
1583
1584     PIN_DIRECTION pin_dir;
1585
1586     hr = p_upstream->EnumPins( &l.p_pin_upstream_enum );
1587     if( FAILED( hr ) )
1588     {
1589         msg_Warn( p_access, "Connect: "\
1590             "Cannot get upstream filter enumerator: hr=0x%8lx", hr );
1591         return hr;
1592     }
1593
1594     do
1595     {
1596         /* Release l.p_pin_upstream before next iteration */
1597         if( l.p_pin_upstream  )
1598             l.p_pin_upstream ->Release();
1599         l.p_pin_upstream = NULL;
1600         hr = l.p_pin_upstream_enum->Next( 1, &l.p_pin_upstream, 0 );
1601         if( hr != S_OK ) break;
1602
1603         hr = l.p_pin_upstream->QueryDirection( &pin_dir );
1604         if( FAILED( hr ) )
1605         {
1606             msg_Warn( p_access, "Connect: "\
1607                 "Cannot get upstream filter pin direction: hr=0x%8lx", hr );
1608             return hr;
1609         }
1610         hr = l.p_pin_upstream->ConnectedTo( &l.p_pin_downstream );
1611         if( SUCCEEDED( hr ) )
1612         {
1613             l.p_pin_downstream->Release();
1614             l.p_pin_downstream = NULL;
1615         }
1616         if( FAILED( hr ) && hr != VFW_E_NOT_CONNECTED )
1617         {
1618             msg_Warn( p_access, "Connect: "\
1619                 "Cannot check upstream filter connection: hr=0x%8lx", hr );
1620             return hr;
1621         }
1622         if( ( pin_dir == PINDIR_OUTPUT ) && ( hr == VFW_E_NOT_CONNECTED ) )
1623         {
1624             /* The upstream pin is not yet connected so check each pin on the
1625              * downstream filter */
1626             hr = p_downstream->EnumPins( &l.p_pin_downstream_enum );
1627             if( FAILED( hr ) )
1628             {
1629                 msg_Warn( p_access, "Connect: Cannot get "\
1630                     "downstream filter enumerator: hr=0x%8lx", hr );
1631                 return hr;
1632             }
1633             do
1634             {
1635                 /* Release l.p_pin_downstream before next iteration */
1636                 if( l.p_pin_downstream  )
1637                     l.p_pin_downstream ->Release();
1638                 l.p_pin_downstream = NULL;
1639
1640                 hr = l.p_pin_downstream_enum->Next( 1, &l.p_pin_downstream, 0 );
1641                 if( hr != S_OK ) break;
1642
1643                 hr = l.p_pin_downstream->QueryDirection( &pin_dir );
1644                 if( FAILED( hr ) )
1645                 {
1646                     msg_Warn( p_access, "Connect: Cannot get "\
1647                         "downstream filter pin direction: hr=0x%8lx", hr );
1648                     return hr;
1649                 }
1650
1651                 /* Looking for a free Pin to connect to
1652                  * A connected Pin may have an reference count > 1
1653                  * so Release and nullify the pointer */
1654                 hr = l.p_pin_downstream->ConnectedTo( &l.p_pin_temp );
1655                 if( SUCCEEDED( hr ) )
1656                 {
1657                     l.p_pin_temp->Release();
1658                     l.p_pin_temp = NULL;
1659                 }
1660                 if( hr != VFW_E_NOT_CONNECTED )
1661                 {
1662                     if( FAILED( hr ) )
1663                     {
1664                         msg_Warn( p_access, "Connect: Cannot check "\
1665                             "downstream filter connection: hr=0x%8lx", hr );
1666                         return hr;
1667                     }
1668                 }
1669                 if( ( pin_dir == PINDIR_INPUT ) &&
1670                     ( hr == VFW_E_NOT_CONNECTED ) )
1671                 {
1672                     hr = p_filter_graph->ConnectDirect( l.p_pin_upstream,
1673                         l.p_pin_downstream, NULL );
1674                     if( SUCCEEDED( hr ) )
1675                     {
1676                         /* If we arrive here then we have a matching pair of
1677                          * pins. */
1678                         return S_OK;
1679                     }
1680                 }
1681                 /* If we arrive here it means this downstream pin is not
1682                  * suitable so try the next downstream pin.
1683                  * l.p_pin_downstream is released at the top of the loop */
1684             }
1685             while( true );
1686             /* If we arrive here then we ran out of pins before we found a
1687              * suitable one. Release outstanding refcounts */
1688             if( l.p_pin_downstream_enum )
1689                 l.p_pin_downstream_enum->Release();
1690             l.p_pin_downstream_enum = NULL;
1691             if( l.p_pin_downstream )
1692                 l.p_pin_downstream->Release();
1693             l.p_pin_downstream = NULL;
1694         }
1695         /* If we arrive here it means this upstream pin is not suitable
1696          * so try the next upstream pin
1697          * l.p_pin_upstream is released at the top of the loop */
1698     }
1699     while( true );
1700     /* If we arrive here it means we did not find any pair of suitable pins
1701      * Outstanding refcounts are released in the destructor */
1702     return E_FAIL;
1703 }
1704
1705 /*****************************************************************************
1706 * Start uses MediaControl to start the graph
1707 *****************************************************************************/
1708 HRESULT BDAGraph::Start()
1709 {
1710     HRESULT hr = S_OK;
1711     OAFilterState i_state; /* State_Stopped, State_Paused, State_Running */
1712
1713     if( !p_media_control )
1714     {
1715         msg_Dbg( p_access, "Start: Media Control has not been created" );
1716         return E_FAIL;
1717     }
1718     hr = p_media_control->Run();
1719     msg_Dbg( p_access, "Graph started hr=0x%lx", hr );
1720     if( hr == S_OK )
1721         return hr;
1722
1723     /* Query the state of the graph - timeout after 100 milliseconds */
1724     while( hr = p_media_control->GetState( 100, &i_state ) != S_OK )
1725     {
1726         if( FAILED( hr ) )
1727         {
1728             msg_Warn( p_access,
1729                 "Start: Cannot get Graph state: hr=0x%8lx", hr );
1730             return hr;
1731         }
1732     }
1733     if( i_state == State_Running )
1734         return hr;
1735
1736     /* The Graph is not running so stop it and return an error */
1737     msg_Warn( p_access, "Start: Graph not started: %d", i_state );
1738     hr = p_media_control->StopWhenReady(); /* Instead of Stop() */
1739     if( FAILED( hr ) )
1740     {
1741         msg_Warn( p_access,
1742             "Start: Cannot stop Graph after Run failed: hr=0x%8lx", hr );
1743         return hr;
1744     }
1745     return E_FAIL;
1746 }
1747
1748 /*****************************************************************************
1749 * Read the stream of data - query the buffer size required
1750 *****************************************************************************/
1751 long BDAGraph::GetBufferSize()
1752 {
1753     long l_buffer_size = 0;
1754     long l_queue_size;
1755
1756     b_ready = true;
1757
1758     for( int i_timer = 0; queue_sample.empty() && i_timer < 200; i_timer++ )
1759         Sleep( 10 );
1760
1761     l_queue_size = queue_sample.size();
1762     if( l_queue_size <= 0 )
1763     {
1764         msg_Warn( p_access, "BDA GetBufferSize: Timed Out waiting for sample" );
1765         return -1;
1766     }
1767
1768     /* Establish the length of the queue as it grows quickly. If the queue
1769      * size is checked dynamically there is a risk of not exiting the loop */
1770     for( long l_queue_count=0; l_queue_count < l_queue_size; l_queue_count++ )
1771     {
1772         l_buffer_size += queue_sample.front()->GetActualDataLength();
1773         queue_buffer.push( queue_sample.front() );
1774         queue_sample.pop();
1775     }
1776     return l_buffer_size;
1777 }
1778
1779 /*****************************************************************************
1780 * Read the stream of data - Retrieve from the buffer queue
1781 ******************************************************************************/
1782 long BDAGraph::ReadBuffer( long* pl_buffer_len, BYTE* p_buffer )
1783 {
1784     HRESULT hr = S_OK;
1785
1786     *pl_buffer_len = 0;
1787     BYTE *p_buff_temp;
1788
1789     while( !queue_buffer.empty() )
1790     {
1791         queue_buffer.front()->GetPointer( &p_buff_temp );
1792         hr = queue_buffer.front()->IsDiscontinuity();
1793         if( hr == S_OK )
1794             msg_Warn( p_access,
1795                 "BDA ReadBuffer: Sample Discontinuity. 0x%8lx", hr );
1796         memcpy( p_buffer + *pl_buffer_len, p_buff_temp,
1797             queue_buffer.front()->GetActualDataLength() );
1798         *pl_buffer_len += queue_buffer.front()->GetActualDataLength();
1799         queue_buffer.front()->Release();
1800         queue_buffer.pop();
1801     }
1802
1803     return *pl_buffer_len;
1804 }
1805
1806 /******************************************************************************
1807 * SampleCB - Callback when the Sample Grabber has a sample
1808 ******************************************************************************/
1809 STDMETHODIMP BDAGraph::SampleCB( double d_time, IMediaSample *p_sample )
1810 {
1811     if( b_ready )
1812     {
1813         p_sample->AddRef();
1814         queue_sample.push( p_sample );
1815     }
1816     else
1817     {
1818         msg_Warn( p_access, "BDA SampleCB: Not ready - dropped sample" );
1819     }
1820     return S_OK;
1821 }
1822
1823 STDMETHODIMP BDAGraph::BufferCB( double d_time, BYTE* p_buffer,
1824     long l_buffer_len )
1825 {
1826     return E_FAIL;
1827 }
1828
1829 /******************************************************************************
1830 * removes each filter from the graph
1831 ******************************************************************************/
1832 HRESULT BDAGraph::Destroy()
1833 {
1834     HRESULT hr = S_OK;
1835     ULONG ul_refcount = 0;
1836
1837     if( p_media_control )
1838         hr = p_media_control->StopWhenReady(); /* Instead of Stop() */
1839
1840     if( d_graph_register )
1841     {
1842         Deregister();
1843     }
1844
1845 /* We need to empty the buffers of any unprocessed data */
1846     msg_Dbg( p_access, "Queue sample size = %d", queue_sample.size() );
1847     while( !queue_sample.empty() )
1848     {
1849         ul_refcount = queue_sample.front()->Release();
1850         queue_sample.pop();
1851         if( ul_refcount )
1852             msg_Warn( p_access, "BDAGraph: Non-zero Ref: %d", ul_refcount );
1853     }
1854     msg_Dbg( p_access, "Queue buffer size = %d", queue_buffer.size() );
1855     while( !queue_buffer.empty() )
1856     {
1857         ul_refcount = queue_buffer.front()->Release();
1858         queue_buffer.pop();
1859         if( ul_refcount )
1860             msg_Warn( p_access, "BDAGraph: Non-zero Ref: %d", ul_refcount );
1861     }
1862     if( p_grabber )
1863     {
1864         p_grabber->Release();
1865         p_grabber = NULL;
1866     }
1867
1868     if( p_transport_info )
1869     {
1870         p_filter_graph->RemoveFilter( p_transport_info );
1871         p_transport_info->Release();
1872         p_transport_info = NULL;
1873     }
1874     if( p_mpeg_demux )
1875     {
1876         p_filter_graph->RemoveFilter( p_mpeg_demux );
1877         p_mpeg_demux->Release();
1878         p_mpeg_demux = NULL;
1879     }
1880     if( p_sample_grabber )
1881     {
1882         p_filter_graph->RemoveFilter( p_sample_grabber );
1883         p_sample_grabber->Release();
1884         p_sample_grabber = NULL;
1885     }
1886     if( p_capture_device )
1887     {
1888         p_filter_graph->RemoveFilter( p_capture_device );
1889         p_capture_device->Release();
1890         p_capture_device = NULL;
1891     }
1892     if( p_tuner_device )
1893     {
1894         p_filter_graph->RemoveFilter( p_tuner_device );
1895         p_tuner_device->Release();
1896         p_tuner_device = NULL;
1897     }
1898     if( p_scanning_tuner )
1899     {
1900         p_scanning_tuner->Release();
1901         p_scanning_tuner = NULL;
1902     }
1903     if( p_network_provider )
1904     {
1905         p_filter_graph->RemoveFilter( p_network_provider );
1906         p_network_provider->Release();
1907         p_network_provider = NULL;
1908     }
1909
1910     if( p_media_control )
1911     {
1912         p_media_control->Release();
1913         p_media_control = NULL;
1914     }
1915     if( p_filter_graph )
1916     {
1917         p_filter_graph->Release();
1918         p_filter_graph = NULL;
1919     }
1920     if( p_system_dev_enum )
1921     {
1922         p_system_dev_enum->Release();
1923         p_system_dev_enum = NULL;
1924     }
1925
1926     return S_OK;
1927 }
1928
1929 /*****************************************************************************
1930 * Add/Remove a DirectShow filter graph to/from the Running Object Table.
1931 * Allows GraphEdit to "spy" on a remote filter graph.
1932 ******************************************************************************/
1933 HRESULT BDAGraph::Register()
1934 {
1935     class localComPtr
1936     {
1937         public:
1938         IMoniker*             p_moniker;
1939         IRunningObjectTable*  p_ro_table;
1940         localComPtr(): p_moniker(NULL), p_ro_table(NULL) {};
1941         ~localComPtr()
1942         {
1943             if( p_moniker )
1944                 p_moniker->Release();
1945             if( p_ro_table )
1946                 p_ro_table->Release();
1947         }
1948     } l;
1949     WCHAR     psz_w_graph_name[128];
1950     HRESULT   hr;
1951
1952     hr = ::GetRunningObjectTable( 0, &l.p_ro_table );
1953     if( FAILED( hr ) )
1954     {
1955         msg_Warn( p_access, "Register: Cannot get ROT: hr=0x%8lx", hr );
1956         return hr;
1957     }
1958
1959     wsprintfW( psz_w_graph_name, L"VLC BDA Graph %08x Pid %08x",
1960         (DWORD_PTR) p_filter_graph, ::GetCurrentProcessId() );
1961     hr = CreateItemMoniker( L"!", psz_w_graph_name, &l.p_moniker );
1962     if( FAILED( hr ) )
1963     {
1964         msg_Warn( p_access, "Register: Cannot Create Moniker: hr=0x%8lx", hr );
1965         return hr;
1966     }
1967     hr = l.p_ro_table->Register( ROTFLAGS_REGISTRATIONKEEPSALIVE,
1968         p_filter_graph, l.p_moniker, &d_graph_register );
1969     if( FAILED( hr ) )
1970     {
1971         msg_Warn( p_access, "Register: Cannot Register Graph: hr=0x%8lx", hr );
1972         return hr;
1973     }
1974 //    msg_Dbg( p_access, "Register: registered Graph: %S", psz_w_graph_name );
1975     return hr;
1976 }
1977
1978 void BDAGraph::Deregister()
1979 {
1980     HRESULT   hr;
1981     IRunningObjectTable* p_ro_table;
1982     hr = ::GetRunningObjectTable( 0, &p_ro_table );
1983     if( SUCCEEDED( hr ) )
1984         p_ro_table->Revoke( d_graph_register );
1985     d_graph_register = 0;
1986     p_ro_table->Release();
1987 }