In docs, I see the following example for sending a string over a WebRTC data channel:
var message = messageInputBox.value;
sendChannel.send(message);
I'm trying to send images and video - so I create an ArrayBuffer
:
var reader = new FileReader();
reader.addEventListener("loadend", function () {
// reader.result contains the contents of blob as a typed array
data.media = reader.result; // ArrayBuffer
outboundMediaQueue.push(data);
});
reader.readAsArrayBuffer(data.media);
I can transmit this ArrayBuffer, but I'm trying to also send some metadata alongside it (media ID). Given that I can't JSON.serialize
an ArrayBuffer - how can I transfer this data with corresponding metadata?
Update:
This works:
sendChannel.send(data.media); // sending `ArrayBuffer` only
But this doesn't work:
sendChannel.send(data); // arbitrary object with desired metadata
And this also doesn't work:
sendChannel.send(JSON.stringify(data));
Update 2
I've composed the following module that seems to handle the encoding and decoding. It's not quite fully tested and hasn't been run against large files; I'll keep this updated as I figure out what works:
define([], function () {
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Solution_2_–_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
// https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
const arrayBufferConverter = {};
arrayBufferConverter.bufferToString = function (outboundBuffer) {
const dataView = new DataView(outboundBuffer);
const decoder = new TextDecoder('utf-8'); // base64?
const decodedString = decoder.decode(dataView);
return decodedString;
}
arrayBufferConverter.stringToBuffer = function (inboundString) {
var encoded = new TextEncoderLite('utf-8').encode(inboundString);
var b64Encoded = base64js.fromByteArray(encoded);
return b64Encoded;
}
return arrayBufferConverter;
});
Update 3
Since my only real need here is to pair metadata with the ArrayBuffer
, I'm going to forget the encoding/decoding nonsense and just generate a digest on both ends of the transaction:
arrayBufferConverter.getDigestFromBuffer = function (buffer) {
const view = new DataView(buffer);
const numBytes = view.byteLength;
const interval = Math.floor(numBytes / 32);
var currentIndex = 0;
var digest = "";
while (currentIndex < numBytes) {
digest += view.getInt8(currentIndex);
currentIndex += interval;
}
return digest;
}
This way I'll send the ArrayBuffers
and corresponding metadata in two separate requests and manage the pairing with some basic queue logic. This totally eliminates ongoing processing cost and the risks inherent in encoding/decoding large media files in favor of a small fixed cost of pairing logic. I'll leave this open in case anyone has any other recommendations.