1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-04 02:09:22 +02:00

Improving the get restream credentials

This commit is contained in:
DanieL 2022-05-20 16:22:54 -03:00
parent 654dda115a
commit 56cb1fd5cb
6058 changed files with 1166166 additions and 1430809 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*/
module.exports = require('./m2ts');
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*/
module.exports = require('./m2ts');

1172
node_modules/mux.js/lib/m2ts/m2ts.js generated vendored

File diff suppressed because it is too large Load diff

View file

@ -1,254 +1,254 @@
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*
* Accepts program elementary stream (PES) data events and parses out
* ID3 metadata from them, if present.
* @see http://id3.org/id3v2.3.0
*/
'use strict';
var
Stream = require('../utils/stream'),
StreamTypes = require('./stream-types'),
// return a percent-encoded representation of the specified byte range
// @see http://en.wikipedia.org/wiki/Percent-encoding
percentEncode = function(bytes, start, end) {
var i, result = '';
for (i = start; i < end; i++) {
result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
}
return result;
},
// return the string representation of the specified byte range,
// interpreted as UTf-8.
parseUtf8 = function(bytes, start, end) {
return decodeURIComponent(percentEncode(bytes, start, end));
},
// return the string representation of the specified byte range,
// interpreted as ISO-8859-1.
parseIso88591 = function(bytes, start, end) {
return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
},
parseSyncSafeInteger = function(data) {
return (data[0] << 21) |
(data[1] << 14) |
(data[2] << 7) |
(data[3]);
},
tagParsers = {
TXXX: function(tag) {
var i;
if (tag.data[0] !== 3) {
// ignore frames with unrecognized character encodings
return;
}
for (i = 1; i < tag.data.length; i++) {
if (tag.data[i] === 0) {
// parse the text fields
tag.description = parseUtf8(tag.data, 1, i);
// do not include the null terminator in the tag value
tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
break;
}
}
tag.data = tag.value;
},
WXXX: function(tag) {
var i;
if (tag.data[0] !== 3) {
// ignore frames with unrecognized character encodings
return;
}
for (i = 1; i < tag.data.length; i++) {
if (tag.data[i] === 0) {
// parse the description and URL fields
tag.description = parseUtf8(tag.data, 1, i);
tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
break;
}
}
},
PRIV: function(tag) {
var i;
for (i = 0; i < tag.data.length; i++) {
if (tag.data[i] === 0) {
// parse the description and URL fields
tag.owner = parseIso88591(tag.data, 0, i);
break;
}
}
tag.privateData = tag.data.subarray(i + 1);
tag.data = tag.privateData;
}
},
MetadataStream;
MetadataStream = function(options) {
var
settings = {
// the bytes of the program-level descriptor field in MP2T
// see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
// program element descriptors"
descriptor: options && options.descriptor
},
// the total size in bytes of the ID3 tag being parsed
tagSize = 0,
// tag data that is not complete enough to be parsed
buffer = [],
// the total number of bytes currently in the buffer
bufferSize = 0,
i;
MetadataStream.prototype.init.call(this);
// calculate the text track in-band metadata track dispatch type
// https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
if (settings.descriptor) {
for (i = 0; i < settings.descriptor.length; i++) {
this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
}
}
this.push = function(chunk) {
var tag, frameStart, frameSize, frame, i, frameHeader;
if (chunk.type !== 'timed-metadata') {
return;
}
// if data_alignment_indicator is set in the PES header,
// we must have the start of a new ID3 tag. Assume anything
// remaining in the buffer was malformed and throw it out
if (chunk.dataAlignmentIndicator) {
bufferSize = 0;
buffer.length = 0;
}
// ignore events that don't look like ID3 data
if (buffer.length === 0 &&
(chunk.data.length < 10 ||
chunk.data[0] !== 'I'.charCodeAt(0) ||
chunk.data[1] !== 'D'.charCodeAt(0) ||
chunk.data[2] !== '3'.charCodeAt(0))) {
this.trigger('log', {
level: 'warn',
message: 'Skipping unrecognized metadata packet'
});
return;
}
// add this chunk to the data we've collected so far
buffer.push(chunk);
bufferSize += chunk.data.byteLength;
// grab the size of the entire frame from the ID3 header
if (buffer.length === 1) {
// the frame size is transmitted as a 28-bit integer in the
// last four bytes of the ID3 header.
// The most significant bit of each byte is dropped and the
// results concatenated to recover the actual value.
tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
// ID3 reports the tag size excluding the header but it's more
// convenient for our comparisons to include it
tagSize += 10;
}
// if the entire frame has not arrived, wait for more data
if (bufferSize < tagSize) {
return;
}
// collect the entire frame so it can be parsed
tag = {
data: new Uint8Array(tagSize),
frames: [],
pts: buffer[0].pts,
dts: buffer[0].dts
};
for (i = 0; i < tagSize;) {
tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
i += buffer[0].data.byteLength;
bufferSize -= buffer[0].data.byteLength;
buffer.shift();
}
// find the start of the first frame and the end of the tag
frameStart = 10;
if (tag.data[5] & 0x40) {
// advance the frame start past the extended header
frameStart += 4; // header size field
frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
// clip any padding off the end
tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
}
// parse one or more ID3 frames
// http://id3.org/id3v2.3.0#ID3v2_frame_overview
do {
// determine the number of bytes in this frame
frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
if (frameSize < 1) {
this.trigger('log', {
level: 'warn',
message: 'Malformed ID3 frame encountered. Skipping metadata parsing.'
});
return;
}
frameHeader = String.fromCharCode(tag.data[frameStart],
tag.data[frameStart + 1],
tag.data[frameStart + 2],
tag.data[frameStart + 3]);
frame = {
id: frameHeader,
data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
};
frame.key = frame.id;
if (tagParsers[frame.id]) {
tagParsers[frame.id](frame);
// handle the special PRIV frame used to indicate the start
// time for raw AAC data
if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
var
d = frame.data,
size = ((d[3] & 0x01) << 30) |
(d[4] << 22) |
(d[5] << 14) |
(d[6] << 6) |
(d[7] >>> 2);
size *= 4;
size += d[7] & 0x03;
frame.timeStamp = size;
// in raw AAC, all subsequent data will be timestamped based
// on the value of this frame
// we couldn't have known the appropriate pts and dts before
// parsing this ID3 tag so set those values now
if (tag.pts === undefined && tag.dts === undefined) {
tag.pts = frame.timeStamp;
tag.dts = frame.timeStamp;
}
this.trigger('timestamp', frame);
}
}
tag.frames.push(frame);
frameStart += 10; // advance past the frame header
frameStart += frameSize; // advance past the frame body
} while (frameStart < tagSize);
this.trigger('data', tag);
};
};
MetadataStream.prototype = new Stream();
module.exports = MetadataStream;
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*
* Accepts program elementary stream (PES) data events and parses out
* ID3 metadata from them, if present.
* @see http://id3.org/id3v2.3.0
*/
'use strict';
var
Stream = require('../utils/stream'),
StreamTypes = require('./stream-types'),
// return a percent-encoded representation of the specified byte range
// @see http://en.wikipedia.org/wiki/Percent-encoding
percentEncode = function(bytes, start, end) {
var i, result = '';
for (i = start; i < end; i++) {
result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
}
return result;
},
// return the string representation of the specified byte range,
// interpreted as UTf-8.
parseUtf8 = function(bytes, start, end) {
return decodeURIComponent(percentEncode(bytes, start, end));
},
// return the string representation of the specified byte range,
// interpreted as ISO-8859-1.
parseIso88591 = function(bytes, start, end) {
return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
},
parseSyncSafeInteger = function(data) {
return (data[0] << 21) |
(data[1] << 14) |
(data[2] << 7) |
(data[3]);
},
tagParsers = {
TXXX: function(tag) {
var i;
if (tag.data[0] !== 3) {
// ignore frames with unrecognized character encodings
return;
}
for (i = 1; i < tag.data.length; i++) {
if (tag.data[i] === 0) {
// parse the text fields
tag.description = parseUtf8(tag.data, 1, i);
// do not include the null terminator in the tag value
tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
break;
}
}
tag.data = tag.value;
},
WXXX: function(tag) {
var i;
if (tag.data[0] !== 3) {
// ignore frames with unrecognized character encodings
return;
}
for (i = 1; i < tag.data.length; i++) {
if (tag.data[i] === 0) {
// parse the description and URL fields
tag.description = parseUtf8(tag.data, 1, i);
tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
break;
}
}
},
PRIV: function(tag) {
var i;
for (i = 0; i < tag.data.length; i++) {
if (tag.data[i] === 0) {
// parse the description and URL fields
tag.owner = parseIso88591(tag.data, 0, i);
break;
}
}
tag.privateData = tag.data.subarray(i + 1);
tag.data = tag.privateData;
}
},
MetadataStream;
MetadataStream = function(options) {
var
settings = {
// the bytes of the program-level descriptor field in MP2T
// see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
// program element descriptors"
descriptor: options && options.descriptor
},
// the total size in bytes of the ID3 tag being parsed
tagSize = 0,
// tag data that is not complete enough to be parsed
buffer = [],
// the total number of bytes currently in the buffer
bufferSize = 0,
i;
MetadataStream.prototype.init.call(this);
// calculate the text track in-band metadata track dispatch type
// https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
if (settings.descriptor) {
for (i = 0; i < settings.descriptor.length; i++) {
this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
}
}
this.push = function(chunk) {
var tag, frameStart, frameSize, frame, i, frameHeader;
if (chunk.type !== 'timed-metadata') {
return;
}
// if data_alignment_indicator is set in the PES header,
// we must have the start of a new ID3 tag. Assume anything
// remaining in the buffer was malformed and throw it out
if (chunk.dataAlignmentIndicator) {
bufferSize = 0;
buffer.length = 0;
}
// ignore events that don't look like ID3 data
if (buffer.length === 0 &&
(chunk.data.length < 10 ||
chunk.data[0] !== 'I'.charCodeAt(0) ||
chunk.data[1] !== 'D'.charCodeAt(0) ||
chunk.data[2] !== '3'.charCodeAt(0))) {
this.trigger('log', {
level: 'warn',
message: 'Skipping unrecognized metadata packet'
});
return;
}
// add this chunk to the data we've collected so far
buffer.push(chunk);
bufferSize += chunk.data.byteLength;
// grab the size of the entire frame from the ID3 header
if (buffer.length === 1) {
// the frame size is transmitted as a 28-bit integer in the
// last four bytes of the ID3 header.
// The most significant bit of each byte is dropped and the
// results concatenated to recover the actual value.
tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
// ID3 reports the tag size excluding the header but it's more
// convenient for our comparisons to include it
tagSize += 10;
}
// if the entire frame has not arrived, wait for more data
if (bufferSize < tagSize) {
return;
}
// collect the entire frame so it can be parsed
tag = {
data: new Uint8Array(tagSize),
frames: [],
pts: buffer[0].pts,
dts: buffer[0].dts
};
for (i = 0; i < tagSize;) {
tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
i += buffer[0].data.byteLength;
bufferSize -= buffer[0].data.byteLength;
buffer.shift();
}
// find the start of the first frame and the end of the tag
frameStart = 10;
if (tag.data[5] & 0x40) {
// advance the frame start past the extended header
frameStart += 4; // header size field
frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
// clip any padding off the end
tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
}
// parse one or more ID3 frames
// http://id3.org/id3v2.3.0#ID3v2_frame_overview
do {
// determine the number of bytes in this frame
frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
if (frameSize < 1) {
this.trigger('log', {
level: 'warn',
message: 'Malformed ID3 frame encountered. Skipping metadata parsing.'
});
return;
}
frameHeader = String.fromCharCode(tag.data[frameStart],
tag.data[frameStart + 1],
tag.data[frameStart + 2],
tag.data[frameStart + 3]);
frame = {
id: frameHeader,
data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
};
frame.key = frame.id;
if (tagParsers[frame.id]) {
tagParsers[frame.id](frame);
// handle the special PRIV frame used to indicate the start
// time for raw AAC data
if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
var
d = frame.data,
size = ((d[3] & 0x01) << 30) |
(d[4] << 22) |
(d[5] << 14) |
(d[6] << 6) |
(d[7] >>> 2);
size *= 4;
size += d[7] & 0x03;
frame.timeStamp = size;
// in raw AAC, all subsequent data will be timestamped based
// on the value of this frame
// we couldn't have known the appropriate pts and dts before
// parsing this ID3 tag so set those values now
if (tag.pts === undefined && tag.dts === undefined) {
tag.pts = frame.timeStamp;
tag.dts = frame.timeStamp;
}
this.trigger('timestamp', frame);
}
}
tag.frames.push(frame);
frameStart += 10; // advance past the frame header
frameStart += frameSize; // advance past the frame body
} while (frameStart < tagSize);
this.trigger('data', tag);
};
};
MetadataStream.prototype = new Stream();
module.exports = MetadataStream;

