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