4

使用JS提取视频中的音频资源

 8 months ago
source link: https://www.zhangxinxu.com/wordpress/2023/12/js-fetch-video-audio-mp3-mp4-wav/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

使用JS提取视频中的音频资源

这篇文章发布于 2023年12月24日,星期日,21:47,归类于 JS实例。 阅读 588 次, 今日 67 次 没有评论

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11077 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。

音频封面图

一、需求背景,故事由来

上个月有介绍过如何提取视频的序列帧,然而真正的视频解码应该是不仅可以提取画面,还可以提出音频数据。

也是使用mp4box.js加WebCodecs API吗?

我有尝试过,但是解码出的音频数据都是0,不知道哪里出了问题,还需要进一步排查下。

实际上,要解决此需求不需要这么麻烦,使用Web Audio API就能搞定。

比方说此demo,您可以狠狠地点击这里:JS提取视频中的音频音轨并下载demo

二、以选择本地文件举例

假设本地选择的文件是file,则我们可以将file转为arraybuffer再使用decodeAudioData转为audioBuffer,有了audioBuffer就可以对音频为所欲为,分割,复制拼接,合并都不在话下,自然也包括资源的提取。

代码示意:

// 开始识别
const reader = new FileReader();
reader.onload = function (event) {
    const arrBuffer = event.target.result;
    // 创建音频上下文
    const audioCtx = new AudioContext();
    // arrayBuffer转audioBuffer
    audioCtx.decodeAudioData(arrBuffer, function(audioBuffer) {
        // audioBuffer就是AudioBuffer
        // 于是就可以对音频资源为所欲为
    });
};
reader.readAsArrayBuffer(file);

核心实现就是这么简单。

三、如果是在线URL地址

如果是在线URL MP4/WebM视频地址,其实现也是类似的,可以使用fetch方法获取视频资源,记得返回arraybuffer类型,代码示意:

fetch(url).then(res => res.arrayBuffer()).then(buffer => {
    // 创建音频上下文
    const audioCtx = new AudioContext();
    // arrayBuffer转audioBuffer
    audioCtx.decodeAudioData(buffer, function(audioBuffer) {
        // audioBuffer就是AudioBuffer
        // 于是就可以对音频资源为所欲为
    });
});

四、从AudioBuffer中提取音频文件

如果希望播放AudioBuffer数据,可以借助createBufferSource方法,代码示意(audioCtx复用上面的上下文):

// 创建AudioBufferSourceNode对象
const source = audioCtx.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioCtx.destination);
// 资源开始播放,可以指定位置,具体看相关API
source.start();

如果希望设置音量,可以使用GainNode实例,如new GainNode(audioCtx),或者audioCtx.createGain()创建gainNode,代码示意:

const audioCtx = new AudioContext();
const source = audioCtx.createBufferSource();
const gainNode = audioCtx.createGain();
// 音量20%
gainNode.gain.value = 0.2;
gainNode.connect(audioCtx.destination);
source.buffer = audioBuffer;
source.connect(gainNode);
source.start();

不过bufferSource资源的播放是一次性的,播放结束,或者执行stop()方法后就会自动销毁,需要重新buffer赋值一次,这一点需要注意下。

更稳健的方法

当然,有个更稳健也更容易理解的方法,那就是将audioBuffer数据直接转为WAV音频资源,其转换方法业内公开的,不足百行代码,这里不展示了,完整代码访问demo页面获取。

无论是本地文件,还是线上资源的音频提取,我都做在这个演示页面上了,您可以狠狠地点击这里:JS提取视频中的音频音轨并下载demo

例如我选择一个在B站最近发布的这个视频文件,稍等数秒后,音频就解析出来了,如下图所示:

本地文件音频解析

也可以点击下方的文字按钮,直接下载对应的WAV音频资源。

由于这里的Wav音频是无损处理的,因此音频资源的体积比一般的要大些,是正常的,不过比视频还是小很多的。

五、如果你只想把视频当音频播放

那就直接播放好了,无论是<audio>元素,还是Web Audio API,都是支持直接播放视频文件的。

所以,如果有一个网络MP4 URL地址,想要作为视频播放,简单:

const url = 'xxxx.mp4';
const audio = new Audio();
audio.src = url;
// 如果页面已经点击或触摸或键盘访问过
audio.play();

如果是本地视频文件,则可以将文件转为Base64地址或Blob地址播放,例如:

file.onchange = function (event) {
    const file = event.target.files[0];
    // 创建音频地址
    const url = URL.createObjectURL(file);
    const audio = new Audio();
    audio.src = url;
    audio.play();
};

是不是简单的有些意外 😎

六、再说点其他点什么

想想看还有没有什么补充的,哦,对了,视频往往是大文件,使用fetch读取往往会有比较长的耗时,最好可以有个进度提示效果。

以下代码应该对你有所帮助:

// 获取视频的arraybuffer数据
fetch(videourl)
  .then((res) => {
    const contentLength = res.headers.get("content-length");
    const reader = res.body.getReader();

    let lengthReceived = 0;
    let chunks = [];

    reader.read().then(function processText({ done, value }) {
      if (done) {
        const chuckAll = new Uint8Array(lengthReceived);
        let position = 0;
        for (const chunk of chunks) {
          chuckAll.set(chunk, position);
          position += chunk.length;
        }
        // 返回 buffer 给 后续功能使用
        const buffer = chuckAll.buffer;

        return;
      }

      chunks.push(value);

      // 流数据是Uint8Array
      lengthReceived += value.length;

      // progress的值就是进度值
      const progress = Math.round((100 * lengthReceived) / contentLength);

      // 继续读取视频流
      return reader.read().then(processText);
    });
  })
  .catch((err) => {
    console.error("获取视频数据错误:", err);
  });

如果视频在50M以内,我觉得弄个菊花转一转就足够了。

好了正文结束,扯淡时间。

最近诸多文章都与音视频相关,有心人已经猜到,我最近应该是在开发音视频相关的需求,嘿,还真是,好在前期有不少积累,因此,还行,产品要的效果都能实现,有时候还能做些他们想不到的东西,不过也导致近期比较忙。

从文章更新频率就可以看出,本月还有一周结束,结果才更新首篇,这种情况……以前也不是没有过,不要担心,平均每周更新一篇的频率是不会变的。

因此,接下来一周,会有至少3篇文章产出,都是哪些内容呢,还是与视觉表现相关的,拭目以待吧。

😺😸😹😻😼😽

(本篇完)1f44d.svg 是不是学到了很多?可以分享到微信
1f44a.svg 有话要说?点击这里

本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11077


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK