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