【问题标题】:Custom MediaStream自定义媒体流
【发布时间】:2014-04-08 10:58:17
【问题描述】:

我通过 websockets 接收到原始 float32 音频,并希望在浏览器中播放它。据我了解,我需要为此使用 MediaStream API。但是,我找不到创建可以将数据缓冲区附加到的 MediaStream 的方法。

实现这一目标的正确方法是什么?

我正在尝试这样的事情:

    var context = new AudioContext();

    context.sampleRate = 48000;

    var stream = null; // ????

    var source = context.createMediaStreamSource(stream);
    source.connect(context.destination);
    source.start(0);

    socket.onmessage = function (event) {
        stream.appendBuffer(new Float32Array(event.data)); // ????
    };

【问题讨论】:

  • 几年后你有什么反馈吗?尝试做同样的事情,但目前似乎仍然无法在流上“附加缓冲区”......
  • @ronag 我为这个问题添加了赏金,因为我很想知道答案。我还对代码进行了一些编辑以使其保持最新状态。 2018 年你碰巧知道了这个问题的答案吗?
  • 1. context.sampleRate 是一个只读属性。 smapleRate docs
  • 2.流必须是 MediaStream 对象。您可以使用构造函数创建它。 stream = new MediaStream()MediaStream docs

标签: javascript html webkitaudiocontext


【解决方案1】:

您应该使用AudioBuffers 从 websocket 的缓冲区中读取声音并播放。

var context = new AudioContext();
var sampleRate = 48000;
var startAt = 0;

socket.onmessage = function (event) {
    var floats = new Float32Array(event.data);
    var source = context.createBufferSource();
    var buffer = context.createBuffer(1, floats.length, sampleRate);
    buffer.getChannelData(0).set(floats);
    source.buffer = buffer;
    source.connect(context.destination);
    startAt = Math.max(context.currentTime, startAt);
    source.start(startAt);
    startAt += buffer.duration;
};

这会从 websocket 播放音乐。

要将 AudioBuffer 转换为 MediaStream,请使用 AudioContext.createMediaStreamDestination()。将 BufferSource 连接到它,以根据缓冲区的数据制作自定义 MediaStream。

var data = getSound(); // Float32Array;
var sampleRate = 48000;
var context = new AudioContext();

var streamDestination = context.createMediaStreamDestination();
var buffer = context.createBuffer(1, data.length, sampleRate);
var source = context.createBufferSource();

buffer.getChannelData(0).set(data);
source.buffer = buffer;
source.connect(streamDestination);
source.loop = true;
source.start();

var stream = streamDestination.stream;

这会从数据数组中读取音频并将其转换为 MediaStream。

【讨论】:

  • event.data的数据类型有哪些?
  • 自定义视频流呢?
【解决方案2】:

关于解码,窗口对象中的 audioContext 应该可以完成这项工作。

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

然后

audioCtx.decodeAudioData(audioData, function(buffer) {

直接在二进制数组上。

关于通信,我宁愿使用 XMLHttpRequest(一个低级函数和旧的)并直接使用响应。

这是MDM人做的一个不错的功能(我更新了ogg文件的url,你可以直接测试):

function getData() {
  source = audioCtx.createBufferSource();
  request = new XMLHttpRequest();
  request.open('GET', 'https://raw.githubusercontent.com/mdn/webaudio-examples/master/decode-audio-data/viper.ogg', true);
  request.responseType = 'arraybuffer';
  request.onload = function() {
    var audioData = request.response;
    audioCtx.decodeAudioData(audioData, function(buffer) {
        myBuffer = buffer;
        songLength = buffer.duration;
        source.buffer = myBuffer;
        source.playbackRate.value = playbackControl.value;
        source.connect(audioCtx.destination);
        source.loop = true;
        loopstartControl.setAttribute('max', Math.floor(songLength));
        loopendControl.setAttribute('max', Math.floor(songLength));
      },
      function(e){"Error with decoding audio data" + e.error});
  }
  request.send();
}

完整的源代码在这里:

https://raw.githubusercontent.com/mdn/webaudio-examples/master/decode-audio-data/index.html

【讨论】:

  • 如果您想以块的形式解码媒体内容,则此方法效果不佳,我的意思是当您必须多次调用 decodeAudioData() 时。每个 decodeAudioData() 在第一个解码帧上产生短暂的沉默(至少在 MP3 中)。所以 decodeAudioData() 只有当你想一次解码所有内容时才是好的解决方案。
猜你喜欢
  • 1970-01-01
  • 2015-03-06
  • 2015-03-23
  • 2023-03-29
  • 2018-12-20
  • 2014-04-12
  • 1970-01-01
  • 2014-08-05
  • 2012-05-17
相关资源
最近更新 更多