First release
This commit is contained in:
commit
fa6c85266e
2339 changed files with 761050 additions and 0 deletions
753
node_modules/mux.js/es/tools/mp4-inspector.js
generated
vendored
Normal file
753
node_modules/mux.js/es/tools/mp4-inspector.js
generated
vendored
Normal file
|
@ -0,0 +1,753 @@
|
|||
/**
|
||||
* mux.js
|
||||
*
|
||||
* Copyright (c) Brightcove
|
||||
* Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
|
||||
*
|
||||
* Parse the internal MP4 structure into an equivalent javascript
|
||||
* object.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var MAX_UINT32 = Math.pow(2, 32);
|
||||
|
||||
var inspectMp4,
|
||||
_textifyMp,
|
||||
parseMp4Date = function parseMp4Date(seconds) {
|
||||
return new Date(seconds * 1000 - 2082844800000);
|
||||
},
|
||||
parseType = require('../mp4/parse-type'),
|
||||
findBox = require('../mp4/find-box'),
|
||||
nalParse = function nalParse(avcStream) {
|
||||
var avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength),
|
||||
result = [],
|
||||
i,
|
||||
length;
|
||||
|
||||
for (i = 0; i + 4 < avcStream.length; i += length) {
|
||||
length = avcView.getUint32(i);
|
||||
i += 4; // bail if this doesn't appear to be an H264 stream
|
||||
|
||||
if (length <= 0) {
|
||||
result.push('<span style=\'color:red;\'>MALFORMED DATA</span>');
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (avcStream[i] & 0x1F) {
|
||||
case 0x01:
|
||||
result.push('slice_layer_without_partitioning_rbsp');
|
||||
break;
|
||||
|
||||
case 0x05:
|
||||
result.push('slice_layer_without_partitioning_rbsp_idr');
|
||||
break;
|
||||
|
||||
case 0x06:
|
||||
result.push('sei_rbsp');
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
result.push('seq_parameter_set_rbsp');
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
result.push('pic_parameter_set_rbsp');
|
||||
break;
|
||||
|
||||
case 0x09:
|
||||
result.push('access_unit_delimiter_rbsp');
|
||||
break;
|
||||
|
||||
default:
|
||||
result.push('UNKNOWN NAL - ' + avcStream[i] & 0x1F);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
// registry of handlers for individual mp4 box types
|
||||
parse = {
|
||||
// codingname, not a first-class box type. stsd entries share the
|
||||
// same format as real boxes so the parsing infrastructure can be
|
||||
// shared
|
||||
avc1: function avc1(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
return {
|
||||
dataReferenceIndex: view.getUint16(6),
|
||||
width: view.getUint16(24),
|
||||
height: view.getUint16(26),
|
||||
horizresolution: view.getUint16(28) + view.getUint16(30) / 16,
|
||||
vertresolution: view.getUint16(32) + view.getUint16(34) / 16,
|
||||
frameCount: view.getUint16(40),
|
||||
depth: view.getUint16(74),
|
||||
config: inspectMp4(data.subarray(78, data.byteLength))
|
||||
};
|
||||
},
|
||||
avcC: function avcC(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
configurationVersion: data[0],
|
||||
avcProfileIndication: data[1],
|
||||
profileCompatibility: data[2],
|
||||
avcLevelIndication: data[3],
|
||||
lengthSizeMinusOne: data[4] & 0x03,
|
||||
sps: [],
|
||||
pps: []
|
||||
},
|
||||
numOfSequenceParameterSets = data[5] & 0x1f,
|
||||
numOfPictureParameterSets,
|
||||
nalSize,
|
||||
offset,
|
||||
i; // iterate past any SPSs
|
||||
|
||||
offset = 6;
|
||||
|
||||
for (i = 0; i < numOfSequenceParameterSets; i++) {
|
||||
nalSize = view.getUint16(offset);
|
||||
offset += 2;
|
||||
result.sps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
|
||||
offset += nalSize;
|
||||
} // iterate past any PPSs
|
||||
|
||||
|
||||
numOfPictureParameterSets = data[offset];
|
||||
offset++;
|
||||
|
||||
for (i = 0; i < numOfPictureParameterSets; i++) {
|
||||
nalSize = view.getUint16(offset);
|
||||
offset += 2;
|
||||
result.pps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
|
||||
offset += nalSize;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
btrt: function btrt(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
return {
|
||||
bufferSizeDB: view.getUint32(0),
|
||||
maxBitrate: view.getUint32(4),
|
||||
avgBitrate: view.getUint32(8)
|
||||
};
|
||||
},
|
||||
edts: function edts(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
elst: function elst(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
version: view.getUint8(0),
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
edits: []
|
||||
},
|
||||
entryCount = view.getUint32(4),
|
||||
i;
|
||||
|
||||
for (i = 8; entryCount; entryCount--) {
|
||||
if (result.version === 0) {
|
||||
result.edits.push({
|
||||
segmentDuration: view.getUint32(i),
|
||||
mediaTime: view.getInt32(i + 4),
|
||||
mediaRate: view.getUint16(i + 8) + view.getUint16(i + 10) / (256 * 256)
|
||||
});
|
||||
i += 12;
|
||||
} else {
|
||||
result.edits.push({
|
||||
segmentDuration: view.getUint32(i) * MAX_UINT32 + view.getUint32(i + 4),
|
||||
mediaTime: view.getUint32(i + 8) * MAX_UINT32 + view.getUint32(i + 12),
|
||||
mediaRate: view.getUint16(i + 16) + view.getUint16(i + 18) / (256 * 256)
|
||||
});
|
||||
i += 20;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
esds: function esds(data) {
|
||||
return {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
esId: data[6] << 8 | data[7],
|
||||
streamPriority: data[8] & 0x1f,
|
||||
decoderConfig: {
|
||||
objectProfileIndication: data[11],
|
||||
streamType: data[12] >>> 2 & 0x3f,
|
||||
bufferSize: data[13] << 16 | data[14] << 8 | data[15],
|
||||
maxBitrate: data[16] << 24 | data[17] << 16 | data[18] << 8 | data[19],
|
||||
avgBitrate: data[20] << 24 | data[21] << 16 | data[22] << 8 | data[23],
|
||||
decoderConfigDescriptor: {
|
||||
tag: data[24],
|
||||
length: data[25],
|
||||
audioObjectType: data[26] >>> 3 & 0x1f,
|
||||
samplingFrequencyIndex: (data[26] & 0x07) << 1 | data[27] >>> 7 & 0x01,
|
||||
channelConfiguration: data[27] >>> 3 & 0x0f
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
ftyp: function ftyp(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
majorBrand: parseType(data.subarray(0, 4)),
|
||||
minorVersion: view.getUint32(4),
|
||||
compatibleBrands: []
|
||||
},
|
||||
i = 8;
|
||||
|
||||
while (i < data.byteLength) {
|
||||
result.compatibleBrands.push(parseType(data.subarray(i, i + 4)));
|
||||
i += 4;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
dinf: function dinf(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
dref: function dref(data) {
|
||||
return {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
dataReferences: inspectMp4(data.subarray(8))
|
||||
};
|
||||
},
|
||||
hdlr: function hdlr(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
version: view.getUint8(0),
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
handlerType: parseType(data.subarray(8, 12)),
|
||||
name: ''
|
||||
},
|
||||
i = 8; // parse out the name field
|
||||
|
||||
for (i = 24; i < data.byteLength; i++) {
|
||||
if (data[i] === 0x00) {
|
||||
// the name field is null-terminated
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
result.name += String.fromCharCode(data[i]);
|
||||
} // decode UTF-8 to javascript's internal representation
|
||||
// see http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
|
||||
|
||||
|
||||
result.name = decodeURIComponent(escape(result.name));
|
||||
return result;
|
||||
},
|
||||
mdat: function mdat(data) {
|
||||
return {
|
||||
byteLength: data.byteLength,
|
||||
nals: nalParse(data)
|
||||
};
|
||||
},
|
||||
mdhd: function mdhd(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
i = 4,
|
||||
language,
|
||||
result = {
|
||||
version: view.getUint8(0),
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
language: ''
|
||||
};
|
||||
|
||||
if (result.version === 1) {
|
||||
i += 4;
|
||||
result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
|
||||
|
||||
i += 8;
|
||||
result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
|
||||
|
||||
i += 4;
|
||||
result.timescale = view.getUint32(i);
|
||||
i += 8;
|
||||
result.duration = view.getUint32(i); // truncating top 4 bytes
|
||||
} else {
|
||||
result.creationTime = parseMp4Date(view.getUint32(i));
|
||||
i += 4;
|
||||
result.modificationTime = parseMp4Date(view.getUint32(i));
|
||||
i += 4;
|
||||
result.timescale = view.getUint32(i);
|
||||
i += 4;
|
||||
result.duration = view.getUint32(i);
|
||||
}
|
||||
|
||||
i += 4; // language is stored as an ISO-639-2/T code in an array of three 5-bit fields
|
||||
// each field is the packed difference between its ASCII value and 0x60
|
||||
|
||||
language = view.getUint16(i);
|
||||
result.language += String.fromCharCode((language >> 10) + 0x60);
|
||||
result.language += String.fromCharCode(((language & 0x03e0) >> 5) + 0x60);
|
||||
result.language += String.fromCharCode((language & 0x1f) + 0x60);
|
||||
return result;
|
||||
},
|
||||
mdia: function mdia(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
mfhd: function mfhd(data) {
|
||||
return {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
sequenceNumber: data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]
|
||||
};
|
||||
},
|
||||
minf: function minf(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
// codingname, not a first-class box type. stsd entries share the
|
||||
// same format as real boxes so the parsing infrastructure can be
|
||||
// shared
|
||||
mp4a: function mp4a(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
// 6 bytes reserved
|
||||
dataReferenceIndex: view.getUint16(6),
|
||||
// 4 + 4 bytes reserved
|
||||
channelcount: view.getUint16(16),
|
||||
samplesize: view.getUint16(18),
|
||||
// 2 bytes pre_defined
|
||||
// 2 bytes reserved
|
||||
samplerate: view.getUint16(24) + view.getUint16(26) / 65536
|
||||
}; // if there are more bytes to process, assume this is an ISO/IEC
|
||||
// 14496-14 MP4AudioSampleEntry and parse the ESDBox
|
||||
|
||||
if (data.byteLength > 28) {
|
||||
result.streamDescriptor = inspectMp4(data.subarray(28))[0];
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
moof: function moof(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
moov: function moov(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
mvex: function mvex(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
mvhd: function mvhd(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
i = 4,
|
||||
result = {
|
||||
version: view.getUint8(0),
|
||||
flags: new Uint8Array(data.subarray(1, 4))
|
||||
};
|
||||
|
||||
if (result.version === 1) {
|
||||
i += 4;
|
||||
result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
|
||||
|
||||
i += 8;
|
||||
result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
|
||||
|
||||
i += 4;
|
||||
result.timescale = view.getUint32(i);
|
||||
i += 8;
|
||||
result.duration = view.getUint32(i); // truncating top 4 bytes
|
||||
} else {
|
||||
result.creationTime = parseMp4Date(view.getUint32(i));
|
||||
i += 4;
|
||||
result.modificationTime = parseMp4Date(view.getUint32(i));
|
||||
i += 4;
|
||||
result.timescale = view.getUint32(i);
|
||||
i += 4;
|
||||
result.duration = view.getUint32(i);
|
||||
}
|
||||
|
||||
i += 4; // convert fixed-point, base 16 back to a number
|
||||
|
||||
result.rate = view.getUint16(i) + view.getUint16(i + 2) / 16;
|
||||
i += 4;
|
||||
result.volume = view.getUint8(i) + view.getUint8(i + 1) / 8;
|
||||
i += 2;
|
||||
i += 2;
|
||||
i += 2 * 4;
|
||||
result.matrix = new Uint32Array(data.subarray(i, i + 9 * 4));
|
||||
i += 9 * 4;
|
||||
i += 6 * 4;
|
||||
result.nextTrackId = view.getUint32(i);
|
||||
return result;
|
||||
},
|
||||
pdin: function pdin(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
return {
|
||||
version: view.getUint8(0),
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
rate: view.getUint32(4),
|
||||
initialDelay: view.getUint32(8)
|
||||
};
|
||||
},
|
||||
sdtp: function sdtp(data) {
|
||||
var result = {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
samples: []
|
||||
},
|
||||
i;
|
||||
|
||||
for (i = 4; i < data.byteLength; i++) {
|
||||
result.samples.push({
|
||||
dependsOn: (data[i] & 0x30) >> 4,
|
||||
isDependedOn: (data[i] & 0x0c) >> 2,
|
||||
hasRedundancy: data[i] & 0x03
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
sidx: require('./parse-sidx.js'),
|
||||
smhd: function smhd(data) {
|
||||
return {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
balance: data[4] + data[5] / 256
|
||||
};
|
||||
},
|
||||
stbl: function stbl(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
ctts: function ctts(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
version: view.getUint8(0),
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
compositionOffsets: []
|
||||
},
|
||||
entryCount = view.getUint32(4),
|
||||
i;
|
||||
|
||||
for (i = 8; entryCount; i += 8, entryCount--) {
|
||||
result.compositionOffsets.push({
|
||||
sampleCount: view.getUint32(i),
|
||||
sampleOffset: view[result.version === 0 ? 'getUint32' : 'getInt32'](i + 4)
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
stss: function stss(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
version: view.getUint8(0),
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
syncSamples: []
|
||||
},
|
||||
entryCount = view.getUint32(4),
|
||||
i;
|
||||
|
||||
for (i = 8; entryCount; i += 4, entryCount--) {
|
||||
result.syncSamples.push(view.getUint32(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
stco: function stco(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
chunkOffsets: []
|
||||
},
|
||||
entryCount = view.getUint32(4),
|
||||
i;
|
||||
|
||||
for (i = 8; entryCount; i += 4, entryCount--) {
|
||||
result.chunkOffsets.push(view.getUint32(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
stsc: function stsc(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
entryCount = view.getUint32(4),
|
||||
result = {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
sampleToChunks: []
|
||||
},
|
||||
i;
|
||||
|
||||
for (i = 8; entryCount; i += 12, entryCount--) {
|
||||
result.sampleToChunks.push({
|
||||
firstChunk: view.getUint32(i),
|
||||
samplesPerChunk: view.getUint32(i + 4),
|
||||
sampleDescriptionIndex: view.getUint32(i + 8)
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
stsd: function stsd(data) {
|
||||
return {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
sampleDescriptions: inspectMp4(data.subarray(8))
|
||||
};
|
||||
},
|
||||
stsz: function stsz(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
sampleSize: view.getUint32(4),
|
||||
entries: []
|
||||
},
|
||||
i;
|
||||
|
||||
for (i = 12; i < data.byteLength; i += 4) {
|
||||
result.entries.push(view.getUint32(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
stts: function stts(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
result = {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
timeToSamples: []
|
||||
},
|
||||
entryCount = view.getUint32(4),
|
||||
i;
|
||||
|
||||
for (i = 8; entryCount; i += 8, entryCount--) {
|
||||
result.timeToSamples.push({
|
||||
sampleCount: view.getUint32(i),
|
||||
sampleDelta: view.getUint32(i + 4)
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
styp: function styp(data) {
|
||||
return parse.ftyp(data);
|
||||
},
|
||||
tfdt: require('./parse-tfdt.js'),
|
||||
tfhd: require('./parse-tfhd.js'),
|
||||
tkhd: function tkhd(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
|
||||
i = 4,
|
||||
result = {
|
||||
version: view.getUint8(0),
|
||||
flags: new Uint8Array(data.subarray(1, 4))
|
||||
};
|
||||
|
||||
if (result.version === 1) {
|
||||
i += 4;
|
||||
result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
|
||||
|
||||
i += 8;
|
||||
result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
|
||||
|
||||
i += 4;
|
||||
result.trackId = view.getUint32(i);
|
||||
i += 4;
|
||||
i += 8;
|
||||
result.duration = view.getUint32(i); // truncating top 4 bytes
|
||||
} else {
|
||||
result.creationTime = parseMp4Date(view.getUint32(i));
|
||||
i += 4;
|
||||
result.modificationTime = parseMp4Date(view.getUint32(i));
|
||||
i += 4;
|
||||
result.trackId = view.getUint32(i);
|
||||
i += 4;
|
||||
i += 4;
|
||||
result.duration = view.getUint32(i);
|
||||
}
|
||||
|
||||
i += 4;
|
||||
i += 2 * 4;
|
||||
result.layer = view.getUint16(i);
|
||||
i += 2;
|
||||
result.alternateGroup = view.getUint16(i);
|
||||
i += 2; // convert fixed-point, base 16 back to a number
|
||||
|
||||
result.volume = view.getUint8(i) + view.getUint8(i + 1) / 8;
|
||||
i += 2;
|
||||
i += 2;
|
||||
result.matrix = new Uint32Array(data.subarray(i, i + 9 * 4));
|
||||
i += 9 * 4;
|
||||
result.width = view.getUint16(i) + view.getUint16(i + 2) / 65536;
|
||||
i += 4;
|
||||
result.height = view.getUint16(i) + view.getUint16(i + 2) / 65536;
|
||||
return result;
|
||||
},
|
||||
traf: function traf(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
trak: function trak(data) {
|
||||
return {
|
||||
boxes: inspectMp4(data)
|
||||
};
|
||||
},
|
||||
trex: function trex(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
return {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
trackId: view.getUint32(4),
|
||||
defaultSampleDescriptionIndex: view.getUint32(8),
|
||||
defaultSampleDuration: view.getUint32(12),
|
||||
defaultSampleSize: view.getUint32(16),
|
||||
sampleDependsOn: data[20] & 0x03,
|
||||
sampleIsDependedOn: (data[21] & 0xc0) >> 6,
|
||||
sampleHasRedundancy: (data[21] & 0x30) >> 4,
|
||||
samplePaddingValue: (data[21] & 0x0e) >> 1,
|
||||
sampleIsDifferenceSample: !!(data[21] & 0x01),
|
||||
sampleDegradationPriority: view.getUint16(22)
|
||||
};
|
||||
},
|
||||
trun: require('./parse-trun.js'),
|
||||
'url ': function url(data) {
|
||||
return {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4))
|
||||
};
|
||||
},
|
||||
vmhd: function vmhd(data) {
|
||||
var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
return {
|
||||
version: data[0],
|
||||
flags: new Uint8Array(data.subarray(1, 4)),
|
||||
graphicsmode: view.getUint16(4),
|
||||
opcolor: new Uint16Array([view.getUint16(6), view.getUint16(8), view.getUint16(10)])
|
||||
};
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Return a javascript array of box objects parsed from an ISO base
|
||||
* media file.
|
||||
* @param data {Uint8Array} the binary data of the media to be inspected
|
||||
* @return {array} a javascript array of potentially nested box objects
|
||||
*/
|
||||
|
||||
|
||||
inspectMp4 = function inspectMp4(data) {
|
||||
var i = 0,
|
||||
result = [],
|
||||
view,
|
||||
size,
|
||||
type,
|
||||
end,
|
||||
box; // Convert data from Uint8Array to ArrayBuffer, to follow Dataview API
|
||||
|
||||
var ab = new ArrayBuffer(data.length);
|
||||
var v = new Uint8Array(ab);
|
||||
|
||||
for (var z = 0; z < data.length; ++z) {
|
||||
v[z] = data[z];
|
||||
}
|
||||
|
||||
view = new DataView(ab);
|
||||
|
||||
while (i < data.byteLength) {
|
||||
// parse box data
|
||||
size = view.getUint32(i);
|
||||
type = parseType(data.subarray(i + 4, i + 8));
|
||||
end = size > 1 ? i + size : data.byteLength; // parse type-specific data
|
||||
|
||||
box = (parse[type] || function (data) {
|
||||
return {
|
||||
data: data
|
||||
};
|
||||
})(data.subarray(i + 8, end));
|
||||
|
||||
box.size = size;
|
||||
box.type = type; // store this box and move to the next
|
||||
|
||||
result.push(box);
|
||||
i = end;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
/**
|
||||
* Returns a textual representation of the javascript represtentation
|
||||
* of an MP4 file. You can use it as an alternative to
|
||||
* JSON.stringify() to compare inspected MP4s.
|
||||
* @param inspectedMp4 {array} the parsed array of boxes in an MP4
|
||||
* file
|
||||
* @param depth {number} (optional) the number of ancestor boxes of
|
||||
* the elements of inspectedMp4. Assumed to be zero if unspecified.
|
||||
* @return {string} a text representation of the parsed MP4
|
||||
*/
|
||||
|
||||
|
||||
_textifyMp = function textifyMp4(inspectedMp4, depth) {
|
||||
var indent;
|
||||
depth = depth || 0;
|
||||
indent = new Array(depth * 2 + 1).join(' '); // iterate over all the boxes
|
||||
|
||||
return inspectedMp4.map(function (box, index) {
|
||||
// list the box type first at the current indentation level
|
||||
return indent + box.type + '\n' + // the type is already included and handle child boxes separately
|
||||
Object.keys(box).filter(function (key) {
|
||||
return key !== 'type' && key !== 'boxes'; // output all the box properties
|
||||
}).map(function (key) {
|
||||
var prefix = indent + ' ' + key + ': ',
|
||||
value = box[key]; // print out raw bytes as hexademical
|
||||
|
||||
if (value instanceof Uint8Array || value instanceof Uint32Array) {
|
||||
var bytes = Array.prototype.slice.call(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)).map(function (byte) {
|
||||
return ' ' + ('00' + byte.toString(16)).slice(-2);
|
||||
}).join('').match(/.{1,24}/g);
|
||||
|
||||
if (!bytes) {
|
||||
return prefix + '<>';
|
||||
}
|
||||
|
||||
if (bytes.length === 1) {
|
||||
return prefix + '<' + bytes.join('').slice(1) + '>';
|
||||
}
|
||||
|
||||
return prefix + '<\n' + bytes.map(function (line) {
|
||||
return indent + ' ' + line;
|
||||
}).join('\n') + '\n' + indent + ' >';
|
||||
} // stringify generic objects
|
||||
|
||||
|
||||
return prefix + JSON.stringify(value, null, 2).split('\n').map(function (line, index) {
|
||||
if (index === 0) {
|
||||
return line;
|
||||
}
|
||||
|
||||
return indent + ' ' + line;
|
||||
}).join('\n');
|
||||
}).join('\n') + ( // recursively textify the child boxes
|
||||
box.boxes ? '\n' + _textifyMp(box.boxes, depth + 1) : '');
|
||||
}).join('\n');
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
inspect: inspectMp4,
|
||||
textify: _textifyMp,
|
||||
parseType: parseType,
|
||||
findBox: findBox,
|
||||
parseTraf: parse.traf,
|
||||
parseTfdt: parse.tfdt,
|
||||
parseHdlr: parse.hdlr,
|
||||
parseTfhd: parse.tfhd,
|
||||
parseTrun: parse.trun,
|
||||
parseSidx: parse.sidx
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue