Skip to content

Added support for Interleave#2

Open
surfdude75 wants to merge 2 commits into
watson:masterfrom
surfdude75:interleave-support
Open

Added support for Interleave#2
surfdude75 wants to merge 2 commits into
watson:masterfrom
surfdude75:interleave-support

Conversation

@surfdude75
Copy link
Copy Markdown

@surfdude75 surfdude75 commented Apr 25, 2016

Implementation works like

decoder.on('packet',function(packet){
  console.log('packet channel:',packet.channel);
  console.log('packet size:',packet.size);
  var bufs = [];
  packet.on('data',function(data){ bufs.push(data); });
  packet.on('end',function(){
    var payload = Buffer.concat(bufs);
    console.log('packet payload:',payload);
  });
});

Implementation works like

decoder.on('packet',function(packet){
  console.log('packet channel:',packet.channel);
  console.log('packet size:',packet.size);
  var bufs = [];
  packet.on('data',function(data){ bufs.push(data); });
  packet.on('end',function(){
    var payload = Buffer.concat(bufs);
    console.log('packet payload:',payload);
  });
});
@surfdude75
Copy link
Copy Markdown
Author

It seems to work. I still working on in it. I am going to try to build some tests.
Aloha!

@watson
Copy link
Copy Markdown
Owner

watson commented May 1, 2016

@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?

@surfdude75
Copy link
Copy Markdown
Author

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

@watson
Copy link
Copy Markdown
Owner

watson commented May 4, 2016

@surfdude75 ah, thanks 😃

Would you be ok with adding tests for Interleaved encoding and decoding to this PR?

@surfdude75
Copy link
Copy Markdown
Author

I am working in add the tests.

I forgot about encoding. I am going to add it too.

Aloha,

Rafael Sobral

Comment thread decoder.js
debug('start of interleaved header')
this._inInterleavedHead = true
}
if (chunk.length - offset < 4) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the 4 here be INTERLEAVED_HEADER_BYTES if I understand the code correctly?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you are right.

@notedit
Copy link
Copy Markdown

notedit commented Sep 26, 2017

will we merge this? i will need this too.

@luislobo
Copy link
Copy Markdown

@surfdude75 Did you had the chance to finish this project?

@savageautomate
Copy link
Copy Markdown

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 getting Invalid RTSP Request-Line errors when more than one RTSP client was connected to a common RTSP endpoint.

My changes are here: savageautomate@7971c99

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.

@max-mapper
Copy link
Copy Markdown

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)
  })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants