Skip to content

How to correctly handle duplicate ftyp/moov in Pinterest streams (error Cannot read properties of undefined (reading 'mdia')) #525

@databorodata

Description

@databorodata

Hello! I ran into a problem when assembling a full-fledged MP4 from a fragmented Pinterest stream. We intercept all SourceBuffer.appendBuffer calls on the page and collect them into one ArrayBuffer, which we pass to mp4box.js for analysis and subsequent remultiplexing. MP4BOX stack trace:

TypeError: Cannot read properties of undefined (reading 'mdia')
    at ISOFile.updateSampleLists (libs/mp4box.all.js:7294)
    at ISOFile.appendBuffer (libs/mp4box.all.js:6332)

Contents of the captured buffer (first atoms):

offset=0    type=ftyp size=36
offset=36   type=free size=64
offset=100  type=moov size=914
offset=1014 type=ftyp size=36     ← second init-segment
offset=1050 type=free size=64
offset=1114 type=moov size=800    ← third init-segment
offset=1914 type=moof size=568
offset=2482 type=mdat size=200786
offset=203268 type=moof size=284
offset=203552 type=mdat size=16146
…

It can be seen that Pinterest sends ftyp+moov before each rather large fragment (in fact, each HLS segment begins with an init segment). After the third moov, mp4box tries to update the tables, but it encounters a trak without mdia and crashes.

Minimum code

// Chrome OffscreenDocument (Service Worker) – mp4box.js v2.1.0
import * as MP4Box from 'mp4box';

async function parseCapturedBuffer(arrayBuffer) {
  const mp4box = MP4Box.createFile(true);
  mp4box.onError = (e) => console.error('mp4box error', e);
  mp4box.onReady = (info) => {
    console.log('tracks', info.tracks.map(t => ({
      id: t.id,
      codec: t.codec,
      nb_samples: t.nb_samples,
    })));
  };

  const buf = arrayBuffer.slice(0);
  buf.fileStart = 0;
  mp4box.appendBuffer(buf);   // crashes here _ISOFile.updateSampleLists
  mp4box.flush();
}

What we have already checked
If you manually delete all repeated ftyp+moov and leave only the first init segment + all moof/mdat, the file is parsed correctly.
If you delay the start of the interception (wait 2 seconds before feeding the mp4box buffer), the situation does not change — Pinterest still sends init segments again, and mp4box crashes.
In the examples from examples/ mp4box, it is used on progressive MP4 or streams without a repeated init segment, so such a case is not shown there.

Questions
Is there in mp4box.is js a regular way to ignore (or automatically process) repeated ftyp/moov when we transfer the combined buffer?
Is it recommended to filter init segments manually before appendBuffer (leaving only the first ftyp+moov), or is there a more correct API for working with such a stream?
Is it possible to transfer a sequence of "pieces" to mp4box (each ArrayBuffer with its own fileStart) so that the library itself correctly builds the structure?

I use mp4box.js: 2.1.0 (mp4box.all.js)

I would be grateful for recommendations or hints on how to properly feed a stream with duplicate init segments without deleting them manually.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions