gpt4 book ai didi

javascript - 浏览器中的mp3流解码

转载 作者:行者123 更新时间:2023-11-30 16:22:57 26 4
gpt4 key购买 nike

我正在尝试使用 emscripten 和 libmad 在浏览器中设置 mp3 流接收器。
我设法使用低级 API 解码 mp3 文件,并将其完全加载到内存中。我的下一步是将其分块加载。
given example我使用随机大小(从 20 到 40 kbyte)分配的缓冲区来模拟碎片包,并将文件部分复制到这些缓冲区。

我的解码算法与 this question 中的答案相关但这有点不同。主要对象是Decoder,它通过addFragment方法接收片段。解码器有一个待处理片段的拉取和一个粘合缓冲区。当用户添加第一个片段时,其尾部被复制到粘合缓冲区的前半部分。当添加第二个片段时,它开始被复制到胶水的后半部分。当解码器到达事件缓冲区末尾时,它会切换到粘合,反之亦然。我确保所有这些缓冲区部分都是一致的,并且 mad_stream 指向切换之前指向的相同逻辑字节。

decoder.cpp 中的重要片段

void Decoder::addFragment          //adds the fragment to decoding queue
(intptr_t bufferPtr, uint32_t length)
{
if (length < GLUE_LENGTH / 2) {
return;
}
uint8_t* buffer = (uint8_t(*))bufferPtr;
RawBuffer rb = {buffer, length};
pending.push_back(rb);

switch (state) {
case empty:
mad_stream_buffer(&stream, buffer, length);

for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[i] = buffer[length - GLUE_LENGTH/2 + i];
}

state = onBufferHalf;
prepareNextBuffer();
break;
case onBufferHalf:
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = buffer[i];
}

state = onBufferFull;
break;
case onGlueHalf:
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = buffer[i];
}

state = onGlueFull;
cached = false;
prepareNextBuffer();
break;
default:
break;
}
}

emscripten::val Decoder::decode //decodes up to requested amount of frames
(uint32_t count)
{
emscripten::val ret = emscripten::val::undefined();

int available = framesLeft(count);
if (available > 0) {
ret = context.call<emscripten::val>("createBuffer", channels, available * samplesPerFrame, sampleRate);

std::vector<emscripten::val> chans(channels, emscripten::val::undefined());
for (int i = 0; i < channels; ++i) {
chans[i] = ret.call<emscripten::val>("getChannelData", i);
}

for (int i = 0; i < available; ++i) {
int res = mad_frame_decode(&frame, &stream);

if (res != 0) {
if (MAD_RECOVERABLE(stream.error)) {
continue;
} else {
break;
}
}

mad_synth_frame(&synth, &frame);
for (int j = 0; j < samplesPerFrame; ++j) {
for (int k = 0; k < channels; ++k) {
float value = mad_f_todouble(synth.pcm.samples[k][j]);
chans[k].set(std::to_string(success * samplesPerFrame + j), emscripten::val(value));
}
}
}

cachedLength -= available;
if (cachedLength == 0) {
cached = false;
prepareNextBuffer();
}
}
return ret;
}


//tells how many frames can be decoded on the same
//sample rate, same amount of channels without switching the buffers
//it is required in Decoder::decode method to understand the size of
//allocating AudioContext::AudioBuffer.

uint32_t Decoder::framesLeft(uint32_t max)
{
if (state == empty || state == onGlueHalf) {
return 0;
}

if (cached == false) {
mad_stream probe;
mad_header ph;
initializeProbe(probe);
mad_header_init(&ph);

while (cachedLength < max) {
if (mad_header_decode(&ph, &probe) == 0) {
if (sampleRate == 0) {
sampleRate = ph.samplerate;
channels = MAD_NCHANNELS(&ph);
samplesPerFrame = MAD_NSBSAMPLES(&ph) * 32;
} else {
if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph) || samplesPerFrame != MAD_NSBSAMPLES(&ph) * 32) {
break;
}
}
if (probe.next_frame > probe.this_frame) {
++cachedLength;
}
} else {
if (!MAD_RECOVERABLE(probe.error)) {
break;
}
}
}

