jmuxer 2.0.1 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -14
- package/dist/jmuxer.js +314 -158
- package/dist/jmuxer.min.js +1 -1
- package/package.json +9 -8
- package/rollup.config.js +2 -1
- package/src/controller/buffer.js +24 -20
- package/src/controller/remux.js +5 -2
- package/src/jmuxer.js +116 -50
- package/src/parsers/h264.js +33 -8
- package/src/remuxer/aac.js +3 -3
- package/src/remuxer/h264.js +5 -3
- package/src/util/exp-golomb.js +6 -0
package/dist/jmuxer.js
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.JMuxer = factory());
|
|
5
|
-
}(this, (function () { 'use strict';
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('stream')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['stream'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.JMuxer = factory(global.stream));
|
|
5
|
+
}(this, (function (stream) { 'use strict';
|
|
6
|
+
|
|
7
|
+
function _typeof(obj) {
|
|
8
|
+
"@babel/helpers - typeof";
|
|
9
|
+
|
|
10
|
+
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
|
11
|
+
_typeof = function (obj) {
|
|
12
|
+
return typeof obj;
|
|
13
|
+
};
|
|
14
|
+
} else {
|
|
15
|
+
_typeof = function (obj) {
|
|
16
|
+
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return _typeof(obj);
|
|
21
|
+
}
|
|
6
22
|
|
|
7
23
|
function _classCallCheck(instance, Constructor) {
|
|
8
24
|
if (!(instance instanceof Constructor)) {
|
|
@@ -120,6 +136,41 @@
|
|
|
120
136
|
};
|
|
121
137
|
}
|
|
122
138
|
|
|
139
|
+
function _slicedToArray(arr, i) {
|
|
140
|
+
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function _arrayWithHoles(arr) {
|
|
144
|
+
if (Array.isArray(arr)) return arr;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function _iterableToArrayLimit(arr, i) {
|
|
148
|
+
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
|
|
149
|
+
var _arr = [];
|
|
150
|
+
var _n = true;
|
|
151
|
+
var _d = false;
|
|
152
|
+
var _e = undefined;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
|
|
156
|
+
_arr.push(_s.value);
|
|
157
|
+
|
|
158
|
+
if (i && _arr.length === i) break;
|
|
159
|
+
}
|
|
160
|
+
} catch (err) {
|
|
161
|
+
_d = true;
|
|
162
|
+
_e = err;
|
|
163
|
+
} finally {
|
|
164
|
+
try {
|
|
165
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
166
|
+
} finally {
|
|
167
|
+
if (_d) throw _e;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return _arr;
|
|
172
|
+
}
|
|
173
|
+
|
|
123
174
|
function _unsupportedIterableToArray(o, minLen) {
|
|
124
175
|
if (!o) return;
|
|
125
176
|
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
@@ -137,6 +188,10 @@
|
|
|
137
188
|
return arr2;
|
|
138
189
|
}
|
|
139
190
|
|
|
191
|
+
function _nonIterableRest() {
|
|
192
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
193
|
+
}
|
|
194
|
+
|
|
140
195
|
function _createForOfIteratorHelper(o, allowArrayLike) {
|
|
141
196
|
var it;
|
|
142
197
|
|
|
@@ -301,7 +356,7 @@
|
|
|
301
356
|
}, {
|
|
302
357
|
key: "isKeyframe",
|
|
303
358
|
value: function isKeyframe() {
|
|
304
|
-
return this.ntype === NALU.IDR
|
|
359
|
+
return this.ntype === NALU.IDR;
|
|
305
360
|
}
|
|
306
361
|
}, {
|
|
307
362
|
key: "getPayload",
|
|
@@ -332,6 +387,30 @@
|
|
|
332
387
|
return NALU;
|
|
333
388
|
}();
|
|
334
389
|
|
|
390
|
+
function appendByteArray(buffer1, buffer2) {
|
|
391
|
+
var tmp = new Uint8Array((buffer1.byteLength | 0) + (buffer2.byteLength | 0));
|
|
392
|
+
tmp.set(buffer1, 0);
|
|
393
|
+
tmp.set(buffer2, buffer1.byteLength | 0);
|
|
394
|
+
return tmp;
|
|
395
|
+
}
|
|
396
|
+
function secToTime(sec) {
|
|
397
|
+
var seconds,
|
|
398
|
+
hours,
|
|
399
|
+
minutes,
|
|
400
|
+
result = '';
|
|
401
|
+
seconds = Math.floor(sec);
|
|
402
|
+
hours = parseInt(seconds / 3600, 10) % 24;
|
|
403
|
+
minutes = parseInt(seconds / 60, 10) % 60;
|
|
404
|
+
seconds = seconds < 0 ? 0 : seconds % 60;
|
|
405
|
+
|
|
406
|
+
if (hours > 0) {
|
|
407
|
+
result += (hours < 10 ? '0' + hours : hours) + ':';
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
result += (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
|
|
411
|
+
return result;
|
|
412
|
+
}
|
|
413
|
+
|
|
335
414
|
/**
|
|
336
415
|
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
|
|
337
416
|
*/
|
|
@@ -478,7 +557,8 @@
|
|
|
478
557
|
value,
|
|
479
558
|
state = 0,
|
|
480
559
|
result = [],
|
|
481
|
-
|
|
560
|
+
left,
|
|
561
|
+
lastIndex = 0;
|
|
482
562
|
|
|
483
563
|
while (i < length) {
|
|
484
564
|
value = buffer[i++]; // finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
|
|
@@ -505,7 +585,7 @@
|
|
|
505
585
|
if (value === 0) {
|
|
506
586
|
state = 3;
|
|
507
587
|
} else if (value === 1 && i < length) {
|
|
508
|
-
if (lastIndex) {
|
|
588
|
+
if (lastIndex != i - state - 1) {
|
|
509
589
|
result.push(buffer.subarray(lastIndex, i - state - 1));
|
|
510
590
|
}
|
|
511
591
|
|
|
@@ -519,11 +599,11 @@
|
|
|
519
599
|
}
|
|
520
600
|
}
|
|
521
601
|
|
|
522
|
-
if (lastIndex) {
|
|
523
|
-
|
|
602
|
+
if (lastIndex < length) {
|
|
603
|
+
left = buffer.subarray(lastIndex, length);
|
|
524
604
|
}
|
|
525
605
|
|
|
526
|
-
return result;
|
|
606
|
+
return [result, left];
|
|
527
607
|
}
|
|
528
608
|
/**
|
|
529
609
|
* Advance the ExpGolomb decoder past a scaling list. The scaling
|
|
@@ -1536,23 +1616,13 @@
|
|
|
1536
1616
|
|
|
1537
1617
|
var track_id = 1;
|
|
1538
1618
|
var BaseRemuxer = /*#__PURE__*/function () {
|
|
1539
|
-
_createClass(BaseRemuxer, null, [{
|
|
1540
|
-
key: "getTrackID",
|
|
1541
|
-
value: function getTrackID() {
|
|
1542
|
-
return track_id++;
|
|
1543
|
-
}
|
|
1544
|
-
}]);
|
|
1545
|
-
|
|
1546
1619
|
function BaseRemuxer() {
|
|
1547
1620
|
_classCallCheck(this, BaseRemuxer);
|
|
1548
|
-
|
|
1549
|
-
this.seq = 1;
|
|
1550
1621
|
}
|
|
1551
1622
|
|
|
1552
1623
|
_createClass(BaseRemuxer, [{
|
|
1553
1624
|
key: "flush",
|
|
1554
1625
|
value: function flush() {
|
|
1555
|
-
this.seq++;
|
|
1556
1626
|
this.mp4track.len = 0;
|
|
1557
1627
|
this.mp4track.samples = [];
|
|
1558
1628
|
}
|
|
@@ -1562,6 +1632,11 @@
|
|
|
1562
1632
|
if (!this.readyToDecode || !this.samples.length) return null;
|
|
1563
1633
|
return true;
|
|
1564
1634
|
}
|
|
1635
|
+
}], [{
|
|
1636
|
+
key: "getTrackID",
|
|
1637
|
+
value: function getTrackID() {
|
|
1638
|
+
return track_id++;
|
|
1639
|
+
}
|
|
1565
1640
|
}]);
|
|
1566
1641
|
|
|
1567
1642
|
return BaseRemuxer;
|
|
@@ -1572,7 +1647,7 @@
|
|
|
1572
1647
|
|
|
1573
1648
|
var _super = _createSuper(AACRemuxer);
|
|
1574
1649
|
|
|
1575
|
-
function AACRemuxer() {
|
|
1650
|
+
function AACRemuxer(timescale) {
|
|
1576
1651
|
var _this;
|
|
1577
1652
|
|
|
1578
1653
|
_classCallCheck(this, AACRemuxer);
|
|
@@ -1581,15 +1656,14 @@
|
|
|
1581
1656
|
_this.readyToDecode = false;
|
|
1582
1657
|
_this.nextDts = 0;
|
|
1583
1658
|
_this.dts = 0;
|
|
1584
|
-
_this.timescale = 1000;
|
|
1585
1659
|
_this.mp4track = {
|
|
1586
1660
|
id: BaseRemuxer.getTrackID(),
|
|
1587
1661
|
type: 'audio',
|
|
1588
1662
|
channelCount: 0,
|
|
1589
1663
|
len: 0,
|
|
1590
1664
|
fragmented: true,
|
|
1591
|
-
timescale:
|
|
1592
|
-
duration:
|
|
1665
|
+
timescale: timescale,
|
|
1666
|
+
duration: timescale,
|
|
1593
1667
|
samples: [],
|
|
1594
1668
|
config: '',
|
|
1595
1669
|
codec: ''
|
|
@@ -1607,6 +1681,8 @@
|
|
|
1607
1681
|
this.mp4track.channelCount = '';
|
|
1608
1682
|
this.mp4track.config = '';
|
|
1609
1683
|
this.mp4track.timescale = this.timescale;
|
|
1684
|
+
this.nextDts = 0;
|
|
1685
|
+
this.dts = 0;
|
|
1610
1686
|
}
|
|
1611
1687
|
}, {
|
|
1612
1688
|
key: "remux",
|
|
@@ -1684,7 +1760,7 @@
|
|
|
1684
1760
|
|
|
1685
1761
|
var _super = _createSuper(H264Remuxer);
|
|
1686
1762
|
|
|
1687
|
-
function H264Remuxer() {
|
|
1763
|
+
function H264Remuxer(timescale) {
|
|
1688
1764
|
var _this;
|
|
1689
1765
|
|
|
1690
1766
|
_classCallCheck(this, H264Remuxer);
|
|
@@ -1693,7 +1769,6 @@
|
|
|
1693
1769
|
_this.readyToDecode = false;
|
|
1694
1770
|
_this.nextDts = 0;
|
|
1695
1771
|
_this.dts = 0;
|
|
1696
|
-
_this.timescale = 1000;
|
|
1697
1772
|
_this.mp4track = {
|
|
1698
1773
|
id: BaseRemuxer.getTrackID(),
|
|
1699
1774
|
type: 'video',
|
|
@@ -1703,8 +1778,8 @@
|
|
|
1703
1778
|
pps: '',
|
|
1704
1779
|
width: 0,
|
|
1705
1780
|
height: 0,
|
|
1706
|
-
timescale:
|
|
1707
|
-
duration:
|
|
1781
|
+
timescale: timescale,
|
|
1782
|
+
duration: timescale,
|
|
1708
1783
|
samples: []
|
|
1709
1784
|
};
|
|
1710
1785
|
_this.samples = [];
|
|
@@ -1718,6 +1793,8 @@
|
|
|
1718
1793
|
this.readyToDecode = false;
|
|
1719
1794
|
this.mp4track.sps = '';
|
|
1720
1795
|
this.mp4track.pps = '';
|
|
1796
|
+
this.nextDts = 0;
|
|
1797
|
+
this.dts = 0;
|
|
1721
1798
|
}
|
|
1722
1799
|
}, {
|
|
1723
1800
|
key: "remux",
|
|
@@ -1830,36 +1907,12 @@
|
|
|
1830
1907
|
return H264Remuxer;
|
|
1831
1908
|
}(BaseRemuxer);
|
|
1832
1909
|
|
|
1833
|
-
function appendByteArray(buffer1, buffer2) {
|
|
1834
|
-
var tmp = new Uint8Array((buffer1.byteLength | 0) + (buffer2.byteLength | 0));
|
|
1835
|
-
tmp.set(buffer1, 0);
|
|
1836
|
-
tmp.set(buffer2, buffer1.byteLength | 0);
|
|
1837
|
-
return tmp;
|
|
1838
|
-
}
|
|
1839
|
-
function secToTime(sec) {
|
|
1840
|
-
var seconds,
|
|
1841
|
-
hours,
|
|
1842
|
-
minutes,
|
|
1843
|
-
result = '';
|
|
1844
|
-
seconds = Math.floor(sec);
|
|
1845
|
-
hours = parseInt(seconds / 3600, 10) % 24;
|
|
1846
|
-
minutes = parseInt(seconds / 60, 10) % 60;
|
|
1847
|
-
seconds = seconds < 0 ? 0 : seconds % 60;
|
|
1848
|
-
|
|
1849
|
-
if (hours > 0) {
|
|
1850
|
-
result += (hours < 10 ? '0' + hours : hours) + ':';
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
|
-
result += (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
|
|
1854
|
-
return result;
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
1910
|
var RemuxController = /*#__PURE__*/function (_Event) {
|
|
1858
1911
|
_inherits(RemuxController, _Event);
|
|
1859
1912
|
|
|
1860
1913
|
var _super = _createSuper(RemuxController);
|
|
1861
1914
|
|
|
1862
|
-
function RemuxController(
|
|
1915
|
+
function RemuxController(env) {
|
|
1863
1916
|
var _this;
|
|
1864
1917
|
|
|
1865
1918
|
_classCallCheck(this, RemuxController);
|
|
@@ -1868,7 +1921,10 @@
|
|
|
1868
1921
|
_this.initialized = false;
|
|
1869
1922
|
_this.trackTypes = [];
|
|
1870
1923
|
_this.tracks = {};
|
|
1871
|
-
_this.
|
|
1924
|
+
_this.seq = 1;
|
|
1925
|
+
_this.env = env;
|
|
1926
|
+
_this.timescale = 1000;
|
|
1927
|
+
_this.mediaDuration = 0;
|
|
1872
1928
|
return _this;
|
|
1873
1929
|
}
|
|
1874
1930
|
|
|
@@ -1876,12 +1932,12 @@
|
|
|
1876
1932
|
key: "addTrack",
|
|
1877
1933
|
value: function addTrack(type) {
|
|
1878
1934
|
if (type === 'video' || type === 'both') {
|
|
1879
|
-
this.tracks.video = new H264Remuxer();
|
|
1935
|
+
this.tracks.video = new H264Remuxer(this.timescale);
|
|
1880
1936
|
this.trackTypes.push('video');
|
|
1881
1937
|
}
|
|
1882
1938
|
|
|
1883
1939
|
if (type === 'audio' || type === 'both') {
|
|
1884
|
-
this.tracks.audio = new AACRemuxer();
|
|
1940
|
+
this.tracks.audio = new AACRemuxer(this.timescale);
|
|
1885
1941
|
this.trackTypes.push('audio');
|
|
1886
1942
|
}
|
|
1887
1943
|
}
|
|
@@ -1916,64 +1972,82 @@
|
|
|
1916
1972
|
if (!this.initialized) {
|
|
1917
1973
|
if (this.isReady()) {
|
|
1918
1974
|
this.dispatch('ready');
|
|
1919
|
-
|
|
1920
|
-
var _iterator2 = _createForOfIteratorHelper(this.trackTypes),
|
|
1921
|
-
_step2;
|
|
1922
|
-
|
|
1923
|
-
try {
|
|
1924
|
-
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
1925
|
-
var type = _step2.value;
|
|
1926
|
-
var track = this.tracks[type];
|
|
1927
|
-
var data = {
|
|
1928
|
-
type: type,
|
|
1929
|
-
payload: MP4.initSegment([track.mp4track], this.mediaDuration, track.mp4track.timescale)
|
|
1930
|
-
};
|
|
1931
|
-
this.dispatch('buffer', data);
|
|
1932
|
-
}
|
|
1933
|
-
} catch (err) {
|
|
1934
|
-
_iterator2.e(err);
|
|
1935
|
-
} finally {
|
|
1936
|
-
_iterator2.f();
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
log('Initial segment generated.');
|
|
1975
|
+
this.initSegment();
|
|
1940
1976
|
this.initialized = true;
|
|
1941
1977
|
this.flush();
|
|
1942
1978
|
}
|
|
1943
1979
|
} else {
|
|
1944
|
-
var
|
|
1945
|
-
|
|
1980
|
+
var _iterator2 = _createForOfIteratorHelper(this.trackTypes),
|
|
1981
|
+
_step2;
|
|
1946
1982
|
|
|
1947
1983
|
try {
|
|
1948
|
-
for (
|
|
1949
|
-
var
|
|
1950
|
-
var
|
|
1951
|
-
|
|
1952
|
-
var pay = _track.getPayload();
|
|
1984
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
1985
|
+
var type = _step2.value;
|
|
1986
|
+
var track = this.tracks[type];
|
|
1987
|
+
var pay = track.getPayload();
|
|
1953
1988
|
|
|
1954
1989
|
if (pay && pay.byteLength) {
|
|
1955
|
-
var moof = MP4.moof(
|
|
1990
|
+
var moof = MP4.moof(this.seq, track.dts, track.mp4track);
|
|
1956
1991
|
var mdat = MP4.mdat(pay);
|
|
1957
1992
|
var payload = appendByteArray(moof, mdat);
|
|
1958
|
-
var
|
|
1959
|
-
type:
|
|
1993
|
+
var data = {
|
|
1994
|
+
type: type,
|
|
1960
1995
|
payload: payload,
|
|
1961
|
-
dts:
|
|
1996
|
+
dts: track.dts
|
|
1962
1997
|
};
|
|
1963
|
-
this.dispatch('buffer',
|
|
1964
|
-
var duration = secToTime(
|
|
1965
|
-
log("put segment (".concat(
|
|
1966
|
-
|
|
1967
|
-
|
|
1998
|
+
this.dispatch('buffer', data);
|
|
1999
|
+
var duration = secToTime(track.dts / this.timescale);
|
|
2000
|
+
log("put segment (".concat(type, "): dts: ").concat(track.dts, " frames: ").concat(track.mp4track.samples.length, " second: ").concat(duration));
|
|
2001
|
+
track.flush();
|
|
2002
|
+
this.seq++;
|
|
1968
2003
|
}
|
|
1969
2004
|
}
|
|
1970
2005
|
} catch (err) {
|
|
1971
|
-
|
|
2006
|
+
_iterator2.e(err);
|
|
1972
2007
|
} finally {
|
|
1973
|
-
|
|
2008
|
+
_iterator2.f();
|
|
1974
2009
|
}
|
|
1975
2010
|
}
|
|
1976
2011
|
}
|
|
2012
|
+
}, {
|
|
2013
|
+
key: "initSegment",
|
|
2014
|
+
value: function initSegment() {
|
|
2015
|
+
var tracks = [];
|
|
2016
|
+
|
|
2017
|
+
var _iterator3 = _createForOfIteratorHelper(this.trackTypes),
|
|
2018
|
+
_step3;
|
|
2019
|
+
|
|
2020
|
+
try {
|
|
2021
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
2022
|
+
var type = _step3.value;
|
|
2023
|
+
var track = this.tracks[type];
|
|
2024
|
+
|
|
2025
|
+
if (this.env == 'browser') {
|
|
2026
|
+
var _data = {
|
|
2027
|
+
type: type,
|
|
2028
|
+
payload: MP4.initSegment([track.mp4track], this.mediaDuration, this.timescale)
|
|
2029
|
+
};
|
|
2030
|
+
this.dispatch('buffer', _data);
|
|
2031
|
+
} else {
|
|
2032
|
+
tracks.push(track.mp4track);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
} catch (err) {
|
|
2036
|
+
_iterator3.e(err);
|
|
2037
|
+
} finally {
|
|
2038
|
+
_iterator3.f();
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
if (this.env == 'node') {
|
|
2042
|
+
var data = {
|
|
2043
|
+
type: 'all',
|
|
2044
|
+
payload: MP4.initSegment(tracks, this.mediaDuration, this.timescale)
|
|
2045
|
+
};
|
|
2046
|
+
this.dispatch('buffer', data);
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
log('Initial segment generated.');
|
|
2050
|
+
}
|
|
1977
2051
|
}, {
|
|
1978
2052
|
key: "isReady",
|
|
1979
2053
|
value: function isReady() {
|
|
@@ -2155,98 +2229,140 @@
|
|
|
2155
2229
|
return BufferController;
|
|
2156
2230
|
}(Event);
|
|
2157
2231
|
|
|
2158
|
-
|
|
2232
|
+
var JMuxer = /*#__PURE__*/function (_Event) {
|
|
2233
|
+
_inherits(JMuxer, _Event);
|
|
2159
2234
|
|
|
2160
|
-
|
|
2161
|
-
_inherits(JMuxmer, _Event);
|
|
2235
|
+
var _super = _createSuper(JMuxer);
|
|
2162
2236
|
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
_createClass(JMuxmer, null, [{
|
|
2237
|
+
_createClass(JMuxer, null, [{
|
|
2166
2238
|
key: "isSupported",
|
|
2167
2239
|
value: function isSupported(codec) {
|
|
2168
2240
|
return window.MediaSource && window.MediaSource.isTypeSupported(codec);
|
|
2169
2241
|
}
|
|
2170
2242
|
}]);
|
|
2171
2243
|
|
|
2172
|
-
function
|
|
2244
|
+
function JMuxer(options) {
|
|
2173
2245
|
var _this;
|
|
2174
2246
|
|
|
2175
|
-
_classCallCheck(this,
|
|
2247
|
+
_classCallCheck(this, JMuxer);
|
|
2176
2248
|
|
|
2177
2249
|
_this = _super.call(this, 'jmuxer');
|
|
2178
|
-
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
|
|
2179
2250
|
var defaults = {
|
|
2180
2251
|
node: '',
|
|
2181
2252
|
mode: 'both',
|
|
2182
2253
|
// both, audio, video
|
|
2183
2254
|
flushingTime: 1500,
|
|
2184
2255
|
clearBuffer: true,
|
|
2185
|
-
onReady: null,
|
|
2186
|
-
// function called when MSE is ready to accept frames
|
|
2187
2256
|
fps: 30,
|
|
2188
|
-
debug: false
|
|
2257
|
+
debug: false,
|
|
2258
|
+
onReady: function onReady() {},
|
|
2259
|
+
// function called when MSE is ready to accept frames
|
|
2260
|
+
onError: function onError() {} // function called when jmuxer encounters any buffer related error
|
|
2261
|
+
|
|
2189
2262
|
};
|
|
2190
2263
|
_this.options = Object.assign({}, defaults, options);
|
|
2264
|
+
_this.env = (typeof process === "undefined" ? "undefined" : _typeof(process)) === 'object' && typeof window === 'undefined' ? 'node' : 'browser';
|
|
2191
2265
|
|
|
2192
2266
|
if (_this.options.debug) {
|
|
2193
2267
|
setLogger();
|
|
2194
2268
|
}
|
|
2195
2269
|
|
|
2196
|
-
if (typeof _this.options.node === 'string' && _this.options.node == '') {
|
|
2197
|
-
error('no video element were found to render, provide a valid video element');
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
2270
|
if (!_this.options.fps) {
|
|
2201
2271
|
_this.options.fps = 30;
|
|
2202
2272
|
}
|
|
2203
2273
|
|
|
2204
|
-
_this.frameDuration = 1000 / _this.options.fps | 0;
|
|
2205
|
-
|
|
2206
|
-
_this.node = typeof _this.options.node === 'string' ? document.getElementById(_this.options.node) : _this.options.node;
|
|
2207
|
-
_this.sourceBuffers = {};
|
|
2208
|
-
_this.isMSESupported = !!window.MediaSource;
|
|
2209
|
-
|
|
2210
|
-
if (!_this.isMSESupported) {
|
|
2211
|
-
throw 'Oops! Browser does not support media source extension.';
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
_this.setupMSE();
|
|
2215
|
-
|
|
2216
|
-
_this.remuxController = new RemuxController(_this.options.clearBuffer);
|
|
2274
|
+
_this.frameDuration = 1000 / _this.options.fps | 0;
|
|
2275
|
+
_this.remuxController = new RemuxController(_this.env);
|
|
2217
2276
|
|
|
2218
2277
|
_this.remuxController.addTrack(_this.options.mode);
|
|
2219
2278
|
|
|
2220
|
-
_this.mseReady = false;
|
|
2221
2279
|
_this.lastCleaningTime = Date.now();
|
|
2222
2280
|
_this.kfPosition = [];
|
|
2223
2281
|
_this.kfCounter = 0;
|
|
2282
|
+
_this.pendingUnits = {};
|
|
2283
|
+
_this.remainingData = new Uint8Array();
|
|
2224
2284
|
/* events callback */
|
|
2225
2285
|
|
|
2226
2286
|
_this.remuxController.on('buffer', _this.onBuffer.bind(_assertThisInitialized(_this)));
|
|
2227
2287
|
|
|
2228
|
-
_this.
|
|
2288
|
+
if (_this.env == 'browser') {
|
|
2289
|
+
_this.remuxController.on('ready', _this.createBuffer.bind(_assertThisInitialized(_this)));
|
|
2290
|
+
|
|
2291
|
+
_this.initBrowser();
|
|
2292
|
+
}
|
|
2229
2293
|
|
|
2230
2294
|
_this.startInterval();
|
|
2231
2295
|
|
|
2232
2296
|
return _this;
|
|
2233
2297
|
}
|
|
2234
2298
|
|
|
2235
|
-
_createClass(
|
|
2299
|
+
_createClass(JMuxer, [{
|
|
2300
|
+
key: "initBrowser",
|
|
2301
|
+
value: function initBrowser() {
|
|
2302
|
+
if (typeof this.options.node === 'string' && this.options.node == '') {
|
|
2303
|
+
error('no video element were found to render, provide a valid video element');
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
this.node = typeof this.options.node === 'string' ? document.getElementById(this.options.node) : this.options.node;
|
|
2307
|
+
this.mseReady = false;
|
|
2308
|
+
this.setupMSE();
|
|
2309
|
+
}
|
|
2310
|
+
}, {
|
|
2311
|
+
key: "createStream",
|
|
2312
|
+
value: function createStream() {
|
|
2313
|
+
var feed = this.feed.bind(this);
|
|
2314
|
+
var destroy = this.destroy.bind(this);
|
|
2315
|
+
this.stream = new stream.Duplex({
|
|
2316
|
+
writableObjectMode: true,
|
|
2317
|
+
read: function read(size) {},
|
|
2318
|
+
write: function write(data, encoding, callback) {
|
|
2319
|
+
feed(data);
|
|
2320
|
+
callback();
|
|
2321
|
+
},
|
|
2322
|
+
"final": function final(callback) {
|
|
2323
|
+
destroy();
|
|
2324
|
+
callback();
|
|
2325
|
+
}
|
|
2326
|
+
});
|
|
2327
|
+
return this.stream;
|
|
2328
|
+
}
|
|
2329
|
+
}, {
|
|
2236
2330
|
key: "setupMSE",
|
|
2237
2331
|
value: function setupMSE() {
|
|
2332
|
+
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
|
|
2333
|
+
|
|
2334
|
+
if (!window.MediaSource) {
|
|
2335
|
+
throw 'Oops! Browser does not support media source extension.';
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
this.isMSESupported = !!window.MediaSource;
|
|
2238
2339
|
this.mediaSource = new MediaSource();
|
|
2239
|
-
this.
|
|
2340
|
+
this.url = URL.createObjectURL(this.mediaSource);
|
|
2341
|
+
this.node.src = this.url;
|
|
2342
|
+
this.mseEnded = false;
|
|
2240
2343
|
this.mediaSource.addEventListener('sourceopen', this.onMSEOpen.bind(this));
|
|
2241
2344
|
this.mediaSource.addEventListener('sourceclose', this.onMSEClose.bind(this));
|
|
2242
2345
|
this.mediaSource.addEventListener('webkitsourceopen', this.onMSEOpen.bind(this));
|
|
2243
2346
|
this.mediaSource.addEventListener('webkitsourceclose', this.onMSEClose.bind(this));
|
|
2244
2347
|
}
|
|
2348
|
+
}, {
|
|
2349
|
+
key: "endMSE",
|
|
2350
|
+
value: function endMSE() {
|
|
2351
|
+
if (!this.mseEnded) {
|
|
2352
|
+
try {
|
|
2353
|
+
this.mseEnded = true;
|
|
2354
|
+
this.mediaSource.endOfStream();
|
|
2355
|
+
} catch (e) {
|
|
2356
|
+
error('mediasource is not available to end');
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2245
2360
|
}, {
|
|
2246
2361
|
key: "feed",
|
|
2247
2362
|
value: function feed(data) {
|
|
2248
2363
|
var remux = false,
|
|
2249
2364
|
slices,
|
|
2365
|
+
left,
|
|
2250
2366
|
duration,
|
|
2251
2367
|
chunks = {
|
|
2252
2368
|
video: [],
|
|
@@ -2256,12 +2372,21 @@
|
|
|
2256
2372
|
duration = data.duration ? parseInt(data.duration) : 0;
|
|
2257
2373
|
|
|
2258
2374
|
if (data.video) {
|
|
2259
|
-
|
|
2375
|
+
data.video = appendByteArray(this.remainingData, data.video);
|
|
2376
|
+
|
|
2377
|
+
var _H264Parser$extractNA = H264Parser.extractNALu(data.video);
|
|
2378
|
+
|
|
2379
|
+
var _H264Parser$extractNA2 = _slicedToArray(_H264Parser$extractNA, 2);
|
|
2380
|
+
|
|
2381
|
+
slices = _H264Parser$extractNA2[0];
|
|
2382
|
+
left = _H264Parser$extractNA2[1];
|
|
2260
2383
|
|
|
2261
2384
|
if (slices.length > 0) {
|
|
2262
2385
|
chunks.video = this.getVideoFrames(slices, duration);
|
|
2263
2386
|
remux = true;
|
|
2264
2387
|
}
|
|
2388
|
+
|
|
2389
|
+
this.remainingData = left || new Uint8Array();
|
|
2265
2390
|
}
|
|
2266
2391
|
|
|
2267
2392
|
if (data.audio) {
|
|
@@ -2292,6 +2417,13 @@
|
|
|
2292
2417
|
keyFrame = false,
|
|
2293
2418
|
vcl = false;
|
|
2294
2419
|
|
|
2420
|
+
if (this.pendingUnits.units) {
|
|
2421
|
+
units = this.pendingUnits.units;
|
|
2422
|
+
vcl = this.pendingUnits.vcl;
|
|
2423
|
+
keyFrame = this.pendingUnits.keyFrame;
|
|
2424
|
+
this.pendingUnits = {};
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2295
2427
|
var _iterator = _createForOfIteratorHelper(nalus),
|
|
2296
2428
|
_step;
|
|
2297
2429
|
|
|
@@ -2325,7 +2457,14 @@
|
|
|
2325
2457
|
}
|
|
2326
2458
|
|
|
2327
2459
|
if (units.length) {
|
|
2328
|
-
|
|
2460
|
+
// lets keep indecisive nalus as pending in case of fixed fps
|
|
2461
|
+
if (!duration) {
|
|
2462
|
+
this.pendingUnits = {
|
|
2463
|
+
units: units,
|
|
2464
|
+
keyFrame: keyFrame,
|
|
2465
|
+
vcl: vcl
|
|
2466
|
+
};
|
|
2467
|
+
} else if (vcl) {
|
|
2329
2468
|
frames.push({
|
|
2330
2469
|
units: units,
|
|
2331
2470
|
keyFrame: keyFrame
|
|
@@ -2395,16 +2534,10 @@
|
|
|
2395
2534
|
value: function destroy() {
|
|
2396
2535
|
this.stopInterval();
|
|
2397
2536
|
|
|
2398
|
-
if (this.
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
}
|
|
2403
|
-
} catch (e) {
|
|
2404
|
-
error("mediasource is not available to end ".concat(e.message));
|
|
2405
|
-
}
|
|
2406
|
-
|
|
2407
|
-
this.mediaSource = null;
|
|
2537
|
+
if (this.stream) {
|
|
2538
|
+
this.remuxController.flush();
|
|
2539
|
+
this.stream.push(null);
|
|
2540
|
+
this.stream = null;
|
|
2408
2541
|
}
|
|
2409
2542
|
|
|
2410
2543
|
if (this.remuxController) {
|
|
@@ -2418,11 +2551,37 @@
|
|
|
2418
2551
|
}
|
|
2419
2552
|
|
|
2420
2553
|
this.bufferControllers = null;
|
|
2554
|
+
this.endMSE();
|
|
2421
2555
|
}
|
|
2422
2556
|
|
|
2423
2557
|
this.node = false;
|
|
2424
2558
|
this.mseReady = false;
|
|
2425
2559
|
this.videoStarted = false;
|
|
2560
|
+
this.mediaSource = null;
|
|
2561
|
+
}
|
|
2562
|
+
}, {
|
|
2563
|
+
key: "reset",
|
|
2564
|
+
value: function reset() {
|
|
2565
|
+
this.node.pause();
|
|
2566
|
+
|
|
2567
|
+
if (this.remuxController) {
|
|
2568
|
+
this.remuxController.reset();
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
if (this.bufferControllers) {
|
|
2572
|
+
for (var type in this.bufferControllers) {
|
|
2573
|
+
this.bufferControllers[type].destroy();
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
this.bufferControllers = null;
|
|
2577
|
+
this.endMSE();
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
if (this.env == 'browser') {
|
|
2581
|
+
this.initBrowser();
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
log('JMuxer was reset');
|
|
2426
2585
|
}
|
|
2427
2586
|
}, {
|
|
2428
2587
|
key: "createBuffer",
|
|
@@ -2433,14 +2592,13 @@
|
|
|
2433
2592
|
for (var type in this.remuxController.tracks) {
|
|
2434
2593
|
var track = this.remuxController.tracks[type];
|
|
2435
2594
|
|
|
2436
|
-
if (!
|
|
2595
|
+
if (!JMuxer.isSupported("".concat(type, "/mp4; codecs=\"").concat(track.mp4track.codec, "\""))) {
|
|
2437
2596
|
error('Browser does not support codec');
|
|
2438
2597
|
return false;
|
|
2439
2598
|
}
|
|
2440
2599
|
|
|
2441
2600
|
var sb = this.mediaSource.addSourceBuffer("".concat(type, "/mp4; codecs=\"").concat(track.mp4track.codec, "\""));
|
|
2442
2601
|
this.bufferControllers[type] = new BufferController(sb, type);
|
|
2443
|
-
this.sourceBuffers[type] = sb;
|
|
2444
2602
|
this.bufferControllers[type].on('error', this.onBufferError.bind(this));
|
|
2445
2603
|
}
|
|
2446
2604
|
}
|
|
@@ -2512,8 +2670,12 @@
|
|
|
2512
2670
|
}, {
|
|
2513
2671
|
key: "onBuffer",
|
|
2514
2672
|
value: function onBuffer(data) {
|
|
2515
|
-
if (this.
|
|
2516
|
-
this.bufferControllers[data.type]
|
|
2673
|
+
if (this.env == 'browser') {
|
|
2674
|
+
if (this.bufferControllers && this.bufferControllers[data.type]) {
|
|
2675
|
+
this.bufferControllers[data.type].feed(data.payload);
|
|
2676
|
+
}
|
|
2677
|
+
} else if (this.stream) {
|
|
2678
|
+
this.stream.push(data.payload);
|
|
2517
2679
|
}
|
|
2518
2680
|
}
|
|
2519
2681
|
/* Events on MSE */
|
|
@@ -2524,10 +2686,10 @@
|
|
|
2524
2686
|
this.mseReady = true;
|
|
2525
2687
|
|
|
2526
2688
|
if (typeof this.options.onReady === 'function') {
|
|
2527
|
-
this.options.onReady();
|
|
2528
|
-
this.options.onReady = null;
|
|
2689
|
+
this.options.onReady.call(null);
|
|
2529
2690
|
}
|
|
2530
2691
|
|
|
2692
|
+
URL.revokeObjectURL(this.url);
|
|
2531
2693
|
this.createBuffer();
|
|
2532
2694
|
}
|
|
2533
2695
|
}, {
|
|
@@ -2542,25 +2704,19 @@
|
|
|
2542
2704
|
if (data.name == 'QuotaExceeded') {
|
|
2543
2705
|
this.bufferControllers[data.type].initCleanup(this.node.currentTime);
|
|
2544
2706
|
return;
|
|
2707
|
+
} else {
|
|
2708
|
+
this.endMSE();
|
|
2545
2709
|
}
|
|
2546
2710
|
|
|
2547
|
-
if (this.
|
|
2548
|
-
this.
|
|
2549
|
-
}
|
|
2550
|
-
|
|
2551
|
-
if (this.mediaSource.sourceBuffers.length == 0) {
|
|
2552
|
-
try {
|
|
2553
|
-
this.mediaSource.endOfStream();
|
|
2554
|
-
} catch (e) {
|
|
2555
|
-
error('mediasource is not available to end');
|
|
2556
|
-
}
|
|
2711
|
+
if (typeof this.options.onError === 'function') {
|
|
2712
|
+
this.options.onError.call(null, data);
|
|
2557
2713
|
}
|
|
2558
2714
|
}
|
|
2559
2715
|
}]);
|
|
2560
2716
|
|
|
2561
|
-
return
|
|
2717
|
+
return JMuxer;
|
|
2562
2718
|
}(Event);
|
|
2563
2719
|
|
|
2564
|
-
return
|
|
2720
|
+
return JMuxer;
|
|
2565
2721
|
|
|
2566
2722
|
})));
|