Start · Sprachen · JavaScript · Referenz · AudioDecoder

AudioDecoder

Schnittstelle

Das <code>AudioDecoder</code>-Interface der WebCodecs API dekodiert kodierte Audioblöcke (<code>EncodedAudioChunk</code>) in rohe <code>AudioData</code>-Frames.

seit JavaScript WebCodecs API – Chrome/Edge 94+, Firefox 130+, Saf Kategorie: webapi

Signatur

interface AudioDecoder extends EventTarget

Beschreibung

AudioDecoder gehört zur WebCodecs API und bietet direkten Zugriff auf die Audio-Decoder des Browsers. Kodierte Audiodaten, etwa Opus, AAC, MP3 oder FLAC, werden als EncodedAudioChunk übergeben. Als Ergebnis erhält man unkomprimierte PCM-Samples in Form von AudioData-Objekten. Anders als <audio> oder AudioContext.decodeAudioData() arbeitet die Schnittstelle blockweise und mit geringer Latenz. Container wie MP4, WebM oder Ogg müssen Sie dafür selbst demultiplexen, zum Beispiel mit mp4box.js.

Die Verarbeitung läuft asynchron und in einer Warteschlange. Der Konstruktor new AudioDecoder({ output, error }) nimmt zwei Callbacks entgegen. Danach wird der Decoder mit configure() eingerichtet (Codec, Abtastrate, Kanalzahl, optional description). Anschließend übergibt man mit decode() einzelne Chunks. Jeder dekodierte Frame kommt über den output-Callback zurück. flush() gibt ein Promise zurück, das erfüllt wird, sobald alle anstehenden Chunks verarbeitet sind.

Wichtige Mitglieder:

  • Eigenschaften: state ("unconfigured", "configured", "closed"), decodeQueueSize (Anzahl der wartenden Dekodier-Anfragen)
  • Methoden: configure(config), decode(chunk), flush(), reset(), close()
  • Statische Methode: AudioDecoder.isConfigSupported(config) prüft, ob eine Konfiguration unterstützt wird
  • Event: dequeue (bzw. ondequeue) wird ausgelöst, wenn decodeQueueSize kleiner wird. Damit lässt sich Gegendruck (Backpressure) steuern.

Typische Einsatzgebiete sind Streaming-Player mit eigener Pufferlogik, WebRTC-ähnliche Echtzeitanwendungen, Audio-Editoren im Browser und die Analyse von Audiodaten in einem Worker. AudioDecoder steht auch in Dedicated Workern zur Verfügung.

Parameter

Name Typ Default Beschreibung
$init Pflicht Object Initialisierungsobjekt für den Konstruktor new AudioDecoder(init).
$init.output Pflicht (data: AudioData) => void Callback, der für jeden dekodierten AudioData-Frame aufgerufen wird. Den Frame nach der Verwendung mit data.close() freigeben.
$init.error Pflicht (error: DOMException) => void Callback bei einem schwerwiegenden Fehler. Der Decoder wechselt danach in den Zustand "closed".
$config.codec Pflicht string Codec-String für configure(), z. B. "opus", "mp4a.40.2" (AAC-LC), "mp3", "flac", "pcm-s16".
$config.sampleRate Pflicht number Abtastrate in Hz, z. B. 48000.
$config.numberOfChannels Pflicht number Anzahl der Audiokanäle, z. B. 2 für Stereo.
$config.description ArrayBuffer | TypedArray | DataView Codec-spezifische Zusatzdaten, z. B. AudioSpecificConfig bei AAC im MP4-Format oder der Opus-Header. Für einige Formate Pflicht.

Beispiele

Unterstützung prüfen und Decoder anlegen

const config = {
  codec: 'opus',
  sampleRate: 48000,
  numberOfChannels: 2,
};

if (!('AudioDecoder' in globalThis)) {
  console.log('WebCodecs wird nicht unterstützt');
} else {
  const { supported } = await AudioDecoder.isConfigSupported(config);
  console.log('Opus unterstützt:', supported);

  const decoder = new AudioDecoder({
    output: (audioData) => {
      console.log(`Frame: ${audioData.numberOfFrames} Samples @ ${audioData.sampleRate} Hz`);
      audioData.close(); // Speicher freigeben
    },
    error: (e) => console.error('Decoder-Fehler:', e.message),
  });

  decoder.configure(config);
  console.log(decoder.state);
}
Opus unterstützt: true configured

Chunks dekodieren und mit flush() abschließen

// packets: Array von { data: Uint8Array, timestamp: number (µs) } aus einem Demuxer
const decodeAll = async (packets, config) => {
  const frames = [];
  const decoder = new AudioDecoder({
    output: (data) => frames.push(data),
    error: (e) => console.error(e),
  });
  decoder.configure(config);

  for (const p of packets) {
    decoder.decode(new EncodedAudioChunk({
      type: 'key', // Audio-Chunks sind in der Regel Keyframes
      timestamp: p.timestamp,
      data: p.data,
    }));
  }

  await decoder.flush(); // wartet, bis alle Frames ausgegeben wurden
  decoder.close();
  return frames;
};

const frames = await decodeAll(packets, { codec: 'opus', sampleRate: 48000, numberOfChannels: 2 });
console.log(`${frames.length} AudioData-Frames dekodiert`);
frames.forEach((f) => f.close());
250 AudioData-Frames dekodiert

Backpressure mit dem dequeue-Event

const decoder = new AudioDecoder({
  output: (data) => { /* an AudioWorklet weitergeben */ data.close(); },
  error: console.error,
});
decoder.configure({ codec: 'mp4a.40.2', sampleRate: 44100, numberOfChannels: 2, description: aacConfig });

const MAX_QUEUE = 20;
const waitForQueue = () => new Promise((resolve) => {
  decoder.addEventListener('dequeue', resolve, { once: true });
});

for (const chunk of chunks) {
  while (decoder.decodeQueueSize > MAX_QUEUE) {
    await waitForQueue();
  }
  decoder.decode(chunk);
}
await decoder.flush();

Dekodierte Samples als Float32 auslesen

const output = (audioData) => {
  const channel0 = new Float32Array(audioData.numberOfFrames);
  audioData.copyTo(channel0, { planeIndex: 0, format: 'f32-planar' });
  const peak = channel0.reduce((m, v) => Math.max(m, Math.abs(v)), 0);
  console.log('Spitzenpegel Kanal 0:', peak.toFixed(3));
  audioData.close();
};

const decoder = new AudioDecoder({ output, error: console.error });
Spitzenpegel Kanal 0: 0.812