如何将无声音频 channel/AAC 编解码器添加到 Safari 的 MP4 文件?

How can I add silent audio channel/AAC codec to an MP4 file for Safari?

想知道是否有人可以帮助我!

我目前正在通过 MediaRecorder API 将 HTML5 Canvas 保存为 MP4 文件。现在我的 canvas 不包含任何音频,但我需要一个内置的音频通道,因为文件只有 h.264 并且没有音频编解码器与我正在使用的软件不兼容。

即使 canvas 中没有使用音频,是否仍然可以强制 Safari 将音频编解码器烘焙到流中?

基本上我正在努力实现以下目标:AAC、H.264

而不是我现在拥有的:H.264

这是我目前所掌握的(减去一些其他细节):

  // setup media recording
  const recordedChunks = [];
  const stream = canvas.captureStream(60);

  mediaRecorder = new MediaRecorder(stream);
  mediaRecorder.ondataavailable = (e) => recordedChunks.push(e.data);
  mediaRecorder.onstop = async (e) => {
    const download = (fileName, url) => {
      const a = document.createElement("a");
      document.body.appendChild(a);
      a.style = "display: none";

      a.href = url;
      a.download = fileName;
      a.click();

      window.URL.revokeObjectURL(url);
    };

    // download video
    const videoData = new Blob(recordedChunks, { type: "video/mp4" });
    download("1.mp4", URL.createObjectURL(videoData));

  }

  // start recording
  mediaRecorder.start();

  // do some canvas related operations
  // ...

  mediaRecorder.stop();

我想如果这里没有解决办法,我可能会求助于通过 FFMPEG 向视频添加无声音频通道。

更新: 接受的答案实际上对我不起作用,所以我求助于通过有效的 FFMPEG 添加音频通道。无论如何接受,因为它确实向输出文件添加了一个音频通道。

谢谢!

谢谢,也许这会有所帮助?

    ```a.href``` = 
    url:

    ````a.download``` = 
    fileName:

    ```a.click```(.264):
    ():
    ```c.AAC```

    ```H.264```

我不熟悉编解码器等,但您可以按如下方式向视频流添加无声音频通道:

const stream = canvas.captureStream(60);

const audioContext = new AudioContext();
const oscillator = audioContext.createOscillator();
oscillator.frequency.value = 0;
const streamAudioDestination = audioContext.createMediaStreamDestination();
oscillator.connect(streamAudioDestination);
oscillator.start();

// add audio track
const audioStream = streamAudioDestination.stream;
const audioTracks = audioStream.getAudioTracks();
const firstAudioTrack = audioTracks[0];
stream.addTrack(firstAudioTrack);

const mediaRecorder = new MediaRecorder(stream);

请注意,AudioContext 的初始化应该发生在响应用户操作时(例如在点击处理程序中)。感谢@Nikola Lukic 注意到这一点!

如果有人The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.

这是使用@ziz Yokubjonov 代码改编的示例:

function LOAD() {

  var canvas = document.getElementById('canvas')
  var ctx = canvas.getContext("2d")
  // setup media recording
  const recordedChunks = [];
  const stream = canvas.captureStream(60);

  // let combined = new MediaStream([...videoStream.getTracks(), ...audioStream.getTracks()]);
  let options = {
    audio: true,
    audioBitsPerSecond: 64000,
  };

  const audioContext = new AudioContext();
  const oscillator = audioContext.createOscillator();
  oscillator.frequency.value = 0;
  const streamAudioDestination = audioContext.createMediaStreamDestination();
  oscillator.connect(streamAudioDestination);
  oscillator.start();

  // add audio track
  const audioStream = streamAudioDestination.stream;
  const audioTracks = audioStream.getAudioTracks();
  const firstAudioTrack = audioTracks[0];
  stream.addTrack(firstAudioTrack);

  mediaRecorder = new MediaRecorder(stream, options);
  mediaRecorder.ondataavailable = (e) => recordedChunks.push(e.data);
  mediaRecorder.onstop = async (e) => {
    const download = (fileName, url) => {
      const a = document.createElement("a");
      document.body.appendChild(a);
      a.style = "display: block";
      a.innerHTML = 'LINK';
      a.href = url;
      a.download = fileName;
      a.click();

      window.URL.revokeObjectURL(url);
    };

    // download video
    const videoData = new Blob(recordedChunks, {type: "video/mp4"});
    download("1.mp4", URL.createObjectURL(videoData));

  }

  // start recording
  mediaRecorder.start();

  // do some canvas related operations
  var x = 10
  setInterval(function() {
    x += 20
    ctx.fillStyle = 'red'
    ctx.fillText("TEST TEST", x, 50, 200, 50);
  }, 1000)
  ctx.fillText("TEST TEST", 10, 50, 200, 50);

  setTimeout(function() {
    mediaRecorder.stop();
  }, 10000)

}

function attachFunction() {
  LOAD();
  window.removeEventListener("click", attachFunction)
}

window.addEventListener("click", attachFunction)
<canvas id='canvas'></canvas>