cachedNext = probe.next_frame;
cachedThis = probe.this_frame;
cachedError = probe.error;
mad_header_finish(&ph);
mad_stream_finish(&probe);
cached = true;
}

return std::min(cachedLength, max);
}

//this method fastforwards the stream
//to the cached end
void Decoder::pullBuffer()
{
if (cached == false) {
throw 2;
}
stream.this_frame = cachedThis;
stream.next_frame = cachedNext;
stream.error = cachedError;
}

//this method switches the stream to glue buffer
//or to the next pending buffer
//copies the parts to the glue buffer if required

void Decoder::changeBuffer()
{
uint32_t left;
switch (state) {
case empty:
throw 3;
case onBufferHalf:
switchToGlue();
state = onGlueHalf;
break;
case onBufferFull:
switchToGlue();
state = onGlueFull;
break;
case onGlueHalf:
throw 4;
break;
case onGlueFull:
switchBuffer(pending[0].ptr, pending[0].length);

for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[i] = pending[0].ptr[pending[0].length - GLUE_LENGTH/2 + i];
}
state = onBufferHalf;

if (pending.size() > 1) {
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = pending[1].ptr[i];
}
state = onBufferFull;
}
}

cached = false;
}

//this method seeks the decodable data in pending buffers
//prepares if any proper data has been found
void Decoder::prepareNextBuffer()
{
bool shift;
do {
shift = false;
framesLeft();
if (cachedLength == 0 && state != empty && state != onGlueHalf) {
pullBuffer();
changeBuffer();
shift = true;
}
} while (shift);
}

//low level method to switch to glue buffer, also frees the drained fragment
void Decoder::switchToGlue()
{
switchBuffer(glue, GLUE_LENGTH);
stream.error = MAD_ERROR_NONE;

free(pending[0].ptr);
pending.pop_front();
}

//low level method which actually switch mad_stream
//to another buffer
void Decoder::switchBuffer(uint8_t* bufferPtr, uint32_t length)
{
uint32_t left;
left = stream.bufend - stream.next_frame;
mad_stream_buffer(&stream, bufferPtr + GLUE_LENGTH / 2 - left, length - (GLUE_LENGTH / 2 - left));
stream.error = MAD_ERROR_NONE;
}

Here是我的带有完整代码的存储库。要尝试它,您需要使用 CMake 构建它(应该安装 emscripten)并从浏览器的构建目录中打开 index.html。

问题
播放失真。我尝试检查移位前后最后一个成功帧周围的字节、mad_stream 的所有不同子结构 - 一切似乎都正常工作,但仍然无法正常工作。我的最新进展是构建和托管here 。我真的很困惑,我不知道该怎么做才能消除播放中的失真。

如果有人帮助我,我将非常感激。

最佳答案

我找到了! MAD 工作得很好,只是因为我的内部计数器,我一直在输出中跳过第一个解码帧。

for (int i = 0; success < available; ++i) {
int res = mad_frame_decode(frame, stream);

if (res == 0) {
++**success**;
} else {
if (MAD_RECOVERABLE(stream->error)) {

std::cout << "Unexpected error during the decoding process: " << mad_stream_errorstr(stream) << std::endl;
continue;
} else {
break;
}
}

mad_synth_frame(synth, frame);

for (int j = 0; j < samplesPerFrame; ++j) {
for (int k = 0; k < channels; ++k) {
float value = mad_f_todouble(synth->pcm.samples[k][j]);
chans[k].set(std::to_string(success * samplesPerFrame + j), emscripten::val(value));
}
}
}

成功更改为i并且成功了。

关于javascript - 浏览器中的mp3流解码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54184689/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com