Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  bdagraph.cpp   Sprache: C

 

/*****************************************************************************
 * 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

#if defined(__MINGW64_VERSION_MAJOR)
DEFINE_GUID(CLSID_DigitalCableNetworkType,
    0x143827AB,0xF77B,0x498d,0x81,0xCA,0x5A,0x00,0x7A,0xEC,0x28,0xBF);

/* KSCATEGORY_BDA */
DEFINE_GUID(KSCATEGORY_BDA_TRANSPORT_INFORMATION,
    0xa2e3074f,0x6c3d,0x11d3,0xb6,0x53,0x00,0xc0,0x4f,0x79,0x49,0x8e);
DEFINE_GUID(KSCATEGORY_BDA_RECEIVER_COMPONENT,
    0xFD0A5AF4,0xB41D,0x11d2,0x9c,0x95,0x00,0xc0,0x4f,0x79,0x71,0xe0);
DEFINE_GUID(KSCATEGORY_BDA_NETWORK_TUNER,
    0x71985f48,0x1ca1,0x11d3,0x9c,0xc8,0x00,0xc0,0x4f,0x79,0x71,0xe0);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_BDA_MPEG2_TRANSPORT,
    0xF4AEB342,0x0329,0x4fdd,0xA8,0xFD,0x4A,0xFF,0x49,0x26,0xC9,0x78);

#if __MINGW64_VERSION_MAJOR < 12
extern "C" {
    extern const CLSID CLSID_SampleGrabber; // found in strmiids
};
#endif
#endif

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 | COINIT_DISABLE_OLE1DDE );
    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 | COINIT_DISABLE_OLE1DDE );
    return d->module->EnumSystems( );
}

float dvb_get_signal_strength (dvb_device_t *d)
{
    ComContext ctx( COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
    return d->module->GetSignalStrength( );
}

float dvb_get_snr (dvb_device_t *d)
{
    ComContext ctx( COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
    return d->module->GetSignalNoiseRatio( );
}

int dvb_set_inversion (dvb_device_t *d, int inversion)
{
    ComContext ctx( COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
    d->inversion = inversion;
    return d->module->SetInversion( d->inversion );
}

int dvb_tune (dvb_device_t *d)
{
    ComContext ctx( COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
    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 *, const 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 | COINIT_DISABLE_OLE1DDE );
    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 | COINIT_DISABLE_OLE1DDE );
    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 | COINIT_DISABLE_OLE1DDE );
    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 | COINIT_DISABLE_OLE1DDE );
    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 | COINIT_DISABLE_OLE1DDE );
    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 | COINIT_DISABLE_OLE1DDE );
    return d->module->SetATSC(freq / 1000);
}

int dvb_set_cqam (dvb_device_t *d, uint32_t freq, const char * /*mod*/)
{
    ComContext ctx( COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
    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_InheritInteger( 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;
    }

    const size_t len = ARRAY_SIZE(pwsz_graph_name);
    _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;

}

Messung
CHZG
84988890

¤ Dauer der Verarbeitung: 0.48 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417346