574
node_modules/mux.js/lib/m2ts/probe.js generated vendored
View file

@ -1,287 +1,287 @@
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*
* Utilities to detect basic properties and metadata about TS Segments.
*/
'use strict';
var StreamTypes = require('./stream-types.js');
var parsePid = function(packet) {
var pid = packet[1] & 0x1f;
pid <<= 8;
pid |= packet[2];
return pid;
};
var parsePayloadUnitStartIndicator = function(packet) {
return !!(packet[1] & 0x40);
};
var parseAdaptionField = function(packet) {
var offset = 0;
// if an adaption field is present, its length is specified by the
// fifth byte of the TS packet header. The adaptation field is
// used to add stuffing to PES packets that don't fill a complete
// TS packet, and to specify some forms of timing and control data
// that we do not currently use.
if (((packet[3] & 0x30) >>> 4) > 0x01) {
offset += packet[4] + 1;
}
return offset;
};
var parseType = function(packet, pmtPid) {
var pid = parsePid(packet);
if (pid === 0) {
return 'pat';
} else if (pid === pmtPid) {
return 'pmt';
} else if (pmtPid) {
return 'pes';
}
return null;
};
var parsePat = function(packet) {
var pusi = parsePayloadUnitStartIndicator(packet);
var offset = 4 + parseAdaptionField(packet);
if (pusi) {
offset += packet[offset] + 1;
}
return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
};
var parsePmt = function(packet) {
var programMapTable = {};
var pusi = parsePayloadUnitStartIndicator(packet);
var payloadOffset = 4 + parseAdaptionField(packet);
if (pusi) {
payloadOffset += packet[payloadOffset] + 1;
}
// PMTs can be sent ahead of the time when they should actually
// take effect. We don't believe this should ever be the case
// for HLS but we'll ignore "forward" PMT declarations if we see
// them. Future PMT declarations have the current_next_indicator
// set to zero.
if (!(packet[payloadOffset + 5] & 0x01)) {
return;
}
var sectionLength, tableEnd, programInfoLength;
// the mapping table ends at the end of the current section
sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
tableEnd = 3 + sectionLength - 4;
// to determine where the table is, we have to figure out how
// long the program info descriptors are
programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11];
// advance the offset to the first entry in the mapping table
var offset = 12 + programInfoLength;
while (offset < tableEnd) {
var i = payloadOffset + offset;
// add an entry that maps the elementary_pid to the stream_type
programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i];
// move to the next table entry
// skip past the elementary stream descriptors, if present
offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
}
return programMapTable;
};
var parsePesType = function(packet, programMapTable) {
var pid = parsePid(packet);
var type = programMapTable[pid];
switch (type) {
case StreamTypes.H264_STREAM_TYPE:
return 'video';
case StreamTypes.ADTS_STREAM_TYPE:
return 'audio';
case StreamTypes.METADATA_STREAM_TYPE:
return 'timed-metadata';
default:
return null;
}
};
var parsePesTime = function(packet) {
var pusi = parsePayloadUnitStartIndicator(packet);
if (!pusi) {
return null;
}
var offset = 4 + parseAdaptionField(packet);
if (offset >= packet.byteLength) {
// From the H 222.0 MPEG-TS spec
// "For transport stream packets carrying PES packets, stuffing is needed when there
// is insufficient PES packet data to completely fill the transport stream packet
// payload bytes. Stuffing is accomplished by defining an adaptation field longer than
// the sum of the lengths of the data elements in it, so that the payload bytes
// remaining after the adaptation field exactly accommodates the available PES packet
// data."
//
// If the offset is >= the length of the packet, then the packet contains no data
// and instead is just adaption field stuffing bytes
return null;
}
var pes = null;
var ptsDtsFlags;
// PES packets may be annotated with a PTS value, or a PTS value
// and a DTS value. Determine what combination of values is
// available to work with.
ptsDtsFlags = packet[offset + 7];
// PTS and DTS are normally stored as a 33-bit number. Javascript
// performs all bitwise operations on 32-bit integers but javascript
// supports a much greater range (52-bits) of integer using standard
// mathematical operations.
// We construct a 31-bit value using bitwise operators over the 31
// most significant bits and then multiply by 4 (equal to a left-shift
// of 2) before we add the final 2 least significant bits of the
// timestamp (equal to an OR.)
if (ptsDtsFlags & 0xC0) {
pes = {};
// the PTS and DTS are not written out directly. For information
// on how they are encoded, see
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
pes.pts = (packet[offset + 9] & 0x0E) << 27 |
(packet[offset + 10] & 0xFF) << 20 |
(packet[offset + 11] & 0xFE) << 12 |
(packet[offset + 12] & 0xFF) << 5 |
(packet[offset + 13] & 0xFE) >>> 3;
pes.pts *= 4; // Left shift by 2
pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
pes.dts = pes.pts;
if (ptsDtsFlags & 0x40) {
pes.dts = (packet[offset + 14] & 0x0E) << 27 |
(packet[offset + 15] & 0xFF) << 20 |
(packet[offset + 16] & 0xFE) << 12 |
(packet[offset + 17] & 0xFF) << 5 |
(packet[offset + 18] & 0xFE) >>> 3;
pes.dts *= 4; // Left shift by 2
pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
}
}
return pes;
};
var parseNalUnitType = function(type) {
switch (type) {
case 0x05:
return 'slice_layer_without_partitioning_rbsp_idr';
case 0x06:
return 'sei_rbsp';
case 0x07:
return 'seq_parameter_set_rbsp';
case 0x08:
return 'pic_parameter_set_rbsp';
case 0x09:
return 'access_unit_delimiter_rbsp';
default:
return null;
}
};
var videoPacketContainsKeyFrame = function(packet) {
var offset = 4 + parseAdaptionField(packet);
var frameBuffer = packet.subarray(offset);
var frameI = 0;
var frameSyncPoint = 0;
var foundKeyFrame = false;
var nalType;
// advance the sync point to a NAL start, if necessary
for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
if (frameBuffer[frameSyncPoint + 2] === 1) {
// the sync point is properly aligned
frameI = frameSyncPoint + 5;
break;
}
}
while (frameI < frameBuffer.byteLength) {
// look at the current byte to determine if we've hit the end of
// a NAL unit boundary
switch (frameBuffer[frameI]) {
case 0:
// skip past non-sync sequences
if (frameBuffer[frameI - 1] !== 0) {
frameI += 2;
break;
} else if (frameBuffer[frameI - 2] !== 0) {
frameI++;
break;
}
if (frameSyncPoint + 3 !== frameI - 2) {
nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
foundKeyFrame = true;
}
}
// drop trailing zeroes
do {
frameI++;
} while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
frameSyncPoint = frameI - 2;
frameI += 3;
break;
case 1:
// skip past non-sync sequences
if (frameBuffer[frameI - 1] !== 0 ||
frameBuffer[frameI - 2] !== 0) {
frameI += 3;
break;
}
nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
foundKeyFrame = true;
}
frameSyncPoint = frameI - 2;
frameI += 3;
break;
default:
// the current byte isn't a one or zero, so it cannot be part
// of a sync sequence
frameI += 3;
break;
}
}
frameBuffer = frameBuffer.subarray(frameSyncPoint);
frameI -= frameSyncPoint;
frameSyncPoint = 0;
// parse the final nal
if (frameBuffer && frameBuffer.byteLength > 3) {
nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
foundKeyFrame = true;
}
}
return foundKeyFrame;
};
module.exports = {
parseType: parseType,
parsePat: parsePat,
parsePmt: parsePmt,
parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
parsePesType: parsePesType,
parsePesTime: parsePesTime,
videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
};
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*
* Utilities to detect basic properties and metadata about TS Segments.
*/
'use strict';
var StreamTypes = require('./stream-types.js');
var parsePid = function(packet) {
var pid = packet[1] & 0x1f;
pid <<= 8;
pid |= packet[2];
return pid;
};
var parsePayloadUnitStartIndicator = function(packet) {
return !!(packet[1] & 0x40);
};
var parseAdaptionField = function(packet) {
var offset = 0;
// if an adaption field is present, its length is specified by the
// fifth byte of the TS packet header. The adaptation field is
// used to add stuffing to PES packets that don't fill a complete
// TS packet, and to specify some forms of timing and control data
// that we do not currently use.
if (((packet[3] & 0x30) >>> 4) > 0x01) {
offset += packet[4] + 1;
}
return offset;
};
var parseType = function(packet, pmtPid) {
var pid = parsePid(packet);
if (pid === 0) {
return 'pat';
} else if (pid === pmtPid) {
return 'pmt';
} else if (pmtPid) {
return 'pes';
}
return null;
};
var parsePat = function(packet) {
var pusi = parsePayloadUnitStartIndicator(packet);
var offset = 4 + parseAdaptionField(packet);
if (pusi) {
offset += packet[offset] + 1;
}
return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
};
var parsePmt = function(packet) {
var programMapTable = {};
var pusi = parsePayloadUnitStartIndicator(packet);
var payloadOffset = 4 + parseAdaptionField(packet);
if (pusi) {
payloadOffset += packet[payloadOffset] + 1;
}
// PMTs can be sent ahead of the time when they should actually
// take effect. We don't believe this should ever be the case
// for HLS but we'll ignore "forward" PMT declarations if we see
// them. Future PMT declarations have the current_next_indicator
// set to zero.
if (!(packet[payloadOffset + 5] & 0x01)) {
return;
}
var sectionLength, tableEnd, programInfoLength;
// the mapping table ends at the end of the current section
sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
tableEnd = 3 + sectionLength - 4;
// to determine where the table is, we have to figure out how
// long the program info descriptors are
programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11];
// advance the offset to the first entry in the mapping table
var offset = 12 + programInfoLength;
while (offset < tableEnd) {
var i = payloadOffset + offset;
// add an entry that maps the elementary_pid to the stream_type
programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i];
// move to the next table entry
// skip past the elementary stream descriptors, if present
offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
}
return programMapTable;
};
var parsePesType = function(packet, programMapTable) {
var pid = parsePid(packet);
var type = programMapTable[pid];
switch (type) {
case StreamTypes.H264_STREAM_TYPE:
return 'video';
case StreamTypes.ADTS_STREAM_TYPE:
return 'audio';
case StreamTypes.METADATA_STREAM_TYPE:
return 'timed-metadata';
default:
return null;
}
};
var parsePesTime = function(packet) {
var pusi = parsePayloadUnitStartIndicator(packet);
if (!pusi) {
return null;
}
var offset = 4 + parseAdaptionField(packet);
if (offset >= packet.byteLength) {
// From the H 222.0 MPEG-TS spec
// "For transport stream packets carrying PES packets, stuffing is needed when there
// is insufficient PES packet data to completely fill the transport stream packet
// payload bytes. Stuffing is accomplished by defining an adaptation field longer than
// the sum of the lengths of the data elements in it, so that the payload bytes
// remaining after the adaptation field exactly accommodates the available PES packet
// data."
//
// If the offset is >= the length of the packet, then the packet contains no data
// and instead is just adaption field stuffing bytes
return null;
}
var pes = null;
var ptsDtsFlags;
// PES packets may be annotated with a PTS value, or a PTS value
// and a DTS value. Determine what combination of values is
// available to work with.
ptsDtsFlags = packet[offset + 7];
// PTS and DTS are normally stored as a 33-bit number. Javascript
// performs all bitwise operations on 32-bit integers but javascript
// supports a much greater range (52-bits) of integer using standard
// mathematical operations.
// We construct a 31-bit value using bitwise operators over the 31
// most significant bits and then multiply by 4 (equal to a left-shift
// of 2) before we add the final 2 least significant bits of the
// timestamp (equal to an OR.)
if (ptsDtsFlags & 0xC0) {
pes = {};
// the PTS and DTS are not written out directly. For information
// on how they are encoded, see
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
pes.pts = (packet[offset + 9] & 0x0E) << 27 |
(packet[offset + 10] & 0xFF) << 20 |
(packet[offset + 11] & 0xFE) << 12 |
(packet[offset + 12] & 0xFF) << 5 |
(packet[offset + 13] & 0xFE) >>> 3;
pes.pts *= 4; // Left shift by 2
pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
pes.dts = pes.pts;
if (ptsDtsFlags & 0x40) {
pes.dts = (packet[offset + 14] & 0x0E) << 27 |
(packet[offset + 15] & 0xFF) << 20 |
(packet[offset + 16] & 0xFE) << 12 |
(packet[offset + 17] & 0xFF) << 5 |
(packet[offset + 18] & 0xFE) >>> 3;
pes.dts *= 4; // Left shift by 2
pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
}
}
return pes;
};
var parseNalUnitType = function(type) {
switch (type) {
case 0x05:
return 'slice_layer_without_partitioning_rbsp_idr';
case 0x06:
return 'sei_rbsp';
case 0x07:
return 'seq_parameter_set_rbsp';
case 0x08:
return 'pic_parameter_set_rbsp';
case 0x09:
return 'access_unit_delimiter_rbsp';
default:
return null;
}
};
var videoPacketContainsKeyFrame = function(packet) {
var offset = 4 + parseAdaptionField(packet);
var frameBuffer = packet.subarray(offset);
var frameI = 0;
var frameSyncPoint = 0;
var foundKeyFrame = false;
var nalType;
// advance the sync point to a NAL start, if necessary
for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
if (frameBuffer[frameSyncPoint + 2] === 1) {
// the sync point is properly aligned
frameI = frameSyncPoint + 5;
break;
}
}
while (frameI < frameBuffer.byteLength) {
// look at the current byte to determine if we've hit the end of
// a NAL unit boundary
switch (frameBuffer[frameI]) {
case 0:
// skip past non-sync sequences
if (frameBuffer[frameI - 1] !== 0) {
frameI += 2;
break;
} else if (frameBuffer[frameI - 2] !== 0) {
frameI++;
break;
}
if (frameSyncPoint + 3 !== frameI - 2) {
nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
foundKeyFrame = true;
}
}
// drop trailing zeroes
do {
frameI++;
} while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
frameSyncPoint = frameI - 2;
frameI += 3;
break;
case 1:
// skip past non-sync sequences
if (frameBuffer[frameI - 1] !== 0 ||
frameBuffer[frameI - 2] !== 0) {
frameI += 3;
break;
}
nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
foundKeyFrame = true;
}
frameSyncPoint = frameI - 2;
frameI += 3;
break;
default:
// the current byte isn't a one or zero, so it cannot be part
// of a sync sequence
frameI += 3;
break;
}
}
frameBuffer = frameBuffer.subarray(frameSyncPoint);
frameI -= frameSyncPoint;
frameSyncPoint = 0;
// parse the final nal
if (frameBuffer && frameBuffer.byteLength > 3) {
nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
foundKeyFrame = true;
}
}
return foundKeyFrame;
};
module.exports = {
parseType: parseType,
parsePat: parsePat,
parsePmt: parsePmt,
parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
parsePesType: parsePesType,
parsePesTime: parsePesTime,
videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
};

