import { Container , Box } from './isom' ; import { Sdp } from '../../../utils/protocols/sdp' ; import { aacSettings } from './aacSettings' ; import { h264Settings } from './h264Settings' ; interface MoofMetadata { trackId : number ; timestamp : number ; byteLength : number ; } const formatDefaults = { 'MPEG4-GENERIC' : aacSettings , H264 : h264Settings , } ; interface TrackData { lastTimestamp : number ; baseMediaDecodeTime : number ; defaultFrameDuration : number ; clockrate : number ; bitrate : number ; framerate : number ; cumulativeByteLength : number ; cumulativeDuration : number ; cumulativeFrames : number ; } const createTrackData = ( ) : TrackData => { return { lastTimestamp : 0 , baseMediaDecodeTime : 0 , defaultFrameDuration : 0 , clockrate : 0 , bitrate : 0 , framerate : 0 , cumulativeByteLength : 0 , cumulativeDuration : 0 , cumulativeFrames : 0 , } ; } ; interface RateInfo { byteLength : number ; duration : number ; } const updateRateInfo = ( trackData , { byteLength , duration } ) => { trackData . cumulativeByteLength += byteLength ; trackData . cumulativeDuration += duration ; trackData . cumulativeFrames ++ ; if ( trackData . cumulativeDuration >= trackData . clockrate ) { const bits = 8 * trackData . cumulativeByteLength ; const frames = trackData . cumulativeFrames ; const seconds = trackData . cumulativeDuration / trackData . clockrate ; trackData . bitrate = bits / seconds ; trackData . framerate = frames / seconds ; trackData . cumulativeByteLength = 0 ; trackData . cumulativeDuration = 0 ; trackData . cumulativeFrames = 0 ; } } ; class export class BoxBuilder { public trackIdMap : { [ key ] : number ; } ; public sequenceNumber : number ; public ntpPresentationTime : number ; public trackData : TrackData [ ] ; public videoTrackId ? : number ; constructor ( ) { this . trackIdMap = { } ; this . sequenceNumber = 0 ; this . ntpPresentationTime = 0 ; this . trackData = [ ] ; } trak ( settings ) { const trak = new Container ( 'trak' ) ; const mdia = new Container ( 'mdia' ) ; const minf = new Container ( 'minf' ) ; const dinf = new Container ( 'dinf' ) ; const dref = new Container ( 'dref' ) ; const stbl = new Container ( 'stbl' ) ; dref . set ( 'entry_count' , 1 ) ; trak . append ( new Box ( 'tkhd' , settings . tkhd ) , mdia . append ( new Box ( 'mdhd' , settings . mdhd ) , new Box ( 'hdlr' , settings . hdlr ) , minf . append ( settings . mediaHeaderBox , dinf . append ( dref . append ( new Box ( 'url ' ) ) ) , stbl . append ( new Container ( 'stsd' , undefined , settings . sampleEntryBox ) , new Box ( 'stts' ) , new Box ( 'stsc' ) , new Box ( 'stco' ) , new Box ( 'stsz' ) , new Box ( 'stss' ) ) ) ) ) ; return trak ; } method param sdp param date return moov ( sdp , date ) { const moov = new Container ( 'moov' ) ; moov . append ( new Box ( 'mvhd' , { creation_time : date , modification_time : date , duration : 0 , } ) ) ; const mvex = new Container ( 'mvex' ) ; this . trackIdMap = { } ; this . sequenceNumber = 0 ; this . ntpPresentationTime = 0 ; let trackId = 0 ; this . trackData = [ ] ; sdp . media . forEach ( ( media ) => { if ( media . rtpmap === undefined ) { return ; } const payloadType = media . rtpmap . payloadType ; const encoding = media . rtpmap . encodingName ; if ( formatDefaults [ encoding ] !== undefined ) { this . trackIdMap [ payloadType ] = ++ trackId ; if ( media . type . toLowerCase ( ) === 'video' ) { this . videoTrackId = trackId ; } const settings = formatDefaults [ encoding ] ( media , date , trackId ) ; media . mime = settings . mime ; media . codec = settings . codec ; const trackData = createTrackData ( ) ; trackData . clockrate = media . rtpmap . clockrate ; trackData . defaultFrameDuration = settings . defaultFrameDuration ; this . trackData . push ( trackData ) ; const trak = this . trak ( settings ) ; moov . append ( trak ) ; mvex . append ( new Box ( 'trex' , { track_ID : trackId } ) ) ; } } ) ; moov . append ( mvex ) ; return moov ; } /**
     * Boxes that carry actual elementary stream fragment metadata + data.
     */ method param metadata return moof ( metadata ) { const { trackId , timestamp , byteLength } = metadata ; const trackOffset = trackId - 1 ; const trackData = this . trackData [ trackOffset ] ; const duration = trackData . lastTimestamp !== 0 ? ( timestamp - trackData . lastTimestamp ) | 0 : trackData . defaultFrameDuration ; trackData . lastTimestamp = timestamp ; const moof = new Container ( 'moof' ) ; const traf = new Container ( 'traf' ) ; const trun = new Box ( 'trun' , { sample_duration : duration , sample_size : byteLength , first_sample_flags : 0x40 , } ) ; moof . append ( new Box ( 'mfhd' , { sequence_number : this . sequenceNumber ++ } ) , traf . append ( new Box ( 'tfhd' , { track_ID : trackId } ) , new Box ( 'tfdt' , { baseMediaDecodeTime : trackData . baseMediaDecodeTime } ) , trun ) ) ; trackData . baseMediaDecodeTime += duration ; trun . set ( 'data_offset' , moof . byteLength + 8 ) ; updateRateInfo ( trackData , { byteLength , duration } ) ; return moof ; } param data return mdat ( data ) { const box = new Box ( 'mdat' ) ; box . add ( 'data' , data ) ; return box ; } setPresentationTime ( trackId , ntpTimestamp ? ) { if ( ! this . ntpPresentationTime && ntpTimestamp && trackId === this . videoTrackId ) { const trackOffset = trackId - 1 ; const trackData = this . trackData [ trackOffset ] ; this . ntpPresentationTime = ntpTimestamp - 1000 * ( trackData . baseMediaDecodeTime / trackData . clockrate ) ; } } }