UNPKG

@jxstjh/jhvideo

Version:

HTML5 jhvideo base on MPEG2-TS Stream Player

511 lines 20.7 kB
/* * Copyright (C) 2016 Bilibili. All Rights Reserved. * * This file is derived from dailymotion's hls.js library (hls.js/src/remux/mp4-generator.js) * @author zheng qian <xqq@xqq.im> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // MP4 boxes generator for ISO BMFF (ISO Base Media File Format, defined in ISO/IEC 14496-12) var MP4 = /** @class */ (function () { function MP4() { } MP4.init = function () { MP4.types = { avc1: [], avcC: [], btrt: [], dinf: [], dref: [], esds: [], ftyp: [], hdlr: [], mdat: [], mdhd: [], mdia: [], mfhd: [], minf: [], moof: [], moov: [], mp4a: [], mvex: [], mvhd: [], sdtp: [], stbl: [], stco: [], stsc: [], stsd: [], stsz: [], stts: [], tfdt: [], tfhd: [], traf: [], trak: [], trun: [], trex: [], tkhd: [], vmhd: [], smhd: [], '.mp3': [] }; for (var name_1 in MP4.types) { if (MP4.types.hasOwnProperty(name_1)) { MP4.types[name_1] = [ name_1.charCodeAt(0), name_1.charCodeAt(1), name_1.charCodeAt(2), name_1.charCodeAt(3) ]; } } var constants = MP4.constants = {}; constants.FTYP = new Uint8Array([ 0x69, 0x73, 0x6F, 0x6D, // major_brand: isom 0x0, 0x0, 0x0, 0x1, // minor_version: 0x01 0x69, 0x73, 0x6F, 0x6D, // isom 0x61, 0x76, 0x63, 0x31 // avc1 ]); constants.STSD_PREFIX = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x01 // entry_count ]); constants.STTS = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00 // entry_count ]); constants.STSC = constants.STCO = constants.STTS; constants.STSZ = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // sample_size 0x00, 0x00, 0x00, 0x00 // sample_count ]); constants.HDLR_VIDEO = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // pre_defined 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide' 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x00 // name: VideoHandler ]); constants.HDLR_AUDIO = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // pre_defined 0x73, 0x6F, 0x75, 0x6E, // handler_type: 'soun' 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x6F, 0x75, 0x6E, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x00 // name: SoundHandler ]); constants.DREF = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x01, // entry_count 0x00, 0x00, 0x00, 0x0C, // entry_size 0x75, 0x72, 0x6C, 0x20, // type 'url ' 0x00, 0x00, 0x00, 0x01 // version(0) + flags ]); // Sound media header constants.SMHD = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00 // balance(2) + reserved(2) ]); // video media header constants.VMHD = new Uint8Array([ 0x00, 0x00, 0x00, 0x01, // version(0) + flags 0x00, 0x00, // graphicsmode: 2 bytes 0x00, 0x00, 0x00, 0x00, // opcolor: 3 * 2 bytes 0x00, 0x00 ]); }; // Generate a box MP4.box = function (type) { var size = 8; var result = null; var datas = Array.prototype.slice.call(arguments, 1); var arrayCount = datas.length; for (var i = 0; i < arrayCount; i++) { size += datas[i].byteLength; } result = new Uint8Array(size); result[0] = (size >>> 24) & 0xFF; // size result[1] = (size >>> 16) & 0xFF; result[2] = (size >>> 8) & 0xFF; result[3] = (size) & 0xFF; result.set(type, 4); // type var offset = 8; for (var i = 0; i < arrayCount; i++) { // data body result.set(datas[i], offset); offset += datas[i].byteLength; } return result; }; // emit ftyp & moov MP4.generateInitSegment = function (meta) { var ftyp = MP4.box(MP4.types.ftyp, MP4.constants.FTYP); var moov = MP4.moov(meta); var result = new Uint8Array(ftyp.byteLength + moov.byteLength); result.set(ftyp, 0); result.set(moov, ftyp.byteLength); return result; }; // Movie metadata box MP4.moov = function (meta) { var mvhd = MP4.mvhd(meta.timescale, meta.duration); var trak = MP4.trak(meta); var mvex = MP4.mvex(meta); return MP4.box(MP4.types.moov, mvhd, trak, mvex); }; // Movie header box MP4.mvhd = function (timescale, duration) { return MP4.box(MP4.types.mvhd, new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // creation_time 0x00, 0x00, 0x00, 0x00, // modification_time (timescale >>> 24) & 0xFF, // timescale: 4 bytes (timescale >>> 16) & 0xFF, (timescale >>> 8) & 0xFF, (timescale) & 0xFF, (duration >>> 24) & 0xFF, // duration: 4 bytes (duration >>> 16) & 0xFF, (duration >>> 8) & 0xFF, (duration) & 0xFF, 0x00, 0x01, 0x00, 0x00, // Preferred rate: 1.0 0x01, 0x00, 0x00, 0x00, // PreferredVolume(1.0, 2bytes) + reserved(2bytes) 0x00, 0x00, 0x00, 0x00, // reserved: 4 + 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix---- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // ----end composition matrix---- 0x00, 0x00, 0x00, 0x00, // ----begin pre_defined 6 * 4 bytes---- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ----end pre_defined 6 * 4 bytes---- 0xFF, 0xFF, 0xFF, 0xFF // next_track_ID ])); }; // Track box MP4.trak = function (meta) { return MP4.box(MP4.types.trak, MP4.tkhd(meta), MP4.mdia(meta)); }; // Track header box MP4.tkhd = function (meta) { var trackId = meta.id, duration = meta.duration; var width = meta.presentWidth, height = meta.presentHeight; return MP4.box(MP4.types.tkhd, new Uint8Array([ 0x00, 0x00, 0x00, 0x07, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // creation_time 0x00, 0x00, 0x00, 0x00, // modification_time (trackId >>> 24) & 0xFF, // track_ID: 4 bytes (trackId >>> 16) & 0xFF, (trackId >>> 8) & 0xFF, (trackId) & 0xFF, 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes (duration >>> 24) & 0xFF, // duration: 4 bytes (duration >>> 16) & 0xFF, (duration >>> 8) & 0xFF, (duration) & 0xFF, 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // layer(2bytes) + alternate_group(2bytes) 0x00, 0x00, 0x00, 0x00, // volume(2bytes) + reserved(2bytes) 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix---- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // ----end composition matrix---- (width >>> 8) & 0xFF, // width and height (width) & 0xFF, 0x00, 0x00, (height >>> 8) & 0xFF, (height) & 0xFF, 0x00, 0x00 ])); }; // Media Box MP4.mdia = function (meta) { return MP4.box(MP4.types.mdia, MP4.mdhd(meta), MP4.hdlr(meta), MP4.minf(meta)); }; // Media header box MP4.mdhd = function (meta) { var timescale = meta.timescale; var duration = meta.duration; return MP4.box(MP4.types.mdhd, new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags 0x00, 0x00, 0x00, 0x00, // creation_time 0x00, 0x00, 0x00, 0x00, // modification_time (timescale >>> 24) & 0xFF, // timescale: 4 bytes (timescale >>> 16) & 0xFF, (timescale >>> 8) & 0xFF, (timescale) & 0xFF, (duration >>> 24) & 0xFF, // duration: 4 bytes (duration >>> 16) & 0xFF, (duration >>> 8) & 0xFF, (duration) & 0xFF, 0x55, 0xC4, // language: und (undetermined) 0x00, 0x00 // pre_defined = 0 ])); }; // Media handler reference box MP4.hdlr = function (meta) { var data = null; if (meta.type === 'audio') { data = MP4.constants.HDLR_AUDIO; } else { data = MP4.constants.HDLR_VIDEO; } return MP4.box(MP4.types.hdlr, data); }; // Media infomation box MP4.minf = function (meta) { var xmhd = null; if (meta.type === 'audio') { xmhd = MP4.box(MP4.types.smhd, MP4.constants.SMHD); } else { xmhd = MP4.box(MP4.types.vmhd, MP4.constants.VMHD); } return MP4.box(MP4.types.minf, xmhd, MP4.dinf(), MP4.stbl(meta)); }; // Data infomation box MP4.dinf = function () { var result = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, MP4.constants.DREF)); return result; }; // Sample table box MP4.stbl = function (meta) { var result = MP4.box(MP4.types.stbl, // type: stbl MP4.stsd(meta), // Sample Description Table MP4.box(MP4.types.stts, MP4.constants.STTS), // Time-To-Sample MP4.box(MP4.types.stsc, MP4.constants.STSC), // Sample-To-Chunk MP4.box(MP4.types.stsz, MP4.constants.STSZ), // Sample size MP4.box(MP4.types.stco, MP4.constants.STCO) // Chunk offset ); return result; }; // Sample description box MP4.stsd = function (meta) { if (meta.type === 'audio') { if (meta.codec === 'mp3') { return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp3(meta)); } // else: aac -> mp4a return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp4a(meta)); } else { return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.avc1(meta)); } }; MP4.mp3 = function (meta) { var channelCount = meta.channelCount; var sampleRate = meta.audioSampleRate; var data = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // reserved(4) 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, channelCount, // channelCount(2) 0x00, 0x10, // sampleSize(2) 0x00, 0x00, 0x00, 0x00, // reserved(4) (sampleRate >>> 8) & 0xFF, // Audio sample rate (sampleRate) & 0xFF, 0x00, 0x00 ]); return MP4.box(MP4.types['.mp3'], data); }; MP4.mp4a = function (meta) { var channelCount = meta.channelCount; var sampleRate = meta.audioSampleRate; var data = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // reserved(4) 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, channelCount, // channelCount(2) 0x00, 0x10, // sampleSize(2) 0x00, 0x00, 0x00, 0x00, // reserved(4) (sampleRate >>> 8) & 0xFF, // Audio sample rate (sampleRate) & 0xFF, 0x00, 0x00 ]); return MP4.box(MP4.types.mp4a, data, MP4.esds(meta)); }; MP4.esds = function (meta) { var config = meta.config || []; var configSize = config.length; var data = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version 0 + flags 0x03, // descriptor_type 0x17 + configSize, // length3 0x00, 0x01, // es_id 0x00, // stream_priority 0x04, // descriptor_type 0x0F + configSize, // length 0x40, // codec: mpeg4_audio 0x15, // stream_type: Audio 0x00, 0x00, 0x00, // buffer_size 0x00, 0x00, 0x00, 0x00, // maxBitrate 0x00, 0x00, 0x00, 0x00, // avgBitrate 0x05 // descriptor_type ].concat([ configSize ]).concat(config).concat([ 0x06, 0x01, 0x02 // GASpecificConfig ])); return MP4.box(MP4.types.esds, data); }; MP4.avc1 = function (meta) { var avcc = meta.avcc; var width = meta.codecWidth, height = meta.codecHeight; var data = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // reserved(4) 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2) 0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (width >>> 8) & 0xFF, // width: 2 bytes (width) & 0xFF, (height >>> 8) & 0xFF, // height: 2 bytes (height) & 0xFF, 0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes 0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes 0x00, 0x01, // frame_count 0x0A, // strlen 0x78, 0x71, 0x71, 0x2F, // compressorname: 32 bytes 0x66, 0x6C, 0x76, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, // depth 0xFF, 0xFF // pre_defined = -1 ]); return MP4.box(MP4.types.avc1, data, MP4.box(MP4.types.avcC, avcc)); }; // Movie Extends box MP4.mvex = function (meta) { return MP4.box(MP4.types.mvex, MP4.trex(meta)); }; // Track Extends box MP4.trex = function (meta) { var trackId = meta.id; var data = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) + flags (trackId >>> 24) & 0xFF, // track_ID (trackId >>> 16) & 0xFF, (trackId >>> 8) & 0xFF, (trackId) & 0xFF, 0x00, 0x00, 0x00, 0x01, // default_sample_description_index 0x00, 0x00, 0x00, 0x00, // default_sample_duration 0x00, 0x00, 0x00, 0x00, // default_sample_size 0x00, 0x01, 0x00, 0x01 // default_sample_flags ]); return MP4.box(MP4.types.trex, data); }; // Movie fragment box MP4.moof = function (track, baseMediaDecodeTime) { return MP4.box(MP4.types.moof, MP4.mfhd(track.sequenceNumber), MP4.traf(track, baseMediaDecodeTime)); }; MP4.mfhd = function (sequenceNumber) { var data = new Uint8Array([ 0x00, 0x00, 0x00, 0x00, (sequenceNumber >>> 24) & 0xFF, // sequence_number: int32 (sequenceNumber >>> 16) & 0xFF, (sequenceNumber >>> 8) & 0xFF, (sequenceNumber) & 0xFF ]); return MP4.box(MP4.types.mfhd, data); }; // Track fragment box MP4.traf = function (track, baseMediaDecodeTime) { var trackId = track.id; // Track fragment header box var tfhd = MP4.box(MP4.types.tfhd, new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) & flags (trackId >>> 24) & 0xFF, // track_ID (trackId >>> 16) & 0xFF, (trackId >>> 8) & 0xFF, (trackId) & 0xFF ])); // Track Fragment Decode Time var tfdt = MP4.box(MP4.types.tfdt, new Uint8Array([ 0x00, 0x00, 0x00, 0x00, // version(0) & flags (baseMediaDecodeTime >>> 24) & 0xFF, // baseMediaDecodeTime: int32 (baseMediaDecodeTime >>> 16) & 0xFF, (baseMediaDecodeTime >>> 8) & 0xFF, (baseMediaDecodeTime) & 0xFF ])); var sdtp = MP4.sdtp(track); var trun = MP4.trun(track, sdtp.byteLength + 16 + 16 + 8 + 16 + 8 + 8); return MP4.box(MP4.types.traf, tfhd, tfdt, trun, sdtp); }; // Sample Dependency Type box MP4.sdtp = function (track) { var samples = track.samples || []; var sampleCount = samples.length; var data = new Uint8Array(4 + sampleCount); // 0~4 bytes: version(0) & flags for (var i = 0; i < sampleCount; i++) { var flags = samples[i].flags; data[i + 4] = (flags.isLeading << 6) // is_leading: 2 (bit) | (flags.dependsOn << 4) // sample_depends_on | (flags.isDependedOn << 2) // sample_is_depended_on | (flags.hasRedundancy); // sample_has_redundancy } return MP4.box(MP4.types.sdtp, data); }; // Track fragment run box MP4.trun = function (track, offset) { var samples = track.samples || []; var sampleCount = samples.length; var dataSize = 12 + 16 * sampleCount; var data = new Uint8Array(dataSize); offset += 8 + dataSize; data.set([ 0x00, 0x00, 0x0F, 0x01, // version(0) & flags (sampleCount >>> 24) & 0xFF, // sample_count (sampleCount >>> 16) & 0xFF, (sampleCount >>> 8) & 0xFF, (sampleCount) & 0xFF, (offset >>> 24) & 0xFF, // data_offset (offset >>> 16) & 0xFF, (offset >>> 8) & 0xFF, (offset) & 0xFF ], 0); for (var i = 0; i < sampleCount; i++) { var duration = samples[i].duration; var size = samples[i].size; var flags = samples[i].flags; var cts = samples[i].cts; data.set([ (duration >>> 24) & 0xFF, // sample_duration (duration >>> 16) & 0xFF, (duration >>> 8) & 0xFF, (duration) & 0xFF, (size >>> 24) & 0xFF, // sample_size (size >>> 16) & 0xFF, (size >>> 8) & 0xFF, (size) & 0xFF, (flags.isLeading << 2) | flags.dependsOn, // sample_flags (flags.isDependedOn << 6) | (flags.hasRedundancy << 4) | flags.isNonSync, 0x00, 0x00, // sample_degradation_priority (cts >>> 24) & 0xFF, // sample_composition_time_offset (cts >>> 16) & 0xFF, (cts >>> 8) & 0xFF, (cts) & 0xFF ], 12 + 16 * i); } return MP4.box(MP4.types.trun, data); }; MP4.mdat = function (data) { return MP4.box(MP4.types.mdat, data); }; return MP4; }()); MP4.init(); export default MP4; //# sourceMappingURL=mp4-generator.js.map