Streampunk / beamcoder

Node.js native bindings to FFmpeg.
GNU General Public License v3.0
397 stars 76 forks source link

Beamcoder resampling filter crashing from MP3 decoded frames, works fine with FLAC decoded frames #31

Open suldashi opened 4 years ago

suldashi commented 4 years ago

I am making a universal audio converter using Beamcoder, and I have successfully managed to set up the audio decoder. The idea is also to take these decoded frames and run them through a resampler before encoding them in the final step (not shown here).

I am having trouble with Beamcoder crashing with some cryptic errors if the decoded frames come from an MP3 file. Frames from a FLAC file don't give me any issues.

Here's the code:

const fs = require("fs");
const beamcoder = require("beamcoder");

let filterer = null;
let outSampleRate = 48000;
let outFormat = "s16";
let bytesPerSample = 2;
let outFile = fs.createWriteStream("resampled.raw");

decodeAudioFile("tri.mp3", async (metadata) => {
    filterer = await beamcoder.filterer({
        filterType: 'audio',
        inputParams: [
          {
            sampleRate: metadata.sampleRate,
            sampleFormat: outFormat,
            channelLayout: metadata.channelLayout,
            timeBase: metadata.timeBase
          }
        ],
        outputParams: [
          {
            sampleRate: outSampleRate,
            sampleFormat: outFormat,
            channelLayout: metadata.channelLayout
          }
        ],
        filterSpec: `aresample=isr=${metadata.sampleRate}:osr=${outSampleRate}:async=1, aformat=sample_fmts=${outFormat}:channel_layouts=${metadata.channelLayout}`
      });
}, async (frameData) => {
    let filteredData = await filterer.filter([frameData]);
    for(var i in filteredData) {
        for(var j in filteredData[i].frames) {
            for(var k in filteredData[i].frames[j].data) {
                let rawData = filteredData[i].frames[j].data[k];
                let cutData = rawData.slice(0, filteredData[i].frames[j].nb_samples*bytesPerSample*filteredData[i].frames[j].channels);
                outFile.write(cutData);
            }
        }
    }   
});

  async function decodeAudioFile(filePath, onInputMetadata, onFrame) {
    let data = {};
    try {
        let demuxer = await beamcoder.demuxer(filePath);
        let decoder = beamcoder.decoder({ demuxer, stream_index: 0, request_sample_fmt: outFormat });
        await onInputMetadata({
            sampleRate: decoder.sample_rate,
            channels: decoder.channels,
            timeBase: decoder.time_base,
            sampleFormat: decoder.sample_fmt,
            channelLayout: decoder.channel_layout
        });
        try {
            packet = await demuxer.read();
        }
        catch(err) {
            console.error(err);
        }
        while(packet != null) {
            try {
                let dec_result = await decoder.decode(packet);
                for(var i in dec_result.frames) {
                    for(var j in dec_result.frames[i].data) {
                        let data = dec_result.frames[i];
                        onFrame(data);
                    }
                }
            }
            catch(err) {
                console.error(err);
            }
            packet = await demuxer.read();
        }
        let frames = await decoder.flush();
        if(frames.frames.length > 0) {
            for(var j in frames.frames[i].data) {
                onFrame(frames.frames[i]);
            }
        }
    }
    catch(err) {
        data.err = err;
    }
    return data;
}

The decodeAudioFile function works properly, as it decodes the audio from MP3 or FLAC into PCM frames. When I put these PCM frames on the resampler, FLAC works fine but MP3 fails with the following console output:

Aerostat Beam Coder  Copyright (C) 2019  Streampunk Media Ltd
GPL v3.0 or later license. This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. Conditions and warranty at:
https://github.com/Streampunk/beamcoder/blob/master/LICENSE
[mp3 @ 0000028017c20900] Could not update timestamps for skipped samples.
PID 18832 received SIGSEGV for address: 0xc34949e6
PID 18832 received SIGSEGV for address: 0xc46b9269
PID 18832 received SIGSEGV for address: 0xc349491a
SymInit: Symbol-SearchPath: '.;C:\Dev\audio-conv;C:\Program Files\nodejs;C:\WINDOWS;C:\WINDOWS\system32;SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols;', symOptions: 530, UserName: 'Ermir Desktop'
SymInit: Symbol-SearchPath: '.;C:\Dev\audio-conv;C:\Program Files\nodejs;C:\WINDOWS;C:\WINDOWS\system32;SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols;', symOptions: 530, UserName: 'Ermir Desktop'
SymInit: Symbol-SearchPath: '.;C:\Dev\audio-conv;C:\Program Files\nodejs;C:\WINDOWS;C:\WINDOWS\system32;SRV*C:\websymbols*http://msdl.microsoft.com/download/symbols;', symOptions: 530, UserName: 'Ermir Desktop'
OS-Version: 10.0.18362 () 0x100-0x1
OS-Version: 10.0.18362 () 0x100-0x1
OS-Version: 10.0.18362 () 0x100-0x1
c:\dev\audio-conv\node_modules\segfault-handler\src\stackwalker.cpp (924): StackWalker::ShowCallstack
c:\dev\audio-conv\node_modules\segfault-handler\src\stackwalker.cpp (924): StackWalker::ShowCallstack
c:\dev\audio-conv\node_modules\segfault-handler\src\stackwalker.cpp (924): StackWalker::ShowCallstack
c:\dev\audio-conv\node_modules\segfault-handler\src\segfault-handler.cpp (242): segfault_handler
c:\dev\audio-conv\node_modules\segfault-handler\src\segfault-handler.cpp (242): segfault_handler
c:\dev\audio-conv\node_modules\segfault-handler\src\segfault-handler.cpp (242): segfault_handler
00007FFBC4638636 (ntdll): (filename not available): RtlIsGenericTableEmpty
00007FFBC4638636 (ntdll): (filename not available): RtlIsGenericTableEmpty
00007FFBC4638636 (ntdll): (filename not available): RtlIsGenericTableEmpty
00007FFBC462A0D6 (ntdll): (filename not available): RtlRaiseException
00007FFBC462A0D6 (ntdll): (filename not available): RtlRaiseException
00007FFBC462A0D6 (ntdll): (filename not available): RtlRaiseException
00007FFBC465FE6E (ntdll): (filename not available): KiUserExceptionDispatcher
00007FFBC465FE6E (ntdll): (filename not available): KiUserExceptionDispatcher
00007FFBC462A043 (ntdll): (filename not available): RtlRaiseException
00007FFBC349491A (msvcrt): (filename not available): memcpy
00007FFBC34949E6 (msvcrt): (filename not available): memcpy
00007FFBC46B9269 (ntdll): (filename not available): RtlIsNonEmptyDirectoryReparsePointAllowed
00007FFBA03493A1 (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBA03493A1 (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBC46B9233 (ntdll): (filename not available): RtlIsNonEmptyDirectoryReparsePointAllowed
00007FFBA03496C9 (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBA03496C9 (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBC46C1622 (ntdll): (filename not available): RtlpNtMakeTemporaryKey
00007FFBA0349FD1 (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBA0349FD1 (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBC46C192A (ntdll): (filename not available): RtlpNtMakeTemporaryKey
00007FFBA034AA75 (swresample-3): (filename not available): swr_convert
00007FFBA034AA75 (swresample-3): (filename not available): swr_convert
00007FFBC46CA8E9 (ntdll): (filename not available): RtlpNtMakeTemporaryKey
00007FFBA034B105 (swresample-3): (filename not available): swr_convert
00007FFBA034B105 (swresample-3): (filename not available): swr_convert
00007FFBC46664AD (ntdll): (filename not available): memset
00007FFBA034AFF7 (swresample-3): (filename not available): swr_convert
00007FFBA034AFF7 (swresample-3): (filename not available): swr_convert
00007FFBC4606139 (ntdll): (filename not available): RtlReAllocateHeap
00007FFBA034B59C (swresample-3): (filename not available): swr_next_pts
00007FFBA034B59C (swresample-3): (filename not available): swr_next_pts
00007FFBC460533B (ntdll): (filename not available): RtlReAllocateHeap
00007FFB40571C08 (avfilter-7): (filename not available): (function-name not available)
00007FFB40571C08 (avfilter-7): (filename not available): (function-name not available)
00007FFBC4604C99 (ntdll): (filename not available): RtlReAllocateHeap
00007FFB405C25DD (avfilter-7): (filename not available): avfilter_pad_get_type
00007FFB405C25DD (avfilter-7): (filename not available): avfilter_pad_get_type
00007FFBC4601EB3 (ntdll): (filename not available): RtlGetCurrentServiceSessionId
00007FFB405C67B1 (avfilter-7): (filename not available): avfilter_graph_request_oldest
00007FFB405C67B1 (avfilter-7): (filename not available): avfilter_graph_request_oldest
00007FFBC4600810 (ntdll): (filename not available): RtlGetCurrentServiceSessionId
c:\dev\audio-conv\node_modules\beamcoder\src\filter.cc (1518): filterExecute
c:\dev\audio-conv\node_modules\beamcoder\src\filter.cc (1518): filterExecute
00007FFBC45FFC11 (ntdll): (filename not available): RtlFreeHeap
00007FF76BE9DF1E (node): (filename not available): uv_queue_work
00007FF76BE9DF1E (node): (filename not available): uv_queue_work
00007FFBC3439CFC (msvcrt): (filename not available): free
00007FF76BE8B71D (node): (filename not available): uv_poll_stop
00007FF76BE8B71D (node): (filename not available): uv_poll_stop
00007FFBC34393D6 (msvcrt): (filename not available): aligned_free
00007FF76CB0FD90 (node): (filename not available): v8::internal::SetupIsolateDelegate::SetupHeap
00007FF76CB0FD90 (node): (filename not available): v8::internal::SetupIsolateDelegate::SetupHeap
00007FFBA03493CA (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBC2B57BD4 (KERNEL32): (filename not available): BaseThreadInitThunk
00007FFBC2B57BD4 (KERNEL32): (filename not available): BaseThreadInitThunk
00007FFBA03496C9 (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBC462CED1 (ntdll): (filename not available): RtlUserThreadStart
00007FFBC462CED1 (ntdll): (filename not available): RtlUserThreadStart
00007FFBA0349FD1 (swresample-3): (filename not available): swr_alloc_set_opts
00007FFBA034AA75 (swresample-3): (filename not available): swr_convert
00007FFBA034B105 (swresample-3): (filename not available): swr_convert
00007FFBA034AFF7 (swresample-3): (filename not available): swr_convert
00007FFBA034B59C (swresample-3): (filename not available): swr_next_pts
00007FFB40571C08 (avfilter-7): (filename not available): (function-name not

The output gets cut off at the end in the middle of the line, I made no mistake when copying.

scriptorian commented 4 years ago

Hi, While I can't at the moment understand what is going wrong, I have a some suggestions for you that may help get past this.

Firstly, beamcoder.decoder creation doesn't do anything with request_sample_fmt. You will need to set this property on the object after creation : decoder.request_sample_fmt = outFormat.

Secondly, you are not checking the stream index of packets from the demuxer before decoding eg: if (packet && packet.stream_index == 0) try {...}. The stream_index value you pass into decoder creation is only used to select the stream to be used for setting up the codec parameters and doesn't set up the decoder to ignore other streams. If your MP3 file has multiple streams this could cause some problems.

Finally I am concerned that the call stack appears to show two filterExecute calls in progress at the same time, which is not a good idea. I can't see a problem in your code but it would be worth checking that you only have one request outstanding at a time.

Good luck!

suldashi commented 4 years ago

I believe there's something wrong with the MP3 decoder in particular. I solved my problem by adding a fake WAV header to the decoded data and running it through a WAV decoder. I finally got proper frame that I sent to the resampler and got the expected result.

However, I could not get the OPUS encoder to work, all packets had length 0. I think I am missing some options, but that's an issue for some other place.