View file

@ -1,13 +1,13 @@
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*/
'use strict';
module.exports = {
H264_STREAM_TYPE: 0x1B,
ADTS_STREAM_TYPE: 0x0F,
METADATA_STREAM_TYPE: 0x15
};
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*/
'use strict';
module.exports = {
H264_STREAM_TYPE: 0x1B,
ADTS_STREAM_TYPE: 0x0F,
METADATA_STREAM_TYPE: 0x15
};

View file

@ -1,101 +1,101 @@
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*
* Accepts program elementary stream (PES) data events and corrects
* decode and presentation time stamps to account for a rollover
* of the 33 bit value.
*/
'use strict';
var Stream = require('../utils/stream');
var MAX_TS = 8589934592;
var RO_THRESH = 4294967296;
var TYPE_SHARED = 'shared';
var handleRollover = function(value, reference) {
var direction = 1;
if (value > reference) {
// If the current timestamp value is greater than our reference timestamp and we detect a
// timestamp rollover, this means the roll over is happening in the opposite direction.
// Example scenario: Enter a long stream/video just after a rollover occurred. The reference
// point will be set to a small number, e.g. 1. The user then seeks backwards over the
// rollover point. In loading this segment, the timestamp values will be very large,
// e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
// the time stamp to be `value - 2^33`.
direction = -1;
}
// Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
// cause an incorrect adjustment.
while (Math.abs(reference - value) > RO_THRESH) {
value += (direction * MAX_TS);
}
return value;
};
var TimestampRolloverStream = function(type) {
var lastDTS, referenceDTS;
TimestampRolloverStream.prototype.init.call(this);
// The "shared" type is used in cases where a stream will contain muxed
// video and audio. We could use `undefined` here, but having a string
// makes debugging a little clearer.
this.type_ = type || TYPE_SHARED;
this.push = function(data) {
// Any "shared" rollover streams will accept _all_ data. Otherwise,
// streams will only accept data that matches their type.
if (this.type_ !== TYPE_SHARED && data.type !== this.type_) {
return;
}
if (referenceDTS === undefined) {
referenceDTS = data.dts;
}
data.dts = handleRollover(data.dts, referenceDTS);
data.pts = handleRollover(data.pts, referenceDTS);
lastDTS = data.dts;
this.trigger('data', data);
};
this.flush = function() {
referenceDTS = lastDTS;
this.trigger('done');
};
this.endTimeline = function() {
this.flush();
this.trigger('endedtimeline');
};
this.discontinuity = function() {
referenceDTS = void 0;
lastDTS = void 0;
};
this.reset = function() {
this.discontinuity();
this.trigger('reset');
};
};
TimestampRolloverStream.prototype = new Stream();
module.exports = {
TimestampRolloverStream: TimestampRolloverStream,
handleRollover: handleRollover
};
/**
* mux.js
*
* Copyright (c) Brightcove
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
*
* Accepts program elementary stream (PES) data events and corrects
* decode and presentation time stamps to account for a rollover
* of the 33 bit value.
*/
'use strict';
var Stream = require('../utils/stream');
var MAX_TS = 8589934592;
var RO_THRESH = 4294967296;
var TYPE_SHARED = 'shared';
var handleRollover = function(value, reference) {
var direction = 1;
if (value > reference) {
// If the current timestamp value is greater than our reference timestamp and we detect a
// timestamp rollover, this means the roll over is happening in the opposite direction.
// Example scenario: Enter a long stream/video just after a rollover occurred. The reference
// point will be set to a small number, e.g. 1. The user then seeks backwards over the
// rollover point. In loading this segment, the timestamp values will be very large,
// e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
// the time stamp to be `value - 2^33`.
direction = -1;
}
// Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
// cause an incorrect adjustment.
while (Math.abs(reference - value) > RO_THRESH) {
value += (direction * MAX_TS);
}
return value;
};
var TimestampRolloverStream = function(type) {
var lastDTS, referenceDTS;
TimestampRolloverStream.prototype.init.call(this);
// The "shared" type is used in cases where a stream will contain muxed
// video and audio. We could use `undefined` here, but having a string
// makes debugging a little clearer.
this.type_ = type || TYPE_SHARED;
this.push = function(data) {
// Any "shared" rollover streams will accept _all_ data. Otherwise,
// streams will only accept data that matches their type.
if (this.type_ !== TYPE_SHARED && data.type !== this.type_) {
return;
}
if (referenceDTS === undefined) {
referenceDTS = data.dts;
}
data.dts = handleRollover(data.dts, referenceDTS);
data.pts = handleRollover(data.pts, referenceDTS);
lastDTS = data.dts;
this.trigger('data', data);
};
this.flush = function() {
referenceDTS = lastDTS;
this.trigger('done');
};
this.endTimeline = function() {
this.flush();
this.trigger('endedtimeline');
};
this.discontinuity = function() {
referenceDTS = void 0;
lastDTS = void 0;
};
this.reset = function() {
this.discontinuity();
this.trigger('reset');
};
};
TimestampRolloverStream.prototype = new Stream();
module.exports = {
TimestampRolloverStream: TimestampRolloverStream,
handleRollover: handleRollover
};