]> git.sesse.net Git - vlc/blob - modules/access/bda/bdagraph.cpp
Fix choppy audio on live play - caching enabled properly
[vlc] / modules / access / bda / bdagraph.cpp
1 /*****************************************************************************
2  * bdagraph.cpp : DirectShow BDA graph for vlc
3  *****************************************************************************
4  * Copyright( C ) 2007 the VideoLAN team
5  *
6  * Author: Ken Self <kens@campoz.fslife.co.uk>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  *( at your option ) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #include "bdagraph.h"
27
28 /****************************************************************************
29  * Interfaces for calls from C
30  ****************************************************************************/
31 extern "C" {
32
33     void dvb_newBDAGraph( access_t* p_access )
34     {
35         p_access->p_sys->p_bda_module = new BDAGraph( p_access );
36     };
37
38     void dvb_deleteBDAGraph( access_t* p_access )
39     {
40         delete p_access->p_sys->p_bda_module;
41     };
42
43     int dvb_SubmitATSCTuneRequest( access_t* p_access )
44     {
45         return p_access->p_sys->p_bda_module->SubmitATSCTuneRequest();
46     };
47
48     int dvb_SubmitDVBTTuneRequest( access_t* p_access )
49     {
50         return p_access->p_sys->p_bda_module->SubmitDVBTTuneRequest();
51     };
52
53     int dvb_SubmitDVBCTuneRequest( access_t* p_access )
54     {
55         return p_access->p_sys->p_bda_module->SubmitDVBCTuneRequest();
56     };    
57
58     int dvb_SubmitDVBSTuneRequest( access_t* p_access )
59     {
60         return p_access->p_sys->p_bda_module->SubmitDVBSTuneRequest();
61     };
62
63     long dvb_GetBufferSize( access_t* p_access )
64     {
65         return p_access->p_sys->p_bda_module->GetBufferSize();
66     };
67
68     long dvb_ReadBuffer( access_t* p_access, long* l_buffer_len, BYTE* p_buff )
69     {
70         return p_access->p_sys->p_bda_module->ReadBuffer( l_buffer_len, 
71             p_buff );
72     };
73 };
74
75 /*****************************************************************************
76 * Constructor
77 *****************************************************************************/
78 BDAGraph::BDAGraph( access_t* p_this ):
79     p_access( p_this ),
80     guid_network_type(GUID_NULL),
81     l_tuner_used(-1),
82     d_graph_register( 0 )
83 {
84     b_ready = FALSE;
85     p_tuning_space = NULL;
86     p_tune_request = NULL;
87     p_media_control = NULL;
88     p_filter_graph = NULL;
89     p_system_dev_enum = NULL;
90     p_network_provider = p_tuner_device = p_capture_device = NULL;
91     p_sample_grabber = p_mpeg_demux = p_transport_info = NULL;
92     p_scanning_tuner = NULL;
93     p_grabber = NULL;
94
95     /* Initialize COM - MS says to use CoInitializeEx in preference to
96      * CoInitialize */
97     CoInitializeEx( 0, COINIT_APARTMENTTHREADED );
98 }
99
100 /*****************************************************************************
101 * Destructor
102 *****************************************************************************/
103 BDAGraph::~BDAGraph()
104 {
105     Destroy();
106     CoUninitialize();
107 }
108
109 /*****************************************************************************
110 * Submit an ATSC Tune Request
111 *****************************************************************************/
112 int BDAGraph::SubmitATSCTuneRequest()
113 {
114     HRESULT hr = S_OK;
115     class localComPtr
116     {
117         public:
118         IATSCChannelTuneRequest* p_atsc_tune_request;
119         IATSCLocator* p_atsc_locator;
120         localComPtr(): p_atsc_tune_request(NULL), p_atsc_locator(NULL) {};
121         ~localComPtr()
122         {
123             if( p_atsc_tune_request )
124                 p_atsc_tune_request->Release();
125             if( p_atsc_locator )
126                 p_atsc_locator->Release();
127         }
128     } l;
129     long l_major_channel, l_minor_channel, l_physical_channel;
130
131     l_major_channel = l_minor_channel = l_physical_channel = -1;
132 /*
133     l_major_channel = var_GetInteger( p_access, "dvb-major-channel" );
134     l_minor_channel = var_GetInteger( p_access, "dvb-minor-channel" );
135     l_physical_channel = var_GetInteger( p_access, "dvb-physical-channel" );
136 */
137
138     guid_network_type = CLSID_ATSCNetworkProvider;
139     hr = CreateTuneRequest();
140     if( FAILED( hr ) )
141     {
142         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
143             "Cannot create Tuning Space: hr=0x%8lx", hr );
144         return VLC_EGENERIC;
145     }
146
147     hr = p_tune_request->QueryInterface( IID_IATSCChannelTuneRequest,
148         (void**)&l.p_atsc_tune_request );
149     if( FAILED( hr ) )
150     {
151         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
152             "Cannot QI for IATSCChannelTuneRequest: hr=0x%8lx", hr );
153         return VLC_EGENERIC;
154     }
155     hr = ::CoCreateInstance( CLSID_ATSCLocator, 0, CLSCTX_INPROC,
156                              IID_IATSCLocator, (void**)&l.p_atsc_locator );
157     if( FAILED( hr ) )
158     {
159         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
160             "Cannot create the ATSC locator: hr=0x%8lx", hr );
161         return VLC_EGENERIC;
162     }
163
164     hr = S_OK;
165     if( l_major_channel > 0 )
166         hr = l.p_atsc_tune_request->put_Channel( l_major_channel );
167     if( SUCCEEDED( hr ) && l_minor_channel > 0 )
168         hr = l.p_atsc_tune_request->put_MinorChannel( l_minor_channel );
169     if( SUCCEEDED( hr ) && l_physical_channel > 0 )
170         hr = l.p_atsc_locator->put_PhysicalChannel( l_physical_channel );
171     if( FAILED( hr ) )
172     {
173         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
174             "Cannot set tuning parameters: hr=0x%8lx", hr );
175         return VLC_EGENERIC;
176     }
177
178     hr = p_tune_request->put_Locator( l.p_atsc_locator );
179     if( FAILED( hr ) )
180     {
181         msg_Warn( p_access, "SubmitATSCTuneRequest: "\
182             "Cannot put the locator: hr=0x%8lx", hr );
183         return VLC_EGENERIC;
184     }
185
186     /* Build and Run the Graph. If a Tuner device is in use the graph will
187      * fail to run. Repeated calls to build will check successive tuner
188      * devices */
189     do
190     {
191         hr = Build();
192         if( FAILED( hr ) )
193         {
194             msg_Warn( p_access, "SubmitATSCTuneRequest: "\
195                 "Cannot Build the Graph: hr=0x%8lx", hr );
196             return VLC_EGENERIC;
197         }
198         hr = Start();
199     }
200     while( hr != S_OK );
201
202     return VLC_SUCCESS;
203 }
204
205 /*****************************************************************************
206 * Submit a DVB-T Tune Request
207 ******************************************************************************/
208 int BDAGraph::SubmitDVBTTuneRequest()
209 {
210     HRESULT hr = S_OK;
211     class localComPtr
212     {
213         public:
214         IDVBTuneRequest* p_dvbt_tune_request;
215         IDVBTLocator* p_dvbt_locator;
216         localComPtr(): p_dvbt_tune_request(NULL), p_dvbt_locator(NULL) {};
217         ~localComPtr()
218         {
219             if( p_dvbt_tune_request )
220                 p_dvbt_tune_request->Release();
221             if( p_dvbt_locator )
222                 p_dvbt_locator->Release();
223         }
224     } l;
225     long l_frequency, l_bandwidth;
226
227     l_frequency = l_bandwidth = -1;
228     l_frequency = var_GetInteger( p_access, "dvb-frequency" );
229     l_bandwidth = var_GetInteger( p_access, "dvb-bandwidth" );
230
231     guid_network_type = CLSID_DVBTNetworkProvider;
232     hr = CreateTuneRequest();
233     if( FAILED( hr ) )
234     {
235         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
236             "Cannot create Tune Request: hr=0x%8lx", hr );
237         return VLC_EGENERIC;
238     }
239
240     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
241         (void**)&l.p_dvbt_tune_request );
242     if( FAILED( hr ) )
243     {
244         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
245             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
246         return VLC_EGENERIC;
247     }
248     l.p_dvbt_tune_request->put_ONID( -1 );
249     l.p_dvbt_tune_request->put_SID( -1 );
250     l.p_dvbt_tune_request->put_TSID( -1 );
251
252     hr = ::CoCreateInstance( CLSID_DVBTLocator, 0, CLSCTX_INPROC,
253         IID_IDVBTLocator, (void**)&l.p_dvbt_locator );
254     if( FAILED( hr ) )
255     {
256         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
257             "Cannot create the DVBT Locator: hr=0x%8lx", hr );
258         return VLC_EGENERIC;
259     }
260
261     hr = S_OK;
262     if( l_frequency > 0 )
263         hr = l.p_dvbt_locator->put_CarrierFrequency( l_frequency );
264     if( SUCCEEDED( hr ) && l_bandwidth > 0 )
265         hr = l.p_dvbt_locator->put_Bandwidth( l_bandwidth );
266     if( FAILED( hr ) )
267     {
268         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
269             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
270         return VLC_EGENERIC;
271     }
272
273     hr = p_tune_request->put_Locator( l.p_dvbt_locator );
274     if( FAILED( hr ) )
275     {
276         msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
277             "Cannot put the locator: hr=0x%8lx", hr );
278         return VLC_EGENERIC;
279     }
280
281     /* Build and Run the Graph. If a Tuner device is in use the graph will
282      * fail to run. Repeated calls to build will check successive tuner
283      * devices */
284     do
285     {
286         hr = Build();
287         if( FAILED( hr ) )
288         {
289             msg_Warn( p_access, "SubmitDVBTTuneRequest: "\
290                 "Cannot Build the Graph: hr=0x%8lx", hr );
291             return VLC_EGENERIC;
292         }
293         hr = Start();
294     }
295     while( hr != S_OK );
296
297     return VLC_SUCCESS;
298 }
299
300 /*****************************************************************************
301 * Submit a DVB-C Tune Request
302 ******************************************************************************/
303 int BDAGraph::SubmitDVBCTuneRequest()
304 {
305     HRESULT hr = S_OK;
306
307     class localComPtr
308     {
309         public:
310         IDVBTuneRequest* p_dvbc_tune_request;
311         IDVBCLocator* p_dvbc_locator;
312         localComPtr(): p_dvbc_tune_request(NULL), p_dvbc_locator(NULL) {};
313         ~localComPtr()
314         {
315             if( p_dvbc_tune_request )
316                 p_dvbc_tune_request->Release();
317             if( p_dvbc_locator )
318                 p_dvbc_locator->Release();
319         }
320     } l;
321
322     long l_frequency, l_symbolrate;
323     int  i_qam;
324     ModulationType i_qam_mod;
325
326     l_frequency = l_symbolrate = i_qam = -1;
327     l_frequency = var_GetInteger( p_access, "dvb-frequency" );
328     l_symbolrate = var_GetInteger( p_access, "dvb-srate" );
329     i_qam = var_GetInteger( p_access, "dvb-modulation" );
330     i_qam_mod = BDA_MOD_NOT_SET;
331     if( i_qam == 16 )
332         i_qam_mod = BDA_MOD_16QAM;
333     if( i_qam == 32 )
334         i_qam_mod = BDA_MOD_32QAM;
335     if( i_qam == 64 )
336         i_qam_mod = BDA_MOD_64QAM;
337     if( i_qam == 128 )
338         i_qam_mod = BDA_MOD_128QAM;
339     if( i_qam == 256 )
340         i_qam_mod = BDA_MOD_256QAM;
341
342     guid_network_type = CLSID_DVBCNetworkProvider;
343     hr = CreateTuneRequest();
344     if( FAILED( hr ) )
345     {
346         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
347             "Cannot create Tune Request: hr=0x%8lx", hr );
348         return VLC_EGENERIC;
349     }
350
351     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
352         (void**)&l.p_dvbc_tune_request );
353     if( FAILED( hr ) )
354     {
355         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
356             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
357         return VLC_EGENERIC;
358     }
359     l.p_dvbc_tune_request->put_ONID( -1 );
360     l.p_dvbc_tune_request->put_SID( -1 );
361     l.p_dvbc_tune_request->put_TSID( -1 );
362
363     hr = ::CoCreateInstance( CLSID_DVBCLocator, 0, CLSCTX_INPROC,
364         IID_IDVBCLocator, (void**)&l.p_dvbc_locator );
365     if( FAILED( hr ) )
366     {
367         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
368             "Cannot create the DVB-C Locator: hr=0x%8lx", hr );
369         return VLC_EGENERIC;
370     }
371
372     hr = S_OK;
373     if( l_frequency > 0 )
374         hr = l.p_dvbc_locator->put_CarrierFrequency( l_frequency );
375     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
376         hr = l.p_dvbc_locator->put_SymbolRate( l_symbolrate );
377     if( SUCCEEDED( hr ) && i_qam_mod != BDA_MOD_NOT_SET )
378         hr = l.p_dvbc_locator->put_Modulation( i_qam_mod );
379     if( FAILED( hr ) )
380     {
381         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
382             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
383         return VLC_EGENERIC;
384     }
385
386     hr = p_tune_request->put_Locator( l.p_dvbc_locator );
387     if( FAILED( hr ) )
388     {
389         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
390             "Cannot put the locator: hr=0x%8lx", hr );
391         return VLC_EGENERIC;
392     }
393
394     /* Build and Run the Graph. If a Tuner device is in use the graph will
395      * fail to run. Repeated calls to build will check successive tuner
396      * devices */
397     do
398     {
399         hr = Build();
400         if( FAILED( hr ) )
401         {
402             msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
403                 "Cannot Build the Graph: hr=0x%8lx", hr );
404             return VLC_EGENERIC;
405         }
406         hr = Start();
407     }
408     while( hr != S_OK );
409
410     return VLC_SUCCESS;
411 }
412
413 /*****************************************************************************
414 * Submit a DVB-S Tune Request
415 ******************************************************************************/
416 int BDAGraph::SubmitDVBSTuneRequest()
417 {
418     HRESULT hr = S_OK;
419
420     class localComPtr
421     {
422         public:
423         IDVBTuneRequest* p_dvbs_tune_request;
424         IDVBSLocator* p_dvbs_locator;
425         localComPtr(): p_dvbs_tune_request(NULL), p_dvbs_locator(NULL) {};
426         ~localComPtr()
427         {
428             if( p_dvbs_tune_request )
429                 p_dvbs_tune_request->Release();
430             if( p_dvbs_locator )
431                 p_dvbs_locator->Release();
432         }
433     } l;
434     long l_frequency, l_symbolrate, l_azimuth, l_elevation, l_longitude;
435     char* psz_polarisation;
436     Polarisation i_polar;
437     VARIANT_BOOL b_west;
438
439     l_frequency = l_symbolrate = l_azimuth = l_elevation = l_longitude = -1;
440     l_frequency = var_GetInteger( p_access, "dvb-frequency" );
441     l_symbolrate = var_GetInteger( p_access, "dvb-srate" );
442     l_azimuth = var_GetInteger( p_access, "dvb-azimuth" );
443     l_elevation = var_GetInteger( p_access, "dvb-elevation" );
444     l_longitude = var_GetInteger( p_access, "dvb-longitude" );
445     psz_polarisation = var_GetString( p_access, "dvb-polarisation" );
446
447     b_west = ( l_longitude < 0 ) ? TRUE : FALSE;
448
449     i_polar = BDA_POLARISATION_NOT_SET;
450     if( *psz_polarisation == 'H' || *psz_polarisation == 'h' )
451         i_polar = BDA_POLARISATION_LINEAR_H;
452     if( *psz_polarisation == 'V' || *psz_polarisation == 'v' )
453         i_polar = BDA_POLARISATION_LINEAR_V;
454     if( *psz_polarisation == 'L' || *psz_polarisation == 'l' )
455         i_polar = BDA_POLARISATION_CIRCULAR_L;
456     if( *psz_polarisation == 'R' || *psz_polarisation == 'r' )
457         i_polar = BDA_POLARISATION_CIRCULAR_R;
458
459     guid_network_type = CLSID_DVBSNetworkProvider;
460     hr = CreateTuneRequest();
461     if( FAILED( hr ) )
462     {
463         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
464             "Cannot create Tune Request: hr=0x%8lx", hr );
465         return VLC_EGENERIC;
466     }
467
468     hr = p_tune_request->QueryInterface( IID_IDVBTuneRequest,
469         (void**)&l.p_dvbs_tune_request );
470     if( FAILED( hr ) )
471     {
472         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
473             "Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
474         return VLC_EGENERIC;
475     }
476     l.p_dvbs_tune_request->put_ONID( -1 );
477     l.p_dvbs_tune_request->put_SID( -1 );
478     l.p_dvbs_tune_request->put_TSID( -1 );
479
480     hr = ::CoCreateInstance( CLSID_DVBSLocator, 0, CLSCTX_INPROC,
481         IID_IDVBSLocator, (void**)&l.p_dvbs_locator );
482     if( FAILED( hr ) )
483     {
484         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
485             "Cannot create the DVBS Locator: hr=0x%8lx", hr );
486         return VLC_EGENERIC;
487     }
488
489     hr = S_OK;
490     if( l_frequency > 0 )
491         hr = l.p_dvbs_locator->put_CarrierFrequency( l_frequency );
492     if( SUCCEEDED( hr ) && l_symbolrate > 0 )
493         hr = l.p_dvbs_locator->put_SymbolRate( l_symbolrate );
494     if( SUCCEEDED( hr ) && l_azimuth > 0 )
495         hr = l.p_dvbs_locator->put_Azimuth( l_azimuth );
496     if( SUCCEEDED( hr ) && l_elevation > 0 )
497         hr = l.p_dvbs_locator->put_Elevation( l_elevation );
498     if( SUCCEEDED( hr ) )
499         hr = l.p_dvbs_locator->put_OrbitalPosition( labs( l_longitude ) );
500     if( SUCCEEDED( hr ) )
501         hr = l.p_dvbs_locator->put_WestPosition( b_west );
502     if( SUCCEEDED( hr ) && i_polar != BDA_POLARISATION_NOT_SET )
503         hr = l.p_dvbs_locator->put_SignalPolarisation( i_polar );
504     if( FAILED( hr ) )
505     {
506         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
507             "Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
508         return VLC_EGENERIC;
509     }
510
511     hr = p_tune_request->put_Locator( l.p_dvbs_locator );
512     if( FAILED( hr ) )
513     {
514         msg_Warn( p_access, "SubmitDVBCTuneRequest: "\
515             "Cannot put the locator: hr=0x%8lx", hr );
516         return VLC_EGENERIC;
517     }
518
519     /* Build and Run the Graph. If a Tuner device is in use the graph will
520      * fail to run. Repeated calls to build will check successive tuner
521      * devices */
522     do
523     {
524         hr = Build();
525         if( FAILED( hr ) )
526         {
527             msg_Warn( p_access, "SubmitDVBSTuneRequest: "\
528                 "Cannot Build the Graph: hr=0x%8lx", hr );
529             return VLC_EGENERIC;
530         }
531         hr = Start();
532     }
533     while( hr != S_OK );
534
535     return VLC_SUCCESS;
536 }
537
538 /*****************************************************************************
539 * Load the Tuning Space from System Tuning Spaces according to the
540 * Network Type requested
541 ******************************************************************************/
542 HRESULT BDAGraph::CreateTuneRequest()
543 {
544     HRESULT hr = S_OK;
545     GUID guid_this_network_type;
546     class localComPtr
547     {
548         public:
549         ITuningSpaceContainer*  p_tuning_space_container;
550         IEnumTuningSpaces*      p_tuning_space_enum;
551         ITuningSpace*           p_this_tuning_space;
552         localComPtr(): p_tuning_space_container(NULL),
553             p_tuning_space_enum(NULL), p_this_tuning_space(NULL) {};
554         ~localComPtr()
555         {
556             if( p_tuning_space_container )
557                 p_tuning_space_container->Release();
558             if( p_tuning_space_enum )
559                 p_tuning_space_enum->Release();
560             if( p_this_tuning_space )
561                 p_this_tuning_space->Release();
562         }
563     } l;
564
565     /* A Tuning Space may already have been set up. If it is for the same
566      * network type then all is well. Otherwise, reset the Tuning Space and get
567      * a new one */
568     if( p_tuning_space )
569     {
570         hr = p_tuning_space->get__NetworkType( &guid_this_network_type );
571         if( FAILED( hr ) ) guid_this_network_type = GUID_NULL;
572         if( guid_this_network_type == guid_network_type )
573         {
574             return S_OK;
575         }
576         else
577         {
578             p_tuning_space->Release();
579             p_tuning_space = NULL;
580         }
581     }
582
583     /* Force use of the first available Tuner Device during Build */
584     l_tuner_used = -1;
585
586     /* Get the SystemTuningSpaces container to enumerate through all the
587      * defined tuning spaces */
588     hr = ::CoCreateInstance( CLSID_SystemTuningSpaces, 0, CLSCTX_INPROC,
589         IID_ITuningSpaceContainer, (void**)&l.p_tuning_space_container );
590     if( FAILED( hr ) )
591     {
592         msg_Warn( p_access, "CreateTuneRequest: "\
593             "Cannot CoCreate SystemTuningSpaces: hr=0x%8lx", hr );
594         return hr;
595     }
596
597     hr = l.p_tuning_space_container->get_EnumTuningSpaces(
598          &l.p_tuning_space_enum );
599     if( FAILED( hr ) )
600     {
601         msg_Warn( p_access, "CreateTuneRequest: "\
602             "Cannot create SystemTuningSpaces Enumerator: hr=0x%8lx", hr );
603         return hr;
604     }
605
606     while( l.p_tuning_space_enum->Next( 1, &l.p_this_tuning_space, NULL ) ==
607         S_OK )
608     {
609        hr = l.p_this_tuning_space->get__NetworkType( &guid_this_network_type );
610
611         /* GUID_NULL means a non-BDA network was found e.g analog
612          * Ignore failures and non-BDA networks and keep looking */
613         if( FAILED( hr ) ) guid_this_network_type == GUID_NULL;
614
615         if( guid_this_network_type == guid_network_type )
616         {
617             hr = l.p_this_tuning_space->QueryInterface( IID_ITuningSpace,
618                 (void**)&p_tuning_space );
619             if( FAILED( hr ) )
620             {
621                 msg_Warn( p_access, "CreateTuneRequest: "\
622                     "Cannot QI Tuning Space: hr=0x%8lx", hr );
623                 return hr;
624             }
625             hr = p_tuning_space->CreateTuneRequest( &p_tune_request );
626             if( FAILED( hr ) )
627             {
628                 msg_Warn( p_access, "CreateTuneRequest: "\
629                     "Cannot Create Tune Request: hr=0x%8lx", hr );
630                 return hr;
631             }
632             return hr;
633         }
634     }
635     hr = E_FAIL;
636     if( FAILED( hr ) )
637     {
638         msg_Warn( p_access, "CreateTuneRequest: "\
639             "Cannot find a suitable System Tuning Space: hr=0x%8lx", hr );
640         return hr;
641     }
642     return hr;
643 }
644
645 /******************************************************************************
646 * Build
647 * Step 4: Build the Filter Graph
648 * Build sets up devices, adds and connects filters
649 ******************************************************************************/
650 HRESULT BDAGraph::Build()
651 {
652     HRESULT hr = S_OK;
653     long l_capture_used, l_tif_used;
654     AM_MEDIA_TYPE grabber_media_type;
655
656     /* If we have already have a filter graph, rebuild it*/
657     Destroy();
658
659     hr = ::CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
660         IID_IGraphBuilder, (void**)&p_filter_graph );
661     if( FAILED( hr ) )
662     {
663         msg_Warn( p_access, "Build: "\
664             "Cannot CoCreate IFilterGraph: hr=0x%8lx", hr );
665         return hr;
666     }
667
668     /* First filter in the graph is the Network Provider and
669      * its Scanning Tuner which takes the Tune Request*/
670     hr = ::CoCreateInstance( guid_network_type, NULL, CLSCTX_INPROC_SERVER,
671         IID_IBaseFilter, (void**)&p_network_provider);
672     if( FAILED( hr ) )
673     {
674         msg_Warn( p_access, "Build: "\
675             "Cannot CoCreate Network Provider: hr=0x%8lx", hr );
676         return hr;
677     }
678     hr = p_filter_graph->AddFilter( p_network_provider, L"Network Provider" );
679     if( FAILED( hr ) )
680     {
681         msg_Warn( p_access, "Build: "\
682             "Cannot load network provider: hr=0x%8lx", hr );
683         return hr;
684     }
685
686     hr = p_network_provider->QueryInterface( IID_IScanningTuner,
687         (void**)&p_scanning_tuner );
688     if( FAILED( hr ) )
689     {
690         msg_Warn( p_access, "Build: "\
691             "Cannot QI Network Provider for Scanning Tuner: hr=0x%8lx", hr );
692         return hr;
693     }
694
695     hr = p_scanning_tuner->Validate( p_tune_request );
696     if( FAILED( hr ) )
697     {
698         msg_Warn( p_access, "Build: "\
699             "Tune Request is invalid: hr=0x%8lx", hr );
700         return hr;
701     }
702     hr = p_scanning_tuner->put_TuneRequest( p_tune_request );
703     if( FAILED( hr ) )
704     {
705         msg_Warn( p_access, "Build: "\
706             "Cannot submit the tune request: hr=0x%8lx", hr );
707         return hr;
708     }
709
710     /* Add the Network Tuner to the Network Provider. On subsequent calls,
711      * l_tuner_used will cause a different tuner to be selected */
712     hr = FindFilter( KSCATEGORY_BDA_NETWORK_TUNER, &l_tuner_used,
713         p_network_provider, &p_tuner_device );
714     if( FAILED( hr ) )
715     {
716         msg_Warn( p_access, "Build: "\
717             "Cannot load tuner device and connect network provider: "\
718             "hr=0x%8lx", hr );
719         return hr;
720     }
721
722     /* Always look for all capture devices to match the Network Tuner */
723     l_capture_used = -1;
724     hr = FindFilter( KSCATEGORY_BDA_RECEIVER_COMPONENT, &l_capture_used,
725         p_tuner_device, &p_capture_device );
726     if( FAILED( hr ) )
727     {
728         /* Some BDA drivers do not provide a Capture Device Filter so force
729          * the Sample Grabber to connect directly to the Tuner Device */
730         p_capture_device = p_tuner_device;
731         p_tuner_device = NULL;
732         msg_Warn( p_access, "Build: "\
733             "Cannot find Capture device. Connecting to tuner: hr=0x%8lx", hr );
734     }
735
736     /* Insert the Sample Grabber to tap into the Transport Stream. */
737     hr = ::CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
738         IID_IBaseFilter, (void**)&p_sample_grabber );
739     if( FAILED( hr ) )
740     {
741         msg_Warn( p_access, "Build: "\
742             "Cannot load Sample Grabber Filter: hr=0x%8lx", hr );
743         return hr;
744     }
745     hr = p_filter_graph->AddFilter( p_sample_grabber, L"Sample Grabber" );
746     if( FAILED( hr ) )
747     {
748         msg_Warn( p_access, "Build: "\
749             "Cannot add Sample Grabber Filter to graph: hr=0x%8lx", hr );
750         return hr;
751     }
752
753     hr = p_sample_grabber->QueryInterface( IID_ISampleGrabber,
754         (void**)&p_grabber );
755     if( FAILED( hr ) )
756     {
757         msg_Warn( p_access, "Build: "\
758             "Cannot QI Sample Grabber Filter: hr=0x%8lx", hr );
759         return hr;
760     }
761
762     ZeroMemory( &grabber_media_type, sizeof( AM_MEDIA_TYPE ) );
763     grabber_media_type.majortype == MEDIATYPE_Stream;
764     grabber_media_type.subtype == MEDIASUBTYPE_MPEG2_TRANSPORT;
765     hr = p_grabber->SetMediaType( &grabber_media_type );
766     if( FAILED( hr ) )
767     {
768         msg_Warn( p_access, "Build: "\
769             "Cannot set media type on grabber filter: hr=0x%8lx", hr );
770         return hr;
771     }
772     hr = Connect( p_capture_device, p_sample_grabber );
773     if( FAILED( hr ) )
774     {
775         msg_Warn( p_access, "Build: "\
776             "Cannot connect Sample Grabber to Capture device: hr=0x%8lx", hr );
777         return hr;
778     }
779
780     /* We need the MPEG2 Demultiplexer even though we are going to use the VLC
781      * TS demuxer. The TIF filter connects to the MPEG2 demux and works with
782      * the Network Provider filter to set up the stream */
783     hr = ::CoCreateInstance( CLSID_MPEG2Demultiplexer, NULL,
784         CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&p_mpeg_demux );
785     if( FAILED( hr ) )
786     {
787         msg_Warn( p_access, "Build: "\
788             "Cannot CoCreateInstance MPEG2 Demultiplexer: hr=0x%8lx", hr );
789         return hr;
790     }
791     hr = p_filter_graph->AddFilter( p_mpeg_demux, L"Demux" );
792     if( FAILED( hr ) )
793     {
794         msg_Warn( p_access, "Build: "\
795             "Cannot add demux filter to graph: hr=0x%8lx", hr );
796         return hr;
797     }
798
799     hr = Connect( p_sample_grabber, p_mpeg_demux );
800     if( FAILED( hr ) )
801     {
802         msg_Warn( p_access, "Build: "\
803             "Cannot connect demux to grabber: hr=0x%8lx", hr );
804         return hr;
805     }
806
807     /* Always look for the Transform Information Filter from the start
808      * of the collection*/
809     l_tif_used = -1;
810     hr = FindFilter( KSCATEGORY_BDA_TRANSPORT_INFORMATION, &l_tif_used,
811         p_mpeg_demux, &p_transport_info );
812     if( FAILED( hr ) )
813     {
814         msg_Warn( p_access, "Build: "\
815             "Cannot load TIF onto demux: hr=0x%8lx", hr );
816         return hr;
817     }
818
819     /* Configure the Sample Grabber to buffer the samples continuously */
820     hr = p_grabber->SetBufferSamples( TRUE );
821     if( FAILED( hr ) )
822     {
823         msg_Warn( p_access, "Build: "\
824             "Cannot set Sample Grabber to buffering: hr=0x%8lx", hr );
825         return hr;
826     }
827     hr = p_grabber->SetOneShot( FALSE );
828     if( FAILED( hr ) )
829     {
830         msg_Warn( p_access, "Build: "\
831             "Cannot set Sample Grabber to multi shot: hr=0x%8lx", hr );
832         return hr;
833     }
834     hr = p_grabber->SetCallback( this, 0 );
835     if( FAILED( hr ) )
836     {
837         msg_Warn( p_access, "Build: "\
838             "Cannot set SampleGrabber Callback: hr=0x%8lx", hr );
839         return hr;
840     }
841
842     hr = Register();
843     if( FAILED( hr ) )
844     {
845         d_graph_register = 0;
846     }
847
848     /* The Media Control is used to Run and Stop the Graph */
849     hr = p_filter_graph->QueryInterface( IID_IMediaControl,
850         (void**)&p_media_control );
851     if( FAILED( hr ) )
852     {
853         msg_Warn( p_access, "Build: "\
854             "Cannot QI Media Control: hr=0x%8lx", hr );
855         return hr;
856     }
857     return hr;
858 }
859
860 /******************************************************************************
861 * FindFilter
862 * Looks up all filters in a category and connects to the upstream filter until
863 * a successful match is found. The index of the connected filter is returned.
864 * On subsequent calls, this can be used to start from that point to find
865 * another match.
866 * This is used when the graph does not run because a tuner device is in use so
867 * another one needs to be slected.
868 ******************************************************************************/
869 HRESULT BDAGraph::FindFilter( REFCLSID clsid, long* i_moniker_used,
870     IBaseFilter* p_upstream, IBaseFilter** p_p_downstream )
871 {
872     HRESULT                 hr = S_OK;
873     int                     i_moniker_index = -1;
874     class localComPtr
875     {
876         public:
877         IMoniker*      p_moniker;
878         IEnumMoniker*  p_moniker_enum;
879         IBaseFilter*   p_filter;
880         IPropertyBag*  p_property_bag;
881         VARIANT        var_bstr;
882         localComPtr():
883             p_moniker(NULL),
884             p_moniker_enum(NULL),
885             p_filter(NULL),
886             p_property_bag(NULL)
887             { ::VariantInit(&var_bstr); };
888         ~localComPtr()
889         {
890             if( p_moniker )
891                 p_moniker->Release();
892             if( p_moniker_enum )
893                 p_moniker_enum->Release();
894             if( p_filter )
895                 p_filter->Release();
896             if( p_property_bag )
897                 p_property_bag->Release();
898             ::VariantClear(&var_bstr);
899         }
900     } l;
901
902     if( !p_system_dev_enum )
903     {
904         hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
905             IID_ICreateDevEnum, (void**)&p_system_dev_enum );
906         if( FAILED( hr ) )
907         {
908             msg_Warn( p_access, "FindFilter: "\
909                 "Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
910             return hr;
911         }
912     }
913
914     hr = p_system_dev_enum->CreateClassEnumerator( clsid,
915         &l.p_moniker_enum, 0 );
916     if( hr != S_OK )
917     {
918         msg_Warn( p_access, "FindFilter: "\
919             "Cannot CreateClassEnumerator: hr=0x%8lx", hr );
920         return E_FAIL;
921     }
922     while( l.p_moniker_enum->Next( 1, &l.p_moniker, 0 ) == S_OK )
923     {
924         i_moniker_index++;
925
926         /* Skip over devices already found on previous calls */
927         if( i_moniker_index <= *i_moniker_used ) continue;
928         *i_moniker_used = i_moniker_index;
929
930         hr = l.p_moniker->BindToObject( NULL, NULL, IID_IBaseFilter,
931             (void**)&l.p_filter );
932         if( FAILED( hr ) )
933         {
934             if( l.p_moniker )
935                 l.p_moniker->Release();
936             l.p_moniker = NULL;
937             if( l.p_filter)
938                  l.p_filter->Release();
939             l.p_filter = NULL;
940             continue;
941         }
942
943         hr = l.p_moniker->BindToStorage( NULL, NULL, IID_IPropertyBag,
944             (void**)&l.p_property_bag );
945         if( FAILED( hr ) )
946         {
947             msg_Warn( p_access, "FindFilter: "\
948                 "Cannot Bind to Property Bag: hr=0x%8lx", hr );
949             return hr;
950         }
951
952         hr = l.p_property_bag->Read( L"FriendlyName", &l.var_bstr, NULL );
953         if( FAILED( hr ) )
954         {
955             msg_Warn( p_access, "FindFilter: "\
956                 "Cannot read filter friendly name: hr=0x%8lx", hr );
957             return hr;
958         }
959
960         hr = p_filter_graph->AddFilter( l.p_filter, l.var_bstr.bstrVal );
961         if( FAILED( hr ) )
962         {
963             msg_Warn( p_access, "FindFilter: "\
964                 "Cannot add filter: hr=0x%8lx", hr );
965             return hr;
966         }
967
968         hr = Connect( p_upstream, l.p_filter );
969         if( SUCCEEDED( hr ) )
970         {
971             msg_Dbg( p_access, "FindFilter: Connected %S", l.var_bstr.bstrVal );
972             l.p_filter->QueryInterface( IID_IBaseFilter,
973                 (void**)p_p_downstream );
974             return S_OK;
975         }
976         /* Not the filter we want so unload and try the next one */
977         hr = p_filter_graph->RemoveFilter( l.p_filter );
978         if( FAILED( hr ) )
979         {
980             msg_Warn( p_access, "FindFilter: "\
981                 "Failed unloading Filter: hr=0x%8lx", hr );
982             return hr;
983         }
984
985         if( l.p_moniker )
986             l.p_moniker->Release();
987         l.p_moniker = NULL;
988         if( l.p_filter)
989             l.p_filter->Release();
990         l.p_filter = NULL;
991     }
992
993     hr = E_FAIL;
994     msg_Warn( p_access, "FindFilter: No filter connected: hr=0x%8lx", hr );
995     return hr;
996 }
997
998 /*****************************************************************************
999 * Connect is called from Build to enumerate and connect pins
1000 *****************************************************************************/
1001 HRESULT BDAGraph::Connect( IBaseFilter* p_upstream, IBaseFilter* p_downstream )
1002 {
1003     HRESULT             hr = E_FAIL;
1004     class localComPtr
1005     {
1006         public:
1007         IPin*      p_pin_upstream;
1008         IPin*      p_pin_downstream;
1009         IEnumPins* p_pin_upstream_enum;
1010         IEnumPins* p_pin_downstream_enum;
1011         IPin*      p_pin_temp;
1012         localComPtr(): p_pin_upstream(NULL), p_pin_downstream(NULL),
1013             p_pin_upstream_enum(NULL), p_pin_downstream_enum(NULL),
1014             p_pin_temp(NULL) { };
1015         ~localComPtr()
1016         {
1017             if( p_pin_upstream )
1018                 p_pin_upstream->Release();
1019             if( p_pin_downstream )
1020                 p_pin_downstream->Release();
1021             if( p_pin_upstream_enum )
1022                 p_pin_upstream_enum->Release();
1023             if( p_pin_downstream_enum )
1024                 p_pin_downstream_enum->Release();
1025             if( p_pin_temp )
1026                 p_pin_temp->Release();
1027         }
1028     } l;
1029
1030     PIN_INFO            pin_info_upstream;
1031     PIN_INFO            pin_info_downstream;
1032
1033     hr = p_upstream->EnumPins( &l.p_pin_upstream_enum );
1034     if( FAILED( hr ) )
1035     {
1036         msg_Warn( p_access, "Connect: "\
1037             "Cannot get upstream filter enumerator: hr=0x%8lx", hr );
1038         return hr;
1039     }
1040     while( l.p_pin_upstream_enum->Next( 1, &l.p_pin_upstream, 0 ) == S_OK )
1041     {
1042         hr = l.p_pin_upstream->QueryPinInfo( &pin_info_upstream );
1043         if( FAILED( hr ) )
1044         {
1045             msg_Warn( p_access, "Connect: "\
1046                 "Cannot get upstream filter pin information: hr=0x%8lx", hr );
1047             return hr;
1048         }
1049         hr = l.p_pin_upstream->ConnectedTo( &l.p_pin_downstream );
1050         if( hr == S_OK )
1051             l.p_pin_downstream->Release();
1052         if(FAILED( hr ) && hr != VFW_E_NOT_CONNECTED )
1053         {
1054             msg_Warn( p_access, "Connect: "\
1055                 "Cannot check upstream filter connection: hr=0x%8lx", hr );
1056             return hr;
1057         }
1058         if(( pin_info_upstream.dir == PINDIR_OUTPUT ) &&
1059            ( hr == VFW_E_NOT_CONNECTED ) )
1060         {
1061             /* The upstream pin is not yet connected so check each pin on the
1062              * downstream filter */
1063             hr = p_downstream->EnumPins( &l.p_pin_downstream_enum );
1064             if( FAILED( hr ) )
1065             {
1066                 msg_Warn( p_access, "Connect: Cannot get "\
1067                     "downstream filter enumerator: hr=0x%8lx", hr );
1068                 return hr;
1069             }
1070             while( l.p_pin_downstream_enum->Next( 1, &l.p_pin_downstream, 0 )
1071                 == S_OK )
1072             {
1073                 hr = l.p_pin_downstream->QueryPinInfo( &pin_info_downstream );
1074                 if( FAILED( hr ) )
1075                 {
1076                     msg_Warn( p_access, "Connect: Cannot get "\
1077                         "downstream filter pin information: hr=0x%8lx", hr );
1078                     return hr;
1079                 }
1080
1081                 hr = l.p_pin_downstream->ConnectedTo( &l.p_pin_temp );
1082                 if( hr == S_OK ) l.p_pin_temp->Release();
1083                 if( hr != VFW_E_NOT_CONNECTED )
1084                 {
1085                     if( FAILED( hr ) )
1086                     {
1087                         msg_Warn( p_access, "Connect: Cannot check "\
1088                             "downstream filter connection: hr=0x%8lx", hr );
1089                         return hr;
1090                     }
1091                 }
1092                 if(( pin_info_downstream.dir == PINDIR_INPUT ) &&
1093                    ( hr == VFW_E_NOT_CONNECTED ) )
1094                 {
1095                     hr = p_filter_graph->ConnectDirect( l.p_pin_upstream,
1096                         l.p_pin_downstream, NULL );
1097                     if( SUCCEEDED( hr ) )
1098                     {
1099                         pin_info_downstream.pFilter->Release();
1100                         pin_info_upstream.pFilter->Release();
1101                         return S_OK;
1102                     }
1103                 }
1104                 /* If we fall out here it means this downstream pin was not
1105                  * suitable so try the next downstream pin */
1106                 l.p_pin_downstream = NULL;
1107                 pin_info_downstream.pFilter->Release();
1108             }
1109         }
1110
1111         /* If we fall out here it means we did not find any suitable downstream
1112          * pin so try the next upstream pin */
1113         l.p_pin_upstream = NULL;
1114         pin_info_upstream.pFilter->Release();
1115     }
1116
1117     /* If we fall out here it means we did not find any pair of suitable pins */
1118     return E_FAIL;
1119 }
1120
1121 /*****************************************************************************
1122 * Start uses MediaControl to start the graph
1123 *****************************************************************************/
1124 HRESULT BDAGraph::Start()
1125 {
1126     HRESULT hr = S_OK;
1127     OAFilterState i_state; /* State_Stopped, State_Paused, State_Running */
1128
1129     if( !p_media_control )
1130     {
1131         msg_Dbg( p_access, "Start: Media Control has not been created" );
1132         return E_FAIL;
1133     }
1134     hr = p_media_control->Run();
1135     if( hr == S_OK )
1136         return hr;
1137
1138     /* Query the state of the graph - timeout after 100 milliseconds */
1139     while( hr = p_media_control->GetState( 100, &i_state ) != S_OK )
1140     {
1141         if( FAILED( hr ) )
1142         {
1143             msg_Warn( p_access,
1144                 "Start: Cannot get Graph state: hr=0x%8lx", hr );
1145             return hr;
1146         }
1147     }
1148     if( i_state == State_Running )
1149         return hr;
1150
1151     /* The Graph is not running so stop it and return an error */
1152     hr = p_media_control->Stop();
1153     if( FAILED( hr ) )
1154     {
1155         msg_Warn( p_access,
1156             "Start: Cannot stop Graph after Run failed: hr=0x%8lx", hr );
1157         return hr;
1158     }
1159     return E_FAIL;
1160 }
1161
1162 /*****************************************************************************
1163 * Read the stream of data - query the buffer size required
1164 *****************************************************************************/
1165 long BDAGraph::GetBufferSize()
1166 {
1167     long l_buffer_size = 0;
1168     long l_queue_size;
1169
1170     b_ready = true;
1171
1172     for( int i_timer = 0; queue_sample.empty() && i_timer < 200; i_timer++ )
1173         Sleep( 10 );
1174
1175     l_queue_size = queue_sample.size();
1176     if( l_queue_size <= 0 )
1177     {
1178         msg_Warn( p_access, "BDA GetBufferSize: Timed Out waiting for sample" );
1179         return -1;
1180     }
1181
1182     /* Establish the length of the queue as it grows quickly. If the queue
1183      * size is checked dynamically there is a risk of not exiting the loop */
1184     for( long l_queue_count=0; l_queue_count < l_queue_size; l_queue_count++ )
1185     {
1186         l_buffer_size += queue_sample.front()->GetActualDataLength();
1187         queue_buffer.push( queue_sample.front() );
1188         queue_sample.pop();
1189     }
1190     return l_buffer_size;
1191 }
1192
1193 /*****************************************************************************
1194 * Read the stream of data - Retrieve from the buffer queue
1195 ******************************************************************************/
1196 long BDAGraph::ReadBuffer( long* pl_buffer_len, BYTE* p_buffer )
1197 {
1198     HRESULT hr = S_OK;
1199
1200     *pl_buffer_len = 0;
1201     BYTE *p_buff_temp;
1202
1203     while( !queue_buffer.empty() )
1204     {
1205         queue_buffer.front()->GetPointer( &p_buff_temp );
1206         hr = queue_buffer.front()->IsDiscontinuity();
1207         if( hr == S_OK )
1208             msg_Warn( p_access,
1209                 "BDA ReadBuffer: Sample Discontinuity. 0x%8lx", hr );
1210         memcpy( p_buffer + *pl_buffer_len, p_buff_temp,
1211             queue_buffer.front()->GetActualDataLength() );
1212         *pl_buffer_len += queue_buffer.front()->GetActualDataLength();
1213         queue_buffer.front()->Release();
1214         queue_buffer.pop();
1215     }
1216
1217     return *pl_buffer_len;
1218 }
1219
1220 /******************************************************************************
1221 * SampleCB - Callback when the Sample Grabber has a sample
1222 ******************************************************************************/
1223 STDMETHODIMP BDAGraph::SampleCB( double d_time, IMediaSample *p_sample )
1224 {
1225     if( b_ready )
1226     {
1227         p_sample->AddRef();
1228         queue_sample.push( p_sample );
1229     }
1230     else
1231     {
1232         msg_Warn( p_access, "BDA SampleCB: Not ready - dropped sample" );
1233     }
1234     return S_OK;
1235 }
1236
1237 STDMETHODIMP BDAGraph::BufferCB( double d_time, BYTE* p_buffer,
1238     long l_buffer_len )
1239 {
1240     return E_FAIL;
1241 }
1242
1243 /******************************************************************************
1244 * removes each filter from the graph
1245 ******************************************************************************/
1246 HRESULT BDAGraph::Destroy()
1247 {
1248     HRESULT hr = S_OK;
1249
1250     if( p_media_control )
1251         hr = p_media_control->Stop();
1252
1253     if( p_transport_info )
1254     {
1255         p_filter_graph->RemoveFilter( p_transport_info );
1256         p_transport_info->Release();
1257         p_transport_info = NULL;
1258     }
1259     if( p_mpeg_demux )
1260     {
1261         p_filter_graph->RemoveFilter( p_mpeg_demux );
1262         p_mpeg_demux->Release();
1263         p_mpeg_demux = NULL;
1264     }
1265     if( p_sample_grabber )
1266     {
1267         p_filter_graph->RemoveFilter( p_sample_grabber );
1268         p_sample_grabber->Release();
1269         p_sample_grabber = NULL;
1270     }
1271     if( p_capture_device )
1272     {
1273         p_filter_graph->RemoveFilter( p_capture_device );
1274         p_capture_device->Release();
1275         p_capture_device = NULL;
1276     }
1277     if( p_tuner_device )
1278     {
1279         p_filter_graph->RemoveFilter( p_tuner_device );
1280         p_tuner_device->Release();
1281         p_tuner_device = NULL;
1282     }
1283     if( p_network_provider )
1284     {
1285         p_filter_graph->RemoveFilter( p_network_provider );
1286         p_network_provider->Release();
1287         p_network_provider = NULL;
1288     }
1289
1290     if( p_scanning_tuner )
1291     {
1292         p_scanning_tuner->Release();
1293         p_scanning_tuner = NULL;
1294     }
1295     if( p_media_control )
1296     {
1297         p_media_control->Release();
1298         p_media_control = NULL;
1299     }
1300     if( p_scanning_tuner )
1301     {
1302         p_filter_graph->Release();
1303         p_filter_graph = NULL;
1304     }
1305
1306     if( d_graph_register )
1307     {
1308         Deregister();
1309     }
1310
1311     return S_OK;
1312 }
1313
1314 /*****************************************************************************
1315 * Add/Remove a DirectShow filter graph to/from the Running Object Table.
1316 * Allows GraphEdit to "spy" on a remote filter graph.
1317 ******************************************************************************/
1318 HRESULT BDAGraph::Register()
1319 {
1320     class localComPtr
1321     {
1322         public:
1323         IMoniker*             p_moniker;
1324         IRunningObjectTable*  p_ro_table;
1325         localComPtr(): p_moniker(NULL), p_ro_table(NULL) {};
1326         ~localComPtr()
1327         {
1328             if( p_moniker )
1329                 p_moniker->Release();
1330             if( p_ro_table )
1331                 p_ro_table->Release();
1332         }
1333     } l;
1334     WCHAR     psz_w_graph_name[128];
1335     HRESULT   hr;
1336
1337     hr = ::GetRunningObjectTable( 0, &l.p_ro_table );
1338     if( FAILED( hr ) )
1339     {
1340         msg_Warn( p_access, "Register: Cannot get ROT: hr=0x%8lx", hr );
1341         return hr;
1342     }
1343
1344     wsprintfW( psz_w_graph_name, L"VLC BDA Graph %08x Pid %08x",
1345         (DWORD_PTR) p_filter_graph, ::GetCurrentProcessId() );
1346     hr = CreateItemMoniker( L"!", psz_w_graph_name, &l.p_moniker );
1347     if( FAILED( hr ) )
1348     {
1349         msg_Warn( p_access, "Register: Cannot Create Moniker: hr=0x%8lx", hr );
1350         return hr;
1351     }
1352     hr = l.p_ro_table->Register( ROTFLAGS_REGISTRATIONKEEPSALIVE,
1353         p_filter_graph, l.p_moniker, &d_graph_register );
1354     if( FAILED( hr ) )
1355     {
1356         msg_Warn( p_access, "Register: Cannot Register Graph: hr=0x%8lx", hr );
1357         return hr;
1358     }
1359     msg_Dbg( p_access, "Register: registered Graph: %S", psz_w_graph_name );
1360     return hr;
1361 }
1362
1363 void BDAGraph::Deregister()
1364 {
1365     HRESULT   hr;
1366     IRunningObjectTable* p_ro_table;
1367     hr = ::GetRunningObjectTable( 0, &p_ro_table );
1368     if( SUCCEEDED( hr ) )
1369         p_ro_table->Revoke( d_graph_register );
1370     d_graph_register = 0;
1371     p_ro_table->Release();
1372 }