HaxeFoundation / hxnodejs

Haxe externs for working with node.js
MIT License
171 stars 63 forks source link

haxe.io.Bytes.getData(): BytesData != ArrayBuffer #173

Open derRaab opened 4 years ago

derRaab commented 4 years ago

Hi! I'm not sure if that is even an issue, but it took me a while to understand the difference between the Browser based implementation and the one used by node (electron). So I figured others might run into this very specific case as well:

In a nutshell this is the actual runtime difference: Bytes.getData() returns ArrayBuffer in the browser but Buffer in node (electron):

// Browser:
haxe.io.Bytes.getData() : js.lib.ArrayBuffer
// Node (electron):
haxe.io.Bytes.getData() : js.node.Buffer

If you're interested - first the JavaScript runtime error I ran into:

Uncaught (in promise) TypeError: Failed to execute 'decodeAudioData' on 'BaseAudioContext': parameter 1 is not of type 'ArrayBuffer'.
    at AudioContextWaveformVc._onArrayBuffer (AudioContextWaveformVc.hx:126)

This is my little handler causing problems where _audioContext is an AudioContext instance:

function _onArrayBuffer( arrayBuffer : ArrayBuffer ) {
    _audioContext.decodeAudioData( arrayBuffer, _onDecodeAudioSuccess, _onDecodeAudioError );
}

This is my bytes receiving handler, which extracts the needed ArrayBuffer and forwards it:

function _onBytes( data : Bytes ): Void {
    var arrayBuffer : ArrayBuffer = data.getData();
    _onArrayBuffer( arrayBuffer );
}

So if I use Google Chrome and Http to load the bytes it works fine:

var http : Http = new Http( url );
    http.onBytes = _onBytes;
    http.request( true );

BUT if I use the File API to read the bytes, decoding audio throws the TypeError:

var bytes : Bytes = File.getBytes( path );
_onBytes( bytes );

After some research I figured out, that running node (electron), Bytes.getData() actually returns a js.node.Buffer instance:

Bildschirmfoto 2020-09-04 um 12 44 23

So in my case I had to convert the node buffer into an array buffer, which led me to this quite old stack overflow answer: https://stackoverflow.com/questions/8609289/convert-a-binary-nodejs-buffer-to-javascript-arraybuffer/31394257#31394257

Just in case you need that:

class BufferUtil {
    public static function toArrayBuffer(buf:Buffer): ArrayBuffer {
        var ab = new ArrayBuffer(buf.length);
        var view = new Uint8Array(ab);
        for (i in 0...buf.length) {
            view[i] = buf[i];
        }
        return ab;
    }
}

So is Bytes.getData() not consistent on all platforms? Is this expected behaviour? Or is this just a very rare edge case?

kevinresol commented 3 years ago

I guess that's because the Bytes instance is created with js.node.Buffer.hxToBytes()?

derRaab commented 3 years ago

I wonder if my conversion method will double the memory usage. And especially if loading a big media file that processing does take a while. Hm.