Le blog tutorial
Voici un petit bout de blog afin d'y mettre mes recherches concernant la programmation d'un tuner dvb-t du MSDN :
Dans ce billet, je tenterai de décrire, certainement maladroitement, la programmation de la réception d’un tuner TNT par le biais de DirectShow.
Après plusieurs heures(voire des jours) de recherches et d’essais, j’ai finalement réussi à recevoir des images via mon tuner tnt usb. Ce n’était pas sans mal car, il faut le reconnaître, le net ne regorge pas de plusieurs exemples en la matière, ni des sources fonctionnant correctement…
La devise et maître mot de ce billet est donc :
« La théorie, c’est quand on sait tout et que rien ne fonctionne. La pratique, c’est quand tout fonctionne et que personne ne sait pourquoi. »
Là il y aura beaucoup de pratique….
D’abord créer une structure pour recevoir les données concernant les tuners et les filtres etc….
struct TUNERSTRUCT
{
BSTR Name;
char *Char_Name;
SmartPtr pTuner;
SmartPtr pDVBTLocator;
SmartPtr TuningSpace;
SmartPtr pTuneRequest;
SmartPtr pDVBTuneRequest;
SmartPtr DirectSoundRenderer;
SmartPtr VideoRenderer;
SmartPtr m_pNetworkProvider;
SmartPtr m_pTunerDemodDevice; // for tuner device filter
SmartPtr m_pBDACaptureDevice; // for tuner device filter
SmartPtr m_pDemux; // for tuner device filter
SmartPtr m_pBDASecTab;
SmartPtr m_pTSFileSink;
SmartPtr m_pBDATIF;
SmartPtr m_pTunerPin; // the tuner pin on the tuner/demod filter
SmartPtr m_pDemodPin; // the demod pin on the tuner/demod filter
SmartPtr m_KsTunerPropSet; // IKsPropertySet for tuner
SmartPtr m_KsDemodPropSet; // IKsPropertySet for demod
};
La première chose à faire est de récupérer le nom des tuners tnt.
HRESULT DVB_Get_Device(void){
DVB_Tuner_Max=0;
HRESULT hr;
BOOL fFoundFilter = FALSE;
SmartPtr pIMoniker;
SmartPtr pIEnumMoniker;
if (!pCreateDevEnum) {
hr=CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (PVOID *)&pCreateDevEnum);
if (FAILED(hr)) {
printf("Cannot CoCreate ICreateDevEnum");
return hr;
}
}
// obtain the enumerator
hr = pCreateDevEnum->CreateClassEnumerator(KSCATEGORY_BDA_RECEIVER_COMPONENT, &pIEnumMoniker, 0);
// the call can return S_FALSE if no moniker exists, so explicitly check S_OK
if (FAILED(hr)) {
TOOL_Debug("DVB_Get_Device","Cannot CreateClassEnumerator");
return hr;
}
if (S_OK != hr) { // Class not found
printf("Class not found, CreateClassEnumerator returned S_FALSE");
return E_UNEXPECTED;
}
// next filter
while (pIEnumMoniker->Next(1, &pIMoniker, 0) == S_OK)
{
// obtain filter's friendly name
SmartPtr pBag;
hr = pIMoniker->BindToStorage(NULL,NULL,IID_IPropertyBag,reinterpret_cast(&pBag));
if (FAILED(hr)) {
TOOL_Debug("DVB_Get_Device","Cannot BindToStorage");
return hr;
}
VARIANT varBSTR;
varBSTR.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &varBSTR, NULL);
TUNER[DVB_Tuner_Max].Name=varBSTR.bstrVal;
BSTRtoASC(TUNER[DVB_Tuner_Max].Name,TUNER[DVB_Tuner_Max].Char_Name);
printf("Ajout du Device_Tuner [%d] : %s",DVB_Tuner_Max,TUNER[DVB_Tuner_Max].Char_Name);
if (FAILED(hr)) {
printf("IPropertyBag->Read method failed");
pIMoniker = NULL;
continue;
}
DVB_Tuner_Max++;
return hr;
}
}
Voilà, alors nous avons récupéré le nom du tuner ( dans mon cas, ça me donne : « DiBcom BDA Digital Capture (Dev1 Path0) »). Cela sera utile pour connecter les filtres entre eux.
Après il est nécessaire un espace de tuning.
HRESULT DVB_Create_Tuning_Space(long DVB_Selected){
HRESULT hr;
hr = CoCreateInstance(CLSID_DVBTuningSpace,NULL, CLSCTX_INPROC_SERVER,IID_IDVBTuningSpace2,(void **) &TUNER[DVB_Selected].TuningSpace);
hr = TUNER[DVB_Selected].TuningSpace->put_SystemType(DVB_Terrestrial);
hr = TUNER[DVB_Selected].TuningSpace->put__NetworkType(CLSID_DVBTNetworkProvider);
return hr;
}
Puis il faut envoyer la requête de chaine :
DVB_Tuning_Request(0,546167,8,-1,-1,-1);
Le chiffre 546167 correspond à la fréquence d’un multiplex.
HRESULT DVB_Tuning_Request(long DVB_Selected,long Mhz, long BW, long ONID, long SID, long TSID){
HRESULT hr;
BSTR TuningName;
TuningName = L"DVB-T";
SmartPtr pDVBTLocator;
hr = CoCreateInstance(CLSID_DVBTLocator,NULL, CLSCTX_INPROC_SERVER,IID_IDVBTLocator,(void **)&TUNER[DVB_Selected].pDVBTLocator);
hr = TUNER[DVB_Selected].pDVBTLocator->put_CarrierFrequency(Mhz);
hr = TUNER[DVB_Selected].pDVBTLocator->put_Bandwidth(BW);
hr = TUNER[DVB_Selected].TuningSpace->put_DefaultLocator(TUNER[DVB_Selected].pDVBTLocator);
TOOL_Debug("DVB_Tuning_Request"," Frequence : %d - BandWith : %d - ONID : %d - SID : %d - TSID : %d",Mhz,BW,ONID,SID,TSID);
hr = TUNER[DVB_Selected].TuningSpace->put_UniqueName(TuningName);
hr = TUNER[DVB_Selected].TuningSpace->put_FriendlyName(TuningName);
hr = TUNER[DVB_Selected].TuningSpace->put_FrequencyMapping(L"");
SmartPtr pTuningSpaceContainer;
hr = CoCreateInstance(CLSID_SystemTuningSpaces,NULL, CLSCTX_INPROC_SERVER,IID_ITuningSpaceContainer,(void **)&pTuningSpaceContainer);
VARIANT tiIndex;
hr = pTuningSpaceContainer->Add(TUNER[DVB_Selected].TuningSpace,&tiIndex);
if (!SUCCEEDED(hr)) {
// Get the enumerator for the collection.
SmartPtr pTuningSpaceEnum;
hr = pTuningSpaceContainer->get_EnumTuningSpaces(&pTuningSpaceEnum);
if (SUCCEEDED(hr)) {
// Loop through the collection.
SmartPtr pTuningSpace;
//ITuningSpace *pTuningSpace;
tiIndex.intVal=0;
while (S_OK == pTuningSpaceEnum->Next(1, &pTuningSpace, NULL)) {
BSTR Name;
hr = pTuningSpace->get_UniqueName(&Name);
if (SUCCEEDED(hr)) {
if (wcscmp(Name, TuningName) == 0) {
hr = pTuningSpaceContainer->put_Item(tiIndex,TUNER[DVB_Selected].TuningSpace);
}
SysFreeString(Name);
}
tiIndex.intVal++;
//pTuningSpace->Release();
pTuningSpace.Release();
}
}
}
hr = TUNER[DVB_Selected].TuningSpace->CreateTuneRequest(&TUNER[DVB_Selected].pTuneRequest);
TUNER[DVB_Selected].pTuneRequest.QueryInterface(&TUNER[DVB_Selected].pDVBTuneRequest);
if(TUNER[DVB_Selected].pDVBTuneRequest) {
hr = TUNER[DVB_Selected].pDVBTuneRequest->put_SID(SID);//1538
hr = TUNER[DVB_Selected].pDVBTuneRequest->put_TSID(TSID);
hr = TUNER[DVB_Selected].pDVBTuneRequest->put_ONID(ONID);//8442
}
GUID CLSIDNetworkType;
hr = TUNER[DVB_Selected].TuningSpace->get__NetworkType(&CLSIDNetworkType);
hr = CoCreateInstance(CLSIDNetworkType, NULL, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void **) &TUNER[DVB_Selected].m_pNetworkProvider);
hr = g_pGB->AddFilter(TUNER[DVB_Selected].m_pNetworkProvider,L"Network Provider");
TUNER[DVB_Selected].m_pNetworkProvider.QueryInterface(&TUNER[DVB_Selected].pTuner);
// Query for ITuner.
if (TUNER[DVB_Selected].pTuner) {
// Submit the tune request to the network provider.
hr = TUNER[DVB_Selected].pTuner->put_TuneRequest(TUNER[DVB_Selected].pTuneRequest);
}
return hr;
}
Après nous avons la délicate tâche de lier les différents filtres dans un ordre logique afin de les connecter ensemble. Un petit exemple de ce que cela doit donner :
dvbt-w2k
A l’aide de cette fonction, nous ajouterons au fur et à mesure les filtres
HRESULT TUNER_Load_Filter(REFCLSID clsid,IBaseFilter** ppFilter,IBaseFilter* pConnectFilter,BOOL fIsUpstream,BSTR Name){
HRESULT hr = S_OK;
BOOL fFoundFilter = FALSE;
SmartPtr pIMoniker;
SmartPtr pIEnumMoniker;
char *Temp;
TOOL_Debug("TUNER_Load_Filter","Debut de la fonction [%s]",TOOL_Convert_to_char(Name));
if (!pCreateDevEnum) {
hr=CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (PVOID *)&pCreateDevEnum);
if (FAILED(hr)) {
TOOL_Debug("TUNER_Load_Filter","Cannot CoCreate ICreateDevEnum");
return hr;
}
}
// obtain the enumerator
hr = pCreateDevEnum->CreateClassEnumerator(clsid, &pIEnumMoniker, 0);
// the call can return S_FALSE if no moniker exists, so explicitly check S_OK
if (FAILED(hr)) {
TOOL_Debug("TUNER_Load_Filter","Cannot CreateClassEnumerator");
return hr;
}
if (S_OK != hr) { // Class not found
TOOL_Debug("TUNER_Load_Filter","Class not found, CreateClassEnumerator returned S_FALSE");
return E_UNEXPECTED;
}
// next filter
while (pIEnumMoniker->Next(1, &pIMoniker, 0) == S_OK) {
// obtain filter's friendly name
SmartPtr pBag;
hr = pIMoniker->BindToStorage(NULL,NULL,IID_IPropertyBag,reinterpret_cast(&pBag));
if (FAILED(hr)) {
TOOL_Debug("TUNER_Load_Filter","Cannot BindToStorage");
return hr;
}
VARIANT varBSTR;
varBSTR.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &varBSTR, NULL);
if (FAILED(hr)) {
TOOL_Debug("TUNER_Load_Filter","IPropertyBag->Read method failed");
pIMoniker = NULL;
continue;
}
pBag = NULL;
if (wcsncmp(varBSTR.bstrVal, Name,sizeof(Name)) == 0) {
//BSTRtoASC(Name,Temp);
TOOL_Debug("TUNER_Load_Filter","Correspondance decouverte avec [%s]",TOOL_Convert_to_char(Name));
// bind the filter
SmartPtr pFilter;
hr = pIMoniker->BindToObject(NULL,NULL,IID_IBaseFilter,reinterpret_cast(&pFilter));
if (FAILED(hr)) {
pIMoniker = NULL;
pFilter = NULL;
continue;
}
hr = g_pGB->AddFilter (pFilter, varBSTR.bstrVal);
if (FAILED(hr)) {
TOOL_Debug("TUNER_Load_Filter","Cannot add filter [%s]",TOOL_Convert_to_char(Name));
return hr;
}
//MessageBox (NULL, _T(""), _T(""), MB_OK);
// test connections
// to upstream filter
if(pConnectFilter==NULL){
fFoundFilter = TRUE;
pFilter.QueryInterface(ppFilter);
return S_OK;
}
if (pConnectFilter) {
if (fIsUpstream) {
hr = DVB_ConnectFilters(pConnectFilter, pFilter);
}
else {
hr = DVB_ConnectFilters(pFilter, pConnectFilter);
}
if (SUCCEEDED(hr)) {
//that's the filter we want
TOOL_Debug("TUNER_Load_Filter","Filters connected [%s]",TOOL_Convert_to_char(Name));
fFoundFilter = TRUE;
pFilter.QueryInterface(ppFilter);
break;
}
else {
fFoundFilter = FALSE;
// that wasn't the the filter we wanted
// so unload and try the next one
hr = g_pGB->RemoveFilter(pFilter);
if (FAILED(hr)) {
TOOL_Debug("TUNER_Load_Filter","Filters not connected");
return hr;
}
}
}
else {
fFoundFilter = TRUE;
pFilter.QueryInterface(ppFilter);
break;
}
pIMoniker = NULL;
pFilter = NULL;
}
}// while
pIEnumMoniker = NULL;
return S_OK;
}
HRESULT DVB_ConnectFilters(IBaseFilter* pFilterUpstream,IBaseFilter* pFilterDownstream){
HRESULT hr = E_FAIL;
SmartPtr pIPinUpstream;
PIN_INFO PinInfoUpstream;
PIN_INFO PinInfoDownstream;
TOOL_Debug("DVB_ConnectFilters","ConnectFilters");
// grab upstream filter's enumerator
SmartPtr pIEnumPinsUpstream;
hr = pFilterUpstream->EnumPins(&pIEnumPinsUpstream);
if(FAILED(hr))
{
TOOL_Debug("DVB_ConnectFilters","Enumerate Upstream Filter's Pins");
return hr;
}
// iterate through upstream filter's pins
while (pIEnumPinsUpstream->Next (1, &pIPinUpstream, 0) == S_OK)
{
hr = pIPinUpstream->QueryPinInfo (&PinInfoUpstream);
if(FAILED(hr))
{
TOOL_Debug("DVB_ConnectFilters","Cannot Obtain Upstream Filter's PIN_INFO");
return hr;
}
SmartPtr pPinDown;
pIPinUpstream->ConnectedTo (&pPinDown);
// bail if pins are connected
// otherwise check direction and connect
if ((PINDIR_OUTPUT == PinInfoUpstream.dir) && (pPinDown == NULL))
{
// grab downstream filter's enumerator
SmartPtr pIEnumPinsDownstream;
hr = pFilterDownstream->EnumPins (&pIEnumPinsDownstream);
if(FAILED(hr))
{
TOOL_Debug("DVB_ConnectFilters","Cannot enumerate pins on downstream filter!");
return hr;
}
// iterate through downstream filter's pins
SmartPtr pIPinDownstream;
while (pIEnumPinsDownstream->Next (1, &pIPinDownstream, 0) == S_OK)
{
// make sure it is an input pin
hr = pIPinDownstream->QueryPinInfo(&PinInfoDownstream);
if(SUCCEEDED(hr))
{
SmartPtr pPinUp;
// Determine if the pin is already connected. Note that
// VFW_E_NOT_CONNECTED is expected if the pin isn't yet connected.
hr = pIPinDownstream->ConnectedTo (&pPinUp);
if(FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
{
TOOL_Debug("DVB_ConnectFilters","Failed in pIPinDownstream->ConnectedTo()!");
continue;
}
if ((PINDIR_INPUT == PinInfoDownstream.dir) && (pPinUp == NULL))
{
if (SUCCEEDED (g_pGB->Connect(pIPinUpstream, pIPinDownstream)))
{
PinInfoDownstream.pFilter->Release();
PinInfoUpstream.pFilter->Release();
return S_OK;
}
}
}
PinInfoDownstream.pFilter->Release();
pIPinDownstream = NULL;
} // while next downstream filter pin
//We are now back into the upstream pin loop
} // if output pin
pIPinUpstream = NULL;
PinInfoUpstream.pFilter->Release();
} // while next upstream filter pin
return E_FAIL;
}
Nous lierons alors, le m_pNetworkProvider au m_pTunerDemodDevice, puis le m_pBDACaptureDevice au m_pTunerDemodDevice :
hr=TUNER_Load_Filter(KSCATEGORY_BDA_NETWORK_TUNER, &TUNER[0].m_pTunerDemodDevice,TUNER[0].m_pNetworkProvider, TRUE,TUNER[0].Name);
hr=TUNER_Load_Filter(KSCATEGORY_BDA_RECEIVER_COMPONENT, &TUNER[0].m_pBDACaptureDevice,TUNER[0].m_pTunerDemodDevice, TRUE,TUNER[0].Name);
Une fois cela effectué il faut charger le demux ( le démultiplexeur)
HRESULT hr = S_OK;
hr = CoCreateInstance(CLSID_MPEG2Demultiplexer,NULL, CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void **) &TUNER[TUNER_Number].m_pDemux);
if (FAILED(hr)) {
TOOL_Debug("Could not CoCreateInstance CLSID_MPEG2Demultiplexer ","");
return hr;
}
hr = g_pGB->AddFilter(TUNER[TUNER_Number].m_pDemux, L"Demux");
if (FAILED(hr)) {
TOOL_Debug("Unable to add demux filter to graph ","");
return hr;
}
Puis il faut connecter directement les filtres du m_pDemux au m_pBDACaptureDevice :
hr = DVB_ConnectFilters(TUNER[0].m_pBDACaptureDevice, TUNER[0].m_pDemux);
Puis nous connectons le reste des filtres :
hr=TUNER_Load_Filter(KSCATEGORY_BDA_TRANSPORT_INFORMATION,&TUNER[0].m_pBDATIF,TUNER[0].m_pDemux, TRUE,L"BDA MPEG2 Transport Information Filter");
hr=TUNER_Load_Filter(KSCATEGORY_BDA_TRANSPORT_INFORMATION,&TUNER[0].m_pBDASecTab,NULL, TRUE,L"MPEG-2 Sections and Tables");
Puis nous y mappons la section et tables du MPEG2 et ce pour obtenir la PAT, SDT etc… Encore une chose qui n’est pas spécialement documentée sur le net… A vous de chercher un peu, car çà à l’air d’être assez simple mais très fastidieux à faire….
HRESULT hr;
SmartPtr PinOut;
SmartPtr pDemux;
hr = TUNER[TUNER_Selected].m_pDemux->QueryInterface(IID_IMpeg2Demultiplexer, (void**)&pDemux);
if (SUCCEEDED(hr)){
// Create a new output pin.
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
GetPSIMedia(&mt);
// Create the pin.
IPin *pPsiPin;
hr = pDemux->CreateOutputPin(&mt, L"PSI", &pPsiPin);
if (SUCCEEDED(hr))
{
IMPEG2PIDMap *pPid = NULL;
hr = pPsiPin->QueryInterface(IID_IMPEG2PIDMap, (void**)&pPid);
// Map to PID 0.
ULONG Pid = 0x00;
hr = pPid->MapPID(1, &Pid, MEDIA_MPEG2_PSI);
//NIT
Pid = 0x10;
hr = pPid->MapPID(1, &Pid, MEDIA_MPEG2_PSI);
//SDT;
Pid = 0x11;
hr = pPid->MapPID(1, &Pid, MEDIA_MPEG2_PSI);
PinOut = GetPin(TUNER[TUNER_Selected].m_pBDASecTab,PINDIR_INPUT);
hr= g_pGB->Connect(PinOut,pPsiPin);
}
}
return hr;
Et puis pour lier le pin vidéo du demux au filtre de rendu…
HRESULT hr;
SmartPtr PinOut;
SmartPtr pDemux;
hr = TUNER[TUNER_Selected].m_pDemux->QueryInterface(IID_IMpeg2Demultiplexer, (void**)&pDemux);
if (SUCCEEDED(hr)){
// Create a new output pin.
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
GetVideoMedia(&mt);
// Create the pin.
IPin *VideoPin;
hr = pDemux->CreateOutputPin(&mt, L"Video", &VideoPin);
if (SUCCEEDED(hr))
{
IMPEG2PIDMap *pPid = NULL;
hr = VideoPin->QueryInterface(IID_IMPEG2PIDMap, (void**)&pPid);
ULONG Pid = 120; ( Pid video de la chaine )
long Filter=2;
hr = pPid->MapPID(1, &Pid, MEDIA_ELEMENTARY_STREAM);
SmartPtr Colour_In;
SmartPtr Colour_Out;
Colour_In = GetPin(Colour_Converter,PINDIR_INPUT);
hr= g_pGB->Connect(VideoPin,Colour_In);
Colour_Out = GetPin(Colour_Converter,PINDIR_OUTPUT);
hr= g_pGB->Render(Colour_Out);
}
}
return hr;
Et le tour est joué!!!
Vous parviendrez à obtenir les pid audio et video en utilisant un logiciel du style pouchtin tv cela vous donnera un truc du style : Pid
Voili voilou, j’espère que je vous aurai aidé dans vos recherche et que finalement vous arrêterez de maudire ce @*¤# de msdn qui ne dit rien du tout….
Et vous n’oublierez pas de lancer le graph aussi!!!!
Si vous avez des questions, des soucis, des remarques, je suis à votre écoute!!!!!!!!!!