ad82ecc0 by David LaPalomento

Merge pull request #405 from dmlap/remove-transmuxing

Remove old transmuxing infrastructure
2 parents cd596f1a f8527bfc
(function(window) {
var
FlvTag = window.videojs.Hls.FlvTag,
adtsSampleingRates = [
96000, 88200,
64000, 48000,
44100, 32000,
24000, 22050,
16000, 12000
];
window.videojs.Hls.AacStream = function() {
var
next_pts, // :uint
state, // :uint
pes_length, // :int
lastMetaPts,
adtsProtectionAbsent, // :Boolean
adtsObjectType, // :int
adtsSampleingIndex, // :int
adtsChanelConfig, // :int
adtsFrameSize, // :int
adtsSampleCount, // :int
adtsDuration, // :int
aacFrame, // :FlvTag = null;
extraData; // :uint;
this.tags = [];
// (pts:uint):void
this.setTimeStampOffset = function(pts) {
// keep track of the last time a metadata tag was written out
// set the initial value so metadata will be generated before any
// payload data
lastMetaPts = pts - 1000;
};
// (pts:uint, pes_size:int, dataAligned:Boolean):void
this.setNextTimeStamp = function(pts, pes_size, dataAligned) {
next_pts = pts;
pes_length = pes_size;
// If data is aligned, flush all internal buffers
if (dataAligned) {
state = 0;
}
};
// (data:ByteArray, o:int = 0, l:int = 0):void
this.writeBytes = function(data, offset, length) {
var
end, // :int
newExtraData, // :uint
bytesToCopy; // :int
// default arguments
offset = offset || 0;
length = length || 0;
// Do not allow more than 'pes_length' bytes to be written
length = (pes_length < length ? pes_length : length);
pes_length -= length;
end = offset + length;
while (offset < end) {
switch (state) {
default:
state = 0;
break;
case 0:
if (offset >= end) {
return;
}
if (0xFF !== data[offset]) {
console.assert(false, 'Error no ATDS header found');
offset += 1;
state = 0;
return;
}
offset += 1;
state = 1;
break;
case 1:
if (offset >= end) {
return;
}
if (0xF0 !== (data[offset] & 0xF0)) {
console.assert(false, 'Error no ATDS header found');
offset +=1;
state = 0;
return;
}
adtsProtectionAbsent = !!(data[offset] & 0x01);
offset += 1;
state = 2;
break;
case 2:
if (offset >= end) {
return;
}
adtsObjectType = ((data[offset] & 0xC0) >>> 6) + 1;
adtsSampleingIndex = ((data[offset] & 0x3C) >>> 2);
adtsChanelConfig = ((data[offset] & 0x01) << 2);
offset += 1;
state = 3;
break;
case 3:
if (offset >= end) {
return;
}
adtsChanelConfig |= ((data[offset] & 0xC0) >>> 6);
adtsFrameSize = ((data[offset] & 0x03) << 11);
offset += 1;
state = 4;
break;
case 4:
if (offset >= end) {
return;
}
adtsFrameSize |= (data[offset] << 3);
offset += 1;
state = 5;
break;
case 5:
if(offset >= end) {
return;
}
adtsFrameSize |= ((data[offset] & 0xE0) >>> 5);
adtsFrameSize -= (adtsProtectionAbsent ? 7 : 9);
offset += 1;
state = 6;
break;
case 6:
if (offset >= end) {
return;
}
adtsSampleCount = ((data[offset] & 0x03) + 1) * 1024;
adtsDuration = (adtsSampleCount * 1000) / adtsSampleingRates[adtsSampleingIndex];
newExtraData = (adtsObjectType << 11) |
(adtsSampleingIndex << 7) |
(adtsChanelConfig << 3);
// write out metadata tags every 1 second so that the decoder
// is re-initialized quickly after seeking into a different
// audio configuration
if (newExtraData !== extraData || next_pts - lastMetaPts >= 1000) {
aacFrame = new FlvTag(FlvTag.METADATA_TAG);
aacFrame.pts = next_pts;
aacFrame.dts = next_pts;
// AAC is always 10
aacFrame.writeMetaDataDouble("audiocodecid", 10);
aacFrame.writeMetaDataBoolean("stereo", 2 === adtsChanelConfig);
aacFrame.writeMetaDataDouble ("audiosamplerate", adtsSampleingRates[adtsSampleingIndex]);
// Is AAC always 16 bit?
aacFrame.writeMetaDataDouble ("audiosamplesize", 16);
this.tags.push(aacFrame);
extraData = newExtraData;
aacFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
// For audio, DTS is always the same as PTS. We want to set the DTS
// however so we can compare with video DTS to determine approximate
// packet order
aacFrame.pts = next_pts;
aacFrame.dts = aacFrame.pts;
aacFrame.view.setUint16(aacFrame.position, newExtraData);
aacFrame.position += 2;
aacFrame.length = Math.max(aacFrame.length, aacFrame.position);
this.tags.push(aacFrame);
lastMetaPts = next_pts;
}
// Skip the checksum if there is one
offset += 1;
state = 7;
break;
case 7:
if (!adtsProtectionAbsent) {
if (2 > (end - offset)) {
return;
} else {
offset += 2;
}
}
aacFrame = new FlvTag(FlvTag.AUDIO_TAG);
aacFrame.pts = next_pts;
aacFrame.dts = next_pts;
state = 8;
break;
case 8:
while (adtsFrameSize) {
if (offset >= end) {
return;
}
bytesToCopy = (end - offset) < adtsFrameSize ? (end - offset) : adtsFrameSize;
aacFrame.writeBytes(data, offset, bytesToCopy);
offset += bytesToCopy;
adtsFrameSize -= bytesToCopy;
}
this.tags.push(aacFrame);
// finished with this frame
state = 0;
next_pts += adtsDuration;
}
}
};
};
})(this);
(function(window) {
/**
* Parser for exponential Golomb codes, a variable-bitwidth number encoding
* scheme used by h264.
*/
window.videojs.Hls.ExpGolomb = function(workingData) {
var
// the number of bytes left to examine in workingData
workingBytesAvailable = workingData.byteLength,
// the current word being examined
workingWord = 0, // :uint
// the number of bits left to examine in the current word
workingBitsAvailable = 0; // :uint;
// ():uint
this.length = function() {
return (8 * workingBytesAvailable);
};
// ():uint
this.bitsAvailable = function() {
return (8 * workingBytesAvailable) + workingBitsAvailable;
};
// ():void
this.loadWord = function() {
var
position = workingData.byteLength - workingBytesAvailable,
workingBytes = new Uint8Array(4),
availableBytes = Math.min(4, workingBytesAvailable);
if (availableBytes === 0) {
throw new Error('no bytes available');
}
workingBytes.set(workingData.subarray(position,
position + availableBytes));
workingWord = new DataView(workingBytes.buffer).getUint32(0);
// track the amount of workingData that has been processed
workingBitsAvailable = availableBytes * 8;
workingBytesAvailable -= availableBytes;
};
// (count:int):void
this.skipBits = function(count) {
var skipBytes; // :int
if (workingBitsAvailable > count) {
workingWord <<= count;
workingBitsAvailable -= count;
} else {
count -= workingBitsAvailable;
skipBytes = count / 8;
count -= (skipBytes * 8);
workingBytesAvailable -= skipBytes;
this.loadWord();
workingWord <<= count;
workingBitsAvailable -= count;
}
};
// (size:int):uint
this.readBits = function(size) {
var
bits = Math.min(workingBitsAvailable, size), // :uint
valu = workingWord >>> (32 - bits); // :uint
console.assert(size < 32, 'Cannot read more than 32 bits at a time');
workingBitsAvailable -= bits;
if (workingBitsAvailable > 0) {
workingWord <<= bits;
} else if (workingBytesAvailable > 0) {
this.loadWord();
}
bits = size - bits;
if (bits > 0) {
return valu << bits | this.readBits(bits);
} else {
return valu;
}
};
// ():uint
this.skipLeadingZeros = function() {
var leadingZeroCount; // :uint
for (leadingZeroCount = 0 ; leadingZeroCount < workingBitsAvailable ; ++leadingZeroCount) {
if (0 !== (workingWord & (0x80000000 >>> leadingZeroCount))) {
// the first bit of working word is 1
workingWord <<= leadingZeroCount;
workingBitsAvailable -= leadingZeroCount;
return leadingZeroCount;
}
}
// we exhausted workingWord and still have not found a 1
this.loadWord();
return leadingZeroCount + this.skipLeadingZeros();
};
// ():void
this.skipUnsignedExpGolomb = function() {
this.skipBits(1 + this.skipLeadingZeros());
};
// ():void
this.skipExpGolomb = function() {
this.skipBits(1 + this.skipLeadingZeros());
};
// ():uint
this.readUnsignedExpGolomb = function() {
var clz = this.skipLeadingZeros(); // :uint
return this.readBits(clz + 1) - 1;
};
// ():int
this.readExpGolomb = function() {
var valu = this.readUnsignedExpGolomb(); // :int
if (0x01 & valu) {
// the number is odd if the low order bit is set
return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
} else {
return -1 * (valu >>> 1); // divide by two then make it negative
}
};
// Some convenience functions
// :Boolean
this.readBoolean = function() {
return 1 === this.readBits(1);
};
// ():int
this.readUnsignedByte = function() {
return this.readBits(8);
};
this.loadWord();
};
})(this);
(function() {
var
H264ExtraData,
ExpGolomb = window.videojs.Hls.ExpGolomb,
FlvTag = window.videojs.Hls.FlvTag;
window.videojs.Hls.H264ExtraData = H264ExtraData = function() {
this.sps = []; // :Array
this.pps = []; // :Array
};
H264ExtraData.prototype.extraDataExists = function() { // :Boolean
return this.sps.length > 0;
};
// (sizeOfScalingList:int, expGolomb:ExpGolomb):void
H264ExtraData.prototype.scaling_list = function(sizeOfScalingList, expGolomb) {
var
lastScale = 8, // :int
nextScale = 8, // :int
j,
delta_scale; // :int
for (j = 0; j < sizeOfScalingList; ++j) {
if (0 !== nextScale) {
delta_scale = expGolomb.readExpGolomb();
nextScale = (lastScale + delta_scale + 256) % 256;
//useDefaultScalingMatrixFlag = ( j = = 0 && nextScale = = 0 )
}
lastScale = (nextScale === 0) ? lastScale : nextScale;
// scalingList[ j ] = ( nextScale == 0 ) ? lastScale : nextScale;
// lastScale = scalingList[ j ]
}
};
/**
* RBSP: raw bit-stream payload. The actual encoded video data.
*
* SPS: sequence parameter set. Part of the RBSP. Metadata to be applied
* to a complete video sequence, like width and height.
*/
H264ExtraData.prototype.getSps0Rbsp = function() { // :ByteArray
var
sps = this.sps[0],
offset = 1,
start = 1,
written = 0,
end = sps.byteLength - 2,
result = new Uint8Array(sps.byteLength);
// In order to prevent 0x0000 01 from being interpreted as a
// NAL start code, occurences of that byte sequence in the
// RBSP are escaped with an "emulation byte". That turns
// sequences of 0x0000 01 into 0x0000 0301. When interpreting
// a NAL payload, they must be filtered back out.
while (offset < end) {
if (sps[offset] === 0x00 &&
sps[offset + 1] === 0x00 &&
sps[offset + 2] === 0x03) {
result.set(sps.subarray(start, offset + 1), written);
written += offset + 1 - start;
start = offset + 3;
}
offset++;
}
result.set(sps.subarray(start), written);
return result.subarray(0, written + (sps.byteLength - start));
};
// (pts:uint):FlvTag
H264ExtraData.prototype.metaDataTag = function(pts) {
var
tag = new FlvTag(FlvTag.METADATA_TAG), // :FlvTag
expGolomb, // :ExpGolomb
profile_idc, // :int
chroma_format_idc, // :int
imax, // :int
i, // :int
pic_order_cnt_type, // :int
num_ref_frames_in_pic_order_cnt_cycle, // :uint
pic_width_in_mbs_minus1, // :int
pic_height_in_map_units_minus1, // :int
frame_mbs_only_flag, // :int
frame_cropping_flag, // :Boolean
frame_crop_left_offset = 0, // :int
frame_crop_right_offset = 0, // :int
frame_crop_top_offset = 0, // :int
frame_crop_bottom_offset = 0, // :int
width,
height;
tag.dts = pts;
tag.pts = pts;
expGolomb = new ExpGolomb(this.getSps0Rbsp());
// :int = expGolomb.readUnsignedByte(); // profile_idc u(8)
profile_idc = expGolomb.readUnsignedByte();
// constraint_set[0-5]_flag, u(1), reserved_zero_2bits u(2), level_idc u(8)
expGolomb.skipBits(16);
// seq_parameter_set_id
expGolomb.skipUnsignedExpGolomb();
if (profile_idc === 100 ||
profile_idc === 110 ||
profile_idc === 122 ||
profile_idc === 244 ||
profile_idc === 44 ||
profile_idc === 83 ||
profile_idc === 86 ||
profile_idc === 118 ||
profile_idc === 128) {
chroma_format_idc = expGolomb.readUnsignedExpGolomb();
if (3 === chroma_format_idc) {
expGolomb.skipBits(1); // separate_colour_plane_flag
}
expGolomb.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
expGolomb.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
expGolomb.skipBits(1); // qpprime_y_zero_transform_bypass_flag
if (expGolomb.readBoolean()) { // seq_scaling_matrix_present_flag
imax = (chroma_format_idc !== 3) ? 8 : 12;
for (i = 0 ; i < imax ; ++i) {
if (expGolomb.readBoolean()) { // seq_scaling_list_present_flag[ i ]
if (i < 6) {
this.scaling_list(16, expGolomb);
} else {
this.scaling_list(64, expGolomb);
}
}
}
}
}
expGolomb.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
pic_order_cnt_type = expGolomb.readUnsignedExpGolomb();
if ( 0 === pic_order_cnt_type ) {
expGolomb.readUnsignedExpGolomb(); //log2_max_pic_order_cnt_lsb_minus4
} else if ( 1 === pic_order_cnt_type ) {
expGolomb.skipBits(1); // delta_pic_order_always_zero_flag
expGolomb.skipExpGolomb(); // offset_for_non_ref_pic
expGolomb.skipExpGolomb(); // offset_for_top_to_bottom_field
num_ref_frames_in_pic_order_cnt_cycle = expGolomb.readUnsignedExpGolomb();
for(i = 0 ; i < num_ref_frames_in_pic_order_cnt_cycle ; ++i) {
expGolomb.skipExpGolomb(); // offset_for_ref_frame[ i ]
}
}
expGolomb.skipUnsignedExpGolomb(); // max_num_ref_frames
expGolomb.skipBits(1); // gaps_in_frame_num_value_allowed_flag
pic_width_in_mbs_minus1 = expGolomb.readUnsignedExpGolomb();
pic_height_in_map_units_minus1 = expGolomb.readUnsignedExpGolomb();
frame_mbs_only_flag = expGolomb.readBits(1);
if (0 === frame_mbs_only_flag) {
expGolomb.skipBits(1); // mb_adaptive_frame_field_flag
}
expGolomb.skipBits(1); // direct_8x8_inference_flag
frame_cropping_flag = expGolomb.readBoolean();
if (frame_cropping_flag) {
frame_crop_left_offset = expGolomb.readUnsignedExpGolomb();
frame_crop_right_offset = expGolomb.readUnsignedExpGolomb();
frame_crop_top_offset = expGolomb.readUnsignedExpGolomb();
frame_crop_bottom_offset = expGolomb.readUnsignedExpGolomb();
}
width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_left_offset * 2 - frame_crop_right_offset * 2;
height = ((2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2);
tag.writeMetaDataDouble("videocodecid", 7);
tag.writeMetaDataDouble("width", width);
tag.writeMetaDataDouble("height", height);
// tag.writeMetaDataDouble("videodatarate", 0 );
// tag.writeMetaDataDouble("framerate", 0);
return tag;
};
// (pts:uint):FlvTag
H264ExtraData.prototype.extraDataTag = function(pts) {
var
i,
tag = new FlvTag(FlvTag.VIDEO_TAG, true);
tag.dts = pts;
tag.pts = pts;
tag.writeByte(0x01);// version
tag.writeByte(this.sps[0][1]);// profile
tag.writeByte(this.sps[0][2]);// compatibility
tag.writeByte(this.sps[0][3]);// level
tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
tag.writeByte(0xE0 | 0x01 ); // reserved (3 bits), num of SPS (5 bits)
tag.writeShort( this.sps[0].length ); // data of SPS
tag.writeBytes( this.sps[0] ); // SPS
tag.writeByte( this.pps.length ); // num of PPS (will there ever be more that 1 PPS?)
for (i = 0 ; i < this.pps.length ; ++i) {
tag.writeShort(this.pps[i].length); // 2 bytes for length of PPS
tag.writeBytes(this.pps[i]); // data of PPS
}
return tag;
};
})();
(function(window) {
var
FlvTag = window.videojs.Hls.FlvTag,
H264ExtraData = window.videojs.Hls.H264ExtraData,
H264Stream,
NALUnitType;
/**
* Network Abstraction Layer (NAL) units are the packets of an H264
* stream. NAL units are divided into types based on their payload
* data. Each type has a unique numeric identifier.
*
* NAL unit
* |- NAL header -|------ RBSP ------|
*
* NAL unit: Network abstraction layer unit. The combination of a NAL
* header and an RBSP.
* NAL header: the encapsulation unit for transport-specific metadata in
* an h264 stream. Exactly one byte.
*/
// incomplete, see Table 7.1 of ITU-T H.264 for 12-32
window.videojs.Hls.NALUnitType = NALUnitType = {
unspecified: 0,
slice_layer_without_partitioning_rbsp_non_idr: 1,
slice_data_partition_a_layer_rbsp: 2,
slice_data_partition_b_layer_rbsp: 3,
slice_data_partition_c_layer_rbsp: 4,
slice_layer_without_partitioning_rbsp_idr: 5,
sei_rbsp: 6,
seq_parameter_set_rbsp: 7,
pic_parameter_set_rbsp: 8,
access_unit_delimiter_rbsp: 9,
end_of_seq_rbsp: 10,
end_of_stream_rbsp: 11
};
window.videojs.Hls.H264Stream = H264Stream = function() {
this._next_pts = 0; // :uint;
this._next_dts = 0; // :uint;
this._h264Frame = null; // :FlvTag
this._oldExtraData = new H264ExtraData(); // :H264ExtraData
this._newExtraData = new H264ExtraData(); // :H264ExtraData
this._nalUnitType = -1; // :int
this._state = 0; // :uint;
this.tags = [];
};
//(pts:uint):void
H264Stream.prototype.setTimeStampOffset = function() {};
//(pts:uint, dts:uint, dataAligned:Boolean):void
H264Stream.prototype.setNextTimeStamp = function(pts, dts, dataAligned) {
// We could end up with a DTS less than 0 here. We need to deal with that!
this._next_pts = pts;
this._next_dts = dts;
// If data is aligned, flush all internal buffers
if (dataAligned) {
this.finishFrame();
}
};
H264Stream.prototype.finishFrame = function() {
if (this._h264Frame) {
// Push SPS before EVERY IDR frame for seeking
if (this._newExtraData.extraDataExists()) {
this._oldExtraData = this._newExtraData;
this._newExtraData = new H264ExtraData();
}
// Check if keyframe and the length of tags.
// This makes sure we write metadata on the first frame of a segment.
if (this._oldExtraData.extraDataExists() &&
(this._h264Frame.keyFrame || this.tags.length === 0)) {
// Push extra data on every IDR frame in case we did a stream change + seek
this.tags.push(this._oldExtraData.metaDataTag(this._h264Frame.pts));
this.tags.push(this._oldExtraData.extraDataTag(this._h264Frame.pts));
}
this._h264Frame.endNalUnit();
this.tags.push(this._h264Frame);
}
this._h264Frame = null;
this._nalUnitType = -1;
this._state = 0;
};
// (data:ByteArray, o:int, l:int):void
H264Stream.prototype.writeBytes = function(data, offset, length) {
var
nalUnitSize, // :uint
start, // :uint
end, // :uint
t; // :int
// default argument values
offset = offset || 0;
length = length || 0;
if (length <= 0) {
// data is empty so there's nothing to write
return;
}
// scan through the bytes until we find the start code (0x000001) for a
// NAL unit and then begin writing it out
// strip NAL start codes as we go
switch (this._state) {
default:
/* falls through */
case 0:
this._state = 1;
/* falls through */
case 1:
// A NAL unit may be split across two TS packets. Look back a bit to
// make sure the prefix of the start code wasn't already written out.
if (data[offset] <= 1) {
nalUnitSize = this._h264Frame ? this._h264Frame.nalUnitSize() : 0;
if (nalUnitSize >= 1 && this._h264Frame.negIndex(1) === 0) {
// ?? ?? 00 | O[01] ?? ??
if (data[offset] === 1 &&
nalUnitSize >= 2 &&
this._h264Frame.negIndex(2) === 0) {
// ?? 00 00 : 01
if (3 <= nalUnitSize && 0 === this._h264Frame.negIndex(3)) {
this._h264Frame.length -= 3; // 00 00 00 : 01
} else {
this._h264Frame.length -= 2; // 00 00 : 01
}
this._state = 3;
return this.writeBytes(data, offset + 1, length - 1);
}
if (length > 1 && data[offset] === 0 && data[offset + 1] === 1) {
// ?? 00 | 00 01
if (nalUnitSize >= 2 && this._h264Frame.negIndex(2) === 0) {
this._h264Frame.length -= 2; // 00 00 : 00 01
} else {
this._h264Frame.length -= 1; // 00 : 00 01
}
this._state = 3;
return this.writeBytes(data, offset + 2, length - 2);
}
if (length > 2 &&
data[offset] === 0 &&
data[offset + 1] === 0 &&
data[offset + 2] === 1) {
// 00 : 00 00 01
// this._h264Frame.length -= 1;
this._state = 3;
return this.writeBytes(data, offset + 3, length - 3);
}
}
}
// allow fall through if the above fails, we may end up checking a few
// bytes a second time. But that case will be VERY rare
this._state = 2;
/* falls through */
case 2:
// Look for start codes in the data from the current offset forward
start = offset;
end = start + length;
for (t = end - 3; offset < t;) {
if (data[offset + 2] > 1) {
// if data[offset + 2] is greater than 1, there is no way a start
// code can begin before offset + 3
offset += 3;
} else if (data[offset + 1] !== 0) {
offset += 2;
} else if (data[offset] !== 0) {
offset += 1;
} else {
// If we get here we have 00 00 00 or 00 00 01
if (data[offset + 2] === 1) {
if (offset > start) {
this._h264Frame.writeBytes(data, start, offset - start);
}
this._state = 3;
offset += 3;
return this.writeBytes(data, offset, end - offset);
}
if (end - offset >= 4 &&
data[offset + 2] === 0 &&
data[offset + 3] === 1) {
if (offset > start) {
this._h264Frame.writeBytes(data, start, offset - start);
}
this._state = 3;
offset += 4;
return this.writeBytes(data, offset, end - offset);
}
// We are at the end of the buffer, or we have 3 NULLS followed by
// something that is not a 1, either way we can step forward by at
// least 3
offset += 3;
}
}
// We did not find any start codes. Try again next packet
this._state = 1;
if (this._h264Frame) {
this._h264Frame.writeBytes(data, start, length);
}
return;
case 3:
// The next byte is the first byte of a NAL Unit
if (this._h264Frame) {
// we've come to a new NAL unit so finish up the one we've been
// working on
switch (this._nalUnitType) {
case NALUnitType.seq_parameter_set_rbsp:
this._h264Frame.endNalUnit(this._newExtraData.sps);
break;
case NALUnitType.pic_parameter_set_rbsp:
this._h264Frame.endNalUnit(this._newExtraData.pps);
break;
case NALUnitType.slice_layer_without_partitioning_rbsp_idr:
this._h264Frame.endNalUnit();
break;
default:
this._h264Frame.endNalUnit();
break;
}
}
// setup to begin processing the new NAL unit
this._nalUnitType = data[offset] & 0x1F;
if (this._h264Frame) {
if (this._nalUnitType === NALUnitType.access_unit_delimiter_rbsp) {
// starting a new access unit, flush the previous one
this.finishFrame();
} else if (this._nalUnitType === NALUnitType.slice_layer_without_partitioning_rbsp_idr) {
this._h264Frame.keyFrame = true;
}
}
// finishFrame may render this._h264Frame null, so we must test again
if (!this._h264Frame) {
this._h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
this._h264Frame.pts = this._next_pts;
this._h264Frame.dts = this._next_dts;
}
this._h264Frame.startNalUnit();
// We know there will not be an overlapping start code, so we can skip
// that test
this._state = 2;
return this.writeBytes(data, offset, length);
} // switch
};
})(this);
/**
* Accepts program elementary stream (PES) data events and parses out
* ID3 metadata from them, if present.
* @see http://id3.org/id3v2.3.0
*/
(function(window, videojs, undefined) {
'use strict';
var
// 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 window.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 window.unescape(percentEncode(bytes, start, end));
},
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 - 1);
break;
}
}
},
'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);
}
},
MetadataStream;
MetadataStream = function(options) {
var
settings = {
debug: !!(options && options.debug),
// 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 = videojs.Hls.SegmentParser.STREAM_TYPES.metadata.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;
// 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))) {
if (settings.debug) {
videojs.log('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 = (chunk.data[6] << 21) |
(chunk.data[7] << 14) |
(chunk.data[8] << 7) |
(chunk.data[9]);
// 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, 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 += (tag.data[10] << 24) |
(tag.data[11] << 16) |
(tag.data[12] << 8) |
(tag.data[13]);
// clip any padding off the end
tagSize -= (tag.data[16] << 24) |
(tag.data[17] << 16) |
(tag.data[18] << 8) |
(tag.data[19]);
}
// 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 = (tag.data[frameStart + 4] << 24) |
(tag.data[frameStart + 5] << 16) |
(tag.data[frameStart + 6] << 8) |
(tag.data[frameStart + 7]);
if (frameSize < 1) {
return videojs.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
}
frame = {
id: String.fromCharCode(tag.data[frameStart],
tag.data[frameStart + 1],
tag.data[frameStart + 2],
tag.data[frameStart + 3]),
data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
};
if (tagParsers[frame.id]) {
tagParsers[frame.id](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 videojs.Hls.Stream();
videojs.Hls.MetadataStream = MetadataStream;
})(window, window.videojs);
(function(window) {
/*
======== A Handy Little QUnit Reference ========
http://api.qunitjs.com/
Test methods:
module(name, {[setup][ ,teardown]})
test(name, callback)
expect(numberOfAssertions)
stop(increment)
start(decrement)
Test assertions:
ok(value, [message])
equal(actual, expected, [message])
notEqual(actual, expected, [message])
deepEqual(actual, expected, [message])
notDeepEqual(actual, expected, [message])
strictEqual(actual, expected, [message])
notStrictEqual(actual, expected, [message])
throws(block, [expected], [message])
*/
var
buffer,
ExpGolomb = window.videojs.Hls.ExpGolomb,
expGolomb;
module('Exponential Golomb coding');
test('small numbers are coded correctly', function() {
var
expected = [
[0xF8, 0],
[0x5F, 1],
[0x7F, 2],
[0x27, 3],
[0x2F, 4],
[0x37, 5],
[0x3F, 6],
[0x11, 7],
[0x13, 8],
[0x15, 9]
],
i = expected.length,
result;
while (i--) {
buffer = new Uint8Array([expected[i][0]]);
expGolomb = new ExpGolomb(buffer);
result = expGolomb.readUnsignedExpGolomb();
equal(expected[i][1], result, expected[i][0] + ' is decoded to ' + expected[i][1]);
}
});
test('drops working data as it is parsed', function() {
var expGolomb = new ExpGolomb(new Uint8Array([0x00, 0xFF]));
expGolomb.skipBits(8);
equal(8, expGolomb.bitsAvailable(), '8 bits remain');
equal(0xFF, expGolomb.readBits(8), 'the second byte is read');
});
test('drops working data when skipping leading zeros', function() {
var expGolomb = new ExpGolomb(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0xFF]));
equal(32, expGolomb.skipLeadingZeros(), '32 leading zeros are dropped');
equal(8, expGolomb.bitsAvailable(), '8 bits remain');
equal(0xFF, expGolomb.readBits(8), 'the second byte is read');
});
test('drops working data when skipping leading zeros', function() {
var expGolomb = new ExpGolomb(new Uint8Array([0x15, 0xab, 0x40, 0xc8, 0xFF]));
equal(3, expGolomb.skipLeadingZeros(), '3 leading zeros are dropped');
equal((8 * 4) + 5, expGolomb.bitsAvailable(), '37 bits remain');
expGolomb.skipBits(1);
equal(0x5a, expGolomb.readBits(8), 'the next bits are read');
});
test('parses a sequence parameter set', function() {
var
sps = new Uint8Array([
0x27, 0x42, 0xe0, 0x0b,
0xa9, 0x18, 0x60, 0x9d,
0x80, 0x35, 0x06, 0x01,
0x06, 0xb6, 0xc2, 0xb5,
0xef, 0x7c, 0x04
]),
expGolomb = new ExpGolomb(sps);
strictEqual(expGolomb.readBits(8), 0x27, 'the NAL type specifies an SPS');
strictEqual(expGolomb.readBits(8), 66, 'profile_idc is 66');
strictEqual(expGolomb.readBits(4), 0x0E, 'constraints 0-3 are correct');
expGolomb.skipBits(4);
strictEqual(expGolomb.readBits(8), 11, 'level_idc is 11');
strictEqual(expGolomb.readUnsignedExpGolomb(), 0, 'seq_parameter_set_id is 0');
strictEqual(expGolomb.readUnsignedExpGolomb(), 1, 'log2_max_frame_num_minus4 is 1');
strictEqual(expGolomb.readUnsignedExpGolomb(), 0, 'pic_order_cnt_type is 0');
strictEqual(expGolomb.readUnsignedExpGolomb(), 3, 'log2_max_pic_order_cnt_lsb_minus4 is 3');
strictEqual(expGolomb.readUnsignedExpGolomb(), 2, 'max_num_ref_frames is 2');
strictEqual(expGolomb.readBits(1), 0, 'gaps_in_frame_num_value_allowed_flag is false');
strictEqual(expGolomb.readUnsignedExpGolomb(), 11, 'pic_width_in_mbs_minus1 is 11');
strictEqual(expGolomb.readUnsignedExpGolomb(), 8, 'pic_height_in_map_units_minus1 is 8');
strictEqual(expGolomb.readBits(1), 1, 'frame_mbs_only_flag is true');
strictEqual(expGolomb.readBits(1), 1, 'direct_8x8_inference_flag is true');
strictEqual(expGolomb.readBits(1), 0, 'frame_cropping_flag is false');
});
})(this);
(function(window) {
/*
======== A Handy Little QUnit Reference ========
http://api.qunitjs.com/
Test methods:
module(name, {[setup][ ,teardown]})
test(name, callback)
expect(numberOfAssertions)
stop(increment)
start(decrement)
Test assertions:
ok(value, [message])
equal(actual, expected, [message])
notEqual(actual, expected, [message])
deepEqual(actual, expected, [message])
notDeepEqual(actual, expected, [message])
strictEqual(actual, expected, [message])
notStrictEqual(actual, expected, [message])
throws(block, [expected], [message])
*/
var FlvTag = window.videojs.Hls.FlvTag;
module('FLV tag');
test('writeBytes with zero length writes the entire array', function() {
var
tag = new FlvTag(FlvTag.VIDEO_TAG),
headerLength = tag.length;
tag.writeBytes(new Uint8Array([0x1, 0x2, 0x3]));
equal(3 + headerLength, tag.length, '3 payload bytes are written');
});
test('writeShort writes a two byte sequence', function() {
var
tag = new FlvTag(FlvTag.VIDEO_TAG),
headerLength = tag.length;
tag.writeShort(0x0102);
equal(2 + headerLength, tag.length, '2 bytes are written');
equal(0x0102,
new DataView(tag.bytes.buffer).getUint16(tag.length - 2),
'the value is written');
});
test('writeBytes grows the internal byte array dynamically', function() {
var
tag = new FlvTag(FlvTag.VIDEO_TAG),
tooManyBytes = new Uint8Array(tag.bytes.byteLength + 1);
try {
tag.writeBytes(tooManyBytes);
ok(true, 'the buffer grew to fit the data');
} catch(e) {
ok(!e, 'the buffer should grow');
}
});
})(this);
(function(videojs) {
module('H264 Stream');
var
nalUnitTypes = window.videojs.Hls.NALUnitType,
FlvTag = window.videojs.Hls.FlvTag,
accessUnitDelimiter = new Uint8Array([
0x00,
0x00,
0x01,
nalUnitTypes.access_unit_delimiter_rbsp
]),
seqParamSet = new Uint8Array([
0x00,
0x00,
0x01,
0x60 | nalUnitTypes.seq_parameter_set_rbsp,
0x00, // profile_idc
0x00, // constraint_set flags
0x00, // level_idc
// seq_parameter_set_id ue(v) 0 => 1
// log2_max_frame_num_minus4 ue(v) 1 => 010
// pic_order_cnt_type ue(v) 0 => 1
// log2_max_pic_order_cnt_lsb_minus4 ue(v) 1 => 010
// max_num_ref_frames ue(v) 1 => 010
// gaps_in_frame_num_value_allowed u(1) 0
// pic_width_in_mbs_minus1 ue(v) 0 => 1
// pic_height_in_map_units_minus1 ue(v) 0 => 1
// frame_mbs_only_flag u(1) 1
// direct_8x8_inference_flag u(1) 0
// frame_cropping_flag u(1) 0
// vui_parameters_present_flag u(1) 0
// 1010 1010 0100 1110 00(00 0000)
0xAA,
0x4E,
0x00
]);
test('metadata is generated for IDRs after a full NAL unit is written', function() {
var
h264Stream = new videojs.Hls.H264Stream(),
idr = new Uint8Array([
0x00,
0x00,
0x01,
nalUnitTypes.slice_layer_without_partitioning_rbsp_idr
]);
h264Stream.setNextTimeStamp(0, 0, true);
h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength);
h264Stream.writeBytes(seqParamSet, 0, seqParamSet.byteLength);
h264Stream.writeBytes(idr, 0, idr.byteLength);
h264Stream.setNextTimeStamp(1, 1, true);
strictEqual(h264Stream.tags.length, 3, 'three tags are written');
ok(FlvTag.isMetaData(h264Stream.tags[0].bytes),
'metadata is written');
ok(FlvTag.isVideoFrame(h264Stream.tags[1].bytes),
'picture parameter set is written');
ok(h264Stream.tags[2].keyFrame, 'key frame is written');
});
test('make sure we add metadata and extra data at the beginning of a stream', function() {
var
H264ExtraData = videojs.Hls.H264ExtraData,
oldExtraData = H264ExtraData.prototype.extraDataTag,
oldMetadata = H264ExtraData.prototype.metaDataTag,
h264Stream;
H264ExtraData.prototype.extraDataTag = function() {
return 'extraDataTag';
};
H264ExtraData.prototype.metaDataTag = function() {
return 'metaDataTag';
};
h264Stream = new videojs.Hls.H264Stream();
h264Stream.setTimeStampOffset(0);
h264Stream.setNextTimeStamp(0, 0, true);
// the sps provides the metadata for the stream
h264Stream.writeBytes(seqParamSet, 0, seqParamSet.byteLength);
h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength);
// make sure that keyFrame is set to false but that we don't have any tags currently written out
h264Stream._h264Frame.keyFrame = false;
h264Stream.tags = [];
h264Stream.setNextTimeStamp(5, 5, true);
h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength);
// flush out the last tag
h264Stream.writeBytes(accessUnitDelimiter, 0, accessUnitDelimiter.byteLength);
strictEqual(h264Stream.tags.length, 4, 'three tags are ready');
strictEqual(h264Stream.tags[0], 'metaDataTag', 'the first tag is the metaDataTag');
strictEqual(h264Stream.tags[1], 'extraDataTag', 'the second tag is the extraDataTag');
H264ExtraData.prototype.extraDataTag = oldExtraData;
H264ExtraData.prototype.metaDataTag = oldMetadata;
});
})(window.videojs);
......@@ -77,25 +77,15 @@ module.exports = function(config) {
'../test/karma-qunit-shim.js',
'../src/videojs-hls.js',
'../src/stream.js',
'../src/flv-tag.js',
'../src/exp-golomb.js',
'../src/h264-extradata.js',
'../src/h264-stream.js',
'../src/aac-stream.js',
'../src/metadata-stream.js',
'../src/segment-parser.js',
'../src/m3u8/m3u8-parser.js',
'../src/xhr.js',
'../src/playlist.js',
'../src/playlist-loader.js',
'../src/decrypter.js',
'../src/mp4-generator.js',
'../src/transmuxer.js',
'../tmp/manifests.js',
'../tmp/expected.js',
'tsSegment-bc.js',
'../src/bin-utils.js',
'../test/muxer/js/mp4-inspector.js',
'../test/*.js',
],
......
......@@ -42,25 +42,15 @@ module.exports = function(config) {
'../test/karma-qunit-shim.js',
'../src/videojs-hls.js',
'../src/stream.js',
'../src/flv-tag.js',
'../src/exp-golomb.js',
'../src/h264-extradata.js',
'../src/h264-stream.js',
'../src/aac-stream.js',
'../src/metadata-stream.js',
'../src/segment-parser.js',
'../src/m3u8/m3u8-parser.js',
'../src/xhr.js',
'../src/playlist.js',
'../src/playlist-loader.js',
'../src/decrypter.js',
'../src/mp4-generator.js',
'../src/transmuxer.js',
'../tmp/manifests.js',
'../tmp/expected.js',
'tsSegment-bc.js',
'../src/bin-utils.js',
'../test/muxer/js/mp4-inspector.js',
'../test/*.js',
],
......
/* ==========================================================================
HTML5 Boilerplate styles - h5bp.com (generated via initializr.com)
========================================================================== */
html,
button,
input,
select,
textarea {
color: #222;
}
body {
font-size: 1em;
line-height: 1.4;
}
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
}
::selection {
background: #b3d4fc;
text-shadow: none;
}
hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #ccc;
margin: 1em 0;
padding: 0;
}
img {
vertical-align: middle;
}
fieldset {
border: 0;
margin: 0;
padding: 0;
}
textarea {
resize: vertical;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
/* ===== Initializr Styles ==================================================
Author: Jonathan Verrecchia - verekia.com/initializr/responsive-template
========================================================================== */
body {
font: 16px/26px Helvetica, Helvetica Neue, Arial;
}
.wrapper {
width: 90%;
margin: 0 5%;
}
/* ===================
ALL: Orange Theme
=================== */
.header-container {
border-bottom: 20px solid #e44d26;
}
.footer-container,
.main aside {
border-top: 20px solid #e44d26;
}
.header-container,
.footer-container,
.main aside {
background: #f16529;
}
.title {
color: white;
}
/* ==============
MOBILE: Menu
============== */
nav ul {
margin: 0;
padding: 0;
}
nav a {
display: block;
margin-bottom: 10px;
padding: 15px 0;
text-align: center;
text-decoration: none;
font-weight: bold;
color: white;
background: #e44d26;
}
nav a:hover,
nav a:visited {
color: white;
}
nav a:hover {
text-decoration: underline;
}
/* ==============
MOBILE: Main
============== */
.main {
padding: 30px 0;
}
.main article h1 {
font-size: 2em;
}
.main aside {
color: white;
padding: 0px 5% 10px;
}
.footer-container footer {
color: white;
padding: 20px 0;
}
/* ===============
ALL: IE Fixes
=============== */
.ie7 .title {
padding-top: 20px;
}
/* ==========================================================================
Author's custom styles
========================================================================== */
section {
clear: both;
}
form label {
display: block;
}
.result-wrapper {
float: left;
margin: 10px 0;
min-width: 422px;
width: 50%;
}
.result {
border: thin solid #aaa;
border-radius: 5px;
font-size: 10px;
line-height: 15px;
margin: 0 5px;
padding: 0 10px;
}
/* ==========================================================================
Media Queries
========================================================================== */
@media only screen and (min-width: 480px) {
/* ====================
INTERMEDIATE: Menu
==================== */
nav a {
float: left;
width: 27%;
margin: 0 1.7%;
padding: 25px 2%;
margin-bottom: 0;
}
nav li:first-child a {
margin-left: 0;
}
nav li:last-child a {
margin-right: 0;
}
/* ========================
INTERMEDIATE: IE Fixes
======================== */
nav ul li {
display: inline;
}
.oldie nav a {
margin: 0 0.7%;
}
}
@media only screen and (min-width: 768px) {
/* ====================
WIDE: CSS3 Effects
==================== */
.header-container,
.main aside {
-webkit-box-shadow: 0 5px 10px #aaa;
-moz-box-shadow: 0 5px 10px #aaa;
box-shadow: 0 5px 10px #aaa;
}
/* ============
WIDE: Menu
============ */
.title {
float: left;
}
nav {
float: right;
width: 38%;
}
/* ============
WIDE: Main
============ */
.main article {
float: left;
width: 100%;
}
}
@media only screen and (min-width: 1140px) {
/* ===============
Maximal Width
=============== */
.wrapper {
width: 1026px; /* 1140px - 10% for margins */
margin: 0 auto;
}
}
/* ==========================================================================
Helper classes
========================================================================== */
.ir {
background-color: transparent;
border: 0;
overflow: hidden;
*text-indent: -9999px;
}
.ir:before {
content: "";
display: block;
width: 0;
height: 150%;
}
.hidden {
display: none !important;
visibility: hidden;
}
.visuallyhidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.visuallyhidden.focusable:active,
.visuallyhidden.focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
width: auto;
}
.invisible {
visibility: hidden;
}
.clearfix:before,
.clearfix:after {
content: " ";
display: table;
}
.clearfix:after {
clear: both;
}
.clearfix {
*zoom: 1;
}
/* ==========================================================================
Print styles
========================================================================== */
@media print {
* {
background: transparent !important;
color: #000 !important; /* Black prints faster: h5bp.com/s */
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
/*
* Don't show links for images, or javascript/internal links
*/
.ir a:after,
a[href^="javascript:"]:after,
a[href^="#"]:after {
content: "";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group; /* h5bp.com/t */
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
@page {
margin: 0.5cm;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
}
/**
* Diff styling
*/
#comparison p {
white-space: normal;
}
#comparison pre {
font-size: 90%;
}
ins, del {
text-decoration: none;
}
ins {
background-color: #C6E746;
}
del {
background-color: #EE5757
}
\ No newline at end of file
/*! normalize.css v1.1.2 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block;
}
/**
* Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address styling not present in IE 7/8/9, Firefox 3, and Safari 4.
* Known issue: no IE 6 support.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/**
* 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using
* `em` units.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-size: 100%; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Address `font-family` inconsistency between `textarea` and other form
* elements.
*/
html,
button,
input,
select,
textarea {
font-family: sans-serif;
}
/**
* Address margins handled incorrectly in IE 6/7.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/**
* Address `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/**
* Address font sizes and margins set differently in IE 6/7.
* Address font sizes within `section` and `article` in Firefox 4+, Safari 5,
* and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
h2 {
font-size: 1.5em;
margin: 0.83em 0;
}
h3 {
font-size: 1.17em;
margin: 1em 0;
}
h4 {
font-size: 1em;
margin: 1.33em 0;
}
h5 {
font-size: 0.83em;
margin: 1.67em 0;
}
h6 {
font-size: 0.67em;
margin: 2.33em 0;
}
/**
* Address styling not present in IE 7/8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
blockquote {
margin: 1em 40px;
}
/**
* Address styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address differences between Firefox and other browsers.
* Known issue: no IE 6/7 normalization.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Address styling not present in IE 6/7/8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address margins set differently in IE 6/7.
*/
p,
pre {
margin: 1em 0;
}
/**
* Correct font family set oddly in IE 6, Safari 4/5, and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
_font-family: 'courier new', monospace;
font-size: 1em;
}
/**
* Improve readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/**
* Address CSS quotes not supported in IE 6/7.
*/
q {
quotes: none;
}
/**
* Address `quotes` property not supported in Safari 4.
*/
q:before,
q:after {
content: '';
content: none;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Lists
========================================================================== */
/**
* Address margins set differently in IE 6/7.
*/
dl,
menu,
ol,
ul {
margin: 1em 0;
}
dd {
margin: 0 0 0 40px;
}
/**
* Address paddings set differently in IE 6/7.
*/
menu,
ol,
ul {
padding: 0 0 0 40px;
}
/**
* Correct list images handled incorrectly in IE 7.
*/
nav ul,
nav ol {
list-style: none;
list-style-image: none;
}
/* ==========================================================================
Embedded content
========================================================================== */
/**
* 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3.
* 2. Improve image quality when scaled in IE 7.
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/**
* Correct overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/**
* Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/**
* Correct margin displayed oddly in IE 6/7.
*/
form {
margin: 0;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct color not being inherited in IE 6/7/8/9.
* 2. Correct text not wrapping in Firefox 3.
* 3. Correct alignment displayed oddly in IE 6/7.
*/
legend {
border: 0; /* 1 */
padding: 0;
white-space: normal; /* 2 */
*margin-left: -7px; /* 3 */
}
/**
* 1. Correct font size not being inherited in all browsers.
* 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5,
* and Chrome.
* 3. Improve appearance and consistency in all browsers.
*/
button,
input,
select,
textarea {
font-size: 100%; /* 1 */
margin: 0; /* 2 */
vertical-align: baseline; /* 3 */
*vertical-align: middle; /* 3 */
}
/**
* Address Firefox 3+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
* 4. Remove inner spacing in IE 7 without affecting normal text inputs.
* Known issue: inner spacing remains in IE 6.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
*overflow: visible; /* 4 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* 1. Address box sizing set to content-box in IE 8/9.
* 2. Remove excess padding in IE 8/9.
* 3. Remove excess padding in IE 7.
* Known issue: excess padding remains in IE 6.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
*height: 13px; /* 3 */
*width: 13px; /* 3 */
}
/**
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Remove inner padding and border in Firefox 3+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* 1. Remove default vertical scrollbar in IE 6/7/8/9.
* 2. Improve readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
/*! normalize.css v1.1.2 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}html,button,input,select,textarea{font-family:sans-serif}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:1em 40px}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:before,q:after{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}dl,menu,ol,ul{margin:1em 0}dd{margin:0 0 0 40px}menu,ol,ul{padding:0 0 0 40px}nav ul,nav ol{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0;white-space:normal;*margin-left:-7px}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}
\ No newline at end of file
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="css/normalize.min.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
<![endif]-->
<div class="header-container">
<header class="wrapper clearfix">
<h1 class="title">Transmux Analyzer</h1>
</header>
</div>
<div class="main-container">
<div class="main wrapper clearfix">
<article>
<header>
<p>
This page can help you compare the results of the
transmuxing performed by videojs-contrib-hls with a known,
working file produced by another tool. You could use
ffmpeg to transform an MPEG-2 transport stream into an FLV
with a command like this:
<pre>ffmpeg -i input.ts -acodec copy -vcodec copy output.flv</pre>
</p>
<p>
This page only compares FLVs files. There is
a <a href="mp4.html">similar utility</a> for testing the mp4
conversion.
</p>
</header>
<section>
<h2>Inputs</h2>
<form id="inputs">
<fieldset>
<label>
Your original MP2T segment:
<input type="file" id="original">
</label>
<label>
Key (optional):
<input type="text" id="key">
</label>
<label>
IV (optional):
<input type="text" id="iv">
</label>
</fieldset>
<label>
A working, FLV version of the underlying stream
produced by another tool:
<input type="file" id="working">
</label>
</form>
</section>
<section>
<h2>Tag Comparison</h2>
<div class="result-wrapper">
<h3>videojs-contrib-hls</h3>
<ol class="vjs-tags">
</ol>
</div>
<div class="result-wrapper">
<h3>Working</h3>
<ol class="working-tags">
</ol>
</div>
</section>
<section>
<h2>Results</h2>
<div class="result-wrapper">
<h3>videojs-contrib-hls</h3>
<div class="vjs-hls-output result">
<p>
The results of transmuxing your input file with
videojs-contrib-hls will show up here.
</p>
</div>
</div>
<div class="result-wrapper">
<h3>Working</h3>
<div class="working-output result">
<p>
The "good" version of the file will show up here.
</p>
</div>
</div>
</section>
</article>
</div> <!-- #main -->
</div> <!-- #main-container -->
<div class="footer-container">
<footer class="wrapper">
<h3>footer</h3>
</footer>
</div>
<script>
window.videojs = {
Hls: {}
};
</script>
<!-- transmuxing -->
<script src="../../src/stream.js"></script>
<script src="../../src/flv-tag.js"></script>
<script src="../../src/exp-golomb.js"></script>
<script src="../../src/h264-stream.js"></script>
<script src="../../src/aac-stream.js"></script>
<script src="../../src/metadata-stream.js"></script>
<script src="../../src/segment-parser.js"></script>
<script src="../../node_modules/pkcs7/dist/pkcs7.unpad.js"></script>
<script src="../../src/decrypter.js"></script>
<script src="../../src/bin-utils.js"></script>
<script>
var inputs = document.getElementById('inputs'),
original = document.getElementById('original'),
key = document.getElementById('key'),
iv = document.getElementById('iv'),
working = document.getElementById('working'),
vjsTags = document.querySelector('.vjs-tags'),
workingTags = document.querySelector('.working-tags'),
vjsOutput = document.querySelector('.vjs-hls-output'),
workingOutput = document.querySelector('.working-output'),
tagTypes = {
0x08: 'audio',
0x09: 'video',
0x12: 'metadata'
};
videojs.log = console.log.bind(console);
original.addEventListener('change', function() {
var reader = new FileReader();
reader.addEventListener('loadend', function() {
var parser = new videojs.Hls.SegmentParser(),
tags = [parser.getFlvHeader()],
tag,
bytes,
hex,
li,
byteLength = 0,
data,
i,
pos;
// clear old tag info
vjsTags.innerHTML = '';
// optionally, decrypt the segment
if (key.value && iv.value) {
bytes = videojs.Hls.decrypt(new Uint8Array(reader.result),
key.value.match(/([0-9a-f]{8})/gi).map(function(e) {
return parseInt(e, 16);
}),
iv.value.match(/([0-9a-f]{8})/gi).map(function(e) {
return parseInt(e, 16);
}));
} else {
bytes = new Uint8Array(reader.result);
}
parser.parseSegmentBinaryData(bytes);
// collect all the tags
while (parser.tagsAvailable()) {
tag = parser.getNextTag();
tags.push(tag.bytes);
li = document.createElement('li');
li.innerHTML = tagTypes[tag.bytes[0]] + ': ' + tag.bytes.byteLength + 'B';
vjsTags.appendChild(li);
}
// create a uint8array for the entire segment and copy everything over
i = tags.length;
while (i--) {
byteLength += tags[i].byteLength;
}
data = new Uint8Array(byteLength);
i = tags.length;
pos = byteLength;
while (i--) {
pos -= tags[i].byteLength;
data.set(tags[i], pos);
}
hex = '<pre>'
hex += videojs.Hls.utils.hexDump(data);
hex += '</pre>'
vjsOutput.innerHTML = hex;
});
reader.readAsArrayBuffer(this.files[0]);
}, false);
working.addEventListener('change', function() {
var reader = new FileReader();
reader.addEventListener('loadend', function() {
var hex = '<pre>',
bytes = new Uint8Array(reader.result),
i = 9, // header
dataSize,
li;
// clear old tag info
workingTags.innerHTML = '';
// traverse the tags
i += 4; // previous tag size
while (i < bytes.byteLength) {
dataSize = bytes[i + 1] << 16;
dataSize |= bytes[i + 2] << 8;
dataSize |= bytes[i + 3];
dataSize += 11;
li = document.createElement('li');
li.innerHTML = tagTypes[bytes[i]] + ': ' + dataSize + 'B';
workingTags.appendChild(li);
i += dataSize; // tag size
i += 4; // previous tag size
}
// output the hex dump
hex += videojs.Hls.utils.hexDump(bytes);
hex += '</pre>';
workingOutput.innerHTML = hex;
});
reader.readAsArrayBuffer(this.files[0]);
}, false);
</script>
<script type="text/plain">
// map nal_unit_types to friendly names
console.log([
'unspecified',
'slice_layer_without_partitioning',
'slice_data_partition_a_layer',
'slice_data_partition_b_layer',
'slice_data_partition_c_layer',
'slice_layer_without_partitioning_idr',
'sei',
'seq_parameter_set',
'pic_parameter_set',
'access_unit_delimiter',
'end_of_seq',
'end_of_stream',
'filler',
'seq_parameter_set_ext',
'prefix_nal_unit',
'subset_seq_parameter_set',
'reserved',
'reserved',
'reserved'
][nalUnitType]);
</script>
</body>
</html>
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>HLS Media Sources Demo</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="css/normalize.min.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
<![endif]-->
<div class="header-container">
<header class="wrapper clearfix">
<h1 class="title">Media Sources Demo</h1>
</header>
</div>
<div class="main-container">
<div class="main wrapper clearfix">
<article>
<header>
<p>
videojs-contrib-hls can convert MPEG transport streams
into Media Source Extension-compatible media segments on
the fly. Check out the demo below in Chrome!
</p>
</header>
<section>
<video id=demo width=640 height=360 controls>
<p>
Your browser is too old to view this demo. How about
upgrading?
</p>
</video>
</section>
</article>
</div> <!-- #main -->
</div> <!-- #main-container -->
<div class="footer-container">
<footer class="wrapper">
</footer>
</div>
<script src="zencoder-haze-1.js"></script>
<!-- transmuxing -->
<script>
window.videojs = window.videojs || {
Hls: {}
};
</script>
<script src="../../src/stream.js"></script>
<script src="../../src/mp4-generator.js"></script>
<script src="../../src/transmuxer.js"></script>
<script src="../../src/exp-golomb.js"></script>
<script src="js/mp4-inspector.js"></script>
<script src="../../src/bin-utils.js"></script>
<script>
var mediaSource = new MediaSource(),
demo = document.getElementById('demo');
// setup the media source
mediaSource.addEventListener('sourceopen', function() {
var videoBuffer = mediaSource.addSourceBuffer('video/mp4;codecs=avc1.4d400d'),
audioBuffer = mediaSource.addSourceBuffer('audio/mp4;codecs=mp4a.40.2'),
transmuxer = new videojs.mp2t.Transmuxer(),
videoSegments = [],
audioSegments = [];
// expose the machinery for debugging
window.vjsMediaSource = mediaSource;
window.vjsSourceBuffer = videoBuffer;
window.vjsVideo = demo;
// transmux the MPEG-TS data to BMFF segments
transmuxer.on('data', function(segment) {
if (segment.type === 'video') {
videoSegments.push(segment);
} else {
audioSegments.push(segment);
}
});
transmuxer.push(hazeVideo);
transmuxer.end();
// buffer up the video data
videoBuffer.appendBuffer(videoSegments.shift().data);
videoBuffer.addEventListener('updateend', function() {
if (videoSegments.length) {
videoBuffer.appendBuffer(videoSegments.shift().data);
}
});
// buffer up the audio data
audioBuffer.appendBuffer(audioSegments.shift().data);
audioBuffer.addEventListener('updateend', function() {
if (audioSegments.length) {
audioBuffer.appendBuffer(audioSegments.shift().data);
}
});
});
demo.src = URL.createObjectURL(mediaSource);
demo.addEventListener('error', console.log.bind(console));
</script>
</body>
</html>
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="css/normalize.min.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.2.min.js"></script>
<script>
window.videojs = window.videojs || {
Hls: {}
};
</script>
<!--<script src="min-m4s.js"></script>-->
<script src="./js/mdat.js"></script>
<script src="../../src/mp4-generator.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
<![endif]-->
<div class="header-container">
<header class="wrapper clearfix">
<h1 class="title">Transmux Analyzer</h1>
</header>
</div>
<div class="main-container">
<div class="main wrapper clearfix">
<article>
<header>
<p>
This page will show a video player that loads a header of an mp4 followed by an mp4 segment.
</p>
</header>
<section>
<video controls autoplay width="320" height="240"></video>
</section>
</article>
</div> <!-- #main -->
</div> <!-- #main-container -->
<div class="footer-container">
<footer class="wrapper">
<h3></h3>
</footer>
</div>
<script>
var ms,
sourceBuffer,
video;
function closed() {
console.log("MediaSource Closed.");
}
var done = false;
function loadFragment() {
if (done) {
return;
}
console.log("LOAD FRAGMENT");
var moov = videojs.mp4.moov(100, 600, 300, "video");
var moof = videojs.mp4.moof([{trackId: 1}]);
var frag = Array.prototype.slice.call(moov);
frag = frag.concat(Array.prototype.slice.call(moof));
frag = frag.concat(Array.prototype.slice.call(mdat));
sourceBuffer.appendBuffer( new Uint8Array( frag ) );
done = true;
}
function loadMdat(){
console.log('mdat', mdat);
setTimeout( function(){
sourceBuffer.appendBuffer(new Uint8Amdat);
},0);
}
function loadInit() {
console.log("LOAD INIT");
var req = new XMLHttpRequest();
req.responseType = "arraybuffer";
req.open("GET", "./ts2/Header.m4s", true);
req.onload = function() {
console.log("INIT DONE LOADING");
sourceBuffer.appendBuffer(new Uint8Array(req.response));
sourceBuffer.addEventListener('update', loadFragment);
};
req.onerror = function () {
window.alert("Could not load init.");
};
req.send();
}
function opened() {
console.log("OPENED");
sourceBuffer = ms.addSourceBuffer('video/mp4;codecs="avc1.42c00d"');
sourceBuffer.addEventListener('updateend', console.log.bind(console));
sourceBuffer.addEventListener('updatestart', console.log.bind(console));
sourceBuffer.addEventListener('update', console.log.bind(console));
loadInit();
}
function load() {
console.log("START");
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
ms = new window.MediaSource();
video = document.querySelector('video');
video.src = window.URL.createObjectURL(ms);
ms.addEventListener('webkitsourceopen', opened, false);
ms.addEventListener('webkitsourceclose', closed, false);
ms.addEventListener('sourceopen', opened, false);
ms.addEventListener('sourceclose', closed, false);
ms.addEventListener('update', console.log.bind(console));
ms.addEventListener('updateend', console.log.bind(console));
}
load();
</script>
</body>
</html>
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="css/normalize.min.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
<![endif]-->
<div class="header-container">
<header class="wrapper clearfix">
<h1 class="title">MP4 Media Sources API Test</h1>
</header>
</div>
<div class="main-container">
<div class="main wrapper clearfix">
<article>
<header>
<p>
This page is used to demo the playing of an mp4 file via the mediaSources API
</p>
</header>
<section>
<h2>Inputs</h2>
<form id="inputs">
<label>
Your original MP2T segment:
<input type="file" id="original">
</label>
<label>
A working, MP4 version of the underlying stream
produced by another tool:
<input type="file" id="working">
</label>
</form>
</section>
<section>
<video controls width="320" height="240">>
<source src="sintel_trailer-480p.mp4" type="video/mp4">
Your browser does not support the <code>video</code> element.
</video>
</section>
</article>
</div> <!-- #main -->
</div> <!-- #main-container -->
<div class="footer-container">
<footer class="wrapper">
<h3></h3>
</footer>
</div>
<script>
var inputs = document.getElementById('inputs'),
original = document.getElementById('original'),
working = document.getElementById('working'),
video = document.createElement('video');
working.addEventListener('change', function() {
console.log('File Selectd');
var reader = new FileReader();
reader.addEventListener('loadend', function() {
console.log('File has been loaded');
var bytes = new Uint8Array(reader.result),
ms = new window.MediaSource(),
video = document.querySelector('video');
video.addEventListener("error", function onError(err) {
console.dir("error", err, video.error);
});
video.pause();
video.src = window.URL.createObjectURL(ms);
var onMediaSourceOpen = function() {
console.log('on media open');
ms.removeEventListener('sourceopen', onMediaSourceOpen);
var videoBuffer = ms.addSourceBuffer('video/mp4;codecs="avc1.4D400D"');
videoBuffer.appendBuffer(bytes);
var audioBuffer = ms.addSourceBuffer('audio/mp4;codecs=mp4a.40.2');
};
ms.addEventListener('sourceopen', onMediaSourceOpen);
console.log('appended bytes', bytes);
});
reader.readAsArrayBuffer(this.files[0]);
});
</script>
</body>
</html>
[
{
"type": "ftyp"
},
{
"type": "free"
},
{
"boxes": [
{
"type": "mvhd"
},
{
"boxes": [
{
"type": "mehd"
},
{
"type": "trex"
}
],
"type": "mvex"
},
{
"boxes": [
{
"type": "tkhd"
},
{
"boxes": [
{
"type": "mdhd"
},
{
"type": "hdlr"
},
{
"boxes": [
{
"type": "vmhd"
},
{
"boxes": [
{
"dataReferences": [
{
"type": "url "
}
],
"type": "dref"
}
],
"type": "dinf"
},
{
"boxes": [
{
"sampleDescriptions": [
{
"config": [
{
"type": "avcC"
},
{
"type": "btrt"
}
],
"type": "avc1"
}
],
"type": "stsd"
},
{
"type": "stts"
},
{
"type": "stsc"
},
{
"type": "stsz"
},
{
"type": "stco"
}
],
"type": "stbl"
}
],
"type": "minf"
}
],
"type": "mdia"
}
],
"type": "trak"
}
],
"type": "moov"
}
]
\ No newline at end of file
This diff could not be displayed because it is too large.
......@@ -19,13 +19,6 @@
<script src="../src/videojs-hls.js"></script>
<script src="../src/xhr.js"></script>
<script src="../src/stream.js"></script>
<script src="../src/flv-tag.js"></script>
<script src="../src/exp-golomb.js"></script>
<script src="../src/h264-extradata.js"></script>
<script src="../src/h264-stream.js"></script>
<script src="../src/aac-stream.js"></script>
<script src="../src/metadata-stream.js"></script>
<script src="../src/segment-parser.js"></script>
<!-- M3U8 -->
<script src="../src/m3u8/m3u8-parser.js"></script>
......@@ -42,11 +35,6 @@
<script src="tsSegment-bc.js"></script>
<script src="../src/bin-utils.js"></script>
<!-- mp4 utilities -->
<script src="../src/mp4-generator.js"></script>
<script src="../src/transmuxer.js"></script>
<script src="muxer/js/mp4-inspector.js"></script>
<!-- Test cases -->
<script>
module('environment');
......@@ -56,18 +44,10 @@
});
</script>
<script src="videojs-hls_test.js"></script>
<script src="segment-parser.js"></script>
<script src="h264-stream_test.js"></script>
<script src="exp-golomb_test.js"></script>
<script src="flv-tag_test.js"></script>
<script src="metadata-stream_test.js"></script>
<script src="m3u8_test.js"></script>
<script src="playlist_test.js"></script>
<script src="playlist-loader_test.js"></script>
<script src="decrypter_test.js"></script>
<script src="transmuxer_test.js"></script>
<script src="mp4-inspector_test.js"></script>
<script src="mp4-generator_test.js"></script>
</head>
<body>
<div id="qunit"></div>
......