You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3401 lines
109 KiB
3401 lines
109 KiB
/*****************************************************************************
|
|
* bdagraph.cpp : DirectShow BDA graph for vlc
|
|
*****************************************************************************
|
|
* Copyright (C) 2007 the VideoLAN team
|
|
* Copyright (C) 2011 Rémi Denis-Courmont
|
|
* Copyright (C) 2012 John Freed
|
|
*
|
|
* Author: Ken Self <kenself(at)optusnet(dot)com(dot)au>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Preamble
|
|
*****************************************************************************/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <cassert>
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_block.h>
|
|
|
|
#include <objbase.h>
|
|
#include <initguid.h>
|
|
#include "bdagraph.hpp"
|
|
#include "dtv.h"
|
|
#undef DEBUG_MONIKER_NAME
|
|
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
static ModulationType dvb_parse_modulation (const char *mod)
|
|
{
|
|
if (!strcmp (mod, "16QAM")) return BDA_MOD_16QAM;
|
|
if (!strcmp (mod, "32QAM")) return BDA_MOD_32QAM;
|
|
if (!strcmp (mod, "64QAM")) return BDA_MOD_64QAM;
|
|
if (!strcmp (mod, "128QAM")) return BDA_MOD_128QAM;
|
|
if (!strcmp (mod, "256QAM")) return BDA_MOD_256QAM;
|
|
return BDA_MOD_NOT_SET;
|
|
}
|
|
|
|
static BinaryConvolutionCodeRate dvb_parse_fec (uint32_t fec)
|
|
{
|
|
switch (fec)
|
|
{
|
|
case VLC_FEC(1,2): return BDA_BCC_RATE_1_2;
|
|
case VLC_FEC(2,3): return BDA_BCC_RATE_2_3;
|
|
case VLC_FEC(3,4): return BDA_BCC_RATE_3_4;
|
|
case VLC_FEC(5,6): return BDA_BCC_RATE_5_6;
|
|
case VLC_FEC(7,8): return BDA_BCC_RATE_7_8;
|
|
}
|
|
return BDA_BCC_RATE_NOT_SET;
|
|
}
|
|
|
|
static GuardInterval dvb_parse_guard (uint32_t guard)
|
|
{
|
|
switch (guard)
|
|
{
|
|
case VLC_GUARD(1, 4): return BDA_GUARD_1_4;
|
|
case VLC_GUARD(1, 8): return BDA_GUARD_1_8;
|
|
case VLC_GUARD(1,16): return BDA_GUARD_1_16;
|
|
case VLC_GUARD(1,32): return BDA_GUARD_1_32;
|
|
}
|
|
return BDA_GUARD_NOT_SET;
|
|
}
|
|
|
|
static TransmissionMode dvb_parse_transmission (int transmit)
|
|
{
|
|
switch (transmit)
|
|
{
|
|
case 2: return BDA_XMIT_MODE_2K;
|
|
case 8: return BDA_XMIT_MODE_8K;
|
|
}
|
|
return BDA_XMIT_MODE_NOT_SET;
|
|
}
|
|
|
|
static HierarchyAlpha dvb_parse_hierarchy (int hierarchy)
|
|
{
|
|
switch (hierarchy)
|
|
{
|
|
case 1: return BDA_HALPHA_1;
|
|
case 2: return BDA_HALPHA_2;
|
|
case 4: return BDA_HALPHA_4;
|
|
}
|
|
return BDA_HALPHA_NOT_SET;
|
|
}
|
|
|
|
static Polarisation dvb_parse_polarization (char pol)
|
|
{
|
|
switch (pol)
|
|
{
|
|
case 'H': return BDA_POLARISATION_LINEAR_H;
|
|
case 'V': return BDA_POLARISATION_LINEAR_V;
|
|
case 'L': return BDA_POLARISATION_CIRCULAR_L;
|
|
case 'R': return BDA_POLARISATION_CIRCULAR_R;
|
|
}
|
|
return BDA_POLARISATION_NOT_SET;
|
|
}
|
|
|
|
static SpectralInversion dvb_parse_inversion (int inversion)
|
|
{
|
|
switch (inversion)
|
|
{
|
|
case 0: return BDA_SPECTRAL_INVERSION_NORMAL;
|
|
case 1: return BDA_SPECTRAL_INVERSION_INVERTED;
|
|
case -1: return BDA_SPECTRAL_INVERSION_AUTOMATIC;
|
|
}
|
|
/* should never happen */
|
|
return BDA_SPECTRAL_INVERSION_NOT_SET;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Interfaces for calls from C
|
|
****************************************************************************/
|
|
struct dvb_device
|
|
{
|
|
BDAGraph *module;
|
|
|
|
/* DVB-S property cache */
|
|
uint32_t frequency;
|
|
uint32_t srate;
|
|
uint32_t fec;
|
|
char inversion;
|
|
char pol;
|
|
uint32_t lowf, highf, switchf;
|
|
};
|
|
|
|
dvb_device_t *dvb_open (vlc_object_t *obj)
|
|
{
|
|
dvb_device_t *d = new dvb_device_t;
|
|
|
|
d->module = new BDAGraph (obj);
|
|
d->frequency = 0;
|
|
d->srate = 0;
|
|
d->fec = VLC_FEC_AUTO;
|
|
d->inversion = -1;
|
|
d->pol = 0;
|
|
d->lowf = d->highf = d->switchf = 0;
|
|
return d;
|
|
}
|
|
|
|
void dvb_close (dvb_device_t *d)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
delete d->module;
|
|
delete d;
|
|
}
|
|
|
|
ssize_t dvb_read (dvb_device_t *d, void *buf, size_t len, int ms)
|
|
{
|
|
return d->module->Pop(buf, len, ms);
|
|
}
|
|
|
|
int dvb_add_pid (dvb_device_t *, uint16_t)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void dvb_remove_pid (dvb_device_t *, uint16_t)
|
|
{
|
|
}
|
|
|
|
bool dvb_get_pid_state (const dvb_device_t *, uint16_t)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
unsigned dvb_enum_systems (dvb_device_t *d)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->EnumSystems( );
|
|
}
|
|
|
|
float dvb_get_signal_strength (dvb_device_t *d)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->GetSignalStrength( );
|
|
}
|
|
|
|
float dvb_get_snr (dvb_device_t *d)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->GetSignalNoiseRatio( );
|
|
}
|
|
|
|
int dvb_set_inversion (dvb_device_t *d, int inversion)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
d->inversion = inversion;
|
|
return d->module->SetInversion( d->inversion );
|
|
}
|
|
|
|
int dvb_tune (dvb_device_t *d)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->SubmitTuneRequest ();
|
|
}
|
|
|
|
int dvb_fill_device_caps( dvb_device_t *, dvb_device_caps_t * )
|
|
{
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool dvb_set_ca_pmt (dvb_device_t *, en50221_capmt_info_t *)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* DVB-C */
|
|
int dvb_set_dvbc (dvb_device_t *d, uint32_t freq, const char *mod,
|
|
uint32_t srate, uint32_t /*fec*/)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->SetDVBC (freq / 1000, mod, srate);
|
|
}
|
|
|
|
/* DVB-S */
|
|
int dvb_set_dvbs (dvb_device_t *d, uint64_t freq, uint32_t srate, uint32_t fec)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
d->frequency = freq / 1000;
|
|
d->srate = srate;
|
|
d->fec = fec;
|
|
return d->module->SetDVBS(d->frequency, d->srate, d->fec, d->inversion,
|
|
d->pol, d->lowf, d->highf, d->switchf);
|
|
}
|
|
|
|
int dvb_set_dvbs2 (dvb_device_t *, uint64_t /*freq*/, const char * /*mod*/,
|
|
uint32_t /*srate*/, uint32_t /*fec*/, int /*pilot*/, int /*rolloff*/,
|
|
uint8_t /*sid*/)
|
|
{
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
int dvb_set_sec (dvb_device_t *d, uint64_t freq, char pol,
|
|
uint32_t lowf, uint32_t highf, uint32_t switchf)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
d->frequency = freq / 1000;
|
|
d->pol = pol;
|
|
d->lowf = lowf;
|
|
d->highf = highf;
|
|
d->switchf = switchf;
|
|
return d->module->SetDVBS(d->frequency, d->srate, d->fec, d->inversion,
|
|
d->pol, d->lowf, d->highf, d->switchf);
|
|
}
|
|
|
|
/* DVB-T */
|
|
int dvb_set_dvbt (dvb_device_t *d, uint32_t freq, const char * /*mod*/,
|
|
uint32_t fec_hp, uint32_t fec_lp, uint32_t bandwidth,
|
|
int transmission, uint32_t guard, int hierarchy)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->SetDVBT(freq / 1000, fec_hp, fec_lp,
|
|
bandwidth, transmission, guard, hierarchy);
|
|
}
|
|
|
|
/* DVB-T2 */
|
|
int dvb_set_dvbt2 (dvb_device_t *d, uint32_t freq, const char * /*mod*/,
|
|
uint32_t fec, uint32_t bandwidth, int transmission,
|
|
uint32_t guard, uint8_t plp)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->SetDVBT2(freq / 1000, fec,
|
|
bandwidth, transmission, guard, plp);
|
|
}
|
|
|
|
/* ISDB-C */
|
|
int dvb_set_isdbc (dvb_device_t *, uint32_t /*freq*/, const char * /*mod*/,
|
|
uint32_t /*srate*/, uint32_t /*fec*/)
|
|
{
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* ISDB-S */
|
|
int dvb_set_isdbs (dvb_device_t *, uint64_t /*freq*/, uint16_t /*ts_id*/)
|
|
{
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* ISDB-T */
|
|
int dvb_set_isdbt (dvb_device_t *, uint32_t /*freq*/, uint32_t /*bandwidth*/,
|
|
int /*transmit_mode*/, uint32_t /*guard*/,
|
|
const isdbt_layer_t /*layers*/[3])
|
|
{
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* ATSC */
|
|
int dvb_set_atsc (dvb_device_t *d, uint32_t freq, const char * /*mod*/)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->SetATSC(freq / 1000);
|
|
}
|
|
|
|
int dvb_set_cqam (dvb_device_t *d, uint32_t freq, const char * /*mod*/)
|
|
{
|
|
ComContext ctx( COINIT_APARTMENTTHREADED );
|
|
return d->module->SetCQAM(freq / 1000);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* BDAOutput
|
|
*****************************************************************************/
|
|
BDAOutput::BDAOutput( ) :
|
|
p_first(NULL), pp_next(&p_first)
|
|
{
|
|
vlc_mutex_init( &lock );
|
|
vlc_cond_init( &wait );
|
|
}
|
|
|
|
BDAOutput::~BDAOutput()
|
|
{
|
|
Empty();
|
|
}
|
|
|
|
void BDAOutput::Push( block_t *p_block )
|
|
{
|
|
vlc_mutex_locker l( &lock );
|
|
|
|
block_ChainLastAppend( &pp_next, p_block );
|
|
vlc_cond_signal( &wait );
|
|
}
|
|
|
|
ssize_t BDAOutput::Pop(void *buf, size_t len, int ms)
|
|
{
|
|
if( ms < 0 )
|
|
ms = 250;
|
|
|
|
vlc_mutex_locker l( &lock );
|
|
|
|
vlc_tick_t i_deadline = vlc_tick_now() + VLC_TICK_FROM_MS( ms );
|
|
while( !p_first )
|
|
{
|
|
if( vlc_cond_timedwait( &wait, &lock, i_deadline ) )
|
|
return -1;
|
|
}
|
|
|
|
size_t i_index = 0;
|
|
while( i_index < len )
|
|
{
|
|
size_t i_copy = __MIN( len - i_index, p_first->i_buffer );
|
|
memcpy( (uint8_t *)buf + i_index, p_first->p_buffer, i_copy );
|
|
|
|
i_index += i_copy;
|
|
|
|
p_first->p_buffer += i_copy;
|
|
p_first->i_buffer -= i_copy;
|
|
|
|
if( p_first->i_buffer <= 0 )
|
|
{
|
|
block_t *p_next = p_first->p_next;
|
|
block_Release( p_first );
|
|
|
|
p_first = p_next;
|
|
if( !p_first )
|
|
{
|
|
pp_next = &p_first;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return i_index;
|
|
}
|
|
|
|
void BDAOutput::Empty()
|
|
{
|
|
vlc_mutex_locker l( &lock );
|
|
|
|
if( p_first )
|
|
block_ChainRelease( p_first );
|
|
p_first = NULL;
|
|
pp_next = &p_first;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Constructor
|
|
*****************************************************************************/
|
|
BDAGraph::BDAGraph( vlc_object_t *p_this ):
|
|
ul_cbrc( 0 ),
|
|
p_access( p_this ),
|
|
clsid_network_type(CLSID_NULL),
|
|
l_tuner_used(-1),
|
|
systems(0),
|
|
d_graph_register( 0 )
|
|
{
|
|
p_tuning_space = NULL;
|
|
|
|
p_filter_graph = NULL;
|
|
p_system_dev_enum = NULL;
|
|
p_network_provider = p_tuner_device = p_capture_device = NULL;
|
|
p_sample_grabber = p_mpeg_demux = p_transport_info = NULL;
|
|
p_scanning_tuner = NULL;
|
|
p_grabber = NULL;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Destructor
|
|
*****************************************************************************/
|
|
BDAGraph::~BDAGraph()
|
|
{
|
|
Destroy();
|
|
|
|
if( p_tuning_space )
|
|
p_tuning_space->Release();
|
|
p_tuning_space = NULL;
|
|
|
|
systems = 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* GetSystem
|
|
* helper function
|
|
*****************************************************************************/
|
|
unsigned BDAGraph::GetSystem( REFCLSID clsid )
|
|
{
|
|
unsigned sys = 0;
|
|
|
|
if( clsid == CLSID_DVBTNetworkProvider )
|
|
sys = DTV_DELIVERY_DVB_T;
|
|
if( clsid == CLSID_DVBCNetworkProvider )
|
|
sys = DTV_DELIVERY_DVB_C;
|
|
if( clsid == CLSID_DVBSNetworkProvider )
|
|
sys = DTV_DELIVERY_DVB_S;
|
|
if( clsid == CLSID_ATSCNetworkProvider )
|
|
sys = DTV_DELIVERY_ATSC;
|
|
if( clsid == CLSID_DigitalCableNetworkType )
|
|
sys = DTV_DELIVERY_CQAM;
|
|
|
|
return sys;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Enumerate Systems
|
|
*****************************************************************************
|
|
* here is logic for special case where user uses an MRL that points
|
|
* to DTV but is not fully specific. This is usually dvb:// and can come
|
|
* either from a playlist, a channels.conf MythTV file, or from manual entry.
|
|
*
|
|
* Since this is done before the real tune request is submitted, we can
|
|
* use the global device enumerator, etc., so long as we do a Destroy() at
|
|
* the end
|
|
*****************************************************************************/
|
|
unsigned BDAGraph::EnumSystems()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
GUID clsid_network_provider = CLSID_NULL;
|
|
|
|
msg_Dbg( p_access, "EnumSystems: Entering " );
|
|
|
|
do
|
|
{
|
|
hr = GetNextNetworkType( &clsid_network_provider );
|
|
if( hr != S_OK ) break;
|
|
hr = Check( clsid_network_provider );
|
|
if( FAILED( hr ) )
|
|
msg_Dbg( p_access, "EnumSystems: Check failed, trying next" );
|
|
}
|
|
while( true );
|
|
|
|
if( p_filter_graph )
|
|
Destroy();
|
|
msg_Dbg( p_access, "EnumSystems: Returning systems 0x%08x", systems );
|
|
return systems;
|
|
}
|
|
|
|
float BDAGraph::GetSignalNoiseRatio(void)
|
|
{
|
|
/* not implemented until Windows 7
|
|
* IBDA_Encoder::GetState */
|
|
return 0.;
|
|
}
|
|
|
|
float BDAGraph::GetSignalStrength(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
long l_strength = 0;
|
|
msg_Dbg( p_access, "GetSignalStrength: entering" );
|
|
if( !p_scanning_tuner)
|
|
return 0.;
|
|
hr = p_scanning_tuner->get_SignalStrength( &l_strength );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "GetSignalStrength: "
|
|
"Cannot get value: hr=0x%8lx", hr );
|
|
return 0.;
|
|
}
|
|
msg_Dbg( p_access, "GetSignalStrength: got %ld", l_strength );
|
|
if( l_strength == -1 )
|
|
return -1.;
|
|
return l_strength / 100.;
|
|
}
|
|
|
|
int BDAGraph::SubmitTuneRequest(void)
|
|
{
|
|
HRESULT hr;
|
|
int i = 0;
|
|
|
|
/* Build and Start the Graph. If a Tuner device is in use the graph will
|
|
* fail to start. Repeated calls to Build will check successive tuner
|
|
* devices */
|
|
do
|
|
{
|
|
msg_Dbg( p_access, "SubmitTuneRequest: Building the Graph" );
|
|
|
|
hr = Build();
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SubmitTuneRequest: "
|
|
"Cannot Build the Graph: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
msg_Dbg( p_access, "SubmitTuneRequest: Starting the Graph" );
|
|
|
|
hr = Start();
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SubmitTuneRequest: "
|
|
"Cannot Start the Graph, retrying: hr=0x%8lx", hr );
|
|
++i;
|
|
}
|
|
}
|
|
while( hr != S_OK && i < 10 ); /* give up after 10 tries */
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SubmitTuneRequest: "
|
|
"Failed to Start the Graph: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Set Clear QAM (DigitalCable)
|
|
* Local ATSC Digital Antenna
|
|
*****************************************************************************/
|
|
int BDAGraph::SetCQAM(long l_frequency)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuneRequest* p_tune_request;
|
|
IDigitalCableTuneRequest* p_cqam_tune_request;
|
|
IDigitalCableLocator* p_cqam_locator;
|
|
localComPtr():
|
|
p_tune_request(NULL),
|
|
p_cqam_tune_request(NULL),
|
|
p_cqam_locator(NULL)
|
|
{};
|
|
~localComPtr()
|
|
{
|
|
if( p_tune_request )
|
|
p_tune_request->Release();
|
|
if( p_cqam_tune_request )
|
|
p_cqam_tune_request->Release();
|
|
if( p_cqam_locator )
|
|
p_cqam_locator->Release();
|
|
}
|
|
} l;
|
|
long l_minor_channel, l_physical_channel;
|
|
|
|
l_physical_channel = var_GetInteger( p_access, "dvb-physical-channel" );
|
|
l_minor_channel = var_GetInteger( p_access, "dvb-minor-channel" );
|
|
|
|
/* try to set p_scanning_tuner */
|
|
hr = Check( CLSID_DigitalCableNetworkType );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetCQAM: "\
|
|
"Cannot create Tuning Space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if( !p_scanning_tuner )
|
|
{
|
|
msg_Warn( p_access, "SetCQAM: Cannot get scanning tuner" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetCQAM: "\
|
|
"Cannot get Tune Request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_tune_request->QueryInterface( IID_PPV_ARGS( &l.p_cqam_tune_request ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetCQAM: "\
|
|
"Cannot QI for IDigitalCableTuneRequest: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = ::CoCreateInstance( __uuidof(DigitalCableLocator), 0, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &l.p_cqam_locator ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetCQAM: "\
|
|
"Cannot create the CQAM locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = S_OK;
|
|
if( SUCCEEDED( hr ) && l_physical_channel > 0 )
|
|
hr = l.p_cqam_locator->put_PhysicalChannel( l_physical_channel );
|
|
if( SUCCEEDED( hr ) && l_frequency > 0 )
|
|
hr = l.p_cqam_locator->put_CarrierFrequency( l_frequency );
|
|
if( SUCCEEDED( hr ) && l_minor_channel > 0 )
|
|
hr = l.p_cqam_tune_request->put_MinorChannel( l_minor_channel );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetCQAM: "\
|
|
"Cannot set tuning parameters: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_cqam_tune_request->put_Locator( l.p_cqam_locator );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetCQAM: "\
|
|
"Cannot put the locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_scanning_tuner->Validate( l.p_cqam_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetCQAM: "\
|
|
"Tune Request cannot be validated: hr=0x%8lx", hr );
|
|
}
|
|
/* increments ref count for scanning tuner */
|
|
hr = p_scanning_tuner->put_TuneRequest( l.p_cqam_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetCQAM: "\
|
|
"Cannot put the tune request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Set ATSC
|
|
*****************************************************************************/
|
|
int BDAGraph::SetATSC(long l_frequency)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuneRequest* p_tune_request;
|
|
IATSCChannelTuneRequest* p_atsc_tune_request;
|
|
IATSCLocator* p_atsc_locator;
|
|
localComPtr():
|
|
p_tune_request(NULL),
|
|
p_atsc_tune_request(NULL),
|
|
p_atsc_locator(NULL)
|
|
{};
|
|
~localComPtr()
|
|
{
|
|
if( p_tune_request )
|
|
p_tune_request->Release();
|
|
if( p_atsc_tune_request )
|
|
p_atsc_tune_request->Release();
|
|
if( p_atsc_locator )
|
|
p_atsc_locator->Release();
|
|
}
|
|
} l;
|
|
long l_major_channel, l_minor_channel, l_physical_channel;
|
|
|
|
/* fixme: these parameters should have dtv prefix, not dvb */
|
|
l_major_channel = var_GetInteger( p_access, "dvb-major-channel" );
|
|
l_minor_channel = var_GetInteger( p_access, "dvb-minor-channel" );
|
|
l_physical_channel = var_GetInteger( p_access, "dvb-physical-channel" );
|
|
|
|
/* try to set p_scanning_tuner */
|
|
hr = Check( CLSID_ATSCNetworkProvider );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetATSC: "\
|
|
"Cannot create Tuning Space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if( !p_scanning_tuner )
|
|
{
|
|
msg_Warn( p_access, "SetATSC: Cannot get scanning tuner" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetATSC: "\
|
|
"Cannot get Tune Request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_tune_request->QueryInterface( IID_PPV_ARGS( &l.p_atsc_tune_request ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetATSC: "\
|
|
"Cannot QI for IATSCChannelTuneRequest: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = ::CoCreateInstance( __uuidof(ATSCLocator), 0, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &l.p_atsc_locator ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetATSC: "\
|
|
"Cannot create the ATSC locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = S_OK;
|
|
if( l_frequency > 0 )
|
|
hr = l.p_atsc_locator->put_CarrierFrequency( l_frequency );
|
|
if( l_major_channel > 0 )
|
|
hr = l.p_atsc_tune_request->put_Channel( l_major_channel );
|
|
if( SUCCEEDED( hr ) && l_minor_channel > 0 )
|
|
hr = l.p_atsc_tune_request->put_MinorChannel( l_minor_channel );
|
|
if( SUCCEEDED( hr ) && l_physical_channel > 0 )
|
|
hr = l.p_atsc_locator->put_PhysicalChannel( l_physical_channel );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetATSC: "\
|
|
"Cannot set tuning parameters: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_atsc_tune_request->put_Locator( l.p_atsc_locator );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetATSC: "\
|
|
"Cannot put the locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_scanning_tuner->Validate( l.p_atsc_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetATSC: "\
|
|
"Tune Request cannot be validated: hr=0x%8lx", hr );
|
|
}
|
|
/* increments ref count for scanning tuner */
|
|
hr = p_scanning_tuner->put_TuneRequest( l.p_atsc_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetATSC: "\
|
|
"Cannot put the tune request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Set DVB-T
|
|
*
|
|
* This provides the tune request that everything else is built upon.
|
|
*
|
|
* Stores the tune request to the scanning tuner, where it is pulled out by
|
|
* dvb_tune a/k/a SubmitTuneRequest.
|
|
******************************************************************************/
|
|
int BDAGraph::SetDVBT(long l_frequency, uint32_t fec_hp, uint32_t fec_lp,
|
|
long l_bandwidth, int transmission, uint32_t guard, int hierarchy)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuneRequest* p_tune_request;
|
|
IDVBTuneRequest* p_dvb_tune_request;
|
|
IDVBTLocator* p_dvbt_locator;
|
|
IDVBTuningSpace2* p_dvb_tuning_space;
|
|
localComPtr():
|
|
p_tune_request(NULL),
|
|
p_dvb_tune_request(NULL),
|
|
p_dvbt_locator(NULL),
|
|
p_dvb_tuning_space(NULL)
|
|
{};
|
|
~localComPtr()
|
|
{
|
|
if( p_tune_request )
|
|
p_tune_request->Release();
|
|
if( p_dvb_tune_request )
|
|
p_dvb_tune_request->Release();
|
|
if( p_dvbt_locator )
|
|
p_dvbt_locator->Release();
|
|
if( p_dvb_tuning_space )
|
|
p_dvb_tuning_space->Release();
|
|
}
|
|
} l;
|
|
|
|
/* create local dvbt-specific tune request and locator
|
|
* then put it to existing scanning tuner */
|
|
BinaryConvolutionCodeRate i_hp_fec = dvb_parse_fec(fec_hp);
|
|
BinaryConvolutionCodeRate i_lp_fec = dvb_parse_fec(fec_lp);
|
|
GuardInterval i_guard = dvb_parse_guard(guard);
|
|
TransmissionMode i_transmission = dvb_parse_transmission(transmission);
|
|
HierarchyAlpha i_hierarchy = dvb_parse_hierarchy(hierarchy);
|
|
|
|
/* try to set p_scanning_tuner */
|
|
msg_Dbg( p_access, "SetDVBT: set up scanning tuner" );
|
|
hr = Check( CLSID_DVBTNetworkProvider );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot create Tuning Space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if( !p_scanning_tuner )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: Cannot get scanning tuner" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot get Tune Request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: Creating DVB tune request" );
|
|
hr = l.p_tune_request->QueryInterface( IID_PPV_ARGS( &l.p_dvb_tune_request ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
l.p_dvb_tune_request->put_ONID( -1 );
|
|
l.p_dvb_tune_request->put_SID( -1 );
|
|
l.p_dvb_tune_request->put_TSID( -1 );
|
|
|
|
msg_Dbg( p_access, "SetDVBT: get TS" );
|
|
hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetDVBT: "\
|
|
"cannot get tuning space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: QI to DVBT TS" );
|
|
hr = p_tuning_space->QueryInterface( IID_PPV_ARGS( &l.p_dvb_tuning_space ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: Creating local locator" );
|
|
hr = ::CoCreateInstance( __uuidof(DVBTLocator), 0, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &l.p_dvbt_locator ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot create the DVBT Locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_dvb_tuning_space->put_SystemType( DVB_Terrestrial );
|
|
if( SUCCEEDED( hr ) && l_frequency > 0 )
|
|
hr = l.p_dvbt_locator->put_CarrierFrequency( l_frequency );
|
|
if( SUCCEEDED( hr ) && l_bandwidth > 0 )
|
|
hr = l.p_dvbt_locator->put_Bandwidth( l_bandwidth );
|
|
if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_InnerFECRate( i_hp_fec );
|
|
if( SUCCEEDED( hr ) && i_lp_fec != BDA_BCC_RATE_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_LPInnerFECRate( i_lp_fec );
|
|
if( SUCCEEDED( hr ) && i_guard != BDA_GUARD_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_Guard( i_guard );
|
|
if( SUCCEEDED( hr ) && i_transmission != BDA_XMIT_MODE_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_Mode( i_transmission );
|
|
if( SUCCEEDED( hr ) && i_hierarchy != BDA_HALPHA_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_HAlpha( i_hierarchy );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: putting DVBT locator into local tune request" );
|
|
|
|
hr = l.p_dvb_tune_request->put_Locator( l.p_dvbt_locator );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot put the locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: putting local Tune Request to scanning tuner" );
|
|
hr = p_scanning_tuner->Validate( l.p_dvb_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetDVBT: "\
|
|
"Tune Request cannot be validated: hr=0x%8lx", hr );
|
|
}
|
|
/* increments ref count for scanning tuner */
|
|
hr = p_scanning_tuner->put_TuneRequest( l.p_dvb_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot put the tune request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: return success" );
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Set DVB-T2
|
|
*
|
|
* This provides the tune request that everything else is built upon.
|
|
*
|
|
* Stores the tune request to the scanning tuner, where it is pulled out by
|
|
* dvb_tune a/k/a SubmitTuneRequest.
|
|
******************************************************************************/
|
|
int BDAGraph::SetDVBT2(long l_frequency, uint32_t fec,
|
|
long l_bandwidth, int transmission, uint32_t guard, int plp)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuneRequest* p_tune_request;
|
|
IDVBTuneRequest* p_dvb_tune_request;
|
|
IDVBTLocator2* p_dvbt_locator;
|
|
IDVBTuningSpace2* p_dvb_tuning_space;
|
|
localComPtr():
|
|
p_tune_request(NULL),
|
|
p_dvb_tune_request(NULL),
|
|
p_dvbt_locator(NULL),
|
|
p_dvb_tuning_space(NULL)
|
|
{};
|
|
~localComPtr()
|
|
{
|
|
if( p_tune_request )
|
|
p_tune_request->Release();
|
|
if( p_dvb_tune_request )
|
|
p_dvb_tune_request->Release();
|
|
if( p_dvbt_locator )
|
|
p_dvbt_locator->Release();
|
|
if( p_dvb_tuning_space )
|
|
p_dvb_tuning_space->Release();
|
|
}
|
|
} l;
|
|
|
|
/* create local dvbt-specific tune request and locator
|
|
* then put it to existing scanning tuner */
|
|
BinaryConvolutionCodeRate i_fec = dvb_parse_fec(fec);
|
|
GuardInterval i_guard = dvb_parse_guard(guard);
|
|
TransmissionMode i_transmission = dvb_parse_transmission(transmission);
|
|
long l_plp = plp;
|
|
|
|
/* try to set p_scanning_tuner */
|
|
msg_Dbg( p_access, "SetDVBT: set up scanning tuner" );
|
|
hr = Check( CLSID_DVBTNetworkProvider );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot create Tuning Space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if( !p_scanning_tuner )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: Cannot get scanning tuner" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot get Tune Request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: Creating DVB tune request" );
|
|
hr = l.p_tune_request->QueryInterface( IID_PPV_ARGS( &l.p_dvb_tune_request ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
l.p_dvb_tune_request->put_ONID( -1 );
|
|
l.p_dvb_tune_request->put_SID( -1 );
|
|
l.p_dvb_tune_request->put_TSID( -1 );
|
|
|
|
msg_Dbg( p_access, "SetDVBT: get TS" );
|
|
hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetDVBT: "\
|
|
"cannot get tuning space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: QI to DVBT TS" );
|
|
hr = p_tuning_space->QueryInterface( IID_PPV_ARGS( &l.p_dvb_tuning_space ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: Creating local locator2" );
|
|
|
|
hr = ::CoCreateInstance(__uuidof(DVBTLocator2), 0, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &l.p_dvbt_locator ) );
|
|
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot create the DVBT Locator2: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_dvb_tuning_space->put_SystemType( DVB_Terrestrial );
|
|
if( SUCCEEDED( hr ) && l_frequency > 0 )
|
|
hr = l.p_dvbt_locator->put_CarrierFrequency( l_frequency );
|
|
if( SUCCEEDED( hr ) && l_bandwidth > 0 )
|
|
hr = l.p_dvbt_locator->put_Bandwidth( l_bandwidth );
|
|
if( SUCCEEDED( hr ) && i_fec != BDA_BCC_RATE_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_InnerFECRate( i_fec );
|
|
if( SUCCEEDED( hr ) && i_fec != BDA_BCC_RATE_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_LPInnerFECRate( i_fec );
|
|
if( SUCCEEDED( hr ) && i_guard != BDA_GUARD_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_Guard( i_guard );
|
|
if( SUCCEEDED( hr ) && i_transmission != BDA_XMIT_MODE_NOT_SET )
|
|
hr = l.p_dvbt_locator->put_Mode( i_transmission );
|
|
if( SUCCEEDED( hr ) && l_plp > 0 ){
|
|
hr = l.p_dvbt_locator->put_PhysicalLayerPipeId( l_plp);
|
|
}
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: putting DVBT locator into local tune request" );
|
|
|
|
hr = l.p_dvb_tune_request->put_Locator( l.p_dvbt_locator );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot put the locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: putting local Tune Request to scanning tuner" );
|
|
hr = p_scanning_tuner->Validate( l.p_dvb_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetDVBT: "\
|
|
"Tune Request cannot be validated: hr=0x%8lx", hr );
|
|
}
|
|
/* increments ref count for scanning tuner */
|
|
hr = p_scanning_tuner->put_TuneRequest( l.p_dvb_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBT: "\
|
|
"Cannot put the tune request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* TBS tuner PLP set workaround */
|
|
/* TODO: Check TBS tuner is present */
|
|
IPin* pinInput0 = FindPinOnFilter( p_tuner_device, "Input0");
|
|
if( pinInput0)
|
|
{
|
|
msg_Dbg( p_access, "SetDVBT: pin Input0 found on tuner filter, trying to get IKsPropertySet interface for TBS tuner..." );
|
|
ComPtr<IKsPropertySet> p_ksPropertySet;
|
|
hr = pinInput0->QueryInterface(IID_PPV_ARGS(&p_ksPropertySet));
|
|
if( FAILED( hr ))
|
|
{
|
|
msg_Dbg( p_access, "SetDVBT: Cannot query for IKsPropertySet (this can be normal if not TBS tuner) : hr=0x%8lx", hr );
|
|
}
|
|
else
|
|
{
|
|
msg_Dbg( p_access, "SetDVBT: found IKsPropertySet interface (using TBS tuner PLP-set workaround)");
|
|
TBS_PLP_INFO plp_info;
|
|
ZeroMemory( &plp_info, sizeof( TBS_PLP_INFO));
|
|
plp_info.plpId = plp;
|
|
p_ksPropertySet->Set( KSPROPSETID_BdaTunerExtensionProperties,
|
|
KSPROPERTY_BDA_PLPINFO,
|
|
NULL,
|
|
0,
|
|
&plp_info,
|
|
sizeof( TBS_PLP_INFO ));
|
|
msg_Dbg( p_access, "SetDVBT: TBS tuner set PLP: %d", plp);
|
|
}
|
|
pinInput0->Release();
|
|
}
|
|
else
|
|
{
|
|
msg_Dbg( p_access, "SetDVBT: no pin Input0 found on tuner filter (this can be normal if not TBS tuner)" );
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBT: return success" );
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Set DVB-C
|
|
******************************************************************************/
|
|
int BDAGraph::SetDVBC(long l_frequency, const char *mod, long l_symbolrate)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuneRequest* p_tune_request;
|
|
IDVBTuneRequest* p_dvb_tune_request;
|
|
IDVBCLocator* p_dvbc_locator;
|
|
IDVBTuningSpace2* p_dvb_tuning_space;
|
|
|
|
localComPtr():
|
|
p_tune_request(NULL),
|
|
p_dvb_tune_request(NULL),
|
|
p_dvbc_locator(NULL),
|
|
p_dvb_tuning_space(NULL)
|
|
{};
|
|
~localComPtr()
|
|
{
|
|
if( p_tune_request )
|
|
p_tune_request->Release();
|
|
if( p_dvb_tune_request )
|
|
p_dvb_tune_request->Release();
|
|
if( p_dvbc_locator )
|
|
p_dvbc_locator->Release();
|
|
if( p_dvb_tuning_space )
|
|
p_dvb_tuning_space->Release();
|
|
}
|
|
} l;
|
|
|
|
ModulationType i_qam_mod = dvb_parse_modulation(mod);
|
|
|
|
/* try to set p_scanning_tuner */
|
|
hr = Check( CLSID_DVBCNetworkProvider );
|
|
msg_Dbg( p_access, "SetDVBC: returned from Check" );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: "\
|
|
"Cannot create Tuning Space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBC: check on scanning tuner" );
|
|
if( !p_scanning_tuner )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: Cannot get scanning tuner" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBC: get tune request" );
|
|
hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: "\
|
|
"Cannot get Tune Request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBC: QI for dvb tune request" );
|
|
hr = l.p_tune_request->QueryInterface( IID_PPV_ARGS( &l.p_dvb_tune_request ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: "\
|
|
"Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
l.p_dvb_tune_request->put_ONID( -1 );
|
|
l.p_dvb_tune_request->put_SID( -1 );
|
|
l.p_dvb_tune_request->put_TSID( -1 );
|
|
|
|
msg_Dbg( p_access, "SetDVBC: create dvbc locator" );
|
|
hr = ::CoCreateInstance( __uuidof(DVBCLocator), 0, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &l.p_dvbc_locator ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: "\
|
|
"Cannot create the DVB-C Locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
|
|
msg_Dbg( p_access, "SetDVBC: get TS" );
|
|
hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetDVBC: "\
|
|
"cannot get tuning space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBC: QI for dvb tuning space" );
|
|
hr = p_tuning_space->QueryInterface( IID_PPV_ARGS( &l.p_dvb_tuning_space ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: "\
|
|
"Cannot QI for IDVBTuningSpace2: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBC: set up locator" );
|
|
hr = S_OK;
|
|
hr = l.p_dvb_tuning_space->put_SystemType( DVB_Cable );
|
|
|
|
if( SUCCEEDED( hr ) && l_frequency > 0 )
|
|
hr = l.p_dvbc_locator->put_CarrierFrequency( l_frequency );
|
|
if( SUCCEEDED( hr ) && l_symbolrate > 0 )
|
|
hr = l.p_dvbc_locator->put_SymbolRate( l_symbolrate );
|
|
if( SUCCEEDED( hr ) && i_qam_mod != BDA_MOD_NOT_SET )
|
|
hr = l.p_dvbc_locator->put_Modulation( i_qam_mod );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: "\
|
|
"Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBC: put locator to dvb tune request" );
|
|
hr = l.p_dvb_tune_request->put_Locator( l.p_dvbc_locator );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: "\
|
|
"Cannot put the locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBC: validate dvb tune request" );
|
|
hr = p_scanning_tuner->Validate( l.p_dvb_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetDVBC: "\
|
|
"Tune Request cannot be validated: hr=0x%8lx", hr );
|
|
}
|
|
|
|
/* increments ref count for scanning tuner */
|
|
msg_Dbg( p_access, "SetDVBC: put dvb tune request to tuner" );
|
|
hr = p_scanning_tuner->put_TuneRequest( l.p_dvb_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBC: "\
|
|
"Cannot put the tune request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
msg_Dbg( p_access, "SetDVBC: return success" );
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Set Inversion
|
|
******************************************************************************/
|
|
int BDAGraph::SetInversion(int inversion)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
class localComPtr
|
|
{
|
|
public:
|
|
IDVBSTuningSpace* p_dvbs_tuning_space;
|
|
localComPtr() :
|
|
p_dvbs_tuning_space(NULL)
|
|
{}
|
|
~localComPtr()
|
|
{
|
|
if( p_dvbs_tuning_space )
|
|
p_dvbs_tuning_space->Release();
|
|
}
|
|
} l;
|
|
|
|
SpectralInversion i_inversion = dvb_parse_inversion( inversion );
|
|
|
|
if( !p_scanning_tuner )
|
|
{
|
|
msg_Warn( p_access, "SetInversion: "\
|
|
"No scanning tuner" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
/* SetInversion is called for all DVB tuners, before the dvb_tune(),
|
|
* in access.c. Since DVBT and DVBC don't support spectral
|
|
* inversion, we need to return VLC_SUCCESS in those cases
|
|
* so that dvb_tune() will be called */
|
|
if( ( GetSystem( clsid_network_type ) & ( DTV_DELIVERY_DVB_S | DTV_DELIVERY_DVB_S2 | DTV_DELIVERY_ISDB_S ) ) == 0 )
|
|
{
|
|
msg_Dbg( p_access, "SetInversion: Not Satellite type" );
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetInversion: get TS" );
|
|
hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetInversion: "\
|
|
"cannot get tuning space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_tuning_space->QueryInterface( IID_PPV_ARGS( &l.p_dvbs_tuning_space ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetInversion: "\
|
|
"Cannot QI for IDVBSTuningSpace: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if( i_inversion != BDA_SPECTRAL_INVERSION_NOT_SET )
|
|
hr = l.p_dvbs_tuning_space->put_SpectralInversion( i_inversion );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetInversion: "\
|
|
"Cannot put inversion: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Set DVB-S
|
|
******************************************************************************/
|
|
int BDAGraph::SetDVBS(long l_frequency, long l_symbolrate, uint32_t fec,
|
|
int inversion, char pol,
|
|
long l_lnb_lof1, long l_lnb_lof2, long l_lnb_slof)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuneRequest* p_tune_request;
|
|
IDVBTuneRequest* p_dvb_tune_request;
|
|
IDVBSLocator* p_dvbs_locator;
|
|
IDVBSTuningSpace* p_dvbs_tuning_space;
|
|
char* psz_polarisation;
|
|
char* psz_input_range;
|
|
BSTR bstr_input_range;
|
|
WCHAR* pwsz_input_range;
|
|
int i_range_len;
|
|
localComPtr() :
|
|
p_tune_request(NULL),
|
|
p_dvb_tune_request(NULL),
|
|
p_dvbs_locator(NULL),
|
|
p_dvbs_tuning_space(NULL),
|
|
psz_polarisation(NULL),
|
|
psz_input_range(NULL),
|
|
bstr_input_range(NULL),
|
|
pwsz_input_range(NULL),
|
|
i_range_len(0)
|
|
{}
|
|
~localComPtr()
|
|
{
|
|
if( p_tune_request )
|
|
p_tune_request->Release();
|
|
if( p_dvb_tune_request )
|
|
p_dvb_tune_request->Release();
|
|
if( p_dvbs_locator )
|
|
p_dvbs_locator->Release();
|
|
if( p_dvbs_tuning_space )
|
|
p_dvbs_tuning_space->Release();
|
|
SysFreeString( bstr_input_range );
|
|
if( pwsz_input_range )
|
|
delete[] pwsz_input_range;
|
|
free( psz_input_range );
|
|
free( psz_polarisation );
|
|
}
|
|
} l;
|
|
long l_azimuth, l_elevation, l_longitude;
|
|
long l_network_id;
|
|
VARIANT_BOOL b_west;
|
|
|
|
BinaryConvolutionCodeRate i_hp_fec = dvb_parse_fec( fec );
|
|
Polarisation i_polar = dvb_parse_polarization( pol );
|
|
SpectralInversion i_inversion = dvb_parse_inversion( inversion );
|
|
|
|
l_azimuth = var_GetInteger( p_access, "dvb-azimuth" );
|
|
l_elevation = var_GetInteger( p_access, "dvb-elevation" );
|
|
l_longitude = var_GetInteger( p_access, "dvb-longitude" );
|
|
l_network_id = var_GetInteger( p_access, "dvb-network-id" );
|
|
|
|
if( asprintf( &l.psz_polarisation, "%c", pol ) == -1 )
|
|
abort();
|
|
|
|
b_west = ( l_longitude < 0 );
|
|
|
|
l.psz_input_range = var_GetNonEmptyString( p_access, "dvb-range" );
|
|
l.i_range_len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
|
|
l.psz_input_range, -1, l.pwsz_input_range, 0 );
|
|
if( l.i_range_len > 0 )
|
|
{
|
|
l.pwsz_input_range = new WCHAR[l.i_range_len];
|
|
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
|
|
l.psz_input_range, -1, l.pwsz_input_range, l.i_range_len );
|
|
l.bstr_input_range = SysAllocString( l.pwsz_input_range );
|
|
}
|
|
|
|
/* try to set p_scanning_tuner */
|
|
hr = Check( CLSID_DVBSNetworkProvider );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: "\
|
|
"Cannot create Tuning Space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
if( !p_scanning_tuner )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: Cannot get scanning tuner" );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: "\
|
|
"Cannot get Tune Request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_tune_request->QueryInterface( IID_PPV_ARGS( &l.p_dvb_tune_request ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: "\
|
|
"Cannot QI for IDVBTuneRequest: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
l.p_dvb_tune_request->put_ONID( -1 );
|
|
l.p_dvb_tune_request->put_SID( -1 );
|
|
l.p_dvb_tune_request->put_TSID( -1 );
|
|
|
|
hr = ::CoCreateInstance( __uuidof(DVBSLocator), 0, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &l.p_dvbs_locator ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: "\
|
|
"Cannot create the DVBS Locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetDVBS: get TS" );
|
|
hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetDVBS: "\
|
|
"cannot get tuning space: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_tuning_space->QueryInterface( IID_PPV_ARGS( &l.p_dvbs_tuning_space ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: "\
|
|
"Cannot QI for IDVBSTuningSpace: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_dvbs_tuning_space->put_SystemType( DVB_Satellite );
|
|
if( SUCCEEDED( hr ) && l_lnb_lof1 > 0 )
|
|
hr = l.p_dvbs_tuning_space->put_LowOscillator( l_lnb_lof1 );
|
|
if( SUCCEEDED( hr ) && l_lnb_slof > 0 )
|
|
hr = l.p_dvbs_tuning_space->put_LNBSwitch( l_lnb_slof );
|
|
if( SUCCEEDED( hr ) && l_lnb_lof2 > 0 )
|
|
hr = l.p_dvbs_tuning_space->put_HighOscillator( l_lnb_lof2 );
|
|
if( SUCCEEDED( hr ) && i_inversion != BDA_SPECTRAL_INVERSION_NOT_SET )
|
|
hr = l.p_dvbs_tuning_space->put_SpectralInversion( i_inversion );
|
|
if( SUCCEEDED( hr ) && l_network_id > 0 )
|
|
hr = l.p_dvbs_tuning_space->put_NetworkID( l_network_id );
|
|
if( SUCCEEDED( hr ) && l.i_range_len > 0 )
|
|
hr = l.p_dvbs_tuning_space->put_InputRange( l.bstr_input_range );
|
|
|
|
if( SUCCEEDED( hr ) && l_frequency > 0 )
|
|
hr = l.p_dvbs_locator->put_CarrierFrequency( l_frequency );
|
|
if( SUCCEEDED( hr ) && l_symbolrate > 0 )
|
|
hr = l.p_dvbs_locator->put_SymbolRate( l_symbolrate );
|
|
if( SUCCEEDED( hr ) && i_polar != BDA_POLARISATION_NOT_SET )
|
|
hr = l.p_dvbs_locator->put_SignalPolarisation( i_polar );
|
|
if( SUCCEEDED( hr ) )
|
|
hr = l.p_dvbs_locator->put_Modulation( BDA_MOD_QPSK );
|
|
if( SUCCEEDED( hr ) && i_hp_fec != BDA_BCC_RATE_NOT_SET )
|
|
hr = l.p_dvbs_locator->put_InnerFECRate( i_hp_fec );
|
|
|
|
if( SUCCEEDED( hr ) && l_azimuth > 0 )
|
|
hr = l.p_dvbs_locator->put_Azimuth( l_azimuth );
|
|
if( SUCCEEDED( hr ) && l_elevation > 0 )
|
|
hr = l.p_dvbs_locator->put_Elevation( l_elevation );
|
|
if( SUCCEEDED( hr ) )
|
|
hr = l.p_dvbs_locator->put_WestPosition( b_west );
|
|
if( SUCCEEDED( hr ) )
|
|
hr = l.p_dvbs_locator->put_OrbitalPosition( labs( l_longitude ) );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: "\
|
|
"Cannot set tuning parameters on Locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = l.p_dvb_tune_request->put_Locator( l.p_dvbs_locator );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: "\
|
|
"Cannot put the locator: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
hr = p_scanning_tuner->Validate( l.p_dvb_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetDVBS: "\
|
|
"Tune Request cannot be validated: hr=0x%8lx", hr );
|
|
}
|
|
|
|
/* increments ref count for scanning tuner */
|
|
hr = p_scanning_tuner->put_TuneRequest( l.p_dvb_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetDVBS: "\
|
|
"Cannot put the tune request: hr=0x%8lx", hr );
|
|
return VLC_EGENERIC;
|
|
}
|
|
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SetUpTuner
|
|
******************************************************************************
|
|
* Sets up global p_scanning_tuner and sets clsid_network_type according
|
|
* to the Network Type requested.
|
|
*
|
|
* Logic: if tuner is set up and is the right network type, use it.
|
|
* Otherwise, poll the tuner for the right tuning space.
|
|
*
|
|
* Then set up a tune request and try to validate it. Finally, put
|
|
* tune request and tuning space to tuner
|
|
*
|
|
* on success, sets globals: p_scanning_tuner and clsid_network_type
|
|
*
|
|
******************************************************************************/
|
|
HRESULT BDAGraph::SetUpTuner( REFCLSID clsid_this_network_type )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuningSpaceContainer* p_tuning_space_container;
|
|
IEnumTuningSpaces* p_tuning_space_enum;
|
|
ITuningSpace* p_test_tuning_space;
|
|
ITuneRequest* p_tune_request;
|
|
IDVBTuneRequest* p_dvb_tune_request;
|
|
|
|
IDigitalCableTuneRequest* p_cqam_tune_request;
|
|
IATSCChannelTuneRequest* p_atsc_tune_request;
|
|
ILocator* p_locator;
|
|
IDVBTLocator* p_dvbt_locator;
|
|
IDVBCLocator* p_dvbc_locator;
|
|
IDVBSLocator* p_dvbs_locator;
|
|
|
|
BSTR bstr_name;
|
|
|
|
CLSID clsid_test_network_type;
|
|
char* psz_network_name;
|
|
char* psz_bstr_name;
|
|
int i_name_len;
|
|
|
|
localComPtr():
|
|
p_tuning_space_container(NULL),
|
|
p_tuning_space_enum(NULL),
|
|
p_test_tuning_space(NULL),
|
|
p_tune_request(NULL),
|
|
p_dvb_tune_request(NULL),
|
|
p_cqam_tune_request(NULL),
|
|
p_atsc_tune_request(NULL),
|
|
p_locator(NULL),
|
|
p_dvbt_locator(NULL),
|
|
p_dvbc_locator(NULL),
|
|
p_dvbs_locator(NULL),
|
|
bstr_name(NULL),
|
|
clsid_test_network_type(CLSID_NULL),
|
|
psz_network_name(NULL),
|
|
psz_bstr_name(NULL),
|
|
i_name_len(0)
|
|
{}
|
|
~localComPtr()
|
|
{
|
|
if( p_tuning_space_enum )
|
|
p_tuning_space_enum->Release();
|
|
if( p_tuning_space_container )
|
|
p_tuning_space_container->Release();
|
|
if( p_test_tuning_space )
|
|
p_test_tuning_space->Release();
|
|
if( p_tune_request )
|
|
p_tune_request->Release();
|
|
if( p_dvb_tune_request )
|
|
p_dvb_tune_request->Release();
|
|
if( p_cqam_tune_request )
|
|
p_cqam_tune_request->Release();
|
|
if( p_atsc_tune_request )
|
|
p_atsc_tune_request->Release();
|
|
if( p_locator )
|
|
p_locator->Release();
|
|
if( p_dvbt_locator )
|
|
p_dvbt_locator->Release();
|
|
if( p_dvbc_locator )
|
|
p_dvbc_locator->Release();
|
|
if( p_dvbs_locator )
|
|
p_dvbs_locator->Release();
|
|
SysFreeString( bstr_name );
|
|
delete[] psz_bstr_name;
|
|
free( psz_network_name );
|
|
}
|
|
} l;
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: entering" );
|
|
|
|
|
|
/* We shall test for a specific Tuning space name supplied on the command
|
|
* line as dvb-network-name=xxx.
|
|
* For some users with multiple cards and/or multiple networks this could
|
|
* be useful. This allows us to reasonably safely apply updates to the
|
|
* System Tuning Space in the registry without disrupting other streams. */
|
|
|
|
l.psz_network_name = var_GetNonEmptyString( p_access, "dvb-network-name" );
|
|
|
|
if( l.psz_network_name )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: Find Tuning Space: %s",
|
|
l.psz_network_name );
|
|
}
|
|
else
|
|
{
|
|
l.psz_network_name = new char[1];
|
|
*l.psz_network_name = '\0';
|
|
}
|
|
|
|
/* Tuner may already have been set up. If it is for the same
|
|
* network type then all is well. Otherwise, reset the Tuning Space and get
|
|
* a new one */
|
|
msg_Dbg( p_access, "SetUpTuner: Checking for tuning space" );
|
|
if( !p_scanning_tuner )
|
|
{
|
|
msg_Warn( p_access, "SetUpTuner: "\
|
|
"Cannot find scanning tuner" );
|
|
return E_FAIL;
|
|
}
|
|
|
|
if( p_tuning_space )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: get network type" );
|
|
hr = p_tuning_space->get__NetworkType( &l.clsid_test_network_type );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Check: "\
|
|
"Cannot get network type: hr=0x%8lx", hr );
|
|
l.clsid_test_network_type = CLSID_NULL;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: see if it's the right one" );
|
|
if( l.clsid_test_network_type == clsid_this_network_type )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: it's the right one" );
|
|
SysFreeString( l.bstr_name );
|
|
|
|
hr = p_tuning_space->get_UniqueName( &l.bstr_name );
|
|
if( FAILED( hr ) )
|
|
{
|
|
/* should never fail on a good tuning space */
|
|
msg_Dbg( p_access, "SetUpTuner: "\
|
|
"Cannot get UniqueName for Tuning Space: hr=0x%8lx", hr );
|
|
goto NoTuningSpace;
|
|
}
|
|
|
|
/* Test for a specific Tuning space name supplied on the command
|
|
* line as dvb-network-name=xxx */
|
|
if( l.psz_bstr_name )
|
|
delete[] l.psz_bstr_name;
|
|
l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
|
|
l.psz_bstr_name, 0, NULL, NULL );
|
|
l.psz_bstr_name = new char[ l.i_name_len ];
|
|
l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
|
|
l.psz_bstr_name, l.i_name_len, NULL, NULL );
|
|
|
|
/* if no name was requested on command line, or if the name
|
|
* requested equals the name of this space, we are OK */
|
|
if( *l.psz_network_name == '\0' ||
|
|
strcmp( l.psz_network_name, l.psz_bstr_name ) == 0 )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: Using Tuning Space: %s",
|
|
l.psz_bstr_name );
|
|
/* p_tuning_space and clsid_network_type are already set */
|
|
/* you probably already have a tune request, also */
|
|
hr = p_scanning_tuner->get_TuneRequest( &l.p_tune_request );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
return S_OK;
|
|
}
|
|
/* CreateTuneRequest adds l.p_tune_request to p_tuning_space
|
|
* l.p_tune_request->RefCount = 1 */
|
|
hr = p_tuning_space->CreateTuneRequest( &l.p_tune_request );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
return S_OK;
|
|
}
|
|
msg_Warn( p_access, "SetUpTuner: "\
|
|
"Cannot Create Tune Request: hr=0x%8lx", hr );
|
|
/* fall through to NoTuningSpace */
|
|
}
|
|
}
|
|
|
|
/* else different clsid_network_type */
|
|
NoTuningSpace:
|
|
if( p_tuning_space )
|
|
p_tuning_space->Release();
|
|
p_tuning_space = NULL;
|
|
/* pro forma; should have returned S_OK if we created this */
|
|
if( l.p_tune_request )
|
|
l.p_tune_request->Release();
|
|
l.p_tune_request = NULL;
|
|
}
|
|
|
|
|
|
/* p_tuning_space is null at this point; we have already
|
|
returned S_OK if it was good. So find a tuning
|
|
space on the scanning tuner. */
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: release TuningSpaces Enumerator" );
|
|
if( l.p_tuning_space_enum )
|
|
l.p_tuning_space_enum->Release();
|
|
msg_Dbg( p_access, "SetUpTuner: nullify TuningSpaces Enumerator" );
|
|
l.p_tuning_space_enum = NULL;
|
|
msg_Dbg( p_access, "SetUpTuner: create TuningSpaces Enumerator" );
|
|
|
|
hr = p_scanning_tuner->EnumTuningSpaces( &l.p_tuning_space_enum );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetUpTuner: "\
|
|
"Cannot create TuningSpaces Enumerator: hr=0x%8lx", hr );
|
|
goto TryToClone;
|
|
}
|
|
|
|
do
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: top of loop" );
|
|
l.clsid_test_network_type = CLSID_NULL;
|
|
if( l.p_test_tuning_space )
|
|
l.p_test_tuning_space->Release();
|
|
l.p_test_tuning_space = NULL;
|
|
if( p_tuning_space )
|
|
p_tuning_space->Release();
|
|
p_tuning_space = NULL;
|
|
SysFreeString( l.bstr_name );
|
|
l.bstr_name = NULL;
|
|
msg_Dbg( p_access, "SetUpTuner: need good TS enum" );
|
|
if( !l.p_tuning_space_enum ) break;
|
|
msg_Dbg( p_access, "SetUpTuner: next tuning space" );
|
|
hr = l.p_tuning_space_enum->Next( 1, &l.p_test_tuning_space, NULL );
|
|
if( hr != S_OK ) break;
|
|
msg_Dbg( p_access, "SetUpTuner: get network type" );
|
|
hr = l.p_test_tuning_space->get__NetworkType( &l.clsid_test_network_type );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Check: "\
|
|
"Cannot get network type: hr=0x%8lx", hr );
|
|
l.clsid_test_network_type = CLSID_NULL;
|
|
}
|
|
if( l.clsid_test_network_type == clsid_this_network_type )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: Found matching space on tuner" );
|
|
|
|
SysFreeString( l.bstr_name );
|
|
msg_Dbg( p_access, "SetUpTuner: get unique name" );
|
|
|
|
hr = l.p_test_tuning_space->get_UniqueName( &l.bstr_name );
|
|
if( FAILED( hr ) )
|
|
{
|
|
/* should never fail on a good tuning space */
|
|
msg_Dbg( p_access, "SetUpTuner: "\
|
|
"Cannot get UniqueName for Tuning Space: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
msg_Dbg( p_access, "SetUpTuner: convert w to multi" );
|
|
if ( l.psz_bstr_name )
|
|
delete[] l.psz_bstr_name;
|
|
l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
|
|
l.psz_bstr_name, 0, NULL, NULL );
|
|
l.psz_bstr_name = new char[ l.i_name_len ];
|
|
l.i_name_len = WideCharToMultiByte( CP_ACP, 0, l.bstr_name, -1,
|
|
l.psz_bstr_name, l.i_name_len, NULL, NULL );
|
|
msg_Dbg( p_access, "SetUpTuner: Using Tuning Space: %s",
|
|
l.psz_bstr_name );
|
|
break;
|
|
}
|
|
|
|
}
|
|
while( true );
|
|
msg_Dbg( p_access, "SetUpTuner: checking what we got" );
|
|
|
|
if( l.clsid_test_network_type == CLSID_NULL)
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: got null, try to clone" );
|
|
goto TryToClone;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: put TS" );
|
|
hr = p_scanning_tuner->put_TuningSpace( l.p_test_tuning_space );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: "\
|
|
"cannot put tuning space: hr=0x%8lx", hr );
|
|
goto TryToClone;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: get default locator" );
|
|
hr = l.p_test_tuning_space->get_DefaultLocator( &l.p_locator );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: "\
|
|
"cannot get default locator: hr=0x%8lx", hr );
|
|
goto TryToClone;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: create tune request" );
|
|
hr = l.p_test_tuning_space->CreateTuneRequest( &l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: "\
|
|
"cannot create tune request: hr=0x%8lx", hr );
|
|
goto TryToClone;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: put locator" );
|
|
hr = l.p_tune_request->put_Locator( l.p_locator );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: "\
|
|
"cannot put locator: hr=0x%8lx", hr );
|
|
goto TryToClone;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: try to validate tune request" );
|
|
hr = p_scanning_tuner->Validate( l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "SetUpTuner: "\
|
|
"Tune Request cannot be validated: hr=0x%8lx", hr );
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: Attach tune request to Scanning Tuner");
|
|
/* increments ref count for scanning tuner */
|
|
hr = p_scanning_tuner->put_TuneRequest( l.p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "SetUpTuner: "\
|
|
"Cannot submit the tune request: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
msg_Dbg( p_access, "SetUpTuner: Tuning Space and Tune Request created" );
|
|
return S_OK;
|
|
|
|
/* Get the SystemTuningSpaces container
|
|
* p_tuning_space_container->Refcount = 1 */
|
|
TryToClone:
|
|
msg_Warn( p_access, "SetUpTuner: won't try to clone " );
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* GetNextNetworkType
|
|
* helper function; this is probably best done as an Enumeration of
|
|
* network providers
|
|
*****************************************************************************/
|
|
HRESULT BDAGraph::GetNextNetworkType( CLSID* clsid_this_network_type )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if( *clsid_this_network_type == CLSID_NULL )
|
|
{
|
|
msg_Dbg( p_access, "GetNextNetworkType: DVB-C" );
|
|
*clsid_this_network_type = CLSID_DVBCNetworkProvider;
|
|
return S_OK;
|
|
}
|
|
if( *clsid_this_network_type == CLSID_DVBCNetworkProvider )
|
|
{
|
|
msg_Dbg( p_access, "GetNextNetworkType: DVB-T" );
|
|
*clsid_this_network_type = CLSID_DVBTNetworkProvider;
|
|
return S_OK;
|
|
}
|
|
if( *clsid_this_network_type == CLSID_DVBTNetworkProvider )
|
|
{
|
|
msg_Dbg( p_access, "GetNextNetworkType: DVB-S" );
|
|
*clsid_this_network_type = CLSID_DVBSNetworkProvider;
|
|
return S_OK;
|
|
}
|
|
if( *clsid_this_network_type == CLSID_DVBSNetworkProvider )
|
|
{
|
|
msg_Dbg( p_access, "GetNextNetworkType: ATSC" );
|
|
*clsid_this_network_type = CLSID_ATSCNetworkProvider;
|
|
return S_OK;
|
|
}
|
|
msg_Dbg( p_access, "GetNextNetworkType: failed" );
|
|
*clsid_this_network_type = CLSID_NULL;
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* Check
|
|
*******************************************************************************
|
|
* Check if tuner supports this network type
|
|
*
|
|
* on success, sets globals:
|
|
* systems, l_tuner_used, p_network_provider, p_scanning_tuner, p_tuner_device,
|
|
* p_tuning_space, p_filter_graph
|
|
******************************************************************************/
|
|
HRESULT BDAGraph::Check( REFCLSID clsid_this_network_type )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuningSpaceContainer* p_tuning_space_container;
|
|
|
|
localComPtr():
|
|
p_tuning_space_container(NULL)
|
|
{};
|
|
~localComPtr()
|
|
{
|
|
if( p_tuning_space_container )
|
|
p_tuning_space_container->Release();
|
|
}
|
|
} l;
|
|
|
|
msg_Dbg( p_access, "Check: entering ");
|
|
|
|
/* Note that the systems global is persistent across Destroy().
|
|
* So we need to see if a tuner has been physically removed from
|
|
* the system since the last Check. Before we do anything,
|
|
* assume that this Check will fail and remove this network type
|
|
* from systems. It will be restored if the Check passes.
|
|
*/
|
|
|
|
systems &= ~( GetSystem( clsid_this_network_type ) );
|
|
|
|
|
|
/* If we have already have a filter graph, rebuild it*/
|
|
msg_Dbg( p_access, "Check: Destroying filter graph" );
|
|
if( p_filter_graph )
|
|
Destroy();
|
|
p_filter_graph = NULL;
|
|
hr = ::CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &p_filter_graph ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Check: "\
|
|
"Cannot CoCreate IFilterGraph: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* First filter in the graph is the Network Provider and
|
|
* its Scanning Tuner which takes the Tune Request */
|
|
if( p_network_provider )
|
|
p_network_provider->Release();
|
|
p_network_provider = NULL;
|
|
hr = ::CoCreateInstance( clsid_this_network_type, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS( &p_network_provider ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Check: "\
|
|
"Cannot CoCreate Network Provider: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
msg_Dbg( p_access, "Check: adding Network Provider to graph");
|
|
hr = p_filter_graph->AddFilter( p_network_provider, L"Network Provider" );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Check: "\
|
|
"Cannot load network provider: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* Add the Network Tuner to the Network Provider. On subsequent calls,
|
|
* l_tuner_used will cause a different tuner to be selected.
|
|
*
|
|
* To select a specific device first get the parameter that nominates the
|
|
* device (dvb-adapter) and use the value to initialise l_tuner_used.
|
|
* Note that dvb-adapter is 1-based, while l_tuner_used is 0-based.
|
|
* When FindFilter returns, check the contents of l_tuner_used.
|
|
* If it is not what was selected, then the requested device was not
|
|
* available, so return with an error. */
|
|
|
|
long l_adapter = -1;
|
|
l_adapter = var_GetInteger( p_access, "dvb-adapter" );
|
|
if( l_tuner_used < 0 && l_adapter >= 0 )
|
|
l_tuner_used = l_adapter - 1;
|
|
|
|
/* If tuner is in cold state, we have to do a successful put_TuneRequest
|
|
* before it will Connect. */
|
|
msg_Dbg( p_access, "Check: Creating Scanning Tuner");
|
|
if( p_scanning_tuner )
|
|
p_scanning_tuner->Release();
|
|
p_scanning_tuner = NULL;
|
|
hr = p_network_provider->QueryInterface( IID_PPV_ARGS( &p_scanning_tuner ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Check: "\
|
|
"Cannot QI Network Provider for Scanning Tuner: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* try to set up p_scanning_tuner */
|
|
msg_Dbg( p_access, "Check: Calling SetUpTuner" );
|
|
hr = SetUpTuner( clsid_this_network_type );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Check: "\
|
|
"Cannot set up scanner in Check mode: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
msg_Dbg( p_access, "Check: "\
|
|
"Calling FindFilter for KSCATEGORY_BDA_NETWORK_TUNER with "\
|
|
"p_network_provider; l_tuner_used=%ld", l_tuner_used );
|
|
hr = FindFilter( KSCATEGORY_BDA_NETWORK_TUNER, &l_tuner_used,
|
|
p_network_provider, &p_tuner_device );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Check: "\
|
|
"Cannot load tuner device and connect network provider: "\
|
|
"hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
if( l_adapter > 0 && l_tuner_used != l_adapter )
|
|
{
|
|
msg_Warn( p_access, "Check: "\
|
|
"Tuner device %ld is not available", l_adapter );
|
|
return E_FAIL;
|
|
}
|
|
|
|
msg_Dbg( p_access, "Check: Using adapter %ld", l_tuner_used );
|
|
/* success!
|
|
* already set l_tuner_used,
|
|
* p_tuning_space
|
|
*/
|
|
msg_Dbg( p_access, "Check: check succeeded: hr=0x%8lx", hr );
|
|
systems |= GetSystem( clsid_this_network_type );
|
|
msg_Dbg( p_access, "Check: returning from Check mode" );
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* Build
|
|
*******************************************************************************
|
|
* Build the Filter Graph
|
|
*
|
|
* connects filters and
|
|
* creates the media control and registers the graph
|
|
* on success, sets globals:
|
|
* d_graph_register, p_media_control, p_grabber, p_sample_grabber,
|
|
* p_mpeg_demux, p_transport_info
|
|
******************************************************************************/
|
|
HRESULT BDAGraph::Build()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
long l_capture_used;
|
|
long l_tif_used;
|
|
AM_MEDIA_TYPE grabber_media_type;
|
|
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ITuningSpaceContainer* p_tuning_space_container;
|
|
localComPtr():
|
|
p_tuning_space_container(NULL)
|
|
{};
|
|
~localComPtr()
|
|
{
|
|
if( p_tuning_space_container )
|
|
p_tuning_space_container->Release();
|
|
}
|
|
} l;
|
|
|
|
msg_Dbg( p_access, "Build: entering");
|
|
|
|
/* at this point, you've connected to a scanning tuner of the right
|
|
* network type.
|
|
*/
|
|
if( !p_scanning_tuner || !p_tuner_device )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Scanning Tuner does not exist" );
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = p_scanning_tuner->get_TuneRequest( &p_tune_request );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: no tune request" );
|
|
return hr;
|
|
}
|
|
hr = p_scanning_tuner->get_TuningSpace( &p_tuning_space );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: no tuning space" );
|
|
return hr;
|
|
}
|
|
hr = p_tuning_space->get__NetworkType( &clsid_network_type );
|
|
|
|
|
|
/* Always look for all capture devices to match the Network Tuner */
|
|
l_capture_used = -1;
|
|
msg_Dbg( p_access, "Build: "\
|
|
"Calling FindFilter for KSCATEGORY_BDA_RECEIVER_COMPONENT with "\
|
|
"p_tuner_device; l_capture_used=%ld", l_capture_used );
|
|
hr = FindFilter( KSCATEGORY_BDA_RECEIVER_COMPONENT, &l_capture_used,
|
|
p_tuner_device, &p_capture_device );
|
|
if( FAILED( hr ) )
|
|
{
|
|
/* Some BDA drivers do not provide a Capture Device Filter so force
|
|
* the Sample Grabber to connect directly to the Tuner Device */
|
|
msg_Dbg( p_access, "Build: "\
|
|
"Cannot find Capture device. Connect to tuner "\
|
|
"and AddRef() : hr=0x%8lx", hr );
|
|
p_capture_device = p_tuner_device;
|
|
p_capture_device->AddRef();
|
|
}
|
|
|
|
if( p_sample_grabber )
|
|
p_sample_grabber->Release();
|
|
p_sample_grabber = NULL;
|
|
/* Insert the Sample Grabber to tap into the Transport Stream. */
|
|
hr = ::CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS( &p_sample_grabber ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot load Sample Grabber Filter: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
hr = p_filter_graph->AddFilter( p_sample_grabber, L"Sample Grabber" );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot add Sample Grabber Filter to graph: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* create the sample grabber */
|
|
if( p_grabber )
|
|
p_grabber->Release();
|
|
p_grabber = NULL;
|
|
hr = p_sample_grabber->QueryInterface( IID_PPV_ARGS( &p_grabber ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot QI Sample Grabber Filter: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* Try the possible stream type */
|
|
hr = E_FAIL;
|
|
for( int i = 0; i < 2; i++ )
|
|
{
|
|
ZeroMemory( &grabber_media_type, sizeof( AM_MEDIA_TYPE ) );
|
|
grabber_media_type.majortype = MEDIATYPE_Stream;
|
|
grabber_media_type.subtype = i == 0 ? MEDIASUBTYPE_MPEG2_TRANSPORT : KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT;
|
|
msg_Dbg( p_access, "Build: "
|
|
"Trying connecting with subtype %s",
|
|
i == 0 ? "MEDIASUBTYPE_MPEG2_TRANSPORT" : "KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT" );
|
|
hr = p_grabber->SetMediaType( &grabber_media_type );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = Connect( p_capture_device, p_sample_grabber );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Build: "\
|
|
"Connected capture device to sample grabber" );
|
|
break;
|
|
}
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot connect Sample Grabber to Capture device: hr=0x%8lx (try %d/2)", hr, 1+i );
|
|
}
|
|
else
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot set media type on grabber filter: hr=0x%8lx (try %d/2", hr, 1+i );
|
|
}
|
|
}
|
|
msg_Dbg( p_access, "Build: This is where it used to return upon success" );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot use capture device: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* We need the MPEG2 Demultiplexer even though we are going to use the VLC
|
|
* TS demuxer. The TIF filter connects to the MPEG2 demux and works with
|
|
* the Network Provider filter to set up the stream */
|
|
//msg_Dbg( p_access, "Build: using MPEG2 demux" );
|
|
if( p_mpeg_demux )
|
|
p_mpeg_demux->Release();
|
|
p_mpeg_demux = NULL;
|
|
hr = ::CoCreateInstance( CLSID_MPEG2Demultiplexer, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_PPV_ARGS( &p_mpeg_demux ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot CoCreateInstance MPEG2 Demultiplexer: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
//msg_Dbg( p_access, "Build: adding demux" );
|
|
hr = p_filter_graph->AddFilter( p_mpeg_demux, L"Demux" );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot add demux filter to graph: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
hr = Connect( p_sample_grabber, p_mpeg_demux );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot connect demux to grabber: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
//msg_Dbg( p_access, "Build: Connected sample grabber to demux" );
|
|
/* Always look for the Transport Information Filter from the start
|
|
* of the collection*/
|
|
l_tif_used = -1;
|
|
msg_Dbg( p_access, "Check: "\
|
|
"Calling FindFilter for KSCATEGORY_BDA_TRANSPORT_INFORMATION with "\
|
|
"p_mpeg_demux; l_tif_used=%ld", l_tif_used );
|
|
|
|
|
|
hr = FindFilter( KSCATEGORY_BDA_TRANSPORT_INFORMATION, &l_tif_used,
|
|
p_mpeg_demux, &p_transport_info );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot load TIF onto demux: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* Configure the Sample Grabber to buffer the samples continuously */
|
|
hr = p_grabber->SetBufferSamples( true );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot set Sample Grabber to buffering: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
hr = p_grabber->SetOneShot( false );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot set Sample Grabber to multi shot: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* Second parameter to SetCallback specifies the callback method; 0 uses
|
|
* the ISampleGrabberCB::SampleCB method, which receives an IMediaSample
|
|
* pointer. */
|
|
//msg_Dbg( p_access, "Build: Adding grabber callback" );
|
|
hr = p_grabber->SetCallback( this, 0 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot set SampleGrabber Callback: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
hr = Register(); /* creates d_graph_register */
|
|
if( FAILED( hr ) )
|
|
{
|
|
d_graph_register = 0;
|
|
msg_Dbg( p_access, "Build: "\
|
|
"Cannot register graph: hr=0x%8lx", hr );
|
|
}
|
|
|
|
/* The Media Control is used to Run and Stop the Graph */
|
|
hr = p_filter_graph->QueryInterface(IID_PPV_ARGS(&p_media_control));
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Build: "\
|
|
"Cannot QI Media Control: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* success! */
|
|
//msg_Dbg( p_access, "Build: succeeded: hr=0x%8lx", hr );
|
|
return S_OK;
|
|
}
|
|
|
|
/* debugging */
|
|
HRESULT BDAGraph::ListFilters( REFCLSID this_clsid )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
class localComPtr
|
|
{
|
|
public:
|
|
ICreateDevEnum* p_local_system_dev_enum;
|
|
IEnumMoniker* p_moniker_enum;
|
|
IMoniker* p_moniker;
|
|
IBaseFilter* p_filter;
|
|
IBaseFilter* p_this_filter;
|
|
IBindCtx* p_bind_context;
|
|
IPropertyBag* p_property_bag;
|
|
|
|
char* psz_downstream;
|
|
char* psz_bstr;
|
|
int i_bstr_len;
|
|
|
|
localComPtr():
|
|
p_local_system_dev_enum(NULL),
|
|
p_moniker_enum(NULL),
|
|
p_moniker(NULL),
|
|
p_filter(NULL),
|
|
p_this_filter(NULL),
|
|
p_bind_context( NULL ),
|
|
p_property_bag(NULL),
|
|
psz_downstream( NULL ),
|
|
psz_bstr( NULL )
|
|
{}
|
|
~localComPtr()
|
|
{
|
|
if( p_property_bag )
|
|
p_property_bag->Release();
|
|
if( p_bind_context )
|
|
p_bind_context->Release();
|
|
if( p_filter )
|
|
p_filter->Release();
|
|
if( p_this_filter )
|
|
p_this_filter->Release();
|
|
if( p_moniker )
|
|
p_moniker->Release();
|
|
if( p_moniker_enum )
|
|
p_moniker_enum->Release();
|
|
if( p_local_system_dev_enum )
|
|
p_local_system_dev_enum->Release();
|
|
if( psz_bstr )
|
|
delete[] psz_bstr;
|
|
if( psz_downstream )
|
|
delete[] psz_downstream;
|
|
}
|
|
} l;
|
|
|
|
|
|
// msg_Dbg( p_access, "ListFilters: Create local system_dev_enum");
|
|
if( l.p_local_system_dev_enum )
|
|
l.p_local_system_dev_enum->Release();
|
|
l.p_local_system_dev_enum = NULL;
|
|
hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &l.p_local_system_dev_enum ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "ListFilters: "\
|
|
"Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
//msg_Dbg( p_access, "ListFilters: Create p_moniker_enum");
|
|
if( l.p_moniker_enum )
|
|
l.p_moniker_enum->Release();
|
|
l.p_moniker_enum = NULL;
|
|
hr = l.p_local_system_dev_enum->CreateClassEnumerator( this_clsid,
|
|
&l.p_moniker_enum, 0 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "ListFilters: "\
|
|
"Cannot CreateClassEnumerator: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
//msg_Dbg( p_access, "ListFilters: Entering main loop" );
|
|
do
|
|
{
|
|
/* We are overwriting l.p_moniker so we should Release and nullify
|
|
* It is important that p_moniker and p_property_bag are fully released
|
|
* l.p_filter may not be dereferenced so we could force to NULL */
|
|
/* msg_Dbg( p_access, "ListFilters: top of main loop");*/
|
|
//msg_Dbg( p_access, "ListFilters: releasing property bag");
|
|
if( l.p_property_bag )
|
|
l.p_property_bag->Release();
|
|
l.p_property_bag = NULL;
|
|
//msg_Dbg( p_access, "ListFilters: releasing filter");
|
|
if( l.p_filter )
|
|
l.p_filter->Release();
|
|
l.p_filter = NULL;
|
|
//msg_Dbg( p_access, "ListFilters: releasing bind context");
|
|
if( l.p_bind_context )
|
|
l.p_bind_context->Release();
|
|
l.p_bind_context = NULL;
|
|
//msg_Dbg( p_access, "ListFilters: releasing moniker");
|
|
if( l.p_moniker )
|
|
l.p_moniker->Release();
|
|
l.p_moniker = NULL;
|
|
//msg_Dbg( p_access, "ListFilters: trying a moniker");
|
|
|
|
if( !l.p_moniker_enum ) break;
|
|
hr = l.p_moniker_enum->Next( 1, &l.p_moniker, 0 );
|
|
if( hr != S_OK ) break;
|
|
|
|
/* l.p_bind_context is Released at the top of the loop */
|
|
hr = CreateBindCtx( 0, &l.p_bind_context );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "ListFilters: "\
|
|
"Cannot create bind_context, trying another: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
/* l.p_filter is Released at the top of the loop */
|
|
hr = l.p_moniker->BindToObject( l.p_bind_context, NULL,
|
|
IID_PPV_ARGS( &l.p_filter ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "ListFilters: "\
|
|
"Cannot create p_filter, trying another: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
#ifdef DEBUG_MONIKER_NAME
|
|
WCHAR* pwsz_downstream = NULL;
|
|
|
|
hr = l.p_moniker->GetDisplayName(l.p_bind_context, NULL,
|
|
&pwsz_downstream );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "ListFilters: "\
|
|
"Cannot get display name, trying another: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
if( l.psz_downstream )
|
|
delete[] l.psz_downstream;
|
|
l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0, pwsz_downstream, -1,
|
|
l.psz_downstream, 0, NULL, NULL );
|
|
l.psz_downstream = new char[ l.i_bstr_len ];
|
|
l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0, pwsz_downstream, -1,
|
|
l.psz_downstream, l.i_bstr_len, NULL, NULL );
|
|
|
|
LPMALLOC p_alloc;
|
|
::CoGetMalloc( 1, &p_alloc );
|
|
p_alloc->Free( pwsz_downstream );
|
|
p_alloc->Release();
|
|
msg_Dbg( p_access, "ListFilters: "\
|
|
"Moniker name is %s", l.psz_downstream );
|
|
#else
|
|
l.psz_downstream = strdup( "Downstream" );
|
|
#endif
|
|
/* l.p_property_bag is released at the top of the loop */
|
|
hr = l.p_moniker->BindToStorage( NULL, NULL,
|
|
IID_PPV_ARGS( &l.p_property_bag ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "ListFilters: "\
|
|
"Cannot Bind to Property Bag: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
//msg_Dbg( p_access, "ListFilters: displaying another" );
|
|
}
|
|
while( true );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* FindFilter
|
|
* Looks up all filters in a category and connects to the upstream filter until
|
|
* a successful match is found. The index of the connected filter is returned.
|
|
* On subsequent calls, this can be used to start from that point to find
|
|
* another match.
|
|
* This is used when the graph does not run because a tuner device is in use so
|
|
* another one needs to be selected.
|
|
******************************************************************************/
|
|
HRESULT BDAGraph::FindFilter( REFCLSID this_clsid, long* i_moniker_used,
|
|
IBaseFilter* p_upstream, IBaseFilter** p_p_downstream )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int i_moniker_index = -1;
|
|
class localComPtr
|
|
{
|
|
public:
|
|
IEnumMoniker* p_moniker_enum;
|
|
IMoniker* p_moniker;
|
|
IBindCtx* p_bind_context;
|
|
IPropertyBag* p_property_bag;
|
|
char* psz_upstream;
|
|
|
|
char* psz_downstream;
|
|
VARIANT var_bstr;
|
|
int i_bstr_len;
|
|
localComPtr():
|
|
p_moniker_enum(NULL),
|
|
p_moniker(NULL),
|
|
p_bind_context( NULL ),
|
|
p_property_bag(NULL),
|
|
psz_upstream( NULL ),
|
|
psz_downstream( NULL )
|
|
{
|
|
::VariantInit(&var_bstr);
|
|
}
|
|
~localComPtr()
|
|
{
|
|
if( p_moniker_enum )
|
|
p_moniker_enum->Release();
|
|
if( p_moniker )
|
|
p_moniker->Release();
|
|
if( p_bind_context )
|
|
p_bind_context->Release();
|
|
if( p_property_bag )
|
|
p_property_bag->Release();
|
|
if( psz_upstream )
|
|
delete[] psz_upstream;
|
|
if( psz_downstream )
|
|
delete[] psz_downstream;
|
|
|
|
::VariantClear(&var_bstr);
|
|
}
|
|
} l;
|
|
|
|
/* create system_dev_enum the first time through, or preserve the
|
|
* existing one to loop through classes */
|
|
if( !p_system_dev_enum )
|
|
{
|
|
msg_Dbg( p_access, "FindFilter: Create p_system_dev_enum");
|
|
hr = ::CoCreateInstance( CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC,
|
|
IID_PPV_ARGS( &p_system_dev_enum ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "FindFilter: "\
|
|
"Cannot CoCreate SystemDeviceEnum: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
msg_Dbg( p_access, "FindFilter: Create p_moniker_enum");
|
|
hr = p_system_dev_enum->CreateClassEnumerator( this_clsid,
|
|
&l.p_moniker_enum, 0 );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "FindFilter: "\
|
|
"Cannot CreateClassEnumerator: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
msg_Dbg( p_access, "FindFilter: get filter name");
|
|
hr = GetFilterName( p_upstream, &l.psz_upstream );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "FindFilter: "\
|
|
"Cannot GetFilterName: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"called with i_moniker_used=%ld, " \
|
|
"p_upstream = %s", *i_moniker_used, l.psz_upstream );
|
|
|
|
do
|
|
{
|
|
/* We are overwriting l.p_moniker so we should Release and nullify
|
|
* It is important that p_moniker and p_property_bag are fully released */
|
|
msg_Dbg( p_access, "FindFilter: top of main loop");
|
|
if( l.p_property_bag )
|
|
l.p_property_bag->Release();
|
|
l.p_property_bag = NULL;
|
|
msg_Dbg( p_access, "FindFilter: releasing bind context");
|
|
if( l.p_bind_context )
|
|
l.p_bind_context->Release();
|
|
l.p_bind_context = NULL;
|
|
msg_Dbg( p_access, "FindFilter: releasing moniker");
|
|
if( l.p_moniker )
|
|
l.p_moniker->Release();
|
|
msg_Dbg( p_access, "FindFilter: null moniker");
|
|
l.p_moniker = NULL;
|
|
|
|
msg_Dbg( p_access, "FindFilter: quit if no enum");
|
|
if( !l.p_moniker_enum ) break;
|
|
msg_Dbg( p_access, "FindFilter: trying a moniker");
|
|
hr = l.p_moniker_enum->Next( 1, &l.p_moniker, 0 );
|
|
if( hr != S_OK ) break;
|
|
|
|
i_moniker_index++;
|
|
|
|
/* Skip over devices already found on previous calls */
|
|
msg_Dbg( p_access, "FindFilter: skip previously found devices");
|
|
|
|
if( i_moniker_index <= *i_moniker_used ) continue;
|
|
*i_moniker_used = i_moniker_index;
|
|
|
|
/* l.p_bind_context is Released at the top of the loop */
|
|
msg_Dbg( p_access, "FindFilter: create bind context");
|
|
hr = CreateBindCtx( 0, &l.p_bind_context );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"Cannot create bind_context, trying another: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
msg_Dbg( p_access, "FindFilter: try to create downstream filter");
|
|
*p_p_downstream = NULL;
|
|
hr = l.p_moniker->BindToObject( l.p_bind_context, NULL,
|
|
IID_PPV_ARGS( p_p_downstream ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"Cannot bind to downstream, trying another: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
#ifdef DEBUG_MONIKER_NAME
|
|
msg_Dbg( p_access, "FindFilter: get downstream filter name");
|
|
|
|
WCHAR* pwsz_downstream = NULL;
|
|
|
|
hr = l.p_moniker->GetDisplayName(l.p_bind_context, NULL,
|
|
&pwsz_downstream );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"Cannot get display name, trying another: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
if( l.psz_downstream )
|
|
delete[] l.psz_downstream;
|
|
l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0, pwsz_downstream, -1,
|
|
l.psz_downstream, 0, NULL, NULL );
|
|
l.psz_downstream = new char[ l.i_bstr_len ];
|
|
l.i_bstr_len = WideCharToMultiByte( CP_ACP, 0, pwsz_downstream, -1,
|
|
l.psz_downstream, l.i_bstr_len, NULL, NULL );
|
|
|
|
LPMALLOC p_alloc;
|
|
::CoGetMalloc( 1, &p_alloc );
|
|
p_alloc->Free( pwsz_downstream );
|
|
p_alloc->Release();
|
|
#else
|
|
l.psz_downstream = strdup( "Downstream" );
|
|
#endif
|
|
|
|
/* l.p_property_bag is released at the top of the loop */
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"Moniker name is %s, binding to storage", l.psz_downstream );
|
|
hr = l.p_moniker->BindToStorage( l.p_bind_context, NULL,
|
|
IID_PPV_ARGS( &l.p_property_bag ) );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"Cannot Bind to Property Bag: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
msg_Dbg( p_access, "FindFilter: read friendly name");
|
|
hr = l.p_property_bag->Read( L"FriendlyName", &l.var_bstr, NULL );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"Cannot read friendly name, next?: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
msg_Dbg( p_access, "FindFilter: add filter to graph" );
|
|
hr = p_filter_graph->AddFilter( *p_p_downstream, l.var_bstr.bstrVal );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"Cannot add filter, trying another: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
|
|
msg_Dbg( p_access, "FindFilter: "\
|
|
"Trying to Connect %s to %s", l.psz_upstream, l.psz_downstream );
|
|
hr = Connect( p_upstream, *p_p_downstream );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "FindFilter: Connected %s", l.psz_downstream );
|
|
return S_OK;
|
|
}
|
|
|
|
/* Not the filter we want so unload and try the next one */
|
|
/* Warning: RemoveFilter does an undocumented Release()
|
|
* on pointer but does not set it to NULL */
|
|
msg_Dbg( p_access, "FindFilter: Removing filter" );
|
|
hr = p_filter_graph->RemoveFilter( *p_p_downstream );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "FindFilter: "\
|
|
"Failed unloading Filter: hr=0x%8lx", hr );
|
|
continue;
|
|
}
|
|
msg_Dbg( p_access, "FindFilter: trying another" );
|
|
}
|
|
while( true );
|
|
|
|
/* nothing found */
|
|
msg_Warn( p_access, "FindFilter: No filter connected" );
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Connect is called from Build to enumerate and connect pins
|
|
*****************************************************************************/
|
|
HRESULT BDAGraph::Connect( IBaseFilter* p_upstream, IBaseFilter* p_downstream )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
class localComPtr
|
|
{
|
|
public:
|
|
IPin* p_pin_upstream;
|
|
IPin* p_pin_downstream;
|
|
IEnumPins* p_pin_upstream_enum;
|
|
IEnumPins* p_pin_downstream_enum;
|
|
IPin* p_pin_temp;
|
|
char* psz_upstream;
|
|
char* psz_downstream;
|
|
|
|
localComPtr():
|
|
p_pin_upstream(NULL), p_pin_downstream(NULL),
|
|
p_pin_upstream_enum(NULL), p_pin_downstream_enum(NULL),
|
|
p_pin_temp(NULL),
|
|
psz_upstream( NULL ),
|
|
psz_downstream( NULL )
|
|
{ };
|
|
~localComPtr()
|
|
{
|
|
if( p_pin_temp )
|
|
p_pin_temp->Release();
|
|
if( p_pin_downstream )
|
|
p_pin_downstream->Release();
|
|
if( p_pin_upstream )
|
|
p_pin_upstream->Release();
|
|
if( p_pin_downstream_enum )
|
|
p_pin_downstream_enum->Release();
|
|
if( p_pin_upstream_enum )
|
|
p_pin_upstream_enum->Release();
|
|
if( psz_upstream )
|
|
delete[] psz_upstream;
|
|
if( psz_downstream )
|
|
delete[] psz_downstream;
|
|
}
|
|
} l;
|
|
|
|
PIN_DIRECTION pin_dir;
|
|
|
|
//msg_Dbg( p_access, "Connect: entering" );
|
|
hr = p_upstream->EnumPins( &l.p_pin_upstream_enum );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Connect: "\
|
|
"Cannot get upstream filter enumerator: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
do
|
|
{
|
|
/* Release l.p_pin_upstream before next iteration */
|
|
if( l.p_pin_upstream )
|
|
l.p_pin_upstream ->Release();
|
|
l.p_pin_upstream = NULL;
|
|
if( !l.p_pin_upstream_enum ) break;
|
|
hr = l.p_pin_upstream_enum->Next( 1, &l.p_pin_upstream, 0 );
|
|
if( hr != S_OK ) break;
|
|
|
|
//msg_Dbg( p_access, "Connect: get pin name");
|
|
hr = GetPinName( l.p_pin_upstream, &l.psz_upstream );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Connect: "\
|
|
"Cannot GetPinName: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
//msg_Dbg( p_access, "Connect: p_pin_upstream = %s", l.psz_upstream );
|
|
|
|
hr = l.p_pin_upstream->QueryDirection( &pin_dir );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Connect: "\
|
|
"Cannot get upstream filter pin direction: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
hr = l.p_pin_upstream->ConnectedTo( &l.p_pin_downstream );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
l.p_pin_downstream->Release();
|
|
l.p_pin_downstream = NULL;
|
|
}
|
|
|
|
if( FAILED( hr ) && hr != VFW_E_NOT_CONNECTED )
|
|
{
|
|
msg_Warn( p_access, "Connect: "\
|
|
"Cannot check upstream filter connection: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
if( ( pin_dir == PINDIR_OUTPUT ) && ( hr == VFW_E_NOT_CONNECTED ) )
|
|
{
|
|
/* The upstream pin is not yet connected so check each pin on the
|
|
* downstream filter */
|
|
//msg_Dbg( p_access, "Connect: enumerating downstream pins" );
|
|
hr = p_downstream->EnumPins( &l.p_pin_downstream_enum );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Connect: Cannot get "\
|
|
"downstream filter enumerator: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
do
|
|
{
|
|
/* Release l.p_pin_downstream before next iteration */
|
|
if( l.p_pin_downstream )
|
|
l.p_pin_downstream ->Release();
|
|
l.p_pin_downstream = NULL;
|
|
if( !l.p_pin_downstream_enum ) break;
|
|
hr = l.p_pin_downstream_enum->Next( 1, &l.p_pin_downstream, 0 );
|
|
if( hr != S_OK ) break;
|
|
|
|
//msg_Dbg( p_access, "Connect: get pin name");
|
|
hr = GetPinName( l.p_pin_downstream, &l.psz_downstream );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Connect: "\
|
|
"Cannot GetPinName: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
/*
|
|
msg_Dbg( p_access, "Connect: Trying p_downstream = %s",
|
|
l.psz_downstream );
|
|
*/
|
|
|
|
hr = l.p_pin_downstream->QueryDirection( &pin_dir );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Connect: Cannot get "\
|
|
"downstream filter pin direction: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
/* Looking for a free Pin to connect to
|
|
* A connected Pin may have an reference count > 1
|
|
* so Release and nullify the pointer */
|
|
hr = l.p_pin_downstream->ConnectedTo( &l.p_pin_temp );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
l.p_pin_temp->Release();
|
|
l.p_pin_temp = NULL;
|
|
}
|
|
|
|
if( hr != VFW_E_NOT_CONNECTED )
|
|
{
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Connect: Cannot check "\
|
|
"downstream filter connection: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if( ( pin_dir == PINDIR_INPUT ) &&
|
|
( hr == VFW_E_NOT_CONNECTED ) )
|
|
{
|
|
//msg_Dbg( p_access, "Connect: trying to connect pins" );
|
|
|
|
hr = p_filter_graph->ConnectDirect( l.p_pin_upstream,
|
|
l.p_pin_downstream, NULL );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
/* If we arrive here then we have a matching pair of
|
|
* pins. */
|
|
return S_OK;
|
|
}
|
|
}
|
|
/* If we arrive here it means this downstream pin is not
|
|
* suitable so try the next downstream pin.
|
|
* l.p_pin_downstream is released at the top of the loop */
|
|
}
|
|
while( true );
|
|
/* If we arrive here then we ran out of pins before we found a
|
|
* suitable one. Release outstanding refcounts */
|
|
if( l.p_pin_downstream_enum )
|
|
l.p_pin_downstream_enum->Release();
|
|
l.p_pin_downstream_enum = NULL;
|
|
if( l.p_pin_downstream )
|
|
l.p_pin_downstream->Release();
|
|
l.p_pin_downstream = NULL;
|
|
}
|
|
/* If we arrive here it means this upstream pin is not suitable
|
|
* so try the next upstream pin
|
|
* l.p_pin_upstream is released at the top of the loop */
|
|
}
|
|
while( true );
|
|
/* If we arrive here it means we did not find any pair of suitable pins
|
|
* Outstanding refcounts are released in the destructor */
|
|
//msg_Dbg( p_access, "Connect: No pins connected" );
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Start uses MediaControl to start the graph
|
|
*****************************************************************************/
|
|
HRESULT BDAGraph::Start()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
OAFilterState i_state; /* State_Stopped, State_Paused, State_Running */
|
|
|
|
msg_Dbg( p_access, "Start: entering" );
|
|
|
|
if( !p_media_control )
|
|
{
|
|
msg_Warn( p_access, "Start: Media Control has not been created" );
|
|
return E_FAIL;
|
|
}
|
|
|
|
msg_Dbg( p_access, "Start: Run()" );
|
|
hr = p_media_control->Run();
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Start: Graph started, hr=0x%lX", hr );
|
|
return S_OK;
|
|
}
|
|
|
|
msg_Dbg( p_access, "Start: would not start, will retry" );
|
|
/* Query the state of the graph - timeout after 100 milliseconds */
|
|
while( (hr = p_media_control->GetState( 100, &i_state) ) != S_OK )
|
|
{
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access,
|
|
"Start: Cannot get Graph state: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
msg_Dbg( p_access, "Start: got state" );
|
|
if( i_state == State_Running )
|
|
{
|
|
msg_Dbg( p_access, "Graph started after a delay, hr=0x%lX", hr );
|
|
return S_OK;
|
|
}
|
|
|
|
/* The Graph is not running so stop it and return an error */
|
|
msg_Warn( p_access, "Start: Graph not started: %d", (int)i_state );
|
|
hr = p_media_control->StopWhenReady(); /* Instead of Stop() */
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access,
|
|
"Start: Cannot stop Graph after Run failed: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Pop the stream of data
|
|
*****************************************************************************/
|
|
ssize_t BDAGraph::Pop(void *buf, size_t len, int ms)
|
|
{
|
|
return output.Pop(buf, len, ms);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* SampleCB - Callback when the Sample Grabber has a sample
|
|
******************************************************************************/
|
|
STDMETHODIMP BDAGraph::SampleCB( double /*date*/, IMediaSample *p_sample )
|
|
{
|
|
if( p_sample->IsDiscontinuity() == S_OK )
|
|
msg_Warn( p_access, "BDA SampleCB: Sample Discontinuity.");
|
|
|
|
const size_t i_sample_size = p_sample->GetActualDataLength();
|
|
|
|
/* The buffer memory is owned by the media sample object, and is automatically
|
|
* released when the media sample is destroyed. The caller should not free or
|
|
* reallocate the buffer. */
|
|
BYTE *p_sample_data;
|
|
p_sample->GetPointer( &p_sample_data );
|
|
|
|
if( i_sample_size > 0 && p_sample_data )
|
|
{
|
|
block_t *p_block = block_Alloc( i_sample_size );
|
|
|
|
if( p_block )
|
|
{
|
|
memcpy( p_block->p_buffer, p_sample_data, i_sample_size );
|
|
output.Push( p_block );
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP BDAGraph::BufferCB( double /*date*/, BYTE* /*buffer*/,
|
|
long /*buffer_len*/ )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* removes each filter from the graph
|
|
******************************************************************************/
|
|
HRESULT BDAGraph::Destroy()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG mem_ref = 0;
|
|
// msg_Dbg( p_access, "Destroy: media control 1" );
|
|
if( p_media_control )
|
|
p_media_control->StopWhenReady(); /* Instead of Stop() */
|
|
|
|
// msg_Dbg( p_access, "Destroy: deregistering graph" );
|
|
if( d_graph_register )
|
|
Deregister();
|
|
|
|
// msg_Dbg( p_access, "Destroy: calling Empty" );
|
|
output.Empty();
|
|
|
|
// msg_Dbg( p_access, "Destroy: TIF" );
|
|
if( p_transport_info )
|
|
{
|
|
/* Warning: RemoveFilter does an undocumented Release()
|
|
* on pointer but does not set it to NULL */
|
|
hr = p_filter_graph->RemoveFilter( p_transport_info );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Failed unloading TIF: hr=0x%8lx", hr );
|
|
}
|
|
p_transport_info = NULL;
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: demux" );
|
|
if( p_mpeg_demux )
|
|
{
|
|
p_filter_graph->RemoveFilter( p_mpeg_demux );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Failed unloading demux: hr=0x%8lx", hr );
|
|
}
|
|
p_mpeg_demux = NULL;
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: sample grabber" );
|
|
if( p_grabber )
|
|
{
|
|
mem_ref = p_grabber->Release();
|
|
if( mem_ref != 0 )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Sample grabber mem_ref (varies): mem_ref=%ld", mem_ref );
|
|
}
|
|
p_grabber = NULL;
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: sample grabber filter" );
|
|
if( p_sample_grabber )
|
|
{
|
|
hr = p_filter_graph->RemoveFilter( p_sample_grabber );
|
|
p_sample_grabber = NULL;
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Failed unloading sampler: hr=0x%8lx", hr );
|
|
}
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: capture device" );
|
|
if( p_capture_device )
|
|
{
|
|
p_filter_graph->RemoveFilter( p_capture_device );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Failed unloading capture device: hr=0x%8lx", hr );
|
|
}
|
|
p_capture_device = NULL;
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: tuner device" );
|
|
if( p_tuner_device )
|
|
{
|
|
//msg_Dbg( p_access, "Destroy: remove filter on tuner device" );
|
|
hr = p_filter_graph->RemoveFilter( p_tuner_device );
|
|
//msg_Dbg( p_access, "Destroy: force tuner device to NULL" );
|
|
p_tuner_device = NULL;
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Failed unloading tuner device: hr=0x%8lx", hr );
|
|
}
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: scanning tuner" );
|
|
if( p_scanning_tuner )
|
|
{
|
|
mem_ref = p_scanning_tuner->Release();
|
|
if( mem_ref != 0 )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Scanning tuner mem_ref (normally 2 if warm, "\
|
|
"3 if active): mem_ref=%ld", mem_ref );
|
|
}
|
|
p_scanning_tuner = NULL;
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: net provider" );
|
|
if( p_network_provider )
|
|
{
|
|
hr = p_filter_graph->RemoveFilter( p_network_provider );
|
|
p_network_provider = NULL;
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Failed unloading net provider: hr=0x%8lx", hr );
|
|
}
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: filter graph" );
|
|
if( p_filter_graph )
|
|
{
|
|
mem_ref = p_filter_graph->Release();
|
|
if( mem_ref != 0 )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Filter graph mem_ref (normally 1 if active): mem_ref=%ld",
|
|
mem_ref );
|
|
}
|
|
p_filter_graph = NULL;
|
|
}
|
|
|
|
/* first call to FindFilter creates p_system_dev_enum */
|
|
|
|
// msg_Dbg( p_access, "Destroy: system dev enum" );
|
|
if( p_system_dev_enum )
|
|
{
|
|
mem_ref = p_system_dev_enum->Release();
|
|
if( mem_ref != 0 )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"System_dev_enum mem_ref: mem_ref=%ld", mem_ref );
|
|
}
|
|
p_system_dev_enum = NULL;
|
|
}
|
|
|
|
// msg_Dbg( p_access, "Destroy: media control 2" );
|
|
if( p_media_control.Get() )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: release media control" );
|
|
mem_ref = p_media_control.Reset();
|
|
if( mem_ref != 0 )
|
|
{
|
|
msg_Dbg( p_access, "Destroy: "\
|
|
"Media control mem_ref: mem_ref=%ld", mem_ref );
|
|
}
|
|
msg_Dbg( p_access, "Destroy: force media control to NULL" );
|
|
}
|
|
|
|
d_graph_register = 0;
|
|
l_tuner_used = -1;
|
|
clsid_network_type = CLSID_NULL;
|
|
|
|
// msg_Dbg( p_access, "Destroy: returning" );
|
|
return S_OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Add/Remove a DirectShow filter graph to/from the Running Object Table.
|
|
* Allows GraphEdit to "spy" on a remote filter graph.
|
|
******************************************************************************/
|
|
HRESULT BDAGraph::Register()
|
|
{
|
|
class localComPtr
|
|
{
|
|
public:
|
|
IMoniker* p_moniker;
|
|
IRunningObjectTable* p_ro_table;
|
|
localComPtr():
|
|
p_moniker(NULL),
|
|
p_ro_table(NULL)
|
|
{};
|
|
~localComPtr()
|
|
{
|
|
if( p_moniker )
|
|
p_moniker->Release();
|
|
if( p_ro_table )
|
|
p_ro_table->Release();
|
|
}
|
|
} l;
|
|
WCHAR pwsz_graph_name[128];
|
|
HRESULT hr;
|
|
|
|
hr = ::GetRunningObjectTable( 0, &l.p_ro_table );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Register: Cannot get ROT: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
size_t len = sizeof(pwsz_graph_name) / sizeof(pwsz_graph_name[0]);
|
|
_snwprintf( pwsz_graph_name, len - 1, L"VLC BDA Graph %08p Pid %08x",
|
|
p_filter_graph, ::GetCurrentProcessId() );
|
|
pwsz_graph_name[len-1] = 0;
|
|
hr = CreateItemMoniker( L"!", pwsz_graph_name, &l.p_moniker );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Register: Cannot Create Moniker: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
hr = l.p_ro_table->Register( ROTFLAGS_REGISTRATIONKEEPSALIVE,
|
|
p_filter_graph, l.p_moniker, &d_graph_register );
|
|
if( FAILED( hr ) )
|
|
{
|
|
msg_Warn( p_access, "Register: Cannot Register Graph: hr=0x%8lx", hr );
|
|
return hr;
|
|
}
|
|
|
|
msg_Dbg( p_access, "Register: registered Graph: %S", pwsz_graph_name );
|
|
return hr;
|
|
}
|
|
|
|
void BDAGraph::Deregister()
|
|
{
|
|
HRESULT hr;
|
|
IRunningObjectTable* p_ro_table;
|
|
hr = ::GetRunningObjectTable( 0, &p_ro_table );
|
|
/* docs say this does a Release() on d_graph_register stuff */
|
|
if( SUCCEEDED( hr ) )
|
|
p_ro_table->Revoke( d_graph_register );
|
|
d_graph_register = 0;
|
|
p_ro_table->Release();
|
|
}
|
|
|
|
HRESULT BDAGraph::GetFilterName( IBaseFilter* p_filter, char** psz_bstr_name )
|
|
{
|
|
FILTER_INFO filter_info;
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = p_filter->QueryFilterInfo(&filter_info);
|
|
if( FAILED( hr ) )
|
|
return hr;
|
|
int i_name_len = WideCharToMultiByte( CP_ACP, 0, filter_info.achName,
|
|
-1, *psz_bstr_name, 0, NULL, NULL );
|
|
*psz_bstr_name = new char[ i_name_len ];
|
|
i_name_len = WideCharToMultiByte( CP_ACP, 0, filter_info.achName,
|
|
-1, *psz_bstr_name, i_name_len, NULL, NULL );
|
|
|
|
// The FILTER_INFO structure holds a pointer to the Filter Graph
|
|
// Manager, with a reference count that must be released.
|
|
if( filter_info.pGraph )
|
|
filter_info.pGraph->Release();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT BDAGraph::GetPinName( IPin* p_pin, char** psz_bstr_name )
|
|
{
|
|
PIN_INFO pin_info;
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = p_pin->QueryPinInfo(&pin_info);
|
|
if( FAILED( hr ) )
|
|
return hr;
|
|
int i_name_len = WideCharToMultiByte( CP_ACP, 0, pin_info.achName,
|
|
-1, *psz_bstr_name, 0, NULL, NULL );
|
|
*psz_bstr_name = new char[ i_name_len ];
|
|
i_name_len = WideCharToMultiByte( CP_ACP, 0, pin_info.achName,
|
|
-1, *psz_bstr_name, i_name_len, NULL, NULL );
|
|
|
|
// The PIN_INFO structure holds a pointer to the Filter,
|
|
// with a referenppce count that must be released.
|
|
if( pin_info.pFilter )
|
|
pin_info.pFilter->Release();
|
|
return S_OK;
|
|
}
|
|
|
|
IPin* BDAGraph::FindPinOnFilter( IBaseFilter* pBaseFilter, const char* pPinName)
|
|
{
|
|
HRESULT hr;
|
|
IEnumPins *pEnumPin = NULL;
|
|
ULONG CountReceived = 0;
|
|
IPin *pPin = NULL, *pThePin = NULL;
|
|
char String[80];
|
|
char* pString;
|
|
PIN_INFO PinInfo;
|
|
int length;
|
|
|
|
if (!pBaseFilter || !pPinName)
|
|
return NULL;
|
|
|
|
// enumerate of pins on the filter
|
|
hr = pBaseFilter->EnumPins(&pEnumPin);
|
|
if (hr == S_OK && pEnumPin)
|
|
{
|
|
pEnumPin->Reset();
|
|
while (pEnumPin->Next( 1, &pPin, &CountReceived) == S_OK && pPin)
|
|
{
|
|
memset(String, 0, sizeof(String));
|
|
|
|
hr = pPin->QueryPinInfo(&PinInfo);
|
|
if (hr == S_OK)
|
|
{
|
|
length = wcslen (PinInfo.achName) + 1;
|
|
pString = new char [length];
|
|
|
|
// get the pin name
|
|
WideCharToMultiByte(CP_ACP, 0, PinInfo.achName, -1, pString, length,
|
|
NULL, NULL);
|
|
|
|
//strcat (String, pString);
|
|
//StringCbCat(String,strlen(String) + strlen(pString)+1,pString);
|
|
snprintf( String, strlen(String) + strlen(pString) + 1, "%s%s", String, pString);
|
|
|
|
// is there a match
|
|
if (strstr(String, pPinName))
|
|
pThePin = pPin; // yes
|
|
else
|
|
pPin = NULL; // no
|
|
|
|
delete[] pString;
|
|
|
|
}
|
|
else
|
|
{
|
|
// need to release this pin
|
|
pPin->Release();
|
|
}
|
|
|
|
|
|
} // end if have pin
|
|
|
|
// need to release the enumerator
|
|
pEnumPin->Release();
|
|
}
|
|
|
|
// return address of pin if found on the filter
|
|
return pThePin;
|
|
|
|
}
|
|
|