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