DirectShow und USB: Die richtigen Capture-Filter ermitteln

Beim Capturen von USB-Geräten mit DirectShow werden Video und Audio meist separat von zwei verschiedenen Filtern eingelesen. Der Grund dafür, dass zwei und nicht ein Filter nötig sind, liegt am USB-Klassenmodell (es gibt je eine Klasse für Video und Audio) und den dazu entsprechenden Treiberschnittstellen, die vom System zur Verfügung gestellt werden. DirectShow bietet jedoch keine Funktion oder Methode an, mit der man die gemeinsamen Filter eines Capture-Geräts ermitteln könnte.

Mit ein bisschen Know-How kann man aber so eine Funktion selbst implementieren. Wie das geht, möchte ich Euch heute zeigen. Für den Artikel setze ich voraus, dass Ihr Euch schon einige Zeit mit DirectShow beschäftigt habt. Daher werde ich nicht bei 0 beginnen.

1. Auflisten der Video-Capture-Filter

Zunächst erzeugen wir eine Liste der verfügbaren Video-Capture-Filter mittels IEnumMoniker und CLSID_VideoInputDeviceCategory.

Während der Auflistung sichern wir uns für jeden IMoniker seinen “Display Name”. Wir entfernen jedoch das vorangestellte “@device:pnp:” und erhalten somit den Pfad auf das Gerät, den man z.B. in CreateFile verwenden könnte.

2. Auflisten der Audio-Capture-Filter

Ähnlich verfahren wir mit den Audio-Capture-Filtern mittels CLSID_AudioInputCategory.

Beim “Display Name” wird das vorangestellte “@device:cm:{33D9A762-90C8-11D0-BD43-00A0C911CE86}\” entfernt. Daraus erhalten wir jedoch noch nicht den Pfad zum Gerät. Den müssen wir als Nächstes ermitteln.

3. Auflisten der waveInxxx-Geräte

Mittels dem waveInxxx-API werden nun alle Audio-Capture-Geräte aufgelistet. Deren Anzahl erhält man über die waveInGetNumDevs-Funktion.

Über waveInGetDevCaps erhält man den Namen des Geräts im Struktur-Member WAVEINCAPS.szPname. Über einen String-Vergleich erhalten wir somit eine Verbindung zu seinem Audio-Capture-Filter.

Jetzt können wir für den jeweiligen Filter den Gerätepfad ermitteln. Dies machen wir mit waveInMessage und den Nachrichten DRV_QUERYDEVICEINTERFACESIZE und DRV_QUERYDEVICEINTERFACE.

4. Ermitteln der Geräte-Instanz

Für jeden gefundenen Gerätepfad (Video und Audio) müssen wir jetzt nur noch seine Geräte-Instanz ermitteln. Dies machen wir über das Setup API:

Zuerst holen wir uns eine Liste von Geräteinformationen über SetupDiGetClassDevs. Für Video vewenden wir als Kategorie KSCATEGORY_CAPTURE und für Audio KSCATEGORY_AUDIO.

Beim Durchgehen der zur Kategorie vorhandenen Geräteschnittstellen mittels SetupDiEnumDeviceInterfaces holen wir uns mit SetupDiGetDeviceInterfaceDetail Informationen über jede einzelne Schnittstelle.

Stimmt der Pfad in SP_DEVICE_INTERFACE_DETAIL_DATA.DevicePath mit einem unserer gespeicherten Pfade überein, so finden wir die Geräte-Instanz in SP_DEVINFO_DATA.devInst und können sie speichern.

5. Die übergeordnete Geräte-Instanz ermitteln

Im letzten Schritt wird für jede gefundene Geräte-Instanz ihre übergeordnete Geräte-Instanz ermittelt. Das machen wir mit der CM_Get_Parent-Funktion aus dem Configuration Manager API.

Findet man für Video und Audio diesselbe übergeordnete Geräte-Instanz, dann hat mein das Filter-Paar für ein Gerät gefunden! Fertig!

Das hört sich alles sehr kompliziert und aufwändig an, ist es aber nicht. Angehängt findet Ihr Beispielcode dazu, der kürzer als dieser Artikel ist!

Download:
CaptureDevice.zip

Sockets mit Fiber Pool

Hallo,

ab heute ist Fiber Pool 1.0.0.21 verfügbar. Der Download ist wie immer über die übliche Seite möglich.

Neben einigen kleinen Features und Bugfixes bietet das Fiber Pool API nun auch eine (ausbaufähige) Socket-Klasse an, mit der man hochperformante Netzwerkanwendungen entwickeln kann.

