import { RtspMessage , MessageType , SdpMessage , RtpMessage , RtcpMessage , } from '../message' ; import { messageFromBuffer } from '../../utils/protocols/sdp' ; import { bodyOffset , extractHeaderValue } from '../../utils/protocols/rtsp' ; import { rtcpMessageFromBuffer } from '../../utils/protocols/rtcp' ; /**
 * The different possible internal parser states.
 */ enum STATE { IDLE = 0 , INTERLEAVED = 1 , RTSP = 2 } const INTERLEAVED_HEADER_BYTES = 4 ; const ASCII_DOLLAR = 0x24 ; interface RtpPacketInfo { channel : number ; begin : number ; end : number ; } param chunks return const rtpPacketInfo = ( chunks ) : RtpPacketInfo => { const header = Buffer . alloc ( INTERLEAVED_HEADER_BYTES ) ; let i = 0 ; let bytesRead = 0 ; while ( bytesRead < header . length ) { const chunk = chunks [ i ++ ] ; const bytesToRead = Math . min ( chunk . length , header . length - bytesRead ) ; chunk . copy ( header , bytesRead , 0 , bytesToRead ) ; bytesRead += bytesToRead ; } const channel = header [ 1 ] ; const begin = header . length ; const length = header . readUInt16BE ( 2 ) ; const end = begin + length ; return { channel , begin , end } ; } ; type { [ type ] } export class Parser { private _chunks : Buffer [ ] = [ ] ; private _length = 0 ; private _state : STATE = STATE . IDLE ; private _packet ? : RtpPacketInfo ; return { undefined } constructor ( ) { this . _init ( ) ; } return { undefined } _init ( ) { this . _chunks = [ ] ; this . _length = 0 ; this . _state = STATE . IDLE ; } _push ( chunk ) { this . _chunks . push ( chunk ) ; this . _length += chunk . length ; } return { Array } _parseRtsp ( ) { const messages = [ ] ; const buffer = Buffer . concat ( this . _chunks ) ; const chunkBodyOffset = bodyOffset ( buffer ) ; if ( chunkBodyOffset === - 1 ) { return messages ; } const rtspHeaderLength = chunkBodyOffset ; const contentLength = extractHeaderValue ( buffer , 'Content-Length' ) ; if ( contentLength && parseInt ( contentLength ) > buffer . length - rtspHeaderLength ) { return messages ; } this . _init ( ) ; if ( rtspHeaderLength === buffer . length || buffer [ rtspHeaderLength ] === ASCII_DOLLAR ) { const packet = buffer . slice ( 0 , rtspHeaderLength ) ; messages . push ( { type : MessageType . RTSP , data : packet } ) ; const trailing = buffer . slice ( rtspHeaderLength ) ; this . _push ( trailing ) ; } else { const packet = buffer ; const body = buffer . slice ( rtspHeaderLength ) ; messages . push ( { type : MessageType . RTSP , data : packet } ) ; messages . push ( messageFromBuffer ( body ) ) ; } return messages ; } return { Array } _parseInterleaved ( ) { const messages = [ ] ; if ( this . _length < INTERLEAVED_HEADER_BYTES ) { return messages ; } if ( ! this . _packet ) { this . _packet = rtpPacketInfo ( this . _chunks ) ; } if ( this . _length < this . _packet . end ) { return messages ; } const buffer = Buffer . concat ( this . _chunks ) ; const packet = buffer . slice ( this . _packet . begin , this . _packet . end ) ; const trailing = buffer . slice ( this . _packet . end ) ; const channel = this . _packet . channel ; delete this . _packet ; this . _init ( ) ; this . _push ( trailing ) ; if ( channel % 2 === 0 ) { messages . push ( { type : MessageType . RTP , data : packet , channel } ) ; } else { let rtcpPackets = packet ; do { const rtcpByteSize = rtcpPackets . readUInt16BE ( 2 ) * 4 + 4 ; messages . push ( rtcpMessageFromBuffer ( channel , rtcpPackets . slice ( 0 , rtcpByteSize ) ) ) ; rtcpPackets = rtcpPackets . slice ( rtcpByteSize ) ; } while ( rtcpPackets . length > 0 ) ; } return messages ; } /**
     * Set the internal state based on the type of the first chunk
     */ _setState ( ) { while ( this . _chunks . length > 0 && this . _chunks [ 0 ] . length === 0 ) { this . _chunks . shift ( ) ; } const firstChunk = this . _chunks [ 0 ] ; if ( this . _chunks . length === 0 ) { this . _state = STATE . IDLE ; } else if ( firstChunk [ 0 ] === ASCII_DOLLAR ) { this . _state = STATE . INTERLEAVED ; } else if ( firstChunk . toString ( 'ascii' , 0 , 4 ) === 'RTSP' ) { this . _state = STATE . RTSP ; } else { throw new Error ( `Unknown chunk of length ${ firstChunk . length }` ) ; } } param chunk return parse ( chunk ) { this . _push ( chunk ) ; if ( this . _state === STATE . IDLE ) { this . _setState ( ) ; } let messages = [ ] ; let done = false ; while ( ! done ) { let extracted = [ ] ; switch ( this . _state ) { case STATE . IDLE : break ; case STATE . INTERLEAVED : extracted = this . _parseInterleaved ( ) ; break ; case STATE . RTSP : extracted = this . _parseRtsp ( ) ; break ; default : throw new Error ( 'internal error: unknown state' ) ; } if ( extracted . length > 0 ) { messages = messages . concat ( extracted ) ; } else { done = true ; } this . _setState ( ) ; } return messages ; } }