Seit 2003 beschäftige ich mich mit DVB auf Linux.
Nach all der Zeit wäre es mal interessant, die ganzen Linux DVB tools mit einer Schnittstelle füer Windows BDA (Broadcast Driver Architecture) zu versehen,
so könnte man DVB Treiber aus der Windows Welt leichter in die Linux Welt transferieren: testen mit den gleichen Tools.
Leider habe ich keine Ahnung von der Windows BDA Architektur und versuche hier Fakten zu sammeln.
Mich interessieren dabei nur Windows 7 oder neuer und ausschließlich 64 bit, als Compiler soll mingw 64-bit zum Einsatz kommen, also native apps.
GraphEdit ist ein Tool zum Visualiseren der Blöcke eines DirectShow Filter Graphs, die komplexe Aufgabe der
Darstellung von Multimedia Dateien wird in kleinere Blöcke zerlegt, die sich miteinander kombinieren lassen.
Das hört sich sehr gut an, funktioniert in der Praxis weit weniger gut als man erwarten möge.
Das Problem beginnt, wenn mehrere Filter mit ähnlicher Funktion existieren oder sich Filter nicht zusammen
schalten lassen wollen.
Wichtig: Damit GraphEdit nutzbar ist, muss die DLL proppage.dll registriert werden:
regsvr32 C:\Windows\System32\proppage.dll
VLC funktioniert für DVB-T unter Windows, wenn man eine xspf Playlist von w_scan benutzt.
Eigentlich habe ich dieses Ausgabeformat nur für den Linux-VLC in w_scan integriert, aber das funzt auch unter Windows recht gut.
Eine Beispiel xspf für Berlin gibt es hier.
Leider ist VLC schlecht als Programmierbeispiel geeignet, weil nicht der mit Windows7 eingeführte generische Networkprovider benutzt wird, der aber für neuere Software in der MSDN empfohlen wird.
Sehr schade auch, dass der so erzeugte DirectShow Filtergraph nicht in der ROT (Running Object Table) registriert wird, so dass der Graph nicht in GraphEdit sichtbar ist.
Und den VLC mit mingw compilieren zu wollen ist aussichtslos.
Hier also eine Sackgasse..
CodeTV sieht wie ein gutes Coding Beispiel aus:
Wie man auf dem Bild sehr schön sehen kann, gehören folgende Komponenten in einen BDA Filter Graph:
Tests:
Sehr schade, der Fehler mit den '2 filters BDA scheme' wäre mit nur wenigen Zeilen Code lösbar.
BDA devices können laut MSDN ein bis drei Filter im Filtergraph benötigen, und die könnte man mit einem automatischen Algorithmus finden..
Soweit ich erkennen kann, funktioniert das automatische Finden von Capture und Tuner devices mit meinen DVB Karten nicht,
nach manueller Zuweisung kann ich tunen.
Beim Stöbern im Internet nach Codeschnipseln konnte ich keine einfach zu verstehenden C++ Codeschnipsel finden,
also ist mein Startpunkt Null. Keine Kenntnisse von DirectShow, keine einfachen Beispiel die wirklich funktionieren.
ALSO: ALLES SELBST BAUEN - EIN ENDLOSES PROJEKT.
Sammlung von Fakten/Schnipseln zum Thema BDA in C++
Jeder Zugriff auf COM Objekte wie z.B. BDA devices erfordert das Initialisieren der COM library mit
CoInitialize() oder
CoInitializeEx(), wobei CoInitializeEx() bevorzugt wird.
Am Ende des Codes muss CoUninitialize() aufgerufen werden.
Andrenfalls wird jeder Zugriff auf eine COM-Funktion scheitern.
HRESULT hr = CoInitialize(NULL, COINIT_APARTMENTTHREADED); (..) CoUninitialize();
Alle COM Objekte (d.h. auch alle Objekte, die mit BDA zu tun haben) werden mit CoCreateInstance() erzeugt.
HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, void ** ppv);
Beim Lesen von Quelltext wird man immer wieder über CComPtr<IUnknown_Derivat> sowie CComQIPtr<IUnknown_Derivat> stoßen.
Beides sind so eine Art SmartPointer aus der ATL library, welche mit ReferenceCounting arbeiten und Microsoft Compiler
Kram erfordern, absolut inkompatibel zu jedem Standard Compiler wie gcc/mingw.
Hier gibt es mehrere Möglichkeiten, das zu umgehen
Alle COM Interfaces haben eine GUID, die Interface IID. Diese wird beim MSVC durch Standard Compiler incompatible Macros erzeugt,
keine Chance das mit dem mingw vernünftig nachzubauen. Hier ist Handarbeit gefragt, aus diesen Grund wird bei mir aus
EXTERN_C const IID IID_IMPEG2TuneRequestFactory; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("14E11ABD-EE37-4893-9EA1-6964DE933E39") IMPEG2TuneRequestFactory : public IDispatch { public: (...)
das hier..
extern const IID IID_IMPEG2TuneRequestFactory; (...) const IID IID_IMPEG2TuneRequestFactory = {0x14E11ABD, 0xEE37, 0x4893, {0x9E, 0xA1, 0x69, 0x64, 0xDE, 0x93, 0x3E, 0x39}}; struct IMPEG2TuneRequestFactory : public IDispatch { public: (...)
Der nächste Punkt macht ebenso Arbeit, beim mingw sind alle COM Interfaces zu Tuner und BDA vollkommen ungeprüft und generieren Warnings.
Es lohnt also nicht, diese Header mit zu installieren, man muss ohnehin alle Header aus dem Win32SDK holen und manuell durchsehen.
Alle COM Funktionen benutzen HRESULT (na ja fast..) als Ergebnis für success/fail.
Man muss schon sehr lange suchen, um zu finden, was genau eigentlich ein "HRESULT ?" ist. Irgendwie ist das ein 'LONG' also 32bit,
der als negativer Wert einen Fehler bedeutet, während positive Werte ebenso eine Warnung sein können.
So betrachtet machen die Macros SUCCEEEDED(HRESULT) sowie FAILED(HRESULT) auch Sinn, die nur auf >=0 bzw. <0 testen.
Die Bits 0..15 enthalten einen Error-Code, dessen Bedeutung sich wie ein Chamäleon je nach Facility (Bits 16..26) ändert. Wer diesen Mist erfunden hat, gehört gesteinigt.
In jedem Falle bläht die Abfrage des Ergebnisses den Source Code ordentlich auf.
Zugriff auf BDA DVB Tuner bedeutet also grob skizziert den folgenden Ablauf, zumindest so wie es lese..
Na, klingt DAS nicht einfach?
Wunderschön ist auch, dass keine Möglichkeit vorgesehen ist, um einen speziellen Tuner anzusprechen. Wer mehr als einen Tuner hat,
der darf DVB-Stick Lotterie spielen, es sei denn ein cleverer Programmierer findet Wege einen Tuner wieder zu erkennen und gezielt zu suchen.
localptr<IGraphBuilder> graph; localptr<IBaseFilter> network_provider; localptr<IBaseFilter> tuner_filter; localptr<IBaseFilter> receiver_filter; localptr<IBaseFilter> demux_filter; localptr<IScanningTuner> scanning_tuner; localptr<ITuningSpaceContainer> tuning_spaces_container; localptr<IEnumTuningSpaces> tuning_spaces; long num_tuning_spaces=0; IBaseFilter * last_filter = NULL; // NOTE: NEVER RELEASE THIS ONE; DO NOT RELY ON. FILTER_INFO filter_info; GUID network_guid = network_providers[0]; // GUID for generic network provider int index = 0; // equiv to /dev/dvb/frontendN; zero-based. uint32_t tuner_id; // actually crc32 of physical name. bool tuner_connected = false; bool demux_connected = false; bool receiver_connected = false; // initialize COM lib first; replace later by CoInitializeEx() CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); // create filter graph. RET_ON_ERR(::CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, reinterpret_cast<void**>(&graph.ptr))); // get the generic ms network provider && add to graph. strncpy_w(filter_info.achName, L"Microsoft Network Provider", 128); network_provider.ptr = GetFilterByName(filter_info.achName, KSCATEGORY_BDA_NETWORK_PROVIDER); RET_ON_ERR(graph.ptr->AddFilter(network_provider.ptr, filter_info.achName)); last_filter = network_provider.ptr; // find the tuner of given index on this system && add it to graph. RET_ON_ERR(FindTuner(graph.ptr, &tuner_filter.ptr, index, &tuner_id)); // connect network provider -> tuner result = ConnectFilters(graph.ptr,network_provider.ptr,tuner_filter.ptr); tuner_connected = (result == S_OK); info("tuner connected: %s\n", boolToStr(tuner_connected)); if (! tuner_connected) return -1; last_filter = tuner_filter.ptr; // we need a pointer to the tuner on nw provider. MSG_ON_ERR(network_provider.ptr->QueryInterface(IID_IScanningTuner, reinterpret_castTime for break..(&scanning_tuner.ptr))); // Get the tuning space collection RET_ON_ERR(::CoCreateInstance(CLSID_SystemTuningSpaces, NULL, CLSCTX_INPROC_SERVER, IID_ITuningSpaceContainer, reinterpret_cast<void**>(&tuning_spaces_container.ptr))); RET_ON_ERR(tuning_spaces_container.ptr->get_EnumTuningSpaces(&tuning_spaces.ptr)); tuning_spaces_container.ptr->get_Count(&num_tuning_spaces); info("--- %ld tuning spaces ---\n", num_tuning_spaces); /* * (...) * This one will get easily a few hundert lines of code. And i added just the provider + tuner here. * What a over complicated API.. */
Auf der Suche nach C++ code bin ich DigitalWatchv2 und ScanChannelsBDA gestoßen.
Interessanterweise ist der Source von 'Mpeg2DataParser.cpp' fast 1:1 der Source aus scan und w_scan als fork von scan.
/** * Mpeg2DataParser.cpp * Copyright (C) 2004 Nate * Copyright (C) 2004 JoeyBloggs * * significant portions of this code are taken from * the linuxtv-dvb-apps-1.1.0 scan.c file
Der/die Autoren von ScanChannelsBDA hätten allerdings die originalen Autoren dieses Codes
nicht aus dem Copyright löschen dürfen, das ist auf diese Art und Weise fast gestohlen.
Wie auch immer, nach etlichen Tagen Codens lässt sich diese Version mit dem gcc/mingw mit 64bit compilieren,
ich habe also eine Spielwiese.
Damit das möglich ist, musste jedes Stück ATL entfernt werden und die bei win32 SDK header per Hand
an den gcc angepasst werden. Die header von mingw sind für COM Programmierung völlig ungeeignet.
Die Version 2.0.8 welche ich gefunden habe, erlaubt den scan des Australischen DVB-T. Damit also für Europa
falsche Frequenzen und ein Kanalraster von 7MHz. Das lässt sich leicht anpassen, ein Problem ist aber die
komplette Stringverarbeitung im Source, welche zu 100% auf ATL Macros und LPWSTR (aka wchar_t*) setzt, das ist so
nicht mit mingw nutzbar.
This program was designed to create channels.ini entries for DigitalWatch using BDA drivers. Usage: Use 'Add Network' to add each frequency for your area. eg. Please Select Menu(1,2,5,6,10):1 Frequency (KHz):506000 Bandwidth (MHz):8 Then use option 2 to generate the channel listing. Once finished you can cut and paste the entries into your channels.ini file. Notes: - If a channel has multiple audio pids then it will be listed multiple times. - If an AC3 PID is detected then an A will be prefixed to the audio pid. - If an AC3 PID is detected and is not the first pid for the program then the name will be suffixed with AC3 (BDACardCollection.cpp line 105) LoadCards:Entering LoadCards().. (BDACardCollection.cpp line 237) LoadCardsFromHardware:Checking for new BDA DVB-T Cards (BDACardCollection.cpp line 243) LoadCardsFromHardware:Tuner - PCTV 80e/290e/460e BDA 2875 TVTuner = @device:pnp:\\?\usb#vid_2013&pid_024f#000000100bql#{71985f48-1ca1-11d3-9cc8-00c04f7971e0}\{7c8095ab-c110-40e5-9f4d-310858bbbf64}(BDACardCollection.cpp line 259) LoadCardsFromHardware:bdaCard == NULL (BDACardCollection.cpp line 323) FindCaptureDevice:Finding Demod and Capture filters (BDACardCollection.cpp line 359) FindCaptureDevice:Trying - PCTV 80e/290e/460e BDA 2875 Capture = @device:pnp:\\?\usb#vid_2013&pid_024f#000000100bql#{fd0a5af4-b41d-11d2-9c95-00c04f7971e0}\{bc102acb-6095-451d-8ed6-16cf3c832ebc} (BDACardCollection.cpp line 363) FindCaptureDevice:SUCCESS (BDACardCollection.cpp line 379) FindCaptureDevice:Checking for Capture filter (BDACardCollection.cpp line 385) FindCaptureDevice: ..don't try to connect the one we already found. (BDACardCollection.cpp line 434) FindCaptureDevice:Finished Finding Demod and Capture filters (BDACardCollection.cpp line 274) LoadCardsFromHardware:Capture - PCTV 80e/290e/460e BDA 2875 Capture = @device:pnp:\\?\usb#vid_2013&pid_024f#000000100bql#{fd0a5af4-b41d-11d2-9c95-00c04f7971e0}\{bc102acb-6095-451d-8ed6-16cf3c832ebc} (BDACardCollection.cpp line 243) LoadCardsFromHardware:Tuner - PCTV 80e/290e/460e BDA 2875 TVTuner C = @device:pnp:\\?\usb#vid_2013&pid_024f#000000100bql#{71985f48-1ca1-11d3-9cc8-00c04f7971e0}\{b50b8116-da24-4f97-80d1-00451702c5f7}(BDACardCollection.cpp line 259) LoadCardsFromHardware:bdaCard == NULL (BDACardCollection.cpp line 323) FindCaptureDevice:Finding Demod and Capture filters (FilterGraphTools.cpp line 493) ConnectFilters:connection failed. (BDACardCollection.cpp line 351) FindCaptureDevice:Failed to connect Network Provider to Tuner Filter (BDACardCollection.cpp line 434) FindCaptureDevice:Finished Finding Demod and Capture filters (BDACardCollection.cpp line 304) LoadCardsFromHardware:Finished Checking for new BDA DVB-T Cards (ScanChannelsBDA.cpp line 68) selectCard:Found BDA device: PCTV 80e/290e/460e BDA 2875 TVTuner 1. Add Network 2. Generate Channels.ini 4. Signal Statistics 5. Scan all German frequencies 6. Verbosity = Off 8. Test TSFileSink 9. Toggle Lock Mode : (HAS_LOCK) 0. Exit Please Select Menu(1,2,4,5,6,8,9,0): 5 (ScanChannelsBDA.cpp line 248) scanAll:trying f = 314000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 322000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 330000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 338000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 346000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 354000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 362000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 370000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 378000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 386000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 394000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 402000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 410000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 418000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 426000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 434000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 442000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 450000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 458000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 466000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 474000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 482000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 490000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 498000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 506000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 654) scanChannel:f = 506000kHz B=8000kHz; has_lock = Y; has_signal = Y; signal_strength = 0; signal_quality = 80 (Mpeg2DataParser.cpp line 1125) PrintDigitalWatchChannelsIni: Network_25("MEDIA BROADCAST", 506000, 8, 1) Program_ 1("RTL Television" , 16405, 337, 338, 336) # Teletext=343 Program_ 2("RTL2" , 16406, 353, 354, 352) # Teletext=359 Program_ 3("Super RTL" , 16411, 433, 434, 432) # Teletext=439 Program_ 4("VOX" , 16418, 545, 546, 544) # Teletext=551 (ScanChannelsBDA.cpp line 248) scanAll:trying f = 514000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 522000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 654) scanChannel:f = 522000kHz B=8000kHz; has_lock = Y; has_signal = Y; signal_strength = 0; signal_quality = 100 (Mpeg2DataParser.cpp line 1125) PrintDigitalWatchChannelsIni: Network_27("DVB-T Berlin/Brandenburg", 522000, 8, 1) Program_ 1("rbb Brandenburg" , 11, 1201, 1202, 1100) # Teletext=1504 Program_ 2("rbb Brandenburg" , 11, 1201, 1203, 1100) # Program_ 3("rbb Berlin" , 12, 1201, 1202, 1200) # Teletext=1204 Program_ 4("rbb Berlin" , 12, 1201, 1203, 1200) # Program_ 5("PHOENIX" , 13, 1301, 1302, 1300) # Teletext=1304 Program_ 6("PHOENIX" , 13, 1301, 1303, 1300) # Program_ 7("Das Erste" , 14, 1401, 1402, 1400) # Teletext=1404 Program_ 8("Das Erste" , 14, 1401, 1403, 1400) # Program_ 9("tagesschau24" , 15, 1501, 1502, 1500) # Teletext=1404 Program_10("tagesschau24" , 15, 1501, 1503, 1500) # (ScanChannelsBDA.cpp line 248) scanAll:trying f = 530000kHz, B = 8000kHz (ScanChannelsBDA.cpp line 248) scanAll:trying f = 538000kHz, B = 8000kHz
Das scheint also der erste Erfolg zu sein.
Fortsetzung folgt..
Dieses Bild zeigt, wie man direkt mit GraphEdit Bild und Ton bekommt.
Diesmal wird ein Cinergy T RC verwendet, die Struktur des Graphs zwischen Network Provider und Demux sieht ein wenig anders aus.
Fortsetzung folgt..