]> git.sesse.net Git - vlc/blob - modules/access/dtv/bdagraph.cpp
fd33a606cfdaadcded732a3a905a0ed128115bfe
[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  * Copyright (C) 2012 John Freed
7  *
8  * Author: Ken Self <kenself(at)optusnet(dot)com(dot)au>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_block.h>
34 #include "dtv/bdagraph.hpp"
35 #include "dtv/dtv.h"
36 #undef DEBUG_MONIKER_NAME
37
38 static ModulationType dvb_parse_modulation (const char *mod)
39 {
40     if (!strcmp (mod, "16QAM"))   return BDA_MOD_16QAM;
41     if (!strcmp (mod, "32QAM"))   return BDA_MOD_32QAM;
42     if (!strcmp (mod, "64QAM"))   return BDA_MOD_64QAM;
43     if (!strcmp (mod, "128QAM"))  return BDA_MOD_128QAM;
44     if (!strcmp (mod, "256QAM"))  return BDA_MOD_256QAM;
45     return BDA_MOD_NOT_SET;
46 }
47
48 static BinaryConvolutionCodeRate dvb_parse_fec (uint32_t fec)
49 {
50     switch (fec)
51     {
52         case VLC_FEC(1,2): return BDA_BCC_RATE_1_2;
53         case VLC_FEC(2,3): return BDA_BCC_RATE_2_3;
54         case VLC_FEC(3,4): return BDA_BCC_RATE_3_4;
55         case VLC_FEC(5,6): return BDA_BCC_RATE_5_6;
56         case VLC_FEC(7,8): return BDA_BCC_RATE_7_8;
57     }
58     return BDA_BCC_RATE_NOT_SET;
59 }
60
61 static GuardInterval dvb_parse_guard (uint32_t guard)
62 {
63     switch (guard)
64     {
65         case VLC_GUARD(1, 4): return BDA_GUARD_1_4;
66         case VLC_GUARD(1, 8): return BDA_GUARD_1_8;
67         case VLC_GUARD(1,16): return BDA_GUARD_1_16;
68         case VLC_GUARD(1,32): return BDA_GUARD_1_32;
69     }
70     return BDA_GUARD_NOT_SET;
71 }
72
73 static TransmissionMode dvb_parse_transmission (int transmit)
74 {
75     switch (transmit)
76     {
77         case 2: return BDA_XMIT_MODE_2K;
78         case 8: return BDA_XMIT_MODE_8K;
79     }
80     return BDA_XMIT_MODE_NOT_SET;
81 }
82
83 static HierarchyAlpha dvb_parse_hierarchy (int hierarchy)
84 {
85     switch (hierarchy)
86     {
87         case 1: return BDA_HALPHA_1;
88         case 2: return BDA_HALPHA_2;
89         case 4: return BDA_HALPHA_4;
90     }
91     return BDA_HALPHA_NOT_SET;
92 }
93
94 static Polarisation dvb_parse_polarization (char pol)
95 {
96     switch (pol)
97     {
98         case 'H': return BDA_POLARISATION_LINEAR_H;
99         case 'V': return BDA_POLARISATION_LINEAR_V;
100         case 'L': return BDA_POLARISATION_CIRCULAR_L;
101         case 'R': return BDA_POLARISATION_CIRCULAR_R;
102     }
103     return BDA_POLARISATION_NOT_SET;
104 }
105
106 static SpectralInversion dvb_parse_inversion (int inversion)
107 {
108     switch (inversion)
109     {
110         case  0: return BDA_SPECTRAL_INVERSION_NORMAL;
111         case  1: return BDA_SPECTRAL_INVERSION_INVERTED;
112         case -1: return BDA_SPECTRAL_INVERSION_AUTOMATIC;
113     }
114     /* should never happen */
115     return BDA_SPECTRAL_INVERSION_NOT_SET;
116 }
117
118 /****************************************************************************
119  * Interfaces for calls from C
120  ****************************************************************************/
121 struct dvb_device
122 {
123     BDAGraph *module;
124
125     /* DVB-S property cache */
126     uint32_t frequency;
127     uint32_t srate;
128     uint32_t fec;
129     char inversion;
130     char pol;
131     uint32_t lowf, highf, switchf;
132 };
133
134 dvb_device_t *dvb_open (vlc_object_t *obj)
135 {
136     dvb_device_t *d = new dvb_device_t;
137
138     d->module = new BDAGraph (obj);
139     d->frequency = 0;
140     d->srate = 0;
141     d->fec = VLC_FEC_AUTO;
142     d->inversion = -1;
143     d->pol = 0;
144     d->lowf = d->highf = d->switchf = 0;
145     return d;
146 }
147
148 void dvb_close (dvb_device_t *d)
149 {
150     delete d->module;
151     delete d;
152 }
153
154 ssize_t dvb_read (dvb_device_t *d, void *buf, size_t len)
155 {
156     return d->module->Pop(buf, len);
157 }
158
159 int dvb_add_pid (dvb_device_t *, uint16_t)
160 {
161     return 0;
162 }
163
164 void dvb_remove_pid (dvb_device_t *, uint16_t)
165 {
166 }
167
168 unsigned dvb_enum_systems (dvb_device_t *d)
169 {
170     return d->module->EnumSystems( );
171 }
172
173 float dvb_get_signal_strength (dvb_device_t *d)
174 {
175     return d->module->GetSignalStrength( );
176 }
177
178 float dvb_get_snr (dvb_device_t *d)
179 {
180     return d->module->GetSignalNoiseRatio( );
181 }
182
183 int dvb_set_inversion (dvb_device_t *d, int inversion)
184 {
185     d->inversion = inversion;
186     return d->module->SetInversion( d->inversion );
187 }
188
189 int dvb_tune (dvb_device_t *d)
190 {
191     return d->module->SubmitTuneRequest ();
192 }
193
194 /* DVB-C */
195 int dvb_set_dvbc (dvb_device_t *d, uint32_t freq, const char *mod,
196                   uint32_t srate, uint32_t /*fec*/)
197 {
198     return d->module->SetDVBC (freq / 1000, mod, srate);
199 }
200
201 /* DVB-S */
202 int dvb_set_dvbs (dvb_device_t *d, uint64_t freq, uint32_t srate, uint32_t fec)
203 {
204     d->frequency = freq / 1000;
205     d->srate = srate;
206     d->fec = fec;
207     return d->module->SetDVBS(d->frequency, d->srate, d->fec, d->inversion,
208                               d->pol, d->lowf, d->highf, d->switchf);
209 }
210
211 int dvb_set_dvbs2 (dvb_device_t *, uint64_t /*freq*/, const char * /*mod*/,
212                    uint32_t /*srate*/, uint32_t /*fec*/, int /*pilot*/, int /*rolloff*/,
213                    uint8_t /*sid*/)
214 {
215     return VLC_EGENERIC;
216 }
217
218 int dvb_set_sec (dvb_device_t *d, uint64_t freq, char pol,
219                  uint32_t lowf, uint32_t highf, uint32_t switchf)
220 {
221     d->frequency = freq / 1000;
222     d->pol = pol;
223     d->lowf = lowf;
224     d->highf = highf;
225     d->switchf = switchf;
226     return d->module->SetDVBS(d->frequency, d->srate, d->fec, d->inversion,
227                               d->pol, d->lowf, d->highf, d->switchf);
228 }
229
230 /* DVB-T */
231 int dvb_set_dvbt (dvb_device_t *d, uint32_t freq, const char * /*mod*/,
232                   uint32_t fec_hp, uint32_t fec_lp, uint32_t bandwidth,
233                   int transmission, uint32_t guard, int hierarchy)
234 {
235     return d->module->SetDVBT(freq / 1000, fec_hp, fec_lp,
236                               bandwidth, transmission, guard, hierarchy);
237 }
238
239 /* DVB-T2 */
240 int dvb_set_dvbt2 (dvb_device_t *d, uint32_t freq, const char * /*mod*/,
241                    uint32_t fec, uint32_t bandwidth, int transmission,
242                    uint32_t guard, uint8_t plp)
243 {
244     return d->module->SetDVBT2(freq / 1000, fec,
245                       bandwidth, transmission, guard, plp);
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 * BDAOutput
282 *****************************************************************************/
283 BDAOutput::BDAOutput( vlc_object_t *p_access ) :
284     p_access(p_access), p_first(NULL), pp_next(&p_first)
285 {
286     vlc_mutex_init( &lock );
287     vlc_cond_init( &wait );
288 }
289
290 BDAOutput::~BDAOutput()
291 {
292     Empty();
293     vlc_mutex_destroy( &lock );
294     vlc_cond_destroy( &wait );
295 }
296
297 void BDAOutput::Push( block_t *p_block )
298 {
299     vlc_mutex_locker l( &lock );
300
301     block_ChainLastAppend( &pp_next, p_block );
302     vlc_cond_signal( &wait );
303 }
304
305 ssize_t BDAOutput::Pop(void *buf, size_t len)
306 {
307     vlc_mutex_locker l( &lock );
308
309     mtime_t i_deadline = mdate() + 250 * 1000;
310     while( !p_first )
311     {
312         if( vlc_cond_timedwait( &wait, &lock, i_deadline ) )
313             return -1;
314     }
315
316     size_t i_index = 0;
317     while( i_index < len )
318     {
319         size_t i_copy = __MIN( len - i_index, p_first->i_buffer );
320         memcpy( (uint8_t *)buf + i_index, p_first->p_buffer, i_copy );
321
322         i_index           += i_copy;
323
324         p_first->p_buffer += i_copy;
325         p_first->i_buffer -= i_copy;
326
327         if( p_first->i_buffer <= 0 )
328         {
329             block_t *p_next = p_first->p_next;
330             block_Release( p_first );
331
332             p_first = p_next;
333             if( !p_first )
334             {
335                 pp_next = &p_first;
336                 break;
337             }
338         }
339     }
340     return i_index;
341 }
342
343 void BDAOutput::Empty()
344 {
345     vlc_mutex_locker l( &lock );
346
347     if( p_first )
348         block_ChainRelease( p_first );
349     p_first = NULL;
350     pp_next = &p_first;
351 }
352
353 /*****************************************************************************
354 * Constructor
355 *****************************************************************************/
356 BDAGraph::BDAGraph( vlc_object_t *p_this ):
357     p_access( p_this ),
358     guid_network_type(GUID_NULL),
359     l_tuner_used(-1),
360     systems(0),
361     d_graph_register( 0 ),
362     output( p_this )
363 {
364     p_media_control = NULL;
365
366     p_tuning_space = NULL;
367
368     p_filter_graph = NULL;
369     p_system_dev_enum = NULL;
370     p_network_provider = p_tuner_device = p_capture_device = NULL;
371     p_sample_grabber = p_mpeg_demux = p_transport_info = NULL;
372     p_scanning_tuner = NULL;
373     p_grabber = NULL;
374
375     CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
376 }
377
378 /*****************************************************************************
379 * Destructor
380 *****************************************************************************/
381 BDAGraph::~BDAGraph()
382 {
383     Destroy();
384
385     if( p_tuning_space )
386         p_tuning_space->Release();
387     p_tuning_space = NULL;
388
389     systems = 0;
390     CoUninitialize();
391 }
392
393 /*****************************************************************************
394 * GetSystem
395 * helper function
396 *****************************************************************************/
397 unsigned BDAGraph::GetSystem( REFCLSID clsid )
398 {
399     unsigned sys = 0;
400
401     if( clsid == CLSID_DVBTNetworkProvider )
402         sys = DVB_T;
403     if( clsid == CLSID_DVBCNetworkProvider )
404         sys = DVB_C;
405     if( clsid == CLSID_DVBSNetworkProvider )
406         sys = DVB_S;
407     if( clsid == CLSID_ATSCNetworkProvider )
408         sys = ATSC;
409     if( clsid == CLSID_DigitalCableNetworkType )
410         sys = CQAM;
411
412     return sys;
413 }
414
415
416 /*****************************************************************************
417  * Enumerate Systems
418  *****************************************************************************
419  * here is logic for special case where user uses an MRL that points
420  * to DTV but is not fully specific. This is usually dvb:// and can come
421  * either from a playlist, a channels.conf MythTV file, or from manual entry.
422  *
423  * Since this is done before the real tune request is submitted, we can
424  * use the global device enumerator, etc., so long as we do a Destroy() at
425  * the end
426  *****************************************************************************/
427 unsigned BDAGraph::EnumSystems()
428 {
429     HRESULT hr = S_OK;
430     GUID guid_network_provider = GUID_NULL;
431
432     msg_Dbg( p_access, "EnumSystems: Entering " );
433
434     do
435     {
436         hr = GetNextNetworkType( &guid_network_provider );
437         if( hr != S_OK ) break;
438         hr = Check( guid_network_provider );
439         if( FAILED( hr ) )
440             msg_Dbg( p_access, "EnumSystems: Check failed, trying next" );
441     }
442     while( true );
443
444     if( p_filter_graph )
445         Destroy();
446     msg_Dbg( p_access, "EnumSystems: Returning systems 0x%08x", systems );
447     return systems;
448 }
449
450 float BDAGraph::GetSignalNoiseRatio(void)
451 {
452     /* not implemented until Windows 7
453      * IBDA_Encoder::GetState */
454     return 0.;
455 }
456
457 float BDAGraph::GetSignalStrength(void)
458 {
459     HRESULT hr = S_OK;
460     long l_strength = 0;
461     msg_Dbg( p_access, "GetSignalStrength: entering" );
462     if( !p_scanning_tuner)
463         return 0.;
464     hr = p_scanning_tuner->get_SignalStrength( &l_strength );
465     if( FAILED( hr ) )
466     {
467         msg_Warn( p_access, "GetSignalStrength: "
468             "Cannot get value: hr=0x%8lx", hr );
469         return 0.;
470     }
471     msg_Dbg( p_access, "GetSignalStrength: got %ld", l_strength );
472     if( l_strength == -1 )
473         return -1.;
474     return l_strength / 100.;
475 }
476
477 int BDAGraph::SubmitTuneRequest(void)
478 {
479     HRESULT hr;
480     int i = 0;
481
482     /* Build and Start the Graph. If a Tuner device is in use the graph will
483      * fail to start. Repeated calls to Build will check successive tuner
484      * devices */
485     do
486     {
487         msg_Dbg( p_access, "SubmitTuneRequest: Building the Graph" );
488
489         hr = Build();
490         if( FAILED( hr ) )
491         {
492             msg_Warn( p_access, "SubmitTuneRequest: "
493                 "Cannot Build the Graph: hr=0x%8lx", hr );
494             return VLC_EGENERIC;
495         }
496         msg_Dbg( p_access, "SubmitTuneRequest: Starting the Graph" );
497
498         hr = Start();
499         if( FAILED( hr ) )
500         {
501             msg_Dbg( p_access, "SubmitTuneRequest: "
502                 "Cannot Start the Graph, retrying: hr=0x%8lx", hr );
503             ++i;
504         }
505     }
506     while( hr != S_OK && i < 10 ); /* give up after 10 tries */
507
508     if( FAILED( hr ) )
509     {
510         msg_Warn( p_access, "SubmitTuneRequest: "
511             "Failed to Start the Graph: hr=0x%8lx", hr );
512         return VLC_EGENERIC;
513     }
514
515     return VLC_SUCCESS;
516 }
517
518 /*****************************************************************************
519 * Set Clear QAM (DigitalCable)
520 * Local ATSC Digital Antenna
521 *****************************************************************************/
522 int BDAGraph::SetCQAM(long l_frequency)
523 {
524     HRESULT hr = S_OK;
525     class localComPtr
526     {
527         public:
528         ITuneRequest*               p_tune_request;
529         IDigitalCableTuneRequest*   p_cqam_tune_request;
530         IDigitalCableLocator*       p_cqam_locator;
531         localComPtr():
532             p_tune_request(NULL),
533             p_cqam_tune_request(NULL),
534             p_cqam_locator(NULL)
535             {};
536         ~localComPtr()
537         {
538             if( p_tune_request )
539                 p_tune_request->Release();
540             if( p_cqam_tune_request )
541                 p_cqam_tune_request->Release();
542             if( p_cqam_locator )
543                 p_cqam_locator->Release();
544         }
545     } l;
546     long l_minor_channel, l_physical_channel;
547
548     l_physical_channel = var_GetInteger( p_access, "dvb-physical-channel" );
549     l_minor_channel    = var_GetInteger( p_access, "dvb-minor-channel" );
550
551     /* try to set p_scanning_tuner */
552     hr = Check( CLSID_DigitalCableNetworkType );
553     if( FAILED( hr ) )
554     {
555         msg_Warn( p_access, "SetCQAM: "\
556             "Cannot create Tuning Space: hr=0x%8lx", hr );
557         return VLC_EGENERIC;
558     }
559
560     if( !p_scanning_tuner )
561     {
562         msg_Warn( p_access, "SetCQAM: Cannot get scanning tuner" );
563         return VLC_EGENERIC;
564     }
565
566     hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
567     if( FAILED( hr ) )
568     {
569         msg_Warn( p_access, "SetCQAM: "\
570             "Cannot get Tune Request: hr=0x%8lx", hr );
571         return VLC_EGENERIC;
572     }
573
574     hr = l.p_tune_request->QueryInterface( IID_IDigitalCableTuneRequest,
575         reinterpret_cast<void**>( &l.p_cqam_tune_request ) );
576     if( FAILED( hr ) )
577     {
578         msg_Warn( p_access, "SetCQAM: "\
579                   "Cannot QI for IDigitalCableTuneRequest: hr=0x%8lx", hr );
580         return VLC_EGENERIC;
581     }
582
583     hr = ::CoCreateInstance( CLSID_DigitalCableLocator, 0, CLSCTX_INPROC,
584         IID_IDigitalCableLocator, reinterpret_cast<void**>( &l.p_cqam_locator ) );
585     if( FAILED( hr ) )
586     {
587         msg_Warn( p_access, "SetCQAM: "\
588                   "Cannot create the CQAM locator: hr=0x%8lx", hr );
589         return VLC_EGENERIC;
590     }
591
592     hr = S_OK;
593     if( SUCCEEDED( hr ) && l_physical_channel > 0 )
594         hr = l.p_cqam_locator->put_PhysicalChannel( l_physical_channel );
595     if( SUCCEEDED( hr ) && l_frequency > 0 )
596         hr = l.p_cqam_locator->put_CarrierFrequency( l_frequency );
597     if( SUCCEEDED( hr ) && l_minor_channel > 0 )
598         hr = l.p_cqam_tune_request->put_MinorChannel( l_minor_channel );
599     if( FAILED( hr ) )
600     {
601         msg_Warn( p_access, "SetCQAM: "\
602                  "Cannot set tuning parameters: hr=0x%8lx", hr );
603         return VLC_EGENERIC;
604     }
605
606     hr = l.p_cqam_tune_request->put_Locator( l.p_cqam_locator );
607     if( FAILED( hr ) )
608     {
609         msg_Warn( p_access, "SetCQAM: "\
610                   "Cannot put the locator: hr=0x%8lx", hr );
611         return VLC_EGENERIC;
612     }
613
614     hr = p_scanning_tuner->Validate( l.p_cqam_tune_request );
615     if( FAILED( hr ) )
616     {
617         msg_Dbg( p_access, "SetCQAM: "\
618             "Tune Request cannot be validated: hr=0x%8lx", hr );
619     }
620     /* increments ref count for scanning tuner */
621     hr = p_scanning_tuner->put_TuneRequest( l.p_cqam_tune_request );
622     if( FAILED( hr ) )
623     {
624         msg_Warn( p_access, "SetCQAM: "\
625             "Cannot put the tune request: hr=0x%8lx", hr );
626         return VLC_EGENERIC;
627     }
628
629     return VLC_SUCCESS;
630 }
631
632 /*****************************************************************************
633 * Set ATSC
634 *****************************************************************************/
635 int BDAGraph::SetATSC(long l_frequency)
636 {
637     HRESULT hr = S_OK;
638     class localComPtr
639     {
640         public:
641         ITuneRequest*               p_tune_request;
642         IATSCChannelTuneRequest*    p_atsc_tune_request;
643         IATSCLocator*               p_atsc_locator;
644         localComPtr():
645             p_tune_request(NULL),
646             p_atsc_tune_request(NULL),
647             p_atsc_locator(NULL)
648             {};
649         ~localComPtr()
650         {
651             if( p_tune_request )
652                 p_tune_request->Release();
653             if( p_atsc_tune_request )
654                 p_atsc_tune_request->Release();
655             if( p_atsc_locator )
656                 p_atsc_locator->Release();
657         }
658     } l;
659     long l_major_channel, l_minor_channel, l_physical_channel;
660
661     /* fixme: these parameters should have dtv prefix, not dvb */
662     l_major_channel     = var_GetInteger( p_access, "dvb-major-channel" );
663     l_minor_channel     = var_GetInteger( p_access, "dvb-minor-channel" );
664     l_physical_channel  = var_GetInteger( p_access, "dvb-physical-channel" );
665
666     /* try to set p_scanning_tuner */
667     hr = Check( CLSID_ATSCNetworkProvider );
668     if( FAILED( hr ) )
669     {
670         msg_Warn( p_access, "SetATSC: "\
671             "Cannot create Tuning Space: hr=0x%8lx", hr );
672         return VLC_EGENERIC;
673     }
674
675     if( !p_scanning_tuner )
676     {
677         msg_Warn( p_access, "SetATSC: Cannot get scanning tuner" );
678         return VLC_EGENERIC;
679     }
680
681     hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
682     if( FAILED( hr ) )
683     {
684         msg_Warn( p_access, "SetATSC: "\
685             "Cannot get Tune Request: hr=0x%8lx", hr );
686         return VLC_EGENERIC;
687     }
688
689     hr = l.p_tune_request->QueryInterface( IID_IATSCChannelTuneRequest,
690         reinterpret_cast<void**>( &l.p_atsc_tune_request ) );
691     if( FAILED( hr ) )
692     {
693         msg_Warn( p_access, "SetATSC: "\
694             "Cannot QI for IATSCChannelTuneRequest: hr=0x%8lx", hr );
695         return VLC_EGENERIC;
696     }
697
698     hr = ::CoCreateInstance( CLSID_ATSCLocator, 0, CLSCTX_INPROC,
699         IID_IATSCLocator, reinterpret_cast<void**>( &l.p_atsc_locator ) );
700     if( FAILED( hr ) )
701     {
702         msg_Warn( p_access, "SetATSC: "\
703             "Cannot create the ATSC locator: hr=0x%8lx", hr );
704         return VLC_EGENERIC;
705     }
706
707     hr = S_OK;
708     if( l_frequency > 0 )
709         hr = l.p_atsc_locator->put_CarrierFrequency( l_frequency );
710     if( l_major_channel > 0 )
711         hr = l.p_atsc_tune_request->put_Channel( l_major_channel );
712     if( SUCCEEDED( hr ) && l_minor_channel > 0 )
713         hr = l.p_atsc_tune_request->put_MinorChannel( l_minor_channel );
714     if( SUCCEEDED( hr ) && l_physical_channel > 0 )
715         hr = l.p_atsc_locator->put_PhysicalChannel( l_physical_channel );
716     if( FAILED( hr ) )
717     {
718         msg_Warn( p_access, "SetATSC: "\
719             "Cannot set tuning parameters: hr=0x%8lx", hr );
720         return VLC_EGENERIC;
721     }
722
723     hr = l.p_atsc_tune_request->put_Locator( l.p_atsc_locator );
724     if( FAILED( hr ) )
725     {
726         msg_Warn( p_access, "SetATSC: "\
727             "Cannot put the locator: hr=0x%8lx", hr );
728         return VLC_EGENERIC;
729     }
730
731     hr = p_scanning_tuner->Validate( l.p_atsc_tune_request );
732     if( FAILED( hr ) )
733     {
734         msg_Dbg( p_access, "SetATSC: "\
735             "Tune Request cannot be validated: hr=0x%8lx", hr );
736     }
737     /* increments ref count for scanning tuner */
738     hr = p_scanning_tuner->put_TuneRequest( l.p_atsc_tune_request );
739     if( FAILED( hr ) )
740     {
741         msg_Warn( p_access, "SetATSC: "\
742             "Cannot put the tune request: hr=0x%8lx", hr );
743         return VLC_EGENERIC;
744     }
745
746     return VLC_SUCCESS;
747 }
748
749 /*****************************************************************************
750 * Set DVB-T
751 *
752 * This provides the tune request that everything else is built upon.
753 *
754 * Stores the tune request to the scanning tuner, where it is pulled out by
755 * dvb_tune a/k/a SubmitTuneRequest.
756 ******************************************************************************/
757 int BDAGraph::SetDVBT(long l_frequency, uint32_t fec_hp, uint32_t fec_lp,
758     long l_bandwidth, int transmission, uint32_t guard, int hierarchy)
759 {
760     HRESULT hr = S_OK;
761
762     class localComPtr
763     {
764         public:
765         ITuneRequest*       p_tune_request;
766         IDVBTuneRequest*    p_dvb_tune_request;
767         IDVBTLocator*       p_dvbt_locator;
768         IDVBTuningSpace2*   p_dvb_tuning_space;
769         localComPtr():
770             p_tune_request(NULL),
771             p_dvb_tune_request(NULL),
772             p_dvbt_locator(NULL),
773             p_dvb_tuning_space(NULL)
774             {};
775         ~localComPtr()
776         {
777             if( p_tune_request )
778                 p_tune_request->Release();
779             if( p_dvb_tune_request )
780                 p_dvb_tune_request->Release();
781             if( p_dvbt_locator )
782                 p_dvbt_locator->Release();
783             if( p_dvb_tuning_space )
784                 p_dvb_tuning_space->Release();
785         }
786     } l;
787
788     /* create local dvbt-specific tune request and locator
789      * then put it to existing scanning tuner */
790     BinaryConvolutionCodeRate i_hp_fec = dvb_parse_fec(fec_hp);
791     BinaryConvolutionCodeRate i_lp_fec = dvb_parse_fec(fec_lp);
792     GuardInterval i_guard = dvb_parse_guard(guard);
793     TransmissionMode i_transmission = dvb_parse_transmission(transmission);
794     HierarchyAlpha i_hierarchy = dvb_parse_hierarchy(hierarchy);
795
796     /* try to set p_scanning_tuner */
797     msg_Dbg( p_access, "SetDVBT: set up scanning tuner" );
798     hr = Check( CLSID_DVBTNetworkProvider );
799     if( FAILED( hr ) )
800     {
801         msg_Warn( p_access, "SetDVBT: "\
802             "Cannot create Tuning Space: hr=0x%8lx", hr );
803         return VLC_EGENERIC;
804     }
805
806     if( !p_scanning_tuner )
807     {
808         msg_Warn( p_access, "SetDVBT: Cannot get scanning tuner" );
809         return VLC_EGENERIC;
810     }
811
812     hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
813     if( FAILED( hr ) )
814     {
815         msg_Warn( p_access, "SetDVBT: "\
816             "Cannot get Tune Request: hr=0x%8lx", hr );
817         return VLC_EGENERIC;
818     }
819
820     msg_Dbg( p_access, "SetDVBT: Creating DVB tune request" );
821     hr = l.p_tune_request->QueryInterface( IID_IDVBTuneRequest,
822         reinterpret_cast<void**>( &l.p_dvb_tune_request ) );
823     if( FAILED( hr ) )
824     {
825         msg_Warn( p_access, "SetDVBT: "\
826             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
827         return VLC_EGENERIC;
828     }
829
830     l.p_dvb_tune_request->put_ONID( -1 );
831     l.p_dvb_tune_request->put_SID( -1 );
832     l.p_dvb_tune_request->put_TSID( -1 );
833
834     msg_Dbg( p_access, "SetDVBT: get TS" );
835     hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
836     if( FAILED( hr ) )
837     {
838         msg_Dbg( p_access, "SetDVBT: "\
839             "cannot get tuning space: hr=0x%8lx", hr );
840         return VLC_EGENERIC;
841     }
842
843     msg_Dbg( p_access, "SetDVBT: QI to DVBT TS" );
844     hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
845         reinterpret_cast<void**>( &l.p_dvb_tuning_space ) );
846     if( FAILED( hr ) )
847     {
848         msg_Warn( p_access, "SetDVBT: "\
849             "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
850         return VLC_EGENERIC;
851     }
852
853     msg_Dbg( p_access, "SetDVBT: Creating local locator" );
854     hr = ::CoCreateInstance( CLSID_DVBTLocator, 0, CLSCTX_INPROC,
855         IID_IDVBTLocator, reinterpret_cast<void**>( &l.p_dvbt_locator ) );
856     if( FAILED( hr ) )
857     {
858         msg_Warn( p_access, "SetDVBT: "\
859             "Cannot create the DVBT Locator: hr=0x%8lx", hr );
860         return VLC_EGENERIC;
861     }
862
863     hr = l.p_dvb_tuning_space->put_SystemType( DVB_Terrestrial );
864     if( SUCCEEDED( hr ) && l_frequency > 0 )
865         hr = l.p_dvbt_locator->put_CarrierFrequency( l_frequency );
866     if( SUCCEEDED( hr ) && l_bandwidth > 0 )
867         hr = l.p_dvbt_locator->put_Bandwidth( l_bandwidth );
868     if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
869         hr = l.p_dvbt_locator->put_InnerFECRate( i_hp_fec );
870     if( SUCCEEDED( hr ) && i_lp_fec != BDA_BCC_RATE_NOT_SET )
871         hr = l.p_dvbt_locator->put_LPInnerFECRate( i_lp_fec );
872     if( SUCCEEDED( hr ) && i_guard != BDA_GUARD_NOT_SET )
873         hr = l.p_dvbt_locator->put_Guard( i_guard );
874     if( SUCCEEDED( hr ) && i_transmission != BDA_XMIT_MODE_NOT_SET )
875         hr = l.p_dvbt_locator->put_Mode( i_transmission );
876     if( SUCCEEDED( hr ) && i_hierarchy != BDA_HALPHA_NOT_SET )
877         hr = l.p_dvbt_locator->put_HAlpha( i_hierarchy );
878
879     if( FAILED( hr ) )
880     {
881         msg_Warn( p_access, "SetDVBT: "\
882             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
883         return VLC_EGENERIC;
884     }
885
886     msg_Dbg( p_access, "SetDVBT: putting DVBT locator into local tune request" );
887
888     hr = l.p_dvb_tune_request->put_Locator( l.p_dvbt_locator );
889     if( FAILED( hr ) )
890     {
891         msg_Warn( p_access, "SetDVBT: "\
892             "Cannot put the locator: hr=0x%8lx", hr );
893         return VLC_EGENERIC;
894     }
895
896     msg_Dbg( p_access, "SetDVBT: putting local Tune Request to scanning tuner" );
897     hr = p_scanning_tuner->Validate( l.p_dvb_tune_request );
898     if( FAILED( hr ) )
899     {
900         msg_Dbg( p_access, "SetDVBT: "\
901             "Tune Request cannot be validated: hr=0x%8lx", hr );
902     }
903     /* increments ref count for scanning tuner */
904     hr = p_scanning_tuner->put_TuneRequest( l.p_dvb_tune_request );
905     if( FAILED( hr ) )
906     {
907         msg_Warn( p_access, "SetDVBT: "\
908             "Cannot put the tune request: hr=0x%8lx", hr );
909         return VLC_EGENERIC;
910     }
911
912     msg_Dbg( p_access, "SetDVBT: return success" );
913     return VLC_SUCCESS;
914 }
915
916 /*****************************************************************************
917 * Set DVB-T2
918 *
919 * This provides the tune request that everything else is built upon.
920 *
921 * Stores the tune request to the scanning tuner, where it is pulled out by
922 * dvb_tune a/k/a SubmitTuneRequest.
923 ******************************************************************************/
924 int BDAGraph::SetDVBT2(long l_frequency, uint32_t fec,
925     long l_bandwidth, int transmission, uint32_t guard, int plp)
926 {
927     HRESULT hr = S_OK;
928
929     class localComPtr
930     {
931         public:
932         ITuneRequest*       p_tune_request;
933         IDVBTuneRequest*    p_dvb_tune_request;
934         IDVBTLocator2*       p_dvbt_locator;
935         IDVBTuningSpace2*   p_dvb_tuning_space;
936         localComPtr():
937             p_tune_request(NULL),
938             p_dvb_tune_request(NULL),
939             p_dvbt_locator(NULL),
940             p_dvb_tuning_space(NULL)
941             {};
942         ~localComPtr()
943         {
944             if( p_tune_request )
945                 p_tune_request->Release();
946             if( p_dvb_tune_request )
947                 p_dvb_tune_request->Release();
948             if( p_dvbt_locator )
949                 p_dvbt_locator->Release();
950             if( p_dvb_tuning_space )
951                 p_dvb_tuning_space->Release();
952         }
953     } l;
954
955     /* create local dvbt-specific tune request and locator
956      * then put it to existing scanning tuner */
957     BinaryConvolutionCodeRate i_fec = dvb_parse_fec(fec);
958     GuardInterval i_guard = dvb_parse_guard(guard);
959     TransmissionMode i_transmission = dvb_parse_transmission(transmission);
960     long l_plp = plp;
961
962     /* try to set p_scanning_tuner */
963     msg_Dbg( p_access, "SetDVBT: set up scanning tuner" );
964     hr = Check( CLSID_DVBTNetworkProvider );
965     if( FAILED( hr ) )
966     {
967         msg_Warn( p_access, "SetDVBT: "\
968             "Cannot create Tuning Space: hr=0x%8lx", hr );
969         return VLC_EGENERIC;
970     }
971
972     if( !p_scanning_tuner )
973     {
974         msg_Warn( p_access, "SetDVBT: Cannot get scanning tuner" );
975         return VLC_EGENERIC;
976     }
977
978     hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
979     if( FAILED( hr ) )
980     {
981         msg_Warn( p_access, "SetDVBT: "\
982             "Cannot get Tune Request: hr=0x%8lx", hr );
983         return VLC_EGENERIC;
984     }
985
986     msg_Dbg( p_access, "SetDVBT: Creating DVB tune request" );
987     hr = l.p_tune_request->QueryInterface( IID_IDVBTuneRequest,
988         reinterpret_cast<void**>( &l.p_dvb_tune_request ) );
989     if( FAILED( hr ) )
990     {
991         msg_Warn( p_access, "SetDVBT: "\
992             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
993         return VLC_EGENERIC;
994     }
995
996     l.p_dvb_tune_request->put_ONID( -1 );
997     l.p_dvb_tune_request->put_SID( -1 );
998     l.p_dvb_tune_request->put_TSID( -1 );
999
1000     msg_Dbg( p_access, "SetDVBT: get TS" );
1001     hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
1002     if( FAILED( hr ) )
1003     {
1004         msg_Dbg( p_access, "SetDVBT: "\
1005             "cannot get tuning space: hr=0x%8lx", hr );
1006         return VLC_EGENERIC;
1007     }
1008
1009     msg_Dbg( p_access, "SetDVBT: QI to DVBT TS" );
1010     hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
1011         reinterpret_cast<void**>( &l.p_dvb_tuning_space ) );
1012     if( FAILED( hr ) )
1013     {
1014         msg_Warn( p_access, "SetDVBT: "\
1015             "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
1016         return VLC_EGENERIC;
1017     }
1018
1019     msg_Dbg( p_access, "SetDVBT: Creating local locator2" );
1020
1021     hr = ::CoCreateInstance( CLSID_DVBTLocator2, 0, CLSCTX_INPROC,
1022         IID_IDVBTLocator2, reinterpret_cast<void**>( &l.p_dvbt_locator ) );
1023
1024
1025     if( FAILED( hr ) )
1026     {
1027         msg_Warn( p_access, "SetDVBT: "\
1028             "Cannot create the DVBT Locator2: hr=0x%8lx", hr );
1029         return VLC_EGENERIC;
1030     }
1031
1032     hr = l.p_dvb_tuning_space->put_SystemType( DVB_Terrestrial );
1033     if( SUCCEEDED( hr ) && l_frequency > 0 )
1034         hr = l.p_dvbt_locator->put_CarrierFrequency( l_frequency );
1035     if( SUCCEEDED( hr ) && l_bandwidth > 0 )
1036         hr = l.p_dvbt_locator->put_Bandwidth( l_bandwidth );
1037     if( SUCCEEDED( hr ) && i_fec != BDA_BCC_RATE_NOT_SET )
1038         hr = l.p_dvbt_locator->put_InnerFECRate( i_fec );
1039     if( SUCCEEDED( hr ) && i_fec != BDA_BCC_RATE_NOT_SET )
1040         hr = l.p_dvbt_locator->put_LPInnerFECRate( i_fec );
1041     if( SUCCEEDED( hr ) && i_guard != BDA_GUARD_NOT_SET )
1042         hr = l.p_dvbt_locator->put_Guard( i_guard );
1043     if( SUCCEEDED( hr ) && i_transmission != BDA_XMIT_MODE_NOT_SET )
1044         hr = l.p_dvbt_locator->put_Mode( i_transmission );
1045     if( SUCCEEDED( hr ) && l_plp > 0 ){
1046         hr = l.p_dvbt_locator->put_PhysicalLayerPipeId( l_plp);
1047     }
1048
1049     if( FAILED( hr ) )
1050     {
1051         msg_Warn( p_access, "SetDVBT: "\
1052             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
1053         return VLC_EGENERIC;
1054     }
1055
1056     msg_Dbg( p_access, "SetDVBT: putting DVBT locator into local tune request" );
1057
1058     hr = l.p_dvb_tune_request->put_Locator( l.p_dvbt_locator );
1059     if( FAILED( hr ) )
1060     {
1061         msg_Warn( p_access, "SetDVBT: "\
1062             "Cannot put the locator: hr=0x%8lx", hr );
1063         return VLC_EGENERIC;
1064     }
1065
1066     msg_Dbg( p_access, "SetDVBT: putting local Tune Request to scanning tuner" );
1067     hr = p_scanning_tuner->Validate( l.p_dvb_tune_request );
1068     if( FAILED( hr ) )
1069     {
1070         msg_Dbg( p_access, "SetDVBT: "\
1071             "Tune Request cannot be validated: hr=0x%8lx", hr );
1072     }
1073     /* increments ref count for scanning tuner */
1074     hr = p_scanning_tuner->put_TuneRequest( l.p_dvb_tune_request );
1075     if( FAILED( hr ) )
1076     {
1077         msg_Warn( p_access, "SetDVBT: "\
1078             "Cannot put the tune request: hr=0x%8lx", hr );
1079         return VLC_EGENERIC;
1080     }
1081
1082     /* TBS tuner PLP set workaround */
1083     /* TODO: Check TBS tuner is present */
1084     IPin* pinInput0 = FindPinOnFilter( p_tuner_device, "Input0");
1085     if( pinInput0)
1086     {
1087         msg_Dbg( p_access, "SetDVBT: pin Input0 found on tuner filter, trying to get IKsPropertySet interface for TBS tuner..." );
1088         IKsPropertySet* p_ksPropertySet;
1089         hr = pinInput0->QueryInterface(IID_IKsPropertySet, reinterpret_cast<void**>(&p_ksPropertySet));
1090         if( FAILED( hr ))
1091         {
1092                 msg_Dbg( p_access, "SetDVBT: Cannot query for IKsPropertySet (this can be normal if not TBS tuner)  : hr=0x%8lx", hr );
1093         }
1094         else
1095         {
1096             msg_Dbg( p_access, "SetDVBT: found IKsPropertySet interface (using TBS tuner PLP-set workaround)");
1097             TBS_PLP_INFO plp_info;
1098             ZeroMemory( &plp_info, sizeof( TBS_PLP_INFO));
1099                 plp_info.plpId = plp;
1100                 p_ksPropertySet->Set( KSPROPSETID_BdaTunerExtensionProperties,
1101                                       KSPROPERTY_BDA_PLPINFO,
1102                                       NULL,
1103                                       0,
1104                                       &plp_info,
1105                                       sizeof( TBS_PLP_INFO ));
1106                 msg_Dbg( p_access, "SetDVBT: TBS tuner set PLP: %d", plp);
1107                 p_ksPropertySet->Release();
1108         }
1109         pinInput0->Release();
1110     }
1111     else
1112     {
1113         msg_Dbg( p_access, "SetDVBT: no pin Input0 found on tuner filter (this can be normal if not TBS tuner)" );
1114     }
1115
1116     msg_Dbg( p_access, "SetDVBT: return success" );
1117     return VLC_SUCCESS;
1118 }
1119
1120
1121 /*****************************************************************************
1122 * Set DVB-C
1123 ******************************************************************************/
1124 int BDAGraph::SetDVBC(long l_frequency, const char *mod, long l_symbolrate)
1125 {
1126     HRESULT hr = S_OK;
1127
1128     class localComPtr
1129     {
1130         public:
1131         ITuneRequest*       p_tune_request;
1132         IDVBTuneRequest*    p_dvb_tune_request;
1133         IDVBCLocator*       p_dvbc_locator;
1134         IDVBTuningSpace2*   p_dvb_tuning_space;
1135
1136         localComPtr():
1137             p_tune_request(NULL),
1138             p_dvb_tune_request(NULL),
1139             p_dvbc_locator(NULL),
1140             p_dvb_tuning_space(NULL)
1141             {};
1142         ~localComPtr()
1143         {
1144             if( p_tune_request )
1145                 p_tune_request->Release();
1146             if( p_dvb_tune_request )
1147                 p_dvb_tune_request->Release();
1148             if( p_dvbc_locator )
1149                 p_dvbc_locator->Release();
1150             if( p_dvb_tuning_space )
1151                 p_dvb_tuning_space->Release();
1152         }
1153     } l;
1154
1155     ModulationType i_qam_mod = dvb_parse_modulation(mod);
1156
1157     /* try to set p_scanning_tuner */
1158     hr = Check( CLSID_DVBCNetworkProvider );
1159     msg_Dbg( p_access, "SetDVBC: returned from Check" );
1160
1161     if( FAILED( hr ) )
1162     {
1163         msg_Warn( p_access, "SetDVBC: "\
1164             "Cannot create Tuning Space: hr=0x%8lx", hr );
1165         return VLC_EGENERIC;
1166     }
1167
1168     msg_Dbg( p_access, "SetDVBC: check on scanning tuner" );
1169     if( !p_scanning_tuner )
1170     {
1171         msg_Warn( p_access, "SetDVBC: Cannot get scanning tuner" );
1172         return VLC_EGENERIC;
1173     }
1174
1175     msg_Dbg( p_access, "SetDVBC: get tune request" );
1176     hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
1177     if( FAILED( hr ) )
1178     {
1179         msg_Warn( p_access, "SetDVBC: "\
1180             "Cannot get Tune Request: hr=0x%8lx", hr );
1181         return VLC_EGENERIC;
1182     }
1183
1184     msg_Dbg( p_access, "SetDVBC: QI for dvb tune request" );
1185     hr = l.p_tune_request->QueryInterface( IID_IDVBTuneRequest,
1186         reinterpret_cast<void**>( &l.p_dvb_tune_request ) );
1187     if( FAILED( hr ) )
1188     {
1189         msg_Warn( p_access, "SetDVBC: "\
1190             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
1191         return VLC_EGENERIC;
1192     }
1193
1194     l.p_dvb_tune_request->put_ONID( -1 );
1195     l.p_dvb_tune_request->put_SID( -1 );
1196     l.p_dvb_tune_request->put_TSID( -1 );
1197
1198     msg_Dbg( p_access, "SetDVBC: create dvbc locator" );
1199     hr = ::CoCreateInstance( CLSID_DVBCLocator, 0, CLSCTX_INPROC,
1200         IID_IDVBCLocator, reinterpret_cast<void**>( &l.p_dvbc_locator ) );
1201     if( FAILED( hr ) )
1202     {
1203         msg_Warn( p_access, "SetDVBC: "\
1204             "Cannot create the DVB-C Locator: hr=0x%8lx", hr );
1205         return VLC_EGENERIC;
1206     }
1207
1208
1209     msg_Dbg( p_access, "SetDVBC: get TS" );
1210     hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
1211     if( FAILED( hr ) )
1212     {
1213         msg_Dbg( p_access, "SetDVBC: "\
1214             "cannot get tuning space: hr=0x%8lx", hr );
1215         return VLC_EGENERIC;
1216     }
1217
1218     msg_Dbg( p_access, "SetDVBC: QI for dvb tuning space" );
1219     hr = p_tuning_space->QueryInterface( IID_IDVBTuningSpace2,
1220         reinterpret_cast<void**>( &l.p_dvb_tuning_space ) );
1221     if( FAILED( hr ) )
1222     {
1223         msg_Warn( p_access, "SetDVBC: "\
1224             "Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
1225         return VLC_EGENERIC;
1226     }
1227
1228     msg_Dbg( p_access, "SetDVBC: set up locator" );
1229     hr = S_OK;
1230     hr = l.p_dvb_tuning_space->put_SystemType( DVB_Cable );
1231
1232     if( SUCCEEDED( hr ) && l_frequency > 0 )
1233         hr = l.p_dvbc_locator->put_CarrierFrequency( l_frequency );
1234     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
1235         hr = l.p_dvbc_locator->put_SymbolRate( l_symbolrate );
1236     if( SUCCEEDED( hr ) && i_qam_mod != BDA_MOD_NOT_SET )
1237         hr = l.p_dvbc_locator->put_Modulation( i_qam_mod );
1238
1239     if( FAILED( hr ) )
1240     {
1241         msg_Warn( p_access, "SetDVBC: "\
1242             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
1243         return VLC_EGENERIC;
1244     }
1245
1246     msg_Dbg( p_access, "SetDVBC: put locator to dvb tune request" );
1247     hr = l.p_dvb_tune_request->put_Locator( l.p_dvbc_locator );
1248     if( FAILED( hr ) )
1249     {
1250         msg_Warn( p_access, "SetDVBC: "\
1251             "Cannot put the locator: hr=0x%8lx", hr );
1252         return VLC_EGENERIC;
1253     }
1254
1255     msg_Dbg( p_access, "SetDVBC: validate dvb tune request" );
1256     hr = p_scanning_tuner->Validate( l.p_dvb_tune_request );
1257     if( FAILED( hr ) )
1258     {
1259         msg_Dbg( p_access, "SetDVBC: "\
1260             "Tune Request cannot be validated: hr=0x%8lx", hr );
1261     }
1262
1263     /* increments ref count for scanning tuner */
1264     msg_Dbg( p_access, "SetDVBC: put dvb tune request to tuner" );
1265     hr = p_scanning_tuner->put_TuneRequest( l.p_dvb_tune_request );
1266     if( FAILED( hr ) )
1267     {
1268         msg_Warn( p_access, "SetDVBC: "\
1269             "Cannot put the tune request: hr=0x%8lx", hr );
1270         return VLC_EGENERIC;
1271     }
1272     msg_Dbg( p_access, "SetDVBC: return success" );
1273
1274     return VLC_SUCCESS;
1275 }
1276
1277 /*****************************************************************************
1278 * Set Inversion
1279 ******************************************************************************/
1280 int BDAGraph::SetInversion(int inversion)
1281 {
1282     HRESULT hr = S_OK;
1283     class localComPtr
1284     {
1285         public:
1286         IDVBSTuningSpace*   p_dvbs_tuning_space;
1287         localComPtr() :
1288             p_dvbs_tuning_space(NULL)
1289         {}
1290         ~localComPtr()
1291         {
1292             if( p_dvbs_tuning_space )
1293                 p_dvbs_tuning_space->Release();
1294         }
1295     } l;
1296
1297     SpectralInversion i_inversion = dvb_parse_inversion( inversion );
1298
1299     if( !p_scanning_tuner )
1300     {
1301         msg_Warn( p_access, "SetInversion: "\
1302             "No scanning tuner" );
1303         return VLC_EGENERIC;
1304     }
1305
1306     /* SetInversion is called for all DVB tuners, before the dvb_tune(),
1307      * in access.c. Since DVBT and DVBC don't support spectral
1308      * inversion, we need to return VLC_SUCCESS in those cases
1309      * so that dvb_tune() will be called */
1310     if( ( GetSystem( guid_network_type ) & ( DVB_S | DVB_S2 | ISDB_S ) ) == 0 )
1311     {
1312         msg_Dbg( p_access, "SetInversion: Not Satellite type" );
1313         return VLC_SUCCESS;
1314     }
1315
1316     msg_Dbg( p_access, "SetInversion: get TS" );
1317     hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
1318     if( FAILED( hr ) )
1319     {
1320         msg_Warn( p_access, "SetInversion: "\
1321             "cannot get tuning space: hr=0x%8lx", hr );
1322         return VLC_EGENERIC;
1323     }
1324
1325     hr = p_tuning_space->QueryInterface( IID_IDVBSTuningSpace,
1326         reinterpret_cast<void**>( &l.p_dvbs_tuning_space ) );
1327     if( FAILED( hr ) )
1328     {
1329         msg_Warn( p_access, "SetInversion: "\
1330             "Cannot QI for IDVBSTuningSpace: hr=0x%8lx", hr );
1331         return VLC_EGENERIC;
1332     }
1333
1334     if( i_inversion != BDA_SPECTRAL_INVERSION_NOT_SET )
1335         hr = l.p_dvbs_tuning_space->put_SpectralInversion( i_inversion );
1336     if( FAILED( hr ) )
1337     {
1338         msg_Warn( p_access, "SetInversion: "\
1339             "Cannot put inversion: hr=0x%8lx", hr );
1340         return VLC_EGENERIC;
1341     }
1342
1343     return VLC_SUCCESS;
1344 }
1345
1346 /*****************************************************************************
1347 * Set DVB-S
1348 ******************************************************************************/
1349 int BDAGraph::SetDVBS(long l_frequency, long l_symbolrate, uint32_t fec,
1350                       int inversion, char pol,
1351                       long l_lnb_lof1, long l_lnb_lof2, long l_lnb_slof)
1352 {
1353     HRESULT hr = S_OK;
1354
1355     class localComPtr
1356     {
1357         public:
1358         ITuneRequest*       p_tune_request;
1359         IDVBTuneRequest*    p_dvb_tune_request;
1360         IDVBSLocator*       p_dvbs_locator;
1361         IDVBSTuningSpace*   p_dvbs_tuning_space;
1362         char*               psz_polarisation;
1363         char*               psz_input_range;
1364         BSTR                bstr_input_range;
1365         WCHAR*              pwsz_input_range;
1366         int                 i_range_len;
1367         localComPtr() :
1368             p_tune_request(NULL),
1369             p_dvb_tune_request(NULL),
1370             p_dvbs_locator(NULL),
1371             p_dvbs_tuning_space(NULL),
1372             psz_polarisation(NULL),
1373             psz_input_range(NULL),
1374             bstr_input_range(NULL),
1375             pwsz_input_range(NULL),
1376             i_range_len(0)
1377         {}
1378         ~localComPtr()
1379         {
1380             if( p_tune_request )
1381                 p_tune_request->Release();
1382             if( p_dvb_tune_request )
1383                 p_dvb_tune_request->Release();
1384             if( p_dvbs_locator )
1385                 p_dvbs_locator->Release();
1386             if( p_dvbs_tuning_space )
1387                 p_dvbs_tuning_space->Release();
1388             SysFreeString( bstr_input_range );
1389             if( pwsz_input_range )
1390                 delete[] pwsz_input_range;
1391             free( psz_input_range );
1392             free( psz_polarisation );
1393         }
1394     } l;
1395     long l_azimuth, l_elevation, l_longitude;
1396     long l_network_id;
1397     VARIANT_BOOL b_west;
1398
1399     BinaryConvolutionCodeRate i_hp_fec = dvb_parse_fec( fec );
1400     Polarisation i_polar = dvb_parse_polarization( pol );
1401     SpectralInversion i_inversion = dvb_parse_inversion( inversion );
1402
1403     l_azimuth          = var_GetInteger( p_access, "dvb-azimuth" );
1404     l_elevation        = var_GetInteger( p_access, "dvb-elevation" );
1405     l_longitude        = var_GetInteger( p_access, "dvb-longitude" );
1406     l_network_id       = var_GetInteger( p_access, "dvb-network-id" );
1407
1408     if( asprintf( &l.psz_polarisation, "%c", pol ) == -1 )
1409         abort();
1410
1411     b_west = ( l_longitude < 0 );
1412
1413     l.psz_input_range  = var_GetNonEmptyString( p_access, "dvb-range" );
1414     l.i_range_len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
1415         l.psz_input_range, -1, l.pwsz_input_range, 0 );
1416     if( l.i_range_len > 0 )
1417     {
1418         l.pwsz_input_range = new WCHAR[l.i_range_len];
1419         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
1420             l.psz_input_range, -1, l.pwsz_input_range, l.i_range_len );
1421         l.bstr_input_range = SysAllocString( l.pwsz_input_range );
1422     }
1423
1424     /* try to set p_scanning_tuner */
1425     hr = Check( CLSID_DVBSNetworkProvider );
1426     if( FAILED( hr ) )
1427     {
1428         msg_Warn( p_access, "SetDVBS: "\
1429             "Cannot create Tuning Space: hr=0x%8lx", hr );
1430         return VLC_EGENERIC;
1431     }
1432
1433     if( !p_scanning_tuner )
1434     {
1435         msg_Warn( p_access, "SetDVBS: Cannot get scanning tuner" );
1436         return VLC_EGENERIC;
1437     }
1438
1439     hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
1440     if( FAILED( hr ) )
1441     {
1442         msg_Warn( p_access, "SetDVBS: "\
1443             "Cannot get Tune Request: hr=0x%8lx", hr );
1444         return VLC_EGENERIC;
1445     }
1446
1447     hr = l.p_tune_request->QueryInterface( IID_IDVBTuneRequest,
1448         reinterpret_cast<void**>( &l.p_dvb_tune_request ) );
1449     if( FAILED( hr ) )
1450     {
1451         msg_Warn( p_access, "SetDVBS: "\
1452             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
1453         return VLC_EGENERIC;
1454     }
1455
1456     l.p_dvb_tune_request->put_ONID( -1 );
1457     l.p_dvb_tune_request->put_SID( -1 );
1458     l.p_dvb_tune_request->put_TSID( -1 );
1459
1460     hr = ::CoCreateInstance( CLSID_DVBSLocator, 0, CLSCTX_INPROC,
1461         IID_IDVBSLocator, reinterpret_cast<void**>( &l.p_dvbs_locator ) );
1462     if( FAILED( hr ) )
1463     {
1464         msg_Warn( p_access, "SetDVBS: "\
1465             "Cannot create the DVBS Locator: hr=0x%8lx", hr );
1466         return VLC_EGENERIC;
1467     }
1468
1469     msg_Dbg( p_access, "SetDVBS: get TS" );
1470     hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
1471     if( FAILED( hr ) )
1472     {
1473         msg_Dbg( p_access, "SetDVBS: "\
1474             "cannot get tuning space: hr=0x%8lx", hr );
1475         return VLC_EGENERIC;
1476     }
1477
1478     hr = p_tuning_space->QueryInterface( IID_IDVBSTuningSpace,
1479         reinterpret_cast<void**>( &l.p_dvbs_tuning_space ) );
1480     if( FAILED( hr ) )
1481     {
1482         msg_Warn( p_access, "SetDVBS: "\
1483             "Cannot QI for IDVBSTuningSpace: hr=0x%8lx", hr );
1484         return VLC_EGENERIC;
1485     }
1486
1487     hr = l.p_dvbs_tuning_space->put_SystemType( DVB_Satellite );
1488     if( SUCCEEDED( hr ) && l_lnb_lof1 > 0 )
1489         hr = l.p_dvbs_tuning_space->put_LowOscillator( l_lnb_lof1 );
1490     if( SUCCEEDED( hr ) && l_lnb_slof > 0 )
1491         hr = l.p_dvbs_tuning_space->put_LNBSwitch( l_lnb_slof );
1492     if( SUCCEEDED( hr ) && l_lnb_lof2 > 0 )
1493         hr = l.p_dvbs_tuning_space->put_HighOscillator( l_lnb_lof2 );
1494     if( SUCCEEDED( hr ) && i_inversion != BDA_SPECTRAL_INVERSION_NOT_SET )
1495         hr = l.p_dvbs_tuning_space->put_SpectralInversion( i_inversion );
1496     if( SUCCEEDED( hr ) && l_network_id > 0 )
1497         hr = l.p_dvbs_tuning_space->put_NetworkID( l_network_id );
1498     if( SUCCEEDED( hr ) && l.i_range_len > 0 )
1499         hr = l.p_dvbs_tuning_space->put_InputRange( l.bstr_input_range );
1500
1501     if( SUCCEEDED( hr ) && l_frequency > 0 )
1502         hr = l.p_dvbs_locator->put_CarrierFrequency( l_frequency );
1503     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
1504         hr = l.p_dvbs_locator->put_SymbolRate( l_symbolrate );
1505     if( SUCCEEDED( hr ) && i_polar != BDA_POLARISATION_NOT_SET )
1506         hr = l.p_dvbs_locator->put_SignalPolarisation( i_polar );
1507     if( SUCCEEDED( hr ) )
1508         hr = l.p_dvbs_locator->put_Modulation( BDA_MOD_QPSK );
1509     if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
1510         hr = l.p_dvbs_locator->put_InnerFECRate( i_hp_fec );
1511
1512     if( SUCCEEDED( hr ) && l_azimuth > 0 )
1513         hr = l.p_dvbs_locator->put_Azimuth( l_azimuth );
1514     if( SUCCEEDED( hr ) && l_elevation > 0 )
1515         hr = l.p_dvbs_locator->put_Elevation( l_elevation );
1516     if( SUCCEEDED( hr ) )
1517         hr = l.p_dvbs_locator->put_WestPosition( b_west );
1518     if( SUCCEEDED( hr ) )
1519         hr = l.p_dvbs_locator->put_OrbitalPosition( labs( l_longitude ) );
1520
1521     if( FAILED( hr ) )
1522     {
1523         msg_Warn( p_access, "SetDVBS: "\
1524             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
1525         return VLC_EGENERIC;
1526     }
1527
1528     hr = l.p_dvb_tune_request->put_Locator( l.p_dvbs_locator );
1529     if( FAILED( hr ) )
1530     {
1531         msg_Warn( p_access, "SetDVBS: "\
1532             "Cannot put the locator: hr=0x%8lx", hr );
1533         return VLC_EGENERIC;
1534     }
1535
1536     hr = p_scanning_tuner->Validate( l.p_dvb_tune_request );
1537     if( FAILED( hr ) )
1538     {
1539         msg_Dbg( p_access, "SetDVBS: "\
1540             "Tune Request cannot be validated: hr=0x%8lx", hr );
1541     }
1542
1543     /* increments ref count for scanning tuner */
1544     hr = p_scanning_tuner->put_TuneRequest( l.p_dvb_tune_request );
1545     if( FAILED( hr ) )
1546     {
1547         msg_Warn( p_access, "SetDVBS: "\
1548             "Cannot put the tune request: hr=0x%8lx", hr );
1549         return VLC_EGENERIC;
1550     }
1551
1552     return VLC_SUCCESS;
1553 }
1554
1555 /*****************************************************************************
1556 * SetUpTuner
1557 ******************************************************************************
1558 * Sets up global p_scanning_tuner and sets guid_network_type according
1559 * to the Network Type requested.
1560 *
1561 * Logic: if tuner is set up and is the right network type, use it.
1562 * Otherwise, poll the tuner for the right tuning space.
1563 *
1564 * Then set up a tune request and try to validate it. Finally, put
1565 * tune request and tuning space to tuner
1566 *
1567 * on success, sets globals: p_scanning_tuner and guid_network_type
1568 *
1569 ******************************************************************************/
1570 HRESULT BDAGraph::SetUpTuner( REFCLSID guid_this_network_type )
1571 {
1572     HRESULT hr = S_OK;
1573     class localComPtr
1574     {
1575         public:
1576         ITuningSpaceContainer*      p_tuning_space_container;
1577         IEnumTuningSpaces*          p_tuning_space_enum;
1578         ITuningSpace*               p_test_tuning_space;
1579         ITuneRequest*               p_tune_request;
1580         IDVBTuneRequest*            p_dvb_tune_request;
1581
1582         IDigitalCableTuneRequest*   p_cqam_tune_request;
1583         IATSCChannelTuneRequest*    p_atsc_tune_request;
1584         ILocator*                   p_locator;
1585         IDVBTLocator*               p_dvbt_locator;
1586         IDVBCLocator*               p_dvbc_locator;
1587         IDVBSLocator*               p_dvbs_locator;
1588
1589         BSTR                        bstr_name;
1590
1591         CLSID                       guid_test_network_type;
1592         char*                       psz_network_name;
1593         char*                       psz_bstr_name;
1594         int                         i_name_len;
1595
1596         localComPtr():
1597             p_tuning_space_container(NULL),
1598             p_tuning_space_enum(NULL),
1599             p_test_tuning_space(NULL),
1600             p_tune_request(NULL),
1601             p_dvb_tune_request(NULL),
1602             p_cqam_tune_request(NULL),
1603             p_atsc_tune_request(NULL),
1604             p_locator(NULL),
1605             p_dvbt_locator(NULL),
1606             p_dvbc_locator(NULL),
1607             p_dvbs_locator(NULL),
1608             bstr_name(NULL),
1609             guid_test_network_type(GUID_NULL),
1610             psz_network_name(NULL),
1611             psz_bstr_name(NULL),
1612             i_name_len(0)
1613         {}
1614         ~localComPtr()
1615         {
1616             if( p_tuning_space_enum )
1617                 p_tuning_space_enum->Release();
1618             if( p_tuning_space_container )
1619                 p_tuning_space_container->Release();
1620             if( p_test_tuning_space )
1621                 p_test_tuning_space->Release();
1622             if( p_tune_request )
1623                 p_tune_request->Release();
1624             if( p_dvb_tune_request )
1625                 p_dvb_tune_request->Release();
1626             if( p_cqam_tune_request )
1627                 p_cqam_tune_request->Release();
1628             if( p_atsc_tune_request )
1629                 p_atsc_tune_request->Release();
1630             if( p_locator )
1631                 p_locator->Release();
1632             if( p_dvbt_locator )
1633                 p_dvbt_locator->Release();
1634             if( p_dvbc_locator )
1635                 p_dvbc_locator->Release();
1636             if( p_dvbs_locator )
1637                 p_dvbs_locator->Release();
1638             SysFreeString( bstr_name );
1639             delete[] psz_bstr_name;
1640             free( psz_network_name );
1641         }
1642     } l;
1643
1644     msg_Dbg( p_access, "SetUpTuner: entering" );
1645
1646
1647     /* We shall test for a specific Tuning space name supplied on the command
1648      * line as dvb-network-name=xxx.
1649      * For some users with multiple cards and/or multiple networks this could
1650      * be useful. This allows us to reasonably safely apply updates to the
1651      * System Tuning Space in the registry without disrupting other streams. */
1652
1653     l.psz_network_name = var_GetNonEmptyString( p_access, "dvb-network-name" );
1654
1655     if( l.psz_network_name )
1656     {
1657         msg_Dbg( p_access, "SetUpTuner: Find Tuning Space: %s",
1658             l.psz_network_name );
1659     }
1660     else
1661     {
1662         l.psz_network_name = new char[1];
1663         *l.psz_network_name = '\0';
1664     }
1665
1666     /* Tuner may already have been set up. If it is for the same
1667      * network type then all is well. Otherwise, reset the Tuning Space and get
1668      * a new one */
1669     msg_Dbg( p_access, "SetUpTuner: Checking for tuning space" );
1670     if( !p_scanning_tuner )
1671     {
1672         msg_Warn( p_access, "SetUpTuner: "\
1673             "Cannot find scanning tuner" );
1674         return E_FAIL;
1675     }
1676
1677     if( p_tuning_space )
1678     {
1679         msg_Dbg( p_access, "SetUpTuner: get network type" );
1680         hr = p_tuning_space->get__NetworkType( &l.guid_test_network_type );
1681         if( FAILED( hr ) )
1682         {
1683             msg_Warn( p_access, "Check: "\
1684                 "Cannot get network type: hr=0x%8lx", hr );
1685             l.guid_test_network_type = GUID_NULL;
1686         }
1687
1688         msg_Dbg( p_access, "SetUpTuner: see if it's the right one" );
1689         if( l.guid_test_network_type == guid_this_network_type )
1690         {
1691             msg_Dbg( p_access, "SetUpTuner: it's the right one" );
1692             SysFreeString( l.bstr_name );
1693
1694             hr = p_tuning_space->get_UniqueName( &l.bstr_name );
1695             if( FAILED( hr ) )
1696             {
1697                 /* should never fail on a good tuning space */
1698                 msg_Dbg( p_access, "SetUpTuner: "\
1699                     "Cannot get UniqueName for Tuning Space: hr=0x%8lx", hr );
1700                 goto NoTuningSpace;
1701             }
1702
1703             /* Test for a specific Tuning space name supplied on the command
1704              * line as dvb-network-name=xxx */
1705             if( l.psz_bstr_name )
1706                 delete[] l.psz_bstr_name;
1707             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
1708                 l.psz_bstr_name, 0, NULL, NULL );
1709             l.psz_bstr_name = new char[ l.i_name_len ];
1710             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
1711                 l.psz_bstr_name, l.i_name_len, NULL, NULL );
1712
1713             /* if no name was requested on command line, or if the name
1714              * requested equals the name of this space, we are OK */
1715             if( *l.psz_network_name == '\0' ||
1716                 strcmp( l.psz_network_name, l.psz_bstr_name ) == 0 )
1717             {
1718                 msg_Dbg( p_access, "SetUpTuner: Using Tuning Space: %s",
1719                     l.psz_bstr_name );
1720                 /* p_tuning_space and guid_network_type are already set */
1721                 /* you probably already have a tune request, also */
1722                 hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
1723                 if( SUCCEEDED( hr ) )
1724                 {
1725                     return S_OK;
1726                 }
1727                 /* CreateTuneRequest adds l.p_tune_request to p_tuning_space
1728                  * l.p_tune_request->RefCount = 1 */
1729                 hr = p_tuning_space->CreateTuneRequest( &l.p_tune_request );
1730                 if( SUCCEEDED( hr ) )
1731                 {
1732                     return S_OK;
1733                 }
1734                 msg_Warn( p_access, "SetUpTuner: "\
1735                     "Cannot Create Tune Request: hr=0x%8lx", hr );
1736                /* fall through to NoTuningSpace */
1737             }
1738         }
1739
1740         /* else different guid_network_type */
1741     NoTuningSpace:
1742         if( p_tuning_space )
1743             p_tuning_space->Release();
1744         p_tuning_space = NULL;
1745         /* pro forma; should have returned S_OK if we created this */
1746         if( l.p_tune_request )
1747             l.p_tune_request->Release();
1748         l.p_tune_request = NULL;
1749     }
1750
1751
1752     /* p_tuning_space is null at this point; we have already
1753        returned S_OK if it was good. So find a tuning
1754        space on the scanning tuner. */
1755
1756     msg_Dbg( p_access, "SetUpTuner: release TuningSpaces Enumerator" );
1757     if( l.p_tuning_space_enum )
1758         l.p_tuning_space_enum->Release();
1759     msg_Dbg( p_access, "SetUpTuner: nullify TuningSpaces Enumerator" );
1760     l.p_tuning_space_enum = NULL;
1761     msg_Dbg( p_access, "SetUpTuner: create TuningSpaces Enumerator" );
1762
1763     hr = p_scanning_tuner->EnumTuningSpaces( &l.p_tuning_space_enum );
1764     if( FAILED( hr ) )
1765     {
1766         msg_Warn( p_access, "SetUpTuner: "\
1767             "Cannot create TuningSpaces Enumerator: hr=0x%8lx", hr );
1768         goto TryToClone;
1769     }
1770
1771     do
1772     {
1773         msg_Dbg( p_access, "SetUpTuner: top of loop" );
1774         l.guid_test_network_type = GUID_NULL;
1775         if( l.p_test_tuning_space )
1776             l.p_test_tuning_space->Release();
1777         l.p_test_tuning_space = NULL;
1778         if( p_tuning_space )
1779             p_tuning_space->Release();
1780         p_tuning_space = NULL;
1781         SysFreeString( l.bstr_name );
1782         msg_Dbg( p_access, "SetUpTuner: need good TS enum" );
1783         if( !l.p_tuning_space_enum ) break;
1784         msg_Dbg( p_access, "SetUpTuner: next tuning space" );
1785         hr = l.p_tuning_space_enum->Next( 1, &l.p_test_tuning_space, NULL );
1786         if( hr != S_OK ) break;
1787         msg_Dbg( p_access, "SetUpTuner: get network type" );
1788         hr = l.p_test_tuning_space->get__NetworkType( &l.guid_test_network_type );
1789         if( FAILED( hr ) )
1790         {
1791             msg_Warn( p_access, "Check: "\
1792                 "Cannot get network type: hr=0x%8lx", hr );
1793             l.guid_test_network_type = GUID_NULL;
1794         }
1795         if( l.guid_test_network_type == guid_this_network_type )
1796         {
1797             msg_Dbg( p_access, "SetUpTuner: Found matching space on tuner" );
1798
1799             SysFreeString( l.bstr_name );
1800             msg_Dbg( p_access, "SetUpTuner: get unique name" );
1801
1802             hr = l.p_test_tuning_space->get_UniqueName( &l.bstr_name );
1803             if( FAILED( hr ) )
1804             {
1805                 /* should never fail on a good tuning space */
1806                 msg_Dbg( p_access, "SetUpTuner: "\
1807                     "Cannot get UniqueName for Tuning Space: hr=0x%8lx", hr );
1808                 continue;
1809             }
1810             msg_Dbg( p_access, "SetUpTuner: convert w to multi" );
1811             if ( l.psz_bstr_name )
1812                 delete[] l.psz_bstr_name;
1813             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
1814                 l.psz_bstr_name, 0, NULL, NULL );
1815             l.psz_bstr_name = new char[ l.i_name_len ];
1816             l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
1817                 l.psz_bstr_name, l.i_name_len, NULL, NULL );
1818             msg_Dbg( p_access, "SetUpTuner: Using Tuning Space: %s",
1819                 l.psz_bstr_name );
1820             break;
1821         }
1822
1823     }
1824     while( true );
1825     msg_Dbg( p_access, "SetUpTuner: checking what we got" );
1826
1827     if( l.guid_test_network_type == GUID_NULL)
1828     {
1829         msg_Dbg( p_access, "SetUpTuner: got null, try to clone" );
1830         goto TryToClone;
1831     }
1832
1833     msg_Dbg( p_access, "SetUpTuner: put TS" );
1834     hr = p_scanning_tuner->put_TuningSpace( l.p_test_tuning_space );
1835     if( FAILED( hr ) )
1836     {
1837         msg_Dbg( p_access, "SetUpTuner: "\
1838             "cannot put tuning space: hr=0x%8lx", hr );
1839         goto TryToClone;
1840     }
1841
1842     msg_Dbg( p_access, "SetUpTuner: get default locator" );
1843     hr = l.p_test_tuning_space->get_DefaultLocator( &l.p_locator );
1844     if( FAILED( hr ) )
1845     {
1846         msg_Dbg( p_access, "SetUpTuner: "\
1847             "cannot get default locator: hr=0x%8lx", hr );
1848         goto TryToClone;
1849     }
1850
1851     msg_Dbg( p_access, "SetUpTuner: create tune request" );
1852     hr = l.p_test_tuning_space->CreateTuneRequest( &l.p_tune_request );
1853     if( FAILED( hr ) )
1854     {
1855         msg_Dbg( p_access, "SetUpTuner: "\
1856             "cannot create tune request: hr=0x%8lx", hr );
1857         goto TryToClone;
1858     }
1859
1860     msg_Dbg( p_access, "SetUpTuner: put locator" );
1861     hr = l.p_tune_request->put_Locator( l.p_locator );
1862     if( FAILED( hr ) )
1863     {
1864         msg_Dbg( p_access, "SetUpTuner: "\
1865             "cannot put locator: hr=0x%8lx", hr );
1866         goto TryToClone;
1867     }
1868
1869     msg_Dbg( p_access, "SetUpTuner: try to validate tune request" );
1870     hr = p_scanning_tuner->Validate( l.p_tune_request );
1871     if( FAILED( hr ) )
1872     {
1873         msg_Dbg( p_access, "SetUpTuner: "\
1874             "Tune Request cannot be validated: hr=0x%8lx", hr );
1875     }
1876
1877     msg_Dbg( p_access, "SetUpTuner: Attach tune request to Scanning Tuner");
1878     /* increments ref count for scanning tuner */
1879     hr = p_scanning_tuner->put_TuneRequest( l.p_tune_request );
1880     if( FAILED( hr ) )
1881     {
1882         msg_Warn( p_access, "SetUpTuner: "\
1883             "Cannot submit the tune request: hr=0x%8lx", hr );
1884         return hr;
1885     }
1886
1887     msg_Dbg( p_access, "SetUpTuner: Tuning Space and Tune Request created" );
1888     return S_OK;
1889
1890     /* Get the SystemTuningSpaces container
1891      * p_tuning_space_container->Refcount = 1  */
1892 TryToClone:
1893     msg_Warn( p_access, "SetUpTuner: won't try to clone " );
1894     return E_FAIL;
1895 }
1896
1897 /*****************************************************************************
1898 * GetNextNetworkType
1899 * helper function; this is probably best done as an Enumeration of
1900 * network providers
1901 *****************************************************************************/
1902 HRESULT BDAGraph::GetNextNetworkType( CLSID* guid_this_network_type )
1903 {
1904     HRESULT hr = S_OK;
1905     if( *guid_this_network_type == GUID_NULL )
1906     {
1907         msg_Dbg( p_access, "GetNextNetworkType: DVB-C" );
1908         *guid_this_network_type = CLSID_DVBCNetworkProvider;
1909         return S_OK;
1910     }
1911     if( *guid_this_network_type == CLSID_DVBCNetworkProvider )
1912     {
1913         msg_Dbg( p_access, "GetNextNetworkType: DVB-T" );
1914         *guid_this_network_type = CLSID_DVBTNetworkProvider;
1915         return S_OK;
1916     }
1917     if( *guid_this_network_type == CLSID_DVBTNetworkProvider )
1918     {
1919         msg_Dbg( p_access, "GetNextNetworkType: DVB-S" );
1920         *guid_this_network_type = CLSID_DVBSNetworkProvider;
1921         return S_OK;
1922     }
1923     if( *guid_this_network_type == CLSID_DVBSNetworkProvider )
1924     {
1925         msg_Dbg( p_access, "GetNextNetworkType: ATSC" );
1926         *guid_this_network_type = CLSID_ATSCNetworkProvider;
1927         return S_OK;
1928     }
1929     msg_Dbg( p_access, "GetNextNetworkType: failed" );
1930     *guid_this_network_type = GUID_NULL;
1931     hr = E_FAIL;
1932     return hr;
1933 }
1934
1935
1936 /******************************************************************************
1937 * Check
1938 *******************************************************************************
1939 * Check if tuner supports this network type
1940 *
1941 * on success, sets globals:
1942 * systems, l_tuner_used, p_network_provider, p_scanning_tuner, p_tuner_device,
1943 * p_tuning_space, p_filter_graph
1944 ******************************************************************************/
1945 HRESULT BDAGraph::Check( REFCLSID guid_this_network_type )
1946 {
1947     HRESULT hr = S_OK;
1948
1949     class localComPtr
1950     {
1951         public:
1952         ITuningSpaceContainer*  p_tuning_space_container;
1953
1954         localComPtr():
1955              p_tuning_space_container(NULL)
1956         {};
1957         ~localComPtr()
1958         {
1959             if( p_tuning_space_container )
1960                 p_tuning_space_container->Release();
1961         }
1962     } l;
1963
1964     msg_Dbg( p_access, "Check: entering ");
1965
1966     /* Note that the systems global is persistent across Destroy().
1967      * So we need to see if a tuner has been physically removed from
1968      * the system since the last Check. Before we do anything,
1969      * assume that this Check will fail and remove this network type
1970      * from systems. It will be restored if the Check passes.
1971      */
1972
1973     systems &= ~( GetSystem( guid_this_network_type ) );
1974
1975
1976     /* If we have already have a filter graph, rebuild it*/
1977     msg_Dbg( p_access, "Check: Destroying filter graph" );
1978     if( p_filter_graph )
1979         Destroy();
1980     p_filter_graph = NULL;
1981     hr = ::CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
1982         IID_IGraphBuilder, reinterpret_cast<void**>( &p_filter_graph ) );
1983     if( FAILED( hr ) )
1984     {
1985         msg_Warn( p_access, "Check: "\
1986             "Cannot CoCreate IFilterGraph: hr=0x%8lx", hr );
1987         return hr;
1988     }
1989
1990     /* First filter in the graph is the Network Provider and
1991      * its Scanning Tuner which takes the Tune Request */
1992     if( p_network_provider )
1993         p_network_provider->Release();
1994     p_network_provider = NULL;
1995     hr = ::CoCreateInstance( guid_this_network_type, NULL, CLSCTX_INPROC_SERVER,
1996         IID_IBaseFilter, reinterpret_cast<void**>( &p_network_provider ) );
1997     if( FAILED( hr ) )
1998     {
1999         msg_Warn( p_access, "Check: "\
2000             "Cannot CoCreate Network Provider: hr=0x%8lx", hr );
2001         return hr;
2002     }
2003
2004     msg_Dbg( p_access, "Check: adding Network Provider to graph");
2005     hr = p_filter_graph->AddFilter( p_network_provider, L"Network Provider" );
2006     if( FAILED( hr ) )
2007     {
2008         msg_Warn( p_access, "Check: "\
2009             "Cannot load network provider: hr=0x%8lx", hr );
2010         return hr;
2011     }
2012
2013     /* Add the Network Tuner to the Network Provider. On subsequent calls,
2014      * l_tuner_used will cause a different tuner to be selected.
2015      *
2016      * To select a specific device first get the parameter that nominates the
2017      * device (dvb-adapter) and use the value to initialise l_tuner_used.
2018      * Note that dvb-adapter is 1-based, while l_tuner_used is 0-based.
2019      * When FindFilter returns, check the contents of l_tuner_used.
2020      * If it is not what was selected, then the requested device was not
2021      * available, so return with an error. */
2022
2023     long l_adapter = -1;
2024     l_adapter = var_GetInteger( p_access, "dvb-adapter" );
2025     if( l_tuner_used < 0 && l_adapter >= 0 )
2026         l_tuner_used = l_adapter - 1;
2027
2028     /* If tuner is in cold state, we have to do a successful put_TuneRequest
2029      * before it will Connect. */
2030     msg_Dbg( p_access, "Check: Creating Scanning Tuner");
2031     if( p_scanning_tuner )
2032         p_scanning_tuner->Release();
2033     p_scanning_tuner = NULL;
2034     hr = p_network_provider->QueryInterface( IID_IScanningTuner,
2035         reinterpret_cast<void**>( &p_scanning_tuner ) );
2036     if( FAILED( hr ) )
2037     {
2038         msg_Warn( p_access, "Check: "\
2039             "Cannot QI Network Provider for Scanning Tuner: hr=0x%8lx", hr );
2040         return hr;
2041     }
2042
2043     /* try to set up p_scanning_tuner */
2044     msg_Dbg( p_access, "Check: Calling SetUpTuner" );
2045     hr = SetUpTuner( guid_this_network_type );
2046     if( FAILED( hr ) )
2047     {
2048         msg_Dbg( p_access, "Check: "\
2049             "Cannot set up scanner in Check mode: hr=0x%8lx", hr );
2050         return hr;
2051     }
2052
2053     msg_Dbg( p_access, "Check: "\
2054         "Calling FindFilter for KSCATEGORY_BDA_NETWORK_TUNER with "\
2055         "p_network_provider; l_tuner_used=%ld", l_tuner_used );
2056     hr = FindFilter( KSCATEGORY_BDA_NETWORK_TUNER, &l_tuner_used,
2057         p_network_provider, &p_tuner_device );
2058     if( FAILED( hr ) )
2059     {
2060         msg_Warn( p_access, "Check: "\
2061             "Cannot load tuner device and connect network provider: "\
2062             "hr=0x%8lx", hr );
2063         return hr;
2064     }
2065
2066     if( l_adapter > 0 && l_tuner_used != l_adapter )
2067     {
2068          msg_Warn( p_access, "Check: "\
2069              "Tuner device %ld is not available", l_adapter );
2070          return E_FAIL;
2071     }
2072
2073     msg_Dbg( p_access, "Check: Using adapter %ld", l_tuner_used );
2074     /* success!
2075      * already set l_tuner_used,
2076      * p_tuning_space
2077      */
2078     msg_Dbg( p_access, "Check: check succeeded: hr=0x%8lx", hr );
2079     systems |= GetSystem( guid_this_network_type );
2080     msg_Dbg( p_access, "Check: returning from Check mode" );
2081     return S_OK;
2082 }
2083
2084
2085 /******************************************************************************
2086 * Build
2087 *******************************************************************************
2088 * Build the Filter Graph
2089 *
2090 * connects filters and
2091 * creates the media control and registers the graph
2092 * on success, sets globals:
2093 * d_graph_register, p_media_control, p_grabber, p_sample_grabber,
2094 * p_mpeg_demux, p_transport_info
2095 ******************************************************************************/
2096 HRESULT BDAGraph::Build()
2097 {
2098     HRESULT hr = S_OK;
2099     long            l_capture_used;
2100     long            l_tif_used;
2101     AM_MEDIA_TYPE   grabber_media_type;
2102
2103     class localComPtr
2104     {
2105         public:
2106         ITuningSpaceContainer*  p_tuning_space_container;
2107         localComPtr():
2108             p_tuning_space_container(NULL)
2109         {};
2110         ~localComPtr()
2111         {
2112             if( p_tuning_space_container )
2113                 p_tuning_space_container->Release();
2114         }
2115     } l;
2116
2117     msg_Dbg( p_access, "Build: entering");
2118
2119     /* at this point, you've connected to a scanning tuner of the right
2120      * network type.
2121      */
2122     if( !p_scanning_tuner || !p_tuner_device )
2123     {
2124         msg_Warn( p_access, "Build: "\
2125             "Scanning Tuner does not exist" );
2126         return E_FAIL;
2127     }
2128
2129     hr = p_scanning_tuner->get_TuneRequest( &p_tune_request );
2130     if( FAILED( hr ) )
2131     {
2132         msg_Warn( p_access, "Build: no tune request" );
2133         return hr;
2134     }
2135     hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
2136     if( FAILED( hr ) )
2137     {
2138         msg_Warn( p_access, "Build: no tuning space" );
2139         return hr;
2140     }
2141     hr = p_tuning_space->get__NetworkType( &guid_network_type );
2142
2143
2144     /* Always look for all capture devices to match the Network Tuner */
2145     l_capture_used = -1;
2146     msg_Dbg( p_access, "Build: "\
2147         "Calling FindFilter for KSCATEGORY_BDA_RECEIVER_COMPONENT with "\
2148         "p_tuner_device; l_capture_used=%ld", l_capture_used );
2149     hr = FindFilter( KSCATEGORY_BDA_RECEIVER_COMPONENT, &l_capture_used,
2150         p_tuner_device, &p_capture_device );
2151     if( FAILED( hr ) )
2152     {
2153         /* Some BDA drivers do not provide a Capture Device Filter so force
2154          * the Sample Grabber to connect directly to the Tuner Device */
2155         msg_Dbg( p_access, "Build: "\
2156             "Cannot find Capture device. Connect to tuner "\
2157             "and AddRef() : hr=0x%8lx", hr );
2158         p_capture_device = p_tuner_device;
2159         p_capture_device->AddRef();
2160     }
2161
2162     if( p_sample_grabber )
2163          p_sample_grabber->Release();
2164     p_sample_grabber = NULL;
2165     /* Insert the Sample Grabber to tap into the Transport Stream. */
2166     hr = ::CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
2167         IID_IBaseFilter, reinterpret_cast<void**>( &p_sample_grabber ) );
2168     if( FAILED( hr ) )
2169     {
2170         msg_Warn( p_access, "Build: "\
2171             "Cannot load Sample Grabber Filter: hr=0x%8lx", hr );
2172         return hr;
2173     }
2174
2175     hr = p_filter_graph->AddFilter( p_sample_grabber, L"Sample Grabber" );
2176     if( FAILED( hr ) )
2177     {
2178         msg_Warn( p_access, "Build: "\
2179             "Cannot add Sample Grabber Filter to graph: hr=0x%8lx", hr );
2180         return hr;
2181     }
2182
2183     /* create the sample grabber */
2184     if( p_grabber )
2185         p_grabber->Release();
2186     p_grabber = NULL;
2187     hr = p_sample_grabber->QueryInterface( IID_ISampleGrabber,
2188         reinterpret_cast<void**>( &p_grabber ) );
2189     if( FAILED( hr ) )
2190     {
2191         msg_Warn( p_access, "Build: "\
2192             "Cannot QI Sample Grabber Filter: hr=0x%8lx", hr );
2193         return hr;
2194     }
2195
2196     /* Try the possible stream type */
2197     hr = E_FAIL;
2198     for( int i = 0; i < 2; i++ )
2199     {
2200         ZeroMemory( &grabber_media_type, sizeof( AM_MEDIA_TYPE ) );
2201         grabber_media_type.majortype = MEDIATYPE_Stream;
2202         grabber_media_type.subtype   =  i == 0 ? MEDIASUBTYPE_MPEG2_TRANSPORT : KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT;
2203         msg_Dbg( p_access, "Build: "
2204                            "Trying connecting with subtype %s",
2205                            i == 0 ? "MEDIASUBTYPE_MPEG2_TRANSPORT" : "KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT" );
2206         hr = p_grabber->SetMediaType( &grabber_media_type );
2207         if( SUCCEEDED( hr ) )
2208         {
2209             hr = Connect( p_capture_device, p_sample_grabber );
2210             if( SUCCEEDED( hr ) )
2211             {
2212                 msg_Dbg( p_access, "Build: "\
2213                     "Connected capture device to sample grabber" );
2214                 break;
2215             }
2216             msg_Warn( p_access, "Build: "\
2217                 "Cannot connect Sample Grabber to Capture device: hr=0x%8lx (try %d/2)", hr, 1+i );
2218         }
2219         else
2220         {
2221             msg_Warn( p_access, "Build: "\
2222                 "Cannot set media type on grabber filter: hr=0x%8lx (try %d/2", hr, 1+i );
2223         }
2224     }
2225     msg_Dbg( p_access, "Build: This is where it used to return upon success" );
2226     if( FAILED( hr ) )
2227     {
2228         msg_Warn( p_access, "Build: "\
2229             "Cannot use capture device: hr=0x%8lx", hr );
2230         return hr;
2231     }
2232
2233     /* We need the MPEG2 Demultiplexer even though we are going to use the VLC
2234      * TS demuxer. The TIF filter connects to the MPEG2 demux and works with
2235      * the Network Provider filter to set up the stream */
2236     //msg_Dbg( p_access, "Build: using MPEG2 demux" );
2237     if( p_mpeg_demux )
2238         p_mpeg_demux->Release();
2239     p_mpeg_demux = NULL;
2240     hr = ::CoCreateInstance( CLSID_MPEG2Demultiplexer, NULL,
2241         CLSCTX_INPROC_SERVER, IID_IBaseFilter,
2242         reinterpret_cast<void**>( &p_mpeg_demux ) );
2243     if( FAILED( hr ) )
2244     {
2245         msg_Warn( p_access, "Build: "\
2246             "Cannot CoCreateInstance MPEG2 Demultiplexer: hr=0x%8lx", hr );
2247         return hr;
2248     }
2249
2250     //msg_Dbg( p_access, "Build: adding demux" );
2251     hr = p_filter_graph->AddFilter( p_mpeg_demux, L"Demux" );
2252     if( FAILED( hr ) )
2253     {
2254         msg_Warn( p_access, "Build: "\
2255             "Cannot add demux filter to graph: hr=0x%8lx", hr );
2256         return hr;
2257     }
2258
2259     hr = Connect( p_sample_grabber, p_mpeg_demux );
2260     if( FAILED( hr ) )
2261     {
2262         msg_Warn( p_access, "Build: "\
2263             "Cannot connect demux to grabber: hr=0x%8lx", hr );
2264         return hr;
2265     }
2266
2267     //msg_Dbg( p_access, "Build: Connected sample grabber to demux" );
2268     /* Always look for the Transport Information Filter from the start
2269      * of the collection*/
2270     l_tif_used = -1;
2271     msg_Dbg( p_access, "Check: "\
2272         "Calling FindFilter for KSCATEGORY_BDA_TRANSPORT_INFORMATION with "\
2273         "p_mpeg_demux; l_tif_used=%ld", l_tif_used );
2274
2275
2276     hr = FindFilter( KSCATEGORY_BDA_TRANSPORT_INFORMATION, &l_tif_used,
2277         p_mpeg_demux, &p_transport_info );
2278     if( FAILED( hr ) )
2279     {
2280         msg_Warn( p_access, "Build: "\
2281             "Cannot load TIF onto demux: hr=0x%8lx", hr );
2282         return hr;
2283     }
2284
2285     /* Configure the Sample Grabber to buffer the samples continuously */
2286     hr = p_grabber->SetBufferSamples( true );
2287     if( FAILED( hr ) )
2288     {
2289         msg_Warn( p_access, "Build: "\
2290             "Cannot set Sample Grabber to buffering: hr=0x%8lx", hr );
2291         return hr;
2292     }
2293
2294     hr = p_grabber->SetOneShot( false );
2295     if( FAILED( hr ) )
2296     {
2297         msg_Warn( p_access, "Build: "\
2298             "Cannot set Sample Grabber to multi shot: hr=0x%8lx", hr );
2299         return hr;
2300     }
2301
2302     /* Second parameter to SetCallback specifies the callback method; 0 uses
2303      * the ISampleGrabberCB::SampleCB method, which receives an IMediaSample
2304      * pointer. */
2305     //msg_Dbg( p_access, "Build: Adding grabber callback" );
2306     hr = p_grabber->SetCallback( this, 0 );
2307     if( FAILED( hr ) )
2308     {
2309         msg_Warn( p_access, "Build: "\
2310             "Cannot set SampleGrabber Callback: hr=0x%8lx", hr );
2311         return hr;
2312     }
2313
2314     hr = Register(); /* creates d_graph_register */
2315     if( FAILED( hr ) )
2316     {
2317         d_graph_register = 0;
2318         msg_Dbg( p_access, "Build: "\
2319             "Cannot register graph: hr=0x%8lx", hr );
2320     }
2321
2322     /* The Media Control is used to Run and Stop the Graph */
2323     if( p_media_control )
2324         p_media_control->Release();
2325     p_media_control = NULL;
2326     hr = p_filter_graph->QueryInterface( IID_IMediaControl,
2327         reinterpret_cast<void**>( &p_media_control ) );
2328     if( FAILED( hr ) )
2329     {
2330         msg_Warn( p_access, "Build: "\
2331             "Cannot QI Media Control: hr=0x%8lx", hr );
2332         return hr;
2333     }
2334
2335     /* success! */
2336     //msg_Dbg( p_access, "Build: succeeded: hr=0x%8lx", hr );
2337     return S_OK;
2338 }
2339
2340 /* debugging */
2341 HRESULT BDAGraph::ListFilters( REFCLSID this_clsid )
2342 {
2343     HRESULT                 hr = S_OK;
2344
2345     class localComPtr
2346     {
2347         public:
2348         ICreateDevEnum*    p_local_system_dev_enum;
2349         IEnumMoniker*      p_moniker_enum;
2350         IMoniker*          p_moniker;
2351         IBaseFilter*       p_filter;
2352         IBaseFilter*       p_this_filter;
2353         IBindCtx*          p_bind_context;
2354         IPropertyBag*      p_property_bag;
2355
2356         char*              psz_downstream;
2357         char*              psz_bstr;
2358         int                i_bstr_len;
2359
2360         localComPtr():
2361             p_local_system_dev_enum(NULL),
2362             p_moniker_enum(NULL),
2363             p_moniker(NULL),
2364             p_filter(NULL),
2365             p_this_filter(NULL),
2366             p_bind_context( NULL ),
2367             p_property_bag(NULL),
2368             psz_downstream( NULL ),
2369             psz_bstr( NULL )
2370         {}
2371         ~localComPtr()
2372         {
2373             if( p_property_bag )
2374                 p_property_bag->Release();
2375             if( p_bind_context )
2376                 p_bind_context->Release();
2377             if( p_filter )
2378                 p_filter->Release();
2379             if( p_this_filter )
2380                 p_this_filter->Release();
2381             if( p_moniker )
2382                 p_moniker->Release();
2383             if( p_moniker_enum )
2384                 p_moniker_enum->Release();
2385             if( p_local_system_dev_enum )
2386                 p_local_system_dev_enum->Release();
2387             if( psz_bstr )
2388                 delete[] psz_bstr;
2389             if( psz_downstream )
2390                 delete[] psz_downstream;
2391         }
2392     } l;
2393
2394
2395 //    msg_Dbg( p_access, "ListFilters: Create local system_dev_enum");
2396     if( l.p_local_system_dev_enum )
2397         l.p_local_system_dev_enum->Release();
2398     l.p_local_system_dev_enum = NULL;
2399     hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
2400         IID_ICreateDevEnum, reinterpret_cast<void**>( &l.p_local_system_dev_enum ) );
2401     if( FAILED( hr ) )
2402     {
2403         msg_Warn( p_access, "ListFilters: "\
2404             "Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
2405         return hr;
2406     }
2407
2408     //msg_Dbg( p_access, "ListFilters: Create p_moniker_enum");
2409     if( l.p_moniker_enum )
2410         l.p_moniker_enum->Release();
2411     l.p_moniker_enum = NULL;
2412     hr = l.p_local_system_dev_enum->CreateClassEnumerator( this_clsid,
2413         &l.p_moniker_enum, 0 );
2414     if( FAILED( hr ) )
2415     {
2416         msg_Warn( p_access, "ListFilters: "\
2417             "Cannot CreateClassEnumerator: hr=0x%8lx", hr );
2418         return hr;
2419     }
2420
2421     //msg_Dbg( p_access, "ListFilters: Entering main loop" );
2422     do
2423     {
2424         /* We are overwriting l.p_moniker so we should Release and nullify
2425          * It is important that p_moniker and p_property_bag are fully released
2426          * l.p_filter may not be dereferenced so we could force to NULL */
2427         /* msg_Dbg( p_access, "ListFilters: top of main loop");*/
2428         //msg_Dbg( p_access, "ListFilters: releasing property bag");
2429         if( l.p_property_bag )
2430             l.p_property_bag->Release();
2431         l.p_property_bag = NULL;
2432         //msg_Dbg( p_access, "ListFilters: releasing filter");
2433         if( l.p_filter )
2434             l.p_filter->Release();
2435         l.p_filter = NULL;
2436         //msg_Dbg( p_access, "ListFilters: releasing bind context");
2437         if( l.p_bind_context )
2438            l.p_bind_context->Release();
2439         l.p_bind_context = NULL;
2440         //msg_Dbg( p_access, "ListFilters: releasing moniker");
2441         if( l.p_moniker )
2442             l.p_moniker->Release();
2443         l.p_moniker = NULL;
2444         //msg_Dbg( p_access, "ListFilters: trying a moniker");
2445
2446         if( !l.p_moniker_enum ) break;
2447         hr = l.p_moniker_enum->Next( 1, &l.p_moniker, 0 );
2448         if( hr != S_OK ) break;
2449
2450         /* l.p_bind_context is Released at the top of the loop */
2451         hr = CreateBindCtx( 0, &l.p_bind_context );
2452         if( FAILED( hr ) )
2453         {
2454             msg_Dbg( p_access, "ListFilters: "\
2455                 "Cannot create bind_context, trying another: hr=0x%8lx", hr );
2456             continue;
2457         }
2458
2459         /* l.p_filter is Released at the top of the loop */
2460         hr = l.p_moniker->BindToObject( l.p_bind_context, NULL, IID_IBaseFilter,
2461             reinterpret_cast<void**>( &l.p_filter ) );
2462         if( FAILED( hr ) )
2463         {
2464             msg_Dbg( p_access, "ListFilters: "\
2465                 "Cannot create p_filter, trying another: hr=0x%8lx", hr );
2466             continue;
2467         }
2468
2469 #ifdef DEBUG_MONIKER_NAME
2470         WCHAR*  pwsz_downstream = NULL;
2471
2472         hr = l.p_moniker->GetDisplayName(l.p_bind_context, NULL,
2473             &pwsz_downstream );
2474         if( FAILED( hr ) )
2475         {
2476             msg_Dbg( p_access, "ListFilters: "\
2477                 "Cannot get display name, trying another: hr=0x%8lx", hr );
2478             continue;
2479         }
2480
2481         if( l.psz_downstream )
2482             delete[] l.psz_downstream;
2483         l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0, pwsz_downstream, -1,
2484             l.psz_downstream, 0, NULL, NULL );
2485         l.psz_downstream = new char[ l.i_bstr_len ];
2486         l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0, pwsz_downstream, -1,
2487             l.psz_downstream, l.i_bstr_len, NULL, NULL );
2488
2489         LPMALLOC p_alloc;
2490         ::CoGetMalloc( 1, &p_alloc );
2491         p_alloc->Free( pwsz_downstream );
2492         p_alloc->Release();
2493         msg_Dbg( p_access, "ListFilters: "\
2494             "Moniker name is  %s",  l.psz_downstream );
2495 #else
2496         l.psz_downstream = strdup( "Downstream" );
2497 #endif
2498         /* l.p_property_bag is released at the top of the loop */
2499         hr = l.p_moniker->BindToStorage( NULL, NULL, IID_IPropertyBag,
2500             reinterpret_cast<void**>( &l.p_property_bag ) );
2501         if( FAILED( hr ) )
2502         {
2503             msg_Dbg( p_access, "ListFilters: "\
2504                 "Cannot Bind to Property Bag: hr=0x%8lx", hr );
2505             continue;
2506         }
2507
2508         //msg_Dbg( p_access, "ListFilters: displaying another" );
2509     }
2510     while( true );
2511
2512     return S_OK;
2513 }
2514
2515 /******************************************************************************
2516 * FindFilter
2517 * Looks up all filters in a category and connects to the upstream filter until
2518 * a successful match is found. The index of the connected filter is returned.
2519 * On subsequent calls, this can be used to start from that point to find
2520 * another match.
2521 * This is used when the graph does not run because a tuner device is in use so
2522 * another one needs to be selected.
2523 ******************************************************************************/
2524 HRESULT BDAGraph::FindFilter( REFCLSID this_clsid, long* i_moniker_used,
2525     IBaseFilter* p_upstream, IBaseFilter** p_p_downstream )
2526 {
2527     HRESULT                 hr = S_OK;
2528     int                     i_moniker_index = -1;
2529     class localComPtr
2530     {
2531         public:
2532         IEnumMoniker*  p_moniker_enum;
2533         IMoniker*      p_moniker;
2534         IBindCtx*      p_bind_context;
2535         IPropertyBag*  p_property_bag;
2536         char*          psz_upstream;
2537         int            i_upstream_len;
2538
2539         char*          psz_downstream;
2540         VARIANT        var_bstr;
2541         int            i_bstr_len;
2542         localComPtr():
2543             p_moniker_enum(NULL),
2544             p_moniker(NULL),
2545             p_bind_context( NULL ),
2546             p_property_bag(NULL),
2547             psz_upstream( NULL ),
2548             psz_downstream( NULL )
2549         {
2550             ::VariantInit(&var_bstr);
2551         }
2552         ~localComPtr()
2553         {
2554             if( p_moniker_enum )
2555                 p_moniker_enum->Release();
2556             if( p_moniker )
2557                 p_moniker->Release();
2558             if( p_bind_context )
2559                 p_bind_context->Release();
2560             if( p_property_bag )
2561                 p_property_bag->Release();
2562             if( psz_upstream )
2563                 delete[] psz_upstream;
2564             if( psz_downstream )
2565                 delete[] psz_downstream;
2566
2567             ::VariantClear(&var_bstr);
2568         }
2569     } l;
2570
2571     /* create system_dev_enum the first time through, or preserve the
2572      * existing one to loop through classes */
2573     if( !p_system_dev_enum )
2574     {
2575         msg_Dbg( p_access, "FindFilter: Create p_system_dev_enum");
2576         hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
2577             IID_ICreateDevEnum, reinterpret_cast<void**>( &p_system_dev_enum ) );
2578         if( FAILED( hr ) )
2579         {
2580             msg_Warn( p_access, "FindFilter: "\
2581                 "Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
2582             return hr;
2583         }
2584     }
2585
2586     msg_Dbg( p_access, "FindFilter: Create p_moniker_enum");
2587     hr = p_system_dev_enum->CreateClassEnumerator( this_clsid,
2588         &l.p_moniker_enum, 0 );
2589     if( FAILED( hr ) )
2590     {
2591         msg_Warn( p_access, "FindFilter: "\
2592             "Cannot CreateClassEnumerator: hr=0x%8lx", hr );
2593         return hr;
2594     }
2595
2596     msg_Dbg( p_access, "FindFilter: get filter name");
2597     hr = GetFilterName( p_upstream, &l.psz_upstream );
2598     if( FAILED( hr ) )
2599     {
2600         msg_Warn( p_access, "FindFilter: "\
2601             "Cannot GetFilterName: hr=0x%8lx", hr );
2602         return hr;
2603     }
2604
2605     msg_Dbg( p_access, "FindFilter: "\
2606         "called with i_moniker_used=%ld, " \
2607         "p_upstream = %s", *i_moniker_used, l.psz_upstream );
2608
2609     do
2610     {
2611         /* We are overwriting l.p_moniker so we should Release and nullify
2612          * It is important that p_moniker and p_property_bag are fully released */
2613         msg_Dbg( p_access, "FindFilter: top of main loop");
2614         if( l.p_property_bag )
2615             l.p_property_bag->Release();
2616         l.p_property_bag = NULL;
2617         msg_Dbg( p_access, "FindFilter: releasing bind context");
2618         if( l.p_bind_context )
2619            l.p_bind_context->Release();
2620         l.p_bind_context = NULL;
2621         msg_Dbg( p_access, "FindFilter: releasing moniker");
2622         if( l.p_moniker )
2623             l.p_moniker->Release();
2624         msg_Dbg( p_access, "FindFilter: null moniker");
2625         l.p_moniker = NULL;
2626
2627         msg_Dbg( p_access, "FindFilter: quit if no enum");
2628         if( !l.p_moniker_enum ) break;
2629         msg_Dbg( p_access, "FindFilter: trying a moniker");
2630         hr = l.p_moniker_enum->Next( 1, &l.p_moniker, 0 );
2631         if( hr != S_OK ) break;
2632
2633         i_moniker_index++;
2634
2635         /* Skip over devices already found on previous calls */
2636         msg_Dbg( p_access, "FindFilter: skip previously found devices");
2637
2638         if( i_moniker_index <= *i_moniker_used ) continue;
2639         *i_moniker_used = i_moniker_index;
2640
2641         /* l.p_bind_context is Released at the top of the loop */
2642         msg_Dbg( p_access, "FindFilter: create bind context");
2643         hr = CreateBindCtx( 0, &l.p_bind_context );
2644         if( FAILED( hr ) )
2645         {
2646             msg_Dbg( p_access, "FindFilter: "\
2647                 "Cannot create bind_context, trying another: hr=0x%8lx", hr );
2648             continue;
2649         }
2650
2651         msg_Dbg( p_access, "FindFilter: try to create downstream filter");
2652         *p_p_downstream = NULL;
2653         hr = l.p_moniker->BindToObject( l.p_bind_context, NULL, IID_IBaseFilter,
2654             reinterpret_cast<void**>( p_p_downstream ) );
2655         if( FAILED( hr ) )
2656         {
2657             msg_Dbg( p_access, "FindFilter: "\
2658                 "Cannot bind to downstream, trying another: hr=0x%8lx", hr );
2659             continue;
2660         }
2661
2662 #ifdef DEBUG_MONIKER_NAME
2663         msg_Dbg( p_access, "FindFilter: get downstream filter name");
2664
2665         WCHAR*  pwsz_downstream = NULL;
2666
2667         hr = l.p_moniker->GetDisplayName(l.p_bind_context, NULL,
2668             &pwsz_downstream );
2669         if( FAILED( hr ) )
2670         {
2671             msg_Dbg( p_access, "FindFilter: "\
2672                 "Cannot get display name, trying another: hr=0x%8lx", hr );
2673             continue;
2674         }
2675
2676         if( l.psz_downstream )
2677             delete[] l.psz_downstream;
2678         l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0, pwsz_downstream, -1,
2679             l.psz_downstream, 0, NULL, NULL );
2680         l.psz_downstream = new char[ l.i_bstr_len ];
2681         l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0, pwsz_downstream, -1,
2682             l.psz_downstream, l.i_bstr_len, NULL, NULL );
2683
2684         LPMALLOC p_alloc;
2685         ::CoGetMalloc( 1, &p_alloc );
2686         p_alloc->Free( pwsz_downstream );
2687         p_alloc->Release();
2688 #else
2689         l.psz_downstream = strdup( "Downstream" );
2690 #endif
2691
2692         /* l.p_property_bag is released at the top of the loop */
2693         msg_Dbg( p_access, "FindFilter: "\
2694             "Moniker name is  %s, binding to storage",  l.psz_downstream );
2695         hr = l.p_moniker->BindToStorage( l.p_bind_context, NULL,
2696             IID_IPropertyBag, reinterpret_cast<void**>( &l.p_property_bag ) );
2697         if( FAILED( hr ) )
2698         {
2699             msg_Dbg( p_access, "FindFilter: "\
2700                 "Cannot Bind to Property Bag: hr=0x%8lx", hr );
2701             continue;
2702         }
2703
2704         msg_Dbg( p_access, "FindFilter: read friendly name");
2705         hr = l.p_property_bag->Read( L"FriendlyName", &l.var_bstr, NULL );
2706         if( FAILED( hr ) )
2707         {
2708            msg_Dbg( p_access, "FindFilter: "\
2709                "Cannot read friendly name, next?: hr=0x%8lx", hr );
2710            continue;
2711         }
2712
2713         msg_Dbg( p_access, "FindFilter: add filter to graph" );
2714         hr = p_filter_graph->AddFilter( *p_p_downstream, l.var_bstr.bstrVal );
2715         if( FAILED( hr ) )
2716         {
2717             msg_Dbg( p_access, "FindFilter: "\
2718                 "Cannot add filter, trying another: hr=0x%8lx", hr );
2719             continue;
2720         }
2721
2722         msg_Dbg( p_access, "FindFilter: "\
2723             "Trying to Connect %s to %s", l.psz_upstream, l.psz_downstream );
2724         hr = Connect( p_upstream, *p_p_downstream );
2725         if( SUCCEEDED( hr ) )
2726         {
2727             msg_Dbg( p_access, "FindFilter: Connected %s", l.psz_downstream );
2728             return S_OK;
2729         }
2730
2731         /* Not the filter we want so unload and try the next one */
2732         /* Warning: RemoveFilter does an undocumented Release()
2733          * on pointer but does not set it to NULL */
2734         msg_Dbg( p_access, "FindFilter: Removing filter" );
2735         hr = p_filter_graph->RemoveFilter( *p_p_downstream );
2736         if( FAILED( hr ) )
2737         {
2738             msg_Warn( p_access, "FindFilter: "\
2739                 "Failed unloading Filter: hr=0x%8lx", hr );
2740             continue;
2741         }
2742         msg_Dbg( p_access, "FindFilter: trying another" );
2743     }
2744     while( true );
2745
2746     /* nothing found */
2747     msg_Warn( p_access, "FindFilter: No filter connected" );
2748     hr = E_FAIL;
2749     return hr;
2750 }
2751
2752 /*****************************************************************************
2753 * Connect is called from Build to enumerate and connect pins
2754 *****************************************************************************/
2755 HRESULT BDAGraph::Connect( IBaseFilter* p_upstream, IBaseFilter* p_downstream )
2756 {
2757     HRESULT             hr = E_FAIL;
2758     class localComPtr
2759     {
2760         public:
2761         IPin*      p_pin_upstream;
2762         IPin*      p_pin_downstream;
2763         IEnumPins* p_pin_upstream_enum;
2764         IEnumPins* p_pin_downstream_enum;
2765         IPin*      p_pin_temp;
2766         char*      psz_upstream;
2767         char*      psz_downstream;
2768
2769         localComPtr():
2770             p_pin_upstream(NULL), p_pin_downstream(NULL),
2771             p_pin_upstream_enum(NULL), p_pin_downstream_enum(NULL),
2772             p_pin_temp(NULL),
2773             psz_upstream( NULL ),
2774             psz_downstream( NULL )
2775             { };
2776         ~localComPtr()
2777         {
2778             if( p_pin_temp )
2779                 p_pin_temp->Release();
2780             if( p_pin_downstream )
2781                 p_pin_downstream->Release();
2782             if( p_pin_upstream )
2783                 p_pin_upstream->Release();
2784             if( p_pin_downstream_enum )
2785                 p_pin_downstream_enum->Release();
2786             if( p_pin_upstream_enum )
2787                 p_pin_upstream_enum->Release();
2788             if( psz_upstream )
2789                 delete[] psz_upstream;
2790             if( psz_downstream )
2791                 delete[] psz_downstream;
2792         }
2793     } l;
2794
2795     PIN_DIRECTION pin_dir;
2796
2797     //msg_Dbg( p_access, "Connect: entering" );
2798     hr = p_upstream->EnumPins( &l.p_pin_upstream_enum );
2799     if( FAILED( hr ) )
2800     {
2801         msg_Warn( p_access, "Connect: "\
2802             "Cannot get upstream filter enumerator: hr=0x%8lx", hr );
2803         return hr;
2804     }
2805
2806     do
2807     {
2808         /* Release l.p_pin_upstream before next iteration */
2809         if( l.p_pin_upstream )
2810             l.p_pin_upstream ->Release();
2811         l.p_pin_upstream = NULL;
2812         if( !l.p_pin_upstream_enum ) break;
2813         hr = l.p_pin_upstream_enum->Next( 1, &l.p_pin_upstream, 0 );
2814         if( hr != S_OK ) break;
2815
2816         //msg_Dbg( p_access, "Connect: get pin name");
2817         hr = GetPinName( l.p_pin_upstream, &l.psz_upstream );
2818         if( FAILED( hr ) )
2819         {
2820             msg_Warn( p_access, "Connect: "\
2821                 "Cannot GetPinName: hr=0x%8lx", hr );
2822             return hr;
2823         }
2824         //msg_Dbg( p_access, "Connect: p_pin_upstream = %s", l.psz_upstream );
2825
2826         hr = l.p_pin_upstream->QueryDirection( &pin_dir );
2827         if( FAILED( hr ) )
2828         {
2829             msg_Warn( p_access, "Connect: "\
2830                 "Cannot get upstream filter pin direction: hr=0x%8lx", hr );
2831             return hr;
2832         }
2833
2834         hr = l.p_pin_upstream->ConnectedTo( &l.p_pin_downstream );
2835         if( SUCCEEDED( hr ) )
2836         {
2837             l.p_pin_downstream->Release();
2838             l.p_pin_downstream = NULL;
2839         }
2840
2841         if( FAILED( hr ) && hr != VFW_E_NOT_CONNECTED )
2842         {
2843             msg_Warn( p_access, "Connect: "\
2844                 "Cannot check upstream filter connection: hr=0x%8lx", hr );
2845             return hr;
2846         }
2847
2848         if( ( pin_dir == PINDIR_OUTPUT ) && ( hr == VFW_E_NOT_CONNECTED ) )
2849         {
2850             /* The upstream pin is not yet connected so check each pin on the
2851              * downstream filter */
2852             //msg_Dbg( p_access, "Connect: enumerating downstream pins" );
2853             hr = p_downstream->EnumPins( &l.p_pin_downstream_enum );
2854             if( FAILED( hr ) )
2855             {
2856                 msg_Warn( p_access, "Connect: Cannot get "\
2857                     "downstream filter enumerator: hr=0x%8lx", hr );
2858                 return hr;
2859             }
2860
2861             do
2862             {
2863                 /* Release l.p_pin_downstream before next iteration */
2864                 if( l.p_pin_downstream )
2865                     l.p_pin_downstream ->Release();
2866                 l.p_pin_downstream = NULL;
2867                 if( !l.p_pin_downstream_enum ) break;
2868                 hr = l.p_pin_downstream_enum->Next( 1, &l.p_pin_downstream, 0 );
2869                 if( hr != S_OK ) break;
2870
2871                 //msg_Dbg( p_access, "Connect: get pin name");
2872                 hr = GetPinName( l.p_pin_downstream, &l.psz_downstream );
2873                 if( FAILED( hr ) )
2874                 {
2875                     msg_Warn( p_access, "Connect: "\
2876                         "Cannot GetPinName: hr=0x%8lx", hr );
2877                     return hr;
2878                 }
2879 /*
2880                 msg_Dbg( p_access, "Connect: Trying p_downstream = %s",
2881                     l.psz_downstream );
2882 */
2883
2884                 hr = l.p_pin_downstream->QueryDirection( &pin_dir );
2885                 if( FAILED( hr ) )
2886                 {
2887                     msg_Warn( p_access, "Connect: Cannot get "\
2888                         "downstream filter pin direction: hr=0x%8lx", hr );
2889                     return hr;
2890                 }
2891
2892                 /* Looking for a free Pin to connect to
2893                  * A connected Pin may have an reference count > 1
2894                  * so Release and nullify the pointer */
2895                 hr = l.p_pin_downstream->ConnectedTo( &l.p_pin_temp );
2896                 if( SUCCEEDED( hr ) )
2897                 {
2898                     l.p_pin_temp->Release();
2899                     l.p_pin_temp = NULL;
2900                 }
2901
2902                 if( hr != VFW_E_NOT_CONNECTED )
2903                 {
2904                     if( FAILED( hr ) )
2905                     {
2906                         msg_Warn( p_access, "Connect: Cannot check "\
2907                             "downstream filter connection: hr=0x%8lx", hr );
2908                         return hr;
2909                     }
2910                 }
2911
2912                 if( ( pin_dir == PINDIR_INPUT ) &&
2913                     ( hr == VFW_E_NOT_CONNECTED ) )
2914                 {
2915                     //msg_Dbg( p_access, "Connect: trying to connect pins" );
2916
2917                     hr = p_filter_graph->ConnectDirect( l.p_pin_upstream,
2918                         l.p_pin_downstream, NULL );
2919                     if( SUCCEEDED( hr ) )
2920                     {
2921                         /* If we arrive here then we have a matching pair of
2922                          * pins. */
2923                         return S_OK;
2924                     }
2925                 }
2926                 /* If we arrive here it means this downstream pin is not
2927                  * suitable so try the next downstream pin.
2928                  * l.p_pin_downstream is released at the top of the loop */
2929             }
2930             while( true );
2931             /* If we arrive here then we ran out of pins before we found a
2932              * suitable one. Release outstanding refcounts */
2933             if( l.p_pin_downstream_enum )
2934                 l.p_pin_downstream_enum->Release();
2935             l.p_pin_downstream_enum = NULL;
2936             if( l.p_pin_downstream )
2937                 l.p_pin_downstream->Release();
2938             l.p_pin_downstream = NULL;
2939         }
2940         /* If we arrive here it means this upstream pin is not suitable
2941          * so try the next upstream pin
2942          * l.p_pin_upstream is released at the top of the loop */
2943     }
2944     while( true );
2945     /* If we arrive here it means we did not find any pair of suitable pins
2946      * Outstanding refcounts are released in the destructor */
2947     //msg_Dbg( p_access, "Connect: No pins connected" );
2948     return E_FAIL;
2949 }
2950
2951 /*****************************************************************************
2952 * Start uses MediaControl to start the graph
2953 *****************************************************************************/
2954 HRESULT BDAGraph::Start()
2955 {
2956     HRESULT hr = S_OK;
2957     OAFilterState i_state; /* State_Stopped, State_Paused, State_Running */
2958
2959     msg_Dbg( p_access, "Start: entering" );
2960
2961     if( !p_media_control )
2962     {
2963         msg_Warn( p_access, "Start: Media Control has not been created" );
2964         return E_FAIL;
2965     }
2966
2967     msg_Dbg( p_access, "Start: Run()" );
2968     hr = p_media_control->Run();
2969     if( SUCCEEDED( hr ) )
2970     {
2971         msg_Dbg( p_access, "Start: Graph started, hr=0x%lx", hr );
2972         return S_OK;
2973     }
2974
2975     msg_Dbg( p_access, "Start: would not start, will retry" );
2976     /* Query the state of the graph - timeout after 100 milliseconds */
2977     while( (hr = p_media_control->GetState( 100, &i_state) ) != S_OK )
2978     {
2979         if( FAILED( hr ) )
2980         {
2981             msg_Warn( p_access,
2982                 "Start: Cannot get Graph state: hr=0x%8lx", hr );
2983             return hr;
2984         }
2985     }
2986
2987     msg_Dbg( p_access, "Start: got state" );
2988     if( i_state == State_Running )
2989     {
2990         msg_Dbg( p_access, "Graph started after a delay, hr=0x%lx", hr );
2991         return S_OK;
2992     }
2993
2994     /* The Graph is not running so stop it and return an error */
2995     msg_Warn( p_access, "Start: Graph not started: %d", (int)i_state );
2996     hr = p_media_control->StopWhenReady(); /* Instead of Stop() */
2997     if( FAILED( hr ) )
2998     {
2999         msg_Warn( p_access,
3000             "Start: Cannot stop Graph after Run failed: hr=0x%8lx", hr );
3001         return hr;
3002     }
3003
3004     return E_FAIL;
3005 }
3006
3007 /*****************************************************************************
3008 * Pop the stream of data
3009 *****************************************************************************/
3010 ssize_t BDAGraph::Pop(void *buf, size_t len)
3011 {
3012     return output.Pop(buf, len);
3013 }
3014
3015 /******************************************************************************
3016 * SampleCB - Callback when the Sample Grabber has a sample
3017 ******************************************************************************/
3018 STDMETHODIMP BDAGraph::SampleCB( double /*date*/, IMediaSample *p_sample )
3019 {
3020     if( p_sample->IsDiscontinuity() == S_OK )
3021         msg_Warn( p_access, "BDA SampleCB: Sample Discontinuity.");
3022
3023     const size_t i_sample_size = p_sample->GetActualDataLength();
3024
3025     /* The buffer memory is owned by the media sample object, and is automatically
3026      * released when the media sample is destroyed. The caller should not free or
3027      * reallocate the buffer. */
3028     BYTE *p_sample_data;
3029     p_sample->GetPointer( &p_sample_data );
3030
3031     if( i_sample_size > 0 && p_sample_data )
3032     {
3033         block_t *p_block = block_Alloc( i_sample_size );
3034
3035         if( p_block )
3036         {
3037             memcpy( p_block->p_buffer, p_sample_data, i_sample_size );
3038             output.Push( p_block );
3039         }
3040      }
3041      return S_OK;
3042 }
3043
3044 STDMETHODIMP BDAGraph::BufferCB( double /*date*/, BYTE* /*buffer*/,
3045                                  long /*buffer_len*/ )
3046 {
3047     return E_FAIL;
3048 }
3049
3050 /******************************************************************************
3051 * removes each filter from the graph
3052 ******************************************************************************/
3053 HRESULT BDAGraph::Destroy()
3054 {
3055     HRESULT hr = S_OK;
3056     ULONG mem_ref = 0;
3057 //    msg_Dbg( p_access, "Destroy: media control 1" );
3058     if( p_media_control )
3059         p_media_control->StopWhenReady(); /* Instead of Stop() */
3060
3061 //    msg_Dbg( p_access, "Destroy: deregistering graph" );
3062     if( d_graph_register )
3063         Deregister();
3064
3065 //    msg_Dbg( p_access, "Destroy: calling Empty" );
3066     output.Empty();
3067
3068 //    msg_Dbg( p_access, "Destroy: TIF" );
3069     if( p_transport_info )
3070     {
3071         /* Warning: RemoveFilter does an undocumented Release()
3072          * on pointer but does not set it to NULL */
3073         hr = p_filter_graph->RemoveFilter( p_transport_info );
3074         if( FAILED( hr ) )
3075         {
3076             msg_Dbg( p_access, "Destroy: "\
3077                 "Failed unloading TIF: hr=0x%8lx", hr );
3078         }
3079         p_transport_info = NULL;
3080     }
3081
3082 //    msg_Dbg( p_access, "Destroy: demux" );
3083     if( p_mpeg_demux )
3084     {
3085         p_filter_graph->RemoveFilter( p_mpeg_demux );
3086         if( FAILED( hr ) )
3087         {
3088             msg_Dbg( p_access, "Destroy: "\
3089                 "Failed unloading demux: hr=0x%8lx", hr );
3090         }
3091         p_mpeg_demux = NULL;
3092     }
3093
3094 //    msg_Dbg( p_access, "Destroy: sample grabber" );
3095     if( p_grabber )
3096     {
3097         mem_ref = p_grabber->Release();
3098         if( mem_ref != 0 )
3099         {
3100             msg_Dbg( p_access, "Destroy: "\
3101                 "Sample grabber mem_ref (varies): mem_ref=%ld", mem_ref );
3102         }
3103         p_grabber = NULL;
3104     }
3105
3106 //    msg_Dbg( p_access, "Destroy: sample grabber filter" );
3107     if( p_sample_grabber )
3108     {
3109         hr = p_filter_graph->RemoveFilter( p_sample_grabber );
3110         p_sample_grabber = NULL;
3111         if( FAILED( hr ) )
3112         {
3113             msg_Dbg( p_access, "Destroy: "\
3114                 "Failed unloading sampler: hr=0x%8lx", hr );
3115         }
3116     }
3117
3118 //    msg_Dbg( p_access, "Destroy: capture device" );
3119     if( p_capture_device )
3120     {
3121         p_filter_graph->RemoveFilter( p_capture_device );
3122         if( FAILED( hr ) )
3123         {
3124             msg_Dbg( p_access, "Destroy: "\
3125                 "Failed unloading capture device: hr=0x%8lx", hr );
3126         }
3127         p_capture_device = NULL;
3128     }
3129
3130 //    msg_Dbg( p_access, "Destroy: tuner device" );
3131     if( p_tuner_device )
3132     {
3133         //msg_Dbg( p_access, "Destroy: remove filter on tuner device" );
3134         hr = p_filter_graph->RemoveFilter( p_tuner_device );
3135         //msg_Dbg( p_access, "Destroy: force tuner device to NULL" );
3136         p_tuner_device = NULL;
3137         if( FAILED( hr ) )
3138         {
3139             msg_Dbg( p_access, "Destroy: "\
3140                 "Failed unloading tuner device: hr=0x%8lx", hr );
3141         }
3142     }
3143
3144 //    msg_Dbg( p_access, "Destroy: scanning tuner" );
3145     if( p_scanning_tuner )
3146     {
3147         mem_ref = p_scanning_tuner->Release();
3148         if( mem_ref != 0 )
3149         {
3150             msg_Dbg( p_access, "Destroy: "\
3151                 "Scanning tuner mem_ref (normally 2 if warm, "\
3152                 "3 if active): mem_ref=%ld", mem_ref );
3153         }
3154         p_scanning_tuner = NULL;
3155     }
3156
3157 //    msg_Dbg( p_access, "Destroy: net provider" );
3158     if( p_network_provider )
3159     {
3160         hr = p_filter_graph->RemoveFilter( p_network_provider );
3161         p_network_provider = NULL;
3162         if( FAILED( hr ) )
3163         {
3164             msg_Dbg( p_access, "Destroy: "\
3165                 "Failed unloading net provider: hr=0x%8lx", hr );
3166         }
3167     }
3168
3169 //    msg_Dbg( p_access, "Destroy: filter graph" );
3170     if( p_filter_graph )
3171     {
3172         mem_ref = p_filter_graph->Release();
3173         if( mem_ref != 0 )
3174         {
3175             msg_Dbg( p_access, "Destroy: "\
3176                 "Filter graph mem_ref (normally 1 if active): mem_ref=%ld",
3177                 mem_ref );
3178         }
3179         p_filter_graph = NULL;
3180     }
3181
3182     /* first call to FindFilter creates p_system_dev_enum */
3183
3184 //    msg_Dbg( p_access, "Destroy: system dev enum" );
3185     if( p_system_dev_enum )
3186     {
3187         mem_ref = p_system_dev_enum->Release();
3188         if( mem_ref != 0 )
3189         {
3190             msg_Dbg( p_access, "Destroy: "\
3191                 "System_dev_enum mem_ref: mem_ref=%ld", mem_ref );
3192         }
3193         p_system_dev_enum = NULL;
3194     }
3195
3196 //    msg_Dbg( p_access, "Destroy: media control 2" );
3197     if( p_media_control )
3198     {
3199         msg_Dbg( p_access, "Destroy: release media control" );
3200         mem_ref = p_media_control->Release();
3201         if( mem_ref != 0 )
3202         {
3203             msg_Dbg( p_access, "Destroy: "\
3204                 "Media control mem_ref: mem_ref=%ld", mem_ref );
3205         }
3206         msg_Dbg( p_access, "Destroy: force media control to NULL" );
3207         p_media_control = NULL;
3208     }
3209
3210     d_graph_register = 0;
3211     l_tuner_used = -1;
3212     guid_network_type = GUID_NULL;
3213
3214 //    msg_Dbg( p_access, "Destroy: returning" );
3215     return S_OK;
3216 }
3217
3218 /*****************************************************************************
3219 * Add/Remove a DirectShow filter graph to/from the Running Object Table.
3220 * Allows GraphEdit to "spy" on a remote filter graph.
3221 ******************************************************************************/
3222 HRESULT BDAGraph::Register()
3223 {
3224     class localComPtr
3225     {
3226         public:
3227         IMoniker*             p_moniker;
3228         IRunningObjectTable*  p_ro_table;
3229         localComPtr():
3230             p_moniker(NULL),
3231             p_ro_table(NULL)
3232         {};
3233         ~localComPtr()
3234         {
3235             if( p_moniker )
3236                 p_moniker->Release();
3237             if( p_ro_table )
3238                 p_ro_table->Release();
3239         }
3240     } l;
3241     WCHAR     pwsz_graph_name[128];
3242     HRESULT   hr;
3243
3244     hr = ::GetRunningObjectTable( 0, &l.p_ro_table );
3245     if( FAILED( hr ) )
3246     {
3247         msg_Warn( p_access, "Register: Cannot get ROT: hr=0x%8lx", hr );
3248         return hr;
3249     }
3250
3251     size_t len = sizeof(pwsz_graph_name) / sizeof(pwsz_graph_name[0]);
3252     _snwprintf( pwsz_graph_name, len - 1, L"VLC BDA Graph %08x Pid %08x",
3253         (DWORD_PTR) p_filter_graph, ::GetCurrentProcessId() );
3254     pwsz_graph_name[len-1] = 0;
3255     hr = CreateItemMoniker( L"!", pwsz_graph_name, &l.p_moniker );
3256     if( FAILED( hr ) )
3257     {
3258         msg_Warn( p_access, "Register: Cannot Create Moniker: hr=0x%8lx", hr );
3259         return hr;
3260     }
3261     hr = l.p_ro_table->Register( ROTFLAGS_REGISTRATIONKEEPSALIVE,
3262         p_filter_graph, l.p_moniker, &d_graph_register );
3263     if( FAILED( hr ) )
3264     {
3265         msg_Warn( p_access, "Register: Cannot Register Graph: hr=0x%8lx", hr );
3266         return hr;
3267     }
3268
3269     msg_Dbg( p_access, "Register: registered Graph: %S", pwsz_graph_name );
3270     return hr;
3271 }
3272
3273 void BDAGraph::Deregister()
3274 {
3275     HRESULT   hr;
3276     IRunningObjectTable* p_ro_table;
3277     hr = ::GetRunningObjectTable( 0, &p_ro_table );
3278     /* docs say this does a Release() on d_graph_register stuff */
3279     if( SUCCEEDED( hr ) )
3280         p_ro_table->Revoke( d_graph_register );
3281     d_graph_register = 0;
3282     p_ro_table->Release();
3283 }
3284
3285 HRESULT BDAGraph::GetFilterName( IBaseFilter* p_filter, char** psz_bstr_name )
3286 {
3287     FILTER_INFO filter_info;
3288     HRESULT     hr = S_OK;
3289
3290     hr = p_filter->QueryFilterInfo(&filter_info);
3291     if( FAILED( hr ) )
3292         return hr;
3293     int i_name_len = WideCharToMultiByte( CP_ACP, 0, filter_info.achName,
3294         -1, *psz_bstr_name, 0, NULL, NULL );
3295     *psz_bstr_name = new char[ i_name_len ];
3296     i_name_len = WideCharToMultiByte( CP_ACP, 0, filter_info.achName,
3297         -1, *psz_bstr_name, i_name_len, NULL, NULL );
3298
3299     // The FILTER_INFO structure holds a pointer to the Filter Graph
3300     // Manager, with a reference count that must be released.
3301     if( filter_info.pGraph )
3302         filter_info.pGraph->Release();
3303     return S_OK;
3304 }
3305
3306 HRESULT BDAGraph::GetPinName( IPin* p_pin, char** psz_bstr_name )
3307 {
3308     PIN_INFO    pin_info;
3309     HRESULT     hr = S_OK;
3310
3311     hr = p_pin->QueryPinInfo(&pin_info);
3312     if( FAILED( hr ) )
3313         return hr;
3314     int i_name_len = WideCharToMultiByte( CP_ACP, 0, pin_info.achName,
3315         -1, *psz_bstr_name, 0, NULL, NULL );
3316     *psz_bstr_name = new char[ i_name_len ];
3317     i_name_len = WideCharToMultiByte( CP_ACP, 0, pin_info.achName,
3318         -1, *psz_bstr_name, i_name_len, NULL, NULL );
3319
3320     // The PIN_INFO structure holds a pointer to the Filter,
3321     // with a referenppce count that must be released.
3322     if( pin_info.pFilter )
3323         pin_info.pFilter->Release();
3324     return S_OK;
3325 }
3326
3327 IPin* BDAGraph::FindPinOnFilter( IBaseFilter* pBaseFilter, const char* pPinName)
3328 {
3329     HRESULT hr;
3330     IEnumPins *pEnumPin = NULL;
3331     ULONG CountReceived = 0;
3332     IPin *pPin = NULL, *pThePin = NULL;
3333     char String[80];
3334     char* pString;
3335     PIN_INFO PinInfo;
3336     int length;
3337
3338     if (!pBaseFilter || !pPinName)
3339         return NULL;
3340
3341     // enumerate of pins on the filter
3342     hr = pBaseFilter->EnumPins(&pEnumPin);
3343     if (hr == S_OK && pEnumPin)
3344     {
3345         pEnumPin->Reset();
3346         while (pEnumPin->Next( 1, &pPin, &CountReceived) == S_OK && pPin)
3347         {
3348             memset(String, 0, sizeof(String));
3349
3350             hr = pPin->QueryPinInfo(&PinInfo);
3351             if (hr == S_OK)
3352             {
3353                 length = wcslen (PinInfo.achName) + 1;
3354                 pString = new char [length];
3355
3356                 // get the pin name
3357                 WideCharToMultiByte(CP_ACP, 0, PinInfo.achName, -1, pString, length,
3358                         NULL, NULL);
3359
3360                 //strcat (String, pString);
3361                 //StringCbCat(String,strlen(String) + strlen(pString)+1,pString);
3362                 snprintf( String, strlen(String) + strlen(pString) + 1, "%s%s", String, pString);
3363
3364                 // is there a match
3365                 if (strstr(String, pPinName))
3366                     pThePin = pPin;   // yes
3367                 else
3368                     pPin = NULL;      // no
3369
3370                 delete pString;
3371
3372             }
3373             else
3374             {
3375                 // need to release this pin
3376                 pPin->Release();
3377             }
3378
3379
3380         }        // end if have pin
3381
3382         // need to release the enumerator
3383         pEnumPin->Release();
3384     }
3385
3386     // return address of pin if found on the filter
3387     return pThePin;
3388
3389 }