[Libav-user] RTP Opus demuxer is missing extradata with Opus header

Jonathan Baudanza jon at jonb.org
Thu Oct 29 23:45:52 EET 2020

I am trying to write the contents of an RTP-OPUS stream to an OGG-OPUS file. The purpose is to send a WebRTC session to Google's Speech-to-text API, which accepts OGG-OPUS as input.

The opus muxer requires that an opus header be present in extradata. It seems that the demuxer from my RTP stream doesn't include this.

If I construct the opus header myself and insert it into extradata, everything works great. This seems like a hack though. Is this something that the RTP demuxer should be doing?

Here is the code. I'm using some node bindings for libavformat called "beamcoder". I can reproduce the same issue with the ffmpeg commandline.

  const demuxer = await beamcoder.demuxer({
    url: 'file:input.sdp',
    options: {
      protocol_whitelist: "file,rtp,udp"

  // Construct an Oups Header.
  // This seems like a hack, but is required to get the opux muxer to work
  const opusHeader = Buffer.alloc(19);
  // https://www.opus-codec.org/docs/opusfile_api-0.4/structOpusHead.html#details
  // https://github.com/xiph/opusfile/blob/4174c26e0aaab19d01afdea0a46f7f95fdc6b3e6/src/info.c#L40
  // magic bytes for opus
  opusHeader.write('OpusHead', 0, 8, "ascii");
  // Version
  opusHeader.writeUInt8(1, 8);
  // Channel count
  opusHeader.writeUInt8(2, 9);
  // Pre skip
  opusHeader.writeUInt16LE(0, 10);
  // Sample rate
  opusHeader.writeUInt32LE(48000, 12);
  // Output gain (db)
  opusHeader.writeUInt16LE(0, 16);
  // Mapping family
  opusHeader.writeUInt8(0, 18);

  const streamConfig = demuxer.streams[0];
  streamConfig.codecpar.extradata = opusHeader;

  const muxer = await beamcoder.muxer({ filename: 'file:test.opus' });

  const stream = muxer.newStream(demuxer.streams[0]);
  await muxer.openIO();

  // This invokes avformat_write_header, which will raise a "no extradata present" error unless I build my own opus header.
  // The error is raised from inside oggenc.c / ogg_init
  await muxer.writeHeader();

  while (true) {
    const packet = await demuxer.read();
    if (packet == null)
    await muxer.writeFrame(packet);
  await muxer.writeTrailer();

This is my SDP file:

  o=- 0 0 IN IP4
  c=IN IP4
  t =0 0
  m=audio 5004 RTP/AVPF 100
  a=rtpmap:100 opus/48000/2

I asked a similar question yesterday in the ffmpeg-users list, but I think it may be more appropriate here:

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://ffmpeg.org/pipermail/libav-user/attachments/20201029/02af93bd/attachment.html>

More information about the Libav-user mailing list