Ive been learning and programming a site in html/js that captures and displays online streams from ip cameras and all is working great, except the audio on this one. I'm stuck. I havent been able to figure it out. Ive done everything I could to make it work but this just goes above my level of understanding on how to read it to play it.
I found this Q&A InitAudio function that definitely goes in the right direction, but unfortunately I couldnt make it work. Using it, I can see the stream starts downloading kb but the html5 audio player doesnt start, I believe, as it does not read the audio file correctly.
In essence, I want to play an audio stream in WAV/PCM (G711) raw format that comes from an online D-Link IP Camera DCS-2230 with JS. Stream URL is:
http://IP:PORT/audio/ACAS.cgi?profileid=1
I read the manual that explains how the specific audio stream works.
This is what it indicates:
This is the specific header file/structure:
**Advanced ip-Camera Stream Audio (ACS) header**
**ACS Audio header**
typedef struct _ACS_AudioHeader
{
unsigned long ulHdrID; //Header ID
unsigned long ulHdrLength;
unsigned long ulDataLength;
unsigned long ulSequenceNumber;
unsigned long ulTimeSec;
unsigned long ulTimeUSec;
unsigned long ulDataCheckSum;
unsigned short usFormat;
unsigned short usChannels;
unsigned short usSampleRate;
unsigned short usSampleBits;
unsigned long ulReserved;
}ACS_AudioHeader, *PACS_AudioHeader;
Description:
The byte order of this header is little-endian
**ulHdrID**: Special id for identifying ACS header. For audio: the value of this id is 0xF6010000 (since our header is in
little-endian so the byte array of this id is ‟00 00 01 F6‟).
**ulHdrLength**: Header length. (32 bytes in current version)
**ulDataLength**: Payload data length.
**ulSequenceNumber**: Sequence number.
**ulTimeSec**: Time stamp in sec since 1970/01/01 00:00.
**ulTimeUSec**: Microsecond part of time stamp
**ulDataCheckSum**: Store last 4 bytes of payload data.
**usFormat**: Audio data format. The possible value:
AFMT_MS_ADPCM: 0
AFMT_MU_LAW: 1
AFMT_A_LAW: 2
AFMT_IMA_ADPCM: 4
AFMT_U8: 8
AFMT_S16_LE: 0x10 /* Little endian signed 16 */
AFMT_S16_BE: 0x20 /* Big endian signed 16 */
AFMT_S8: 0x40
AFMT_U16_LE: 0x80 /* Little endian U16 */
AFMT_U16_BE: 0x100 /* Big endian U16 */
AFMT_MPEG: 0x200 /* MPEG (2) audio */
AFMT_AC3: 0x400
AFMT_AMR: 0x800
**usChannels**: Audio channels number: mono(1) or stereo(2).
**usSampleRate**: Sample rate.
**usSampleBits**: Bits count per sample.
**ulReserved**: Reserved.
This is the code so far
<script>
async function initAudio() {
// specify your file and its audio properties
const url = 'http://IP:PORT/audio/ACAS.cgi?profileid=1'
const sampleRate = 8000
const numChannels = 1 // mono or stereo
const isFloat = false // integer or floating point
const buffer = await (await fetch(url, {method: 'GET', headers: {'Content-Type': 'audio/ACAS','Authorization': 'Basic YWRtaW46cGFzc3dvcmQ='}
})).arrayBuffer()
// create WAV header
const \[type, format\] = isFloat ? \[Float32Array, 3\] : \[Uint8Array, 1\]
const wavHeader = new Uint8Array(buildWaveHeader({
numFrames: buffer.byteLength / type.BYTES_PER_ELEMENT,
bytesPerSample: type.BYTES_PER_ELEMENT,
sampleRate,
numChannels,
format
}))
// create WAV file with header and downloaded PCM audio
const wavBytes = new Uint8Array(wavHeader.length + buffer.byteLength)
wavBytes.set(wavHeader, 0)
wavBytes.set(new Uint8Array(buffer), wavHeader.length)
// show audio player
const audio = document.getElementById('myLiveAudio');
const blob = new Blob(\[wavBytes\], { type: 'audio/wav' });
audio.src = URL.createObjectURL(blob);
audio.play();
}
// **I dont know how to build the proper ACAS header**
// adapted from https://gist.github.com/also/900023
function buildWaveHeader(opts) {
const numFrames = opts.numFrames;
const numChannels = opts.numChannels || 2;
const sampleRate = opts.sampleRate || 44100;
const bytesPerSample = opts.bytesPerSample || 2;
const format = opts.format
const blockAlign = numChannels \* bytesPerSample;
const byteRate = sampleRate \* blockAlign;
const dataSize = numFrames \* blockAlign;
const buffer = new ArrayBuffer(44);
const dv = new DataView(buffer);
let p = 0;
function writeString(s) {
for (let i = 0; i \< s.length; i++) {
dv.setUint8(p + i, s.charCodeAt(i));
}
p += s.length;
}
function writeUint32(d) {
dv.setUint32(p, d, true);
p += 4;
}
function writeUint16(d) {
dv.setUint16(p, d, true);
p += 2;
}
writeString('RIFF'); // ChunkID
writeUint32(dataSize + 36); // ChunkSize
writeString('WAVE'); // Format
writeString('fmt '); // Subchunk1ID
writeUint32(16); // Subchunk1Size
writeUint16(format); // AudioFormat
writeUint16(numChannels); // NumChannels
writeUint32(sampleRate); // SampleRate
writeUint32(byteRate); // ByteRate
writeUint16(blockAlign); // BlockAlign
writeUint16(bytesPerSample \* 8); // BitsPerSample
writeString('data'); // Subchunk2ID
writeUint32(dataSize); // Subchunk2Size
return buffer;
}
</script>
<audio type="audio/wav" preload ="none" volume="1" id="myLiveAudio" controls></audio>
<span class="speaker" id="speaker" alt="Live sound" title="Live sound" onclick="initAudio();">🕨</span>
Thanks for reading!!