Open surfdude75 opened 8 years ago
It seems to work. I still working on in it. I am going to try to build some tests. Aloha!
@surfdude75 thanks for PR 😄 Looks good so far. I have no clue what this "Interleave" thing is - do you have a link or something to a page explaining this?
hi @watson,
It is to get stream packet over the same tcp connection used by RTSP protocol You can find some info in the following link: Embedded (Interleaved) Binary Data at https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol I am using it to get camera stream data.
Thanks for sharing this project.
Aloha,
Rafael Sobral
@surfdude75 ah, thanks 😃
Would you be ok with adding tests for Interleaved encoding and decoding to this PR?
I am working in add the tests.
I forgot about encoding. I am going to add it too.
Aloha,
Rafael Sobral
will we merge this? i will need this too.
@surfdude75 Did you had the chance to finish this project?
Ah .. I just found this PR after writing my own filtering logic to strip out the TCP interleaved RTP packets. Using the Live555 Proxy server I was gettingInvalid RTSP Request-Line
errors when more than one RTSP client was connected to a common RTSP endpoint.
My changes are here: https://github.com/savageautomate/rtsp-stream/commit/7971c9942cc7f5b04f285d8125c495a0ba27e397
I was going to open a new PR for my changes but I don't want to overlap or create conflicts .. plus this PR looks to be more comprehensive.
I was messing with this branch to try to get a rtsp client working, but it doesn't expect to work in client mode I think, it crashed with
packet size: 2
buffer.js:615
return _copy(this, target, targetStart, sourceStart, sourceEnd);
^
RangeError: Index out of range
hacky code:
var net = require('net')
var rtsp = require('./rtsp-stream')
var crypto = require('crypto')
var encoder = new rtsp.Encoder()
var decoder = new rtsp.Decoder()
let WWW_AUTH_REGEX = new RegExp('([a-z]+)=\"([^,\s]+)\"')
let uri = process.argv[2]
let seq = 0
// connect to RTSP server
var socket = net.connect({ host: 'localhost', port: 8080 }, function () {
console.error('making request')
// make a request to the RTSP server
var req = encoder.request({ method: 'OPTIONS', uri: uri })
req.setHeader('CSeq', ++seq)
req.end()
})
let auth
let session
decoder.on('packet',function(packet){
if (packet.size && packet.size > 0) {
console.error('packet channel:',packet.channel);
console.error('packet size:',packet.size);
var bufs = [];
packet.on('data',function(data){ bufs.push(data); });
packet.on('end',function(){
var payload = Buffer.concat(bufs);
if (payload.length) process.stdout.write(payload)
});
}
});
// handle response from server
decoder.on('response', async function (res) {
console.error('--> received response from server:', res.statusCode)
console.error('--> headers:', res.headers)
if (!session && res.headers.session) {
session = res.headers.session.split(';')[0]
console.error('PLAY', session)
var req = encoder.request({ method: 'PLAY', uri: uri })
req.setHeader('CSeq', ++seq)
req.setHeader('Authorization', auth)
req.setHeader('Session', session)
req.end()
return
}
if (res.statusCode === 200 && res.headers.public) {
var req = encoder.request({ method: 'DESCRIBE', uri: uri })
req.setHeader('CSeq', ++seq)
req.setHeader('Authorization', auth)
req.setHeader('Accept', 'application/sdp')
req.end()
await(delay(1000))
var req = encoder.request({ method: 'SETUP', uri: uri + '/trackID=0'})
req.setHeader('CSeq', ++seq)
req.setHeader('Authorization', auth)
req.setHeader('Session', 'null')
req.setHeader('Transport', 'DH/AVP/TCP;unicast;interleaved=0-1')
req.end()
}
if (res.headers['www-authenticate']) {
let authHeaders = {}
let x = res.headers['www-authenticate']
x.split(',')
.map((x)=>x.trim())
.map((x)=>x.split('='))
.map((x)=>{
x[0] = x[0].replace('Digest ', '')
x[1] = x[1].replace(/"/ig, '')
authHeaders[x[0]] = x[1]
})
auth = getDigest('OPTIONS', uri, 'admin', 'password', authHeaders.realm, authHeaders.nonce)
var req = encoder.request({ method: 'OPTIONS', uri: uri })
req.setHeader('CSeq', ++seq)
req.setHeader('Authorization', auth)
req.end()
}
})
// connect the plumbing
encoder.pipe(socket).pipe(decoder)
function getDigest(requestName, url, username, password, realm, nonce) {
let ha1 = getMD5Hash(`${username}:${realm}:${password}`);
let ha2 = getMD5Hash(`${requestName}:${url}`);
let ha3 = getMD5Hash(`${ha1}:${nonce}:${ha2}`);
return `Digest username="${username}",realm="${realm}",nonce="${nonce}",uri="${url}",response="${ha3}"`;
}
function getMD5Hash(data) {
return crypto.createHash('md5').update(data).digest('hex')
}
async function delay (ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, ms)
})
}
Implementation works like