Last active
April 11, 2024 04:40
-
-
Save philhartung/87d336a3c432e2ce5452befcad1b945f to your computer and use it in GitHub Desktop.
Relay a Dante multicast stream to AES67. This assumes the AES67 device is synced to the same PTP master, as no PTP timestamping is done (timestamp from Dante is copied to AES67 RTP packet)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const dgram = require('dgram'); | |
const client = dgram.createSocket({ type: 'udp4', reuseAddr: true }); | |
const sdp = require('./sdp'); | |
//config | |
const addr = '10.10.1.100'; | |
const danteMulticast = '239.255.220.221'; | |
const aes67Multicast = '239.69.1.122'; | |
const samplerate = 48000; | |
const channels = 2; | |
const encoding = 'L24'; | |
const name = 'Dante Multicast Relay'; | |
const sessID = Math.round(Date.now() / 1000); | |
const sessVersion = sessID; | |
const ptpMaster = '08-00-00-ff-fe-00-00-1f:0'; | |
//rtp specific vars | |
var seqNum = 0; | |
client.on('listening', function() { | |
client.addMembership(danteMulticast, addr); | |
client.setMulticastInterface(addr); | |
}); | |
client.on('message', function(buffer, remote) { | |
//read values from buffer | |
var channelCount = buffer.readUInt8(0); | |
var timestampSeconds = buffer.readUInt32BE(1); | |
//bytes 6 and 7 seem to be always 0x00, maybe reserved bytes | |
var timestampMedia = buffer.readUInt16BE(7); | |
var pcmData = buffer.slice(9); | |
//calculate media timestamp for rtp | |
var timestampRTP = ((timestampSeconds * samplerate) + timestampMedia) & 0xffffffff; | |
//create RTP header | |
var rtpHeader = Buffer.alloc(12); | |
rtpHeader.writeUInt16BE(0x8061, 0); | |
rtpHeader.writeUInt16BE(seqNum, 2); | |
rtpHeader.writeInt32BE(timestampRTP, 4); | |
rtpHeader.writeUInt32BE(0xaf12af34, 8); | |
//create and send RTP packet | |
var rtpBuffer = Buffer.concat([rtpHeader, pcmData]); | |
client.send(rtpBuffer, 5004, aes67Multicast); | |
//increase seqnum | |
seqNum = (seqNum + 1) % 65536; | |
}); | |
client.bind(4321); | |
sdp.start(addr, aes67Multicast, samplerate, channels, encoding, name, sessID, sessVersion, ptpMaster); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var dgram = require('dgram'); | |
var socket = dgram.createSocket({ type: 'udp4', reuseAddr: true }); | |
var constructSDPMsg = function(addr, multicastAddr, samplerate, channels, encoding, name, sessID, sessVersion, ptpMaster){ | |
var sapHeader = Buffer.alloc(8); | |
var sapContentType = Buffer.from('application/sdp\0'); | |
var ip = addr.split('.'); | |
//write version/options | |
sapHeader.writeUInt8(0x20); | |
//write hash | |
sapHeader.writeUInt16LE(0xefef, 2); | |
//write ip | |
sapHeader.writeUInt8(parseInt(ip[0]), 4); | |
sapHeader.writeUInt8(parseInt(ip[1]), 5); | |
sapHeader.writeUInt8(parseInt(ip[2]), 6); | |
sapHeader.writeUInt8(parseInt(ip[3]), 7); | |
var sdpConfig = [ | |
'v=0', | |
'o=- '+sessID+' '+sessVersion+' IN IP4 '+addr, | |
's='+name, | |
'c=IN IP4 '+multicastAddr+'/32', | |
't=0 0', | |
'a=clock-domain:PTPv2 0', | |
'm=audio 5004 RTP/AVP 96', | |
'a=rtpmap:96 '+encoding+'/'+samplerate+'/'+channels, | |
'a=sync-time:0', | |
'a=framecount:48', | |
'a=ptime:1', | |
'a=mediaclk:direct=0', | |
'a=ts-refclk:ptp=IEEE1588-2008:'+ptpMaster, | |
'a=recvonly', | |
'' | |
]; | |
var sdpBody = Buffer.from(sdpConfig.join('\r\n')); | |
return Buffer.concat([sapHeader, sapContentType, sdpBody]); | |
} | |
exports.start = function(addr, multicastAddr, samplerate, channels, encoding, name, sessID, sessVersion, ptpMaster){ | |
sdpMSG = constructSDPMsg(addr, multicastAddr, samplerate, channels, encoding, name, sessID, sessVersion, ptpMaster); | |
socket.bind(9875, function(){ | |
socket.setMulticastInterface(addr); | |
socket.send(sdpMSG, 9875, '239.255.255.255', function(err){}); | |
}); | |
setInterval(function(){ | |
socket.send(sdpMSG, 9875, '239.255.255.255', function(err){}); | |
}, 30*1000); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Most reliable way to do this if you must use Windows is to stick within the "Dante" eco system and get Audinate's VSC. Then you can handle recording your 5 minute wav dumps however you see fit. The RDL should show up as a Dante device, you don't need to involve AES67 here.
Basically, you'll use Dante Controller to wire up the 4 channels from the RDL to this virtual device on your Windows machine, which would be trivial for you to record from.