1

I am sending buffer from client to server and then save them as a blob into an mp4 file like this :

async function saveIntoMp4(format) {
    if (chunks.length) {
      const options = recOptions.mimeType ? { type: recOptions.mimeType } : { type: "video/mp4" }
      let blob = new Blob(chunks, options);
      if (blob.size > 9999) {
        console.log(`blob.size`, blob.size);
        const buffer = Buffer.from(await blob.arrayBuffer());
        try {
          fs.writeFileSync( // writeFile
          `${__dirname}/videos/1.mp4`,
          buffer,
          () => console.log("video saved!")
        );

However I am not able to play the saved files remotely from the video tag since the moov atom is not in the begining of the file or is missing altogether. I am saving most of my files in h264 encoding and I wonder if there is a way without using a third party library such as FFMPEG to add MOOV to the begining of my files and make them virtually playable.

Hypothesis
  • 1,208
  • 3
  • 17
  • 43
  • **(1)** _"Since the moov atom is not in the beginning of the file or is missing altogether"_ Is HTML5 **MediaRecorder** involved in making the file? If yes, be aware that not every browser will encode as MP4 (it could be WebM data but your shown code saves it with an MP4 filename regardless). **(2)** Get a **Hex editor** (use HxD if on Windows). Use it to check (open) your saved first chunk... If you see `ftyp` in the first line (usually from 5th byte onwards) then you have an MP4 or else If it is `webm` on the second line (usually from 25th byte onwards) then you a WebM file. – VC.One Jun 06 '23 at 17:32
  • You can check the byte values by scanning the first chunk in the Chunks array. Use a While or For loop to look for some header text that is unique to the checked-for format. Think about how only a WebM video would have the text "webm" somewhere in its header. You scan by checking each array slot's value from a small section of the header (_eg:_ first 64 bytes). If the value as String makes a "w" then check if the next 3 values are "e" and "b" and "m". If found then you save as `.webm`. The browser decides the format because it knows its own licensing rights for encoding certain formats – VC.One Jun 06 '23 at 18:22
  • Try the tips in my "super comment" below posted as an Answer. You haven't started so it's hard to advise further. Try something and ask a new question when stuck (showing any problematic bytes as copied from hex editor). – VC.One Jun 06 '23 at 23:14

1 Answers1

1

"I wonder if there is a way without using a third party library such as FFMPEG to add MOOV to the begining of my files and make them virtually playable?"

Yes but you must be familiar with bytes handling...

About re-arranging the MOOV atom (if you have MP4 data)

(1) You will have to extract the MOOV atom's section from the last chunk as a separate Array.

To find the atom in last chunk, look for bytes/array slots with a sequence of letters for text "mdat".
Bytes are written in hex format (as String) and this looks like 6D 64 61 74.
JS will read array in decimals (as Integer) not hex so same sequence looks like 109, 100, 97, 116.

This means inside the array, find sequence [... 109, 100, 97, 116 ...] so look for a decimal 109 (which is equivalent to byte 0x6D) and check if next array slot has a decimal 100 itself followed by 97 and so on.

After finding start position of m-d-a-t sequence, the previous four array slots are the size so read those four bytes into temp variables (eg: tempA, tempB, tempC, tempD) and then concat as one 32-bit integer:

tempA = MP4_Bytes[ mdat_Start-3 ];
tempB = MP4_Bytes[ mdat_Start-2 ];
tempC = MP4_Bytes[ mdat_Start-1 ];
tempD = MP4_Bytes[ mdat_Start-0 ];

//# concat into one 32-bit integer
mdat_Size = ( tempA << 24 | tempB << 16 | tempC << 8 | tempD );

Now you have size of MOOV for cut/copy size. A quick cheat is to just find start pos of m-d-a-t and copy from (start_pos - 4) to also grab the four "size" bytes.

(2) Next you have to update your a/v Sample offsets inside the MOOV.

A simplified example: if at your chunk/file's end the MOOV was 100 bytes long and then you also had 3 video frames, at file's start, at offsets 0, 10 and 20... When you put MOOV in front then frame 1 is now pushed forward to a new position at offset 100 and frame 2 is now at 110 and frame 3 is now at 120 etc. The extracted MOOV header holds the original old offsets of frames as (0,10,20) so you simply update those numbers as (100, 110, 120) to account for new data in front of the 3 frames.

How to update offsets?
Try editing the STCO atom first. See configure MP4 time for some explanation. You edit all the STCO's listed chunk offsets by increasing their current listed size to become listed_size + (size_of_mdat + 4).

You will need to write the "size" number across four array slots (or spread over 4 bytes).

Test if the video plays.

VC.One
  • 14,790
  • 4
  • 25
  • 57