Start · Sprachen · JavaScript · Referenz · USBDevice.isochronousTransferIn()

USBDevice.isochronousTransferIn()

Methode

Startet eine isochrone Eingangsübertragung von einem USB-Endpunkt und liefert ein Promise mit einem <code>USBIsochronousInTransferResult</code>.

seit JavaScript Chrome/Edge 61+ (WebUSB, experimentell, kein Basel Kategorie: webapi

Signatur

USBDevice.prototype.isochronousTransferIn(endpointNumber, packetLengths)

Beschreibung

isochronousTransferIn() liest zeitkritische Daten von einem isochronen IN-Endpunkt eines USB-Geräts. Isochrone Übertragungen garantieren eine feste Bandbreite und gleichmäßige Taktung, aber keine Fehlerkorrektur durch erneutes Senden. Sie eignen sich daher für Datenströme, bei denen Aktualität wichtiger ist als Vollständigkeit – etwa Audio-Interfaces, Mikrofone, Webcams oder Messgeräte mit kontinuierlichem Datenstrom.

Ein Aufruf fordert mehrere Pakete auf einmal an: Über packetLengths gibt man für jedes Paket die maximal erwartete Größe in Bytes an. Das Ergebnis enthält in data einen DataView über den gesamten Puffer und in packets ein Array von USBIsochronousInTransferPacket-Objekten. Jedes Paket hat einen eigenen data-View und einen eigenen status ("ok", "stall" oder "babble"). Die Pakete sollten einzeln ausgewertet werden, da sie unterschiedlich lang sein und einzeln fehlschlagen können.

Voraussetzung: Das Gerät wurde mit navigator.usb.requestDevice() ausgewählt, mit open() geöffnet, per selectConfiguration() konfiguriert, das Interface mit claimInterface() beansprucht und – sehr häufig nötig – über selectAlternateInterface() eine alternative Einstellung gewählt, die den isochronen Endpunkt überhaupt erst bereitstellt (die Standardeinstellung 0 hat bei USB-Audio/-Video meist keine Bandbreite).

Für einen durchgehenden Datenstrom ruft man die Methode in einer Schleife auf und hält idealerweise mehrere Übertragungen gleichzeitig „in der Luft“, damit zwischen zwei Anfragen keine Lücken entstehen.

Parameter

Name Typ Default Beschreibung
$endpointNumber Pflicht number Nummer des isochronen IN-Endpunkts (ohne Richtungsbit, also z. B. 1 statt 0x81). Siehe USBEndpoint.endpointNumber.
$packetLengths Pflicht number[] Array mit der maximal erwarteten Länge (in Bytes) jedes einzelnen Pakets. Die Anzahl der Einträge bestimmt, wie viele Pakete angefordert werden. Üblicherweise USBEndpoint.packetSize pro Eintrag.

Rückgabewert

Typ
Promise<USBIsochronousInTransferResult>
Beschreibung
Promise, das mit einem USBIsochronousInTransferResult erfüllt wird: data (DataView über alle Pakete) und packets (Array von USBIsochronousInTransferPacket mit data und status). Wird mit einer DOMException abgelehnt, z. B. InvalidStateError (Gerät nicht geöffnet), NotFoundError (Endpunkt existiert nicht bzw. Interface nicht beansprucht) oder NetworkError (Übertragungsfehler, Gerät getrennt).

Beispiele

Gerät vorbereiten und einmalig isochron lesen

// Muss durch eine Nutzeraktion (z. B. Klick) ausgelöst werden
const button = document.querySelector('#connect');

button.addEventListener('click', async () => {
  const device = await navigator.usb.requestDevice({
    filters: [{ vendorId: 0x1234 }]
  });

  await device.open();
  if (device.configuration === null) {
    await device.selectConfiguration(1);
  }
  await device.claimInterface(1);
  // Alternative Einstellung 1 stellt den isochronen Endpunkt bereit
  await device.selectAlternateInterface(1, 1);

  // 8 Pakete à max. 192 Bytes anfordern
  const packetLengths = new Array(8).fill(192);
  const result = await device.isochronousTransferIn(1, packetLengths);

  console.log('Pakete:', result.packets.length);
  console.log('Gesamtpuffer:', result.data.byteLength, 'Bytes');
});
Pakete: 8 Gesamtpuffer: 1536 Bytes

Pakete einzeln auswerten (Status prüfen)

const readPackets = async (device, endpoint) => {
  const lengths = new Array(10).fill(endpoint.packetSize);
  const { packets } = await device.isochronousTransferIn(
    endpoint.endpointNumber,
    lengths
  );

  const chunks = [];
  for (const packet of packets) {
    if (packet.status !== 'ok') {
      console.warn('Paket verworfen:', packet.status);
      continue; // isochron: kein erneutes Senden, einfach überspringen
    }
    // Tatsächlich empfangene Länge kann kleiner als angefordert sein
    chunks.push(new Uint8Array(
      packet.data.buffer,
      packet.data.byteOffset,
      packet.data.byteLength
    ));
  }
  return chunks;
};

Kontinuierlicher Datenstrom mit mehreren parallelen Übertragungen

const streamAudio = async (device, endpointNumber, onChunk, signal) => {
  const lengths = new Array(8).fill(192);
  const QUEUE_DEPTH = 3;

  const transfer = () => device.isochronousTransferIn(endpointNumber, lengths);
  // Mehrere Anfragen vorab starten, damit keine Lücken entstehen
  const queue = Array.from({ length: QUEUE_DEPTH }, transfer);

  while (!signal.aborted) {
    const result = await queue.shift();
    queue.push(transfer());
    for (const p of result.packets) {
      if (p.status === 'ok' && p.data.byteLength > 0) onChunk(p.data);
    }
  }
};

// Nutzung
const controller = new AbortController();
// streamAudio(device, 1, (view) => verarbeite(view), controller.signal);
// später: controller.abort();

// Wichtig · Fallstricke

  • Browser-Unterstützung: WebUSB ist experimentell und nur in Chromium-basierten Browsern (Chrome, Edge, Opera) verfügbar, nicht in Firefox und Safari. Nur im sicheren Kontext (HTTPS) und ggf. eingeschränkt durch die Permissions Policy usb. Vor Nutzung mit 'usb' in navigator prüfen.
  • Alternative Einstellung vergessen: Häufigster Fehler – ohne selectAlternateInterface() existiert der isochrone Endpunkt oft nicht, und das Promise wird mit NotFoundError abgelehnt.
  • Kein Resend: Fehlerhafte Pakete ("stall", "babble") werden nicht wiederholt. Immer den status jedes Pakets prüfen, nicht nur das Gesamtergebnis.
  • Die tatsächliche Paketlänge (packet.data.byteLength) kann kleiner sein als die angeforderte; result.data enthält dann Lücken. Deshalb über packets iterieren statt den Gesamtpuffer linear auszuwerten.
  • Betriebssystem-Treiber: Geräteklassen wie USB-Audio oder UVC-Webcams werden meist vom Betriebssystem beansprucht; claimInterface() schlägt dann fehl (unter Windows ggf. WinUSB-Treiber nötig). Chrome blockiert zudem bestimmte geschützte Interface-Klassen.
  • Für Lücken-freie Streams mehrere Übertragungen parallel anstoßen; ein einzelner sequentieller Aufruf pro Schleifendurchlauf führt oft zu Aussetzern.