Als “kleines” Sample dazu habe ich einen einfachen HTTP-Server bereitgestellt, der gleichzeitig auf mehreren Adressen auf Anfragen reagieren kann und für jede Adresse mehrere virtuelle Server unterstützt.
Das HTTP-Protokoll Version 1.1 wird unterstützt, sodass Request-Pipelining möglich ist. Requests und Responses werden asynchron von den verfügbaren Prozessoren bearbeitet.
Aktuell werden zwar nur statische Seiten übertragen, sodass hauptsächlich der File I/O Scheduler zum Tragen kommt, evtl. kommt aber später noch eine Serverskriptsprache (z.B. PHP) hinzu, bei der die Prozessoren dann tatsächlich etwas zu tun kriegen.

Zu FLAC wollte ich eigentlich auch eine hybride CPU/GPU-Version herausbringen, allerdings hat das zeitlich nicht hingehauen. Ob es sie jemals geben wird, wird die (viel zu wenige) Zeit zeigen…

Eulersche Zahl e "einfach" unendlich genau berechnen

Ich hatte es beim Start dieses Blogs angekündigt und heute wird es wahr: Der erste Artikel zur Mathematik!

Ich möchte mit einem Algorithmus beginnen, mit dem man auf sehr einfache Weise die Eulersche Zahl e mit einem Computer genau berechnen kann. Als Rechenoperationen werden lediglich die Integer-Addition und die Shift-Operation benötigt.

Die Formel:
Eine einfache Berechnung benötigt eine einfache Formel, und da Computer am liebsten mit Binärzahlen arbeiten, sollte die Formel dafür ausgelegt sein.

Man muss nicht lange nach so einer Formel suchen, denn es genügt, die Formel zur Berechnung von e, die wir aus der Schulmathematik kennen, nur leicht abzuwandeln, und zwar wie folgt:

Durch die Verwendung von 2n statt n, erhält man nun für ein beliebiges n immer einen Binärbruch als Basis und somit eine Zahl, die der Computer mag.

Die Dimensionen:
Der Basisbruch benötigt 1 Bit vor dem Komma und n Bits dahinter.

Da wir bereits wissen, dass e eine 2 vor dem Komma hat, wird das Ergebnis vor dem Komma zwei Bits benötigen.

Die Anzahl der Bits hinter dem Komma wird sich jedoch mit jeder Quadrierung verdoppeln. Und da n Quadrierungen durchgeführt werden müssen, lässt sich die Gesamtanzahl an Bits mit folgender Formel berechnen:

Für n=10 benötigt man etwa 10 kbits, für n=20 21 Mbits, für n=30 32 Gbits, für n=35 1,2 TBits und für n=58 (der maximale Wert bei 64-Bit-Adressierung) schlappe 16,7 Ebits (Exabits).

Der Algorithmus:
Das Quadrieren im Binärformat ist einfach. Betrachten wir dazu zunächst den allgemeinen Fall:

Man sieht, dass alle Summanden quadriert werden und zusätzlich alle Summanden paarweise miteinander multipliziert werden. Letztere Operation kommt beim Ausmultiplizieren zweimal vor, daher die 2 an jedem Term.

Übertragen auf eine Binärzahl, erhalten wir z.B.

Daraus können wir zwei Operationen für das Quadrieren ableiten:

1. Ist Bit n im Eingabewert auf 1 gesetzt, dann setze Bit 2n auf 1 im Ausgabewert.
2. Sind im Bitpaar (m, n), m<n, beide Bits auf 1 gesetzt, dann setze Bit m+n+1 auf 1 im Ausgabewert.

Das sog. “Setzen des Bits auf 1″ impliziert einen möglichen Übertrag: Ist das Bit bereits auf 1 gesetzt, so wird es auf 0 und das nachfolgende Bit auf 1 gesetzt.

Mit all den Sachen kann man schließlich den Algorithmus zur Berechnung von e als Pseudocode wie folgt beschreiben:

void euler(int n)
{
  Binary input;
  Binary output = 0;

  output[0] = 1;
  output[n] = 1;

  for (int q = 0; q < n; ++q) {
    input = output;
    output = 0;

    for (int i = 0; i < input.length; ++i) {
      if (input[i]) {
        output.setBit(2 * i);

        for (j = i + 1; j < input.length; ++j) {
          if (input[j])
            output.setBit(i + j + 1);
        }
      }
    }
  }
}

Tja, und das war's dann auch schon. Mehr als simple Arithmetik ist nicht nötig, um e zu berechnen.