xgplayer-mp4 1.2.3 → 2.0.1
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/browser/index.js +1 -1
- package/browser/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
- package/src/constants.js +14 -0
- package/src/fmp4/buffer.js +6 -0
- package/src/fmp4/mp4.js +181 -84
- package/src/gap_jump.js +151 -0
- package/src/index.js +550 -315
- package/src/media/mse.js +77 -22
- package/src/media/task.js +68 -17
- package/src/mp4.js +284 -265
- package/src/parse/box/MP4DecConfigDescrTag.js +3 -0
- package/src/parse/box/MP4DecSpecificDescrTag.js +4 -0
- package/src/parse/box/avcC.js +4 -1
- package/src/parse/box/elst.js +0 -13
- package/src/parse/box/esds.js +9 -4
- package/src/parse/box/frma.js +10 -0
- package/src/parse/box/golomb.js +88 -0
- package/src/parse/box/hvc1.js +30 -0
- package/src/parse/box/hvcC.js +55 -0
- package/src/parse/box/pasp.js +2 -3
- package/src/parse/box/schm.js +18 -0
- package/src/parse/box/sps.js +286 -0
- package/src/parse/box/stsz.js +6 -1
- package/src/parse/box/tfhd.js +43 -0
- package/src/parse/box.js +5 -1
- package/src/parse/stream.js +4 -0
- package/src/util/getHeaders.js +18 -0
- package/src/util/index.js +138 -20
- package/src/util/intervalTimer.js +36 -0
- package/src/util/isSupport.js +20 -0
- package/src/util/proxyPromise.js +27 -0
- package/src/util/timer.js +46 -0
- package/src/util/xhr.js +46 -0
- package/version.json +1 -1
- package/webpack.config.js +45 -39
- package/src/util/download.js +0 -13
- package/src/write/box/MP4DecConfigDescrTag.js +0 -21
- package/src/write/box/MP4DecSpecificDescrTag.js +0 -19
- package/src/write/box/MP4ESDescrTag.js +0 -19
- package/src/write/box/SLConfigDescriptor.js +0 -16
- package/src/write/box/avc1.js +0 -33
- package/src/write/box/avcC.js +0 -32
- package/src/write/box/btrt.js +0 -14
- package/src/write/box/co64.js +0 -17
- package/src/write/box/ctts.js +0 -19
- package/src/write/box/dref.js +0 -25
- package/src/write/box/elst.js +0 -27
- package/src/write/box/esds.js +0 -11
- package/src/write/box/ftyp.js +0 -17
- package/src/write/box/hdlr.js +0 -15
- package/src/write/box/iods.js +0 -14
- package/src/write/box/mdat.js +0 -5
- package/src/write/box/mdhd.js +0 -27
- package/src/write/box/mehd.js +0 -12
- package/src/write/box/mfhd.js +0 -12
- package/src/write/box/mfro.js +0 -14
- package/src/write/box/mp4a.js +0 -26
- package/src/write/box/mvhd.js +0 -34
- package/src/write/box/pasp.js +0 -10
- package/src/write/box/smhd.js +0 -17
- package/src/write/box/stco.js +0 -17
- package/src/write/box/stsc.js +0 -19
- package/src/write/box/stsd.js +0 -23
- package/src/write/box/stss.js +0 -17
- package/src/write/box/stsz.js +0 -18
- package/src/write/box/stts.js +0 -18
- package/src/write/box/tfdt.js +0 -16
- package/src/write/box/tfhd.js +0 -18
- package/src/write/box/tfra.js +0 -29
- package/src/write/box/tkhd.js +0 -44
- package/src/write/box/trex.js +0 -16
- package/src/write/box/trun.js +0 -40
- package/src/write/box/udta.js +0 -5
- package/src/write/box/url.js +0 -16
- package/src/write/box/vmhd.js +0 -19
- package/src/write/box/wide.js +0 -12
- package/src/write/box.js +0 -37
- package/src/write/buffer.js +0 -12
- package/src/write/index.js +0 -33
- package/src/write/stream.js +0 -99
- package/webpack.config.test.js +0 -8
package/src/mp4.js
CHANGED
|
@@ -1,53 +1,67 @@
|
|
|
1
1
|
import EventEmitter from 'event-emitter'
|
|
2
|
-
import Merge from 'deepmerge'
|
|
3
2
|
import Parser from './parse'
|
|
4
3
|
import Buffer from './fmp4/buffer'
|
|
5
4
|
import FMP4 from './fmp4/mp4'
|
|
6
5
|
import Task from './media/task'
|
|
7
6
|
import util from './util'
|
|
8
7
|
import Errors from './error'
|
|
8
|
+
import {CUSTOM_EVENTS, TASK_ERROR} from './constants'
|
|
9
|
+
import Player from 'xgplayer'
|
|
9
10
|
|
|
10
11
|
class MP4 {
|
|
11
12
|
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
constructor (url,
|
|
13
|
+
* [constructor 构造函数]
|
|
14
|
+
* @param {String} url [视频地址]
|
|
15
|
+
* @param {Number} [chunk_size=Math.pow(25, 4)] [请求的数据块大小,对于长视频设置的较大些可以避免二次请求]
|
|
16
|
+
*/
|
|
17
|
+
constructor (url, xhrSetup, player, chunkSize = Math.pow(25, 4), ext = {}) {
|
|
17
18
|
EventEmitter(this)
|
|
18
19
|
this.url = url
|
|
19
|
-
this.
|
|
20
|
-
FMP4.videoOnly = this.videoOnly = options.videoOnly || false
|
|
20
|
+
this.xhrSetup = xhrSetup
|
|
21
21
|
this.CHUNK_SIZE = chunkSize
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.cache = new Buffer()
|
|
25
|
-
this.bufferCache = new Set()
|
|
22
|
+
this.player = player
|
|
23
|
+
this.ext = ext
|
|
26
24
|
this.timeRage = []
|
|
27
25
|
this.canDownload = true
|
|
26
|
+
this.init()
|
|
27
|
+
Player.util.once(this, 'moovReady', this.moovParse.bind(this))
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
getData (start = 0, end = start + this.CHUNK_SIZE) {
|
|
31
|
+
* [getData 根据字节区间下载二进制数据]
|
|
32
|
+
* @param {Number} [start=0] [起始字节]
|
|
33
|
+
* @param {Number} [end=start + this.CHUNK_SIZE] [截止字节]
|
|
34
|
+
*/
|
|
35
|
+
getData (start = 0, end = start + this.CHUNK_SIZE - 1) {
|
|
36
36
|
let self = this
|
|
37
37
|
return new Promise((resolve, reject) => {
|
|
38
38
|
let task = new Task(this.url, [
|
|
39
39
|
start, end
|
|
40
|
-
], this.
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
], this.xhrSetup, (data)=>{
|
|
41
|
+
if(self.hasDestroyed){
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
resolve(data)
|
|
45
|
+
}, self.ext)
|
|
46
|
+
// Player.util.once(task, 'error', err => {
|
|
47
|
+
// self.emit('error', err)
|
|
48
|
+
// })
|
|
49
|
+
|
|
50
|
+
Player.util.once(task, CUSTOM_EVENTS.MEDIA_EXPIRED, ()=>{
|
|
51
|
+
self.player && self.player.onMediaExpired();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
Player.util.once(task, TASK_ERROR, params => {
|
|
55
|
+
//非403状态或者非200,206状态逻辑
|
|
56
|
+
reject({code: params.code, status: params.status})
|
|
43
57
|
})
|
|
44
58
|
})
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
* [moovParse 解析视频信息]
|
|
63
|
+
* @return {[type]} [description]
|
|
64
|
+
*/
|
|
51
65
|
moovParse () {
|
|
52
66
|
let self = this
|
|
53
67
|
let moov = this.moovBox
|
|
@@ -61,46 +75,69 @@ class MP4 {
|
|
|
61
75
|
audioTimeScale
|
|
62
76
|
let sps,
|
|
63
77
|
pps,
|
|
78
|
+
vps,
|
|
64
79
|
profile,
|
|
65
80
|
width,
|
|
66
81
|
height
|
|
67
82
|
let channelCount,
|
|
68
83
|
sampleRate,
|
|
69
84
|
decoderConfig
|
|
70
|
-
|
|
85
|
+
let hvc1Data, hvcCData
|
|
86
|
+
let pixelRatio = [1, 1]
|
|
87
|
+
if (!Array.isArray(traks)) {
|
|
88
|
+
traks = [traks]
|
|
89
|
+
}
|
|
71
90
|
traks.forEach(trak => {
|
|
72
91
|
let hdlr = util.findBox(trak, 'hdlr')
|
|
73
92
|
let mdhd = util.findBox(trak, 'mdhd')
|
|
74
93
|
if (!hdlr || !mdhd) {
|
|
75
|
-
self.emit('error', new Errors('parse', '', {line:
|
|
94
|
+
self.emit('error', new Errors('parse', '', {line: 101, handle: '[MP4] moovParse', url: self.url}))
|
|
76
95
|
return
|
|
77
96
|
}
|
|
78
|
-
if(hdlr.handleType === 'vide' && self.videoOnly) {
|
|
79
|
-
let elst = util.findBox(trak, 'elst')
|
|
80
|
-
trak.empty_duration = 0
|
|
81
|
-
if (elst.empty_duration) {
|
|
82
|
-
trak.empty_duration = elst.empty_duration * mdhd.timescale / mvhd.timeScale;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
trak.time_offset = elst.start_time - trak.empty_duration;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
97
|
let stsd = util.findBox(trak, 'stsd')
|
|
89
98
|
let codecBox = stsd.subBox[0]
|
|
90
99
|
if (hdlr.handleType === 'vide') {
|
|
91
100
|
let avcC = util.findBox(trak, 'avcC')
|
|
101
|
+
let hvcC
|
|
102
|
+
if(!avcC) {
|
|
103
|
+
hvcC = util.findBox(trak, 'hvcC')
|
|
104
|
+
}
|
|
105
|
+
|
|
92
106
|
let tkhd = util.findBox(trak, 'tkhd')
|
|
93
107
|
videoTrak = trak
|
|
94
108
|
videoTimeScale = mdhd.timescale
|
|
95
109
|
if (avcC) {
|
|
96
110
|
videoCodec = `${codecBox.type}.` + util.toHex(avcC.profile, avcC.profileCompatibility, avcC.AVCLevelIndication).join('')
|
|
97
|
-
sps = avcC.sequence
|
|
111
|
+
sps = avcC.sequence
|
|
112
|
+
pixelRatio = [avcC.spsInfo.par_ratio.width, avcC.spsInfo.par_ratio.height]
|
|
113
|
+
if (avcC.spsInfo.codec_size) {
|
|
114
|
+
width = avcC.spsInfo.codec_size.width
|
|
115
|
+
height = avcC.spsInfo.codec_size.height
|
|
116
|
+
}
|
|
98
117
|
pps = avcC.pps && avcC.pps.map((item) => Number(`0x${item}`))
|
|
99
118
|
profile = avcC.profile
|
|
119
|
+
} else if(hvcC) {
|
|
120
|
+
hvcCData = hvcC.data
|
|
121
|
+
let hvc1 = util.findBox(trak, 'hvc1')
|
|
122
|
+
if(hvc1) {
|
|
123
|
+
hvc1Data = hvc1.data
|
|
124
|
+
width = hvc1.width
|
|
125
|
+
height = hvc1.height
|
|
126
|
+
}
|
|
127
|
+
videoCodec = `${codecBox.type}.` + util.toHex(hvcC.profile, hvcC.profileCompatibility, hvcC.profileCompatibilityIndications).join('')
|
|
128
|
+
vps = hvcC.vps && hvcC.vps.map((item) => Number(`0x${item}`))
|
|
129
|
+
sps = hvcC.sequence
|
|
130
|
+
// pixelRatio = [hvcC.spsInfo.par_ratio.width, hvcC.spsInfo.par_ratio.height]
|
|
131
|
+
// if (hvcC.spsInfo.codec_size) {
|
|
132
|
+
// width = hvcC.spsInfo.codec_size.width
|
|
133
|
+
// height = hvcC.spsInfo.codec_size.height
|
|
134
|
+
// }
|
|
135
|
+
pps = hvcC.pps && hvcC.pps.map((item) => Number(`0x${item}`))
|
|
136
|
+
profile = hvcC.profile
|
|
100
137
|
} else {
|
|
101
138
|
videoCodec = `${codecBox.type}`
|
|
102
139
|
}
|
|
103
|
-
if (tkhd) {
|
|
140
|
+
if (tkhd && !width) {
|
|
104
141
|
width = tkhd.width
|
|
105
142
|
height = tkhd.height
|
|
106
143
|
}
|
|
@@ -125,16 +162,14 @@ class MP4 {
|
|
|
125
162
|
}
|
|
126
163
|
}
|
|
127
164
|
})
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
165
|
+
if (!sps) return
|
|
166
|
+
// this.videoTrak = Merge({}, videoTrak)
|
|
167
|
+
// this.audioTrak = audioTrak ? Merge({}, audioTrak) : null
|
|
168
|
+
this.videoTrak = videoTrak;
|
|
169
|
+
this.audioTrak = audioTrak ? audioTrak : null
|
|
132
170
|
let mdat = this._boxes.find(item => item.type === 'mdat')
|
|
133
171
|
let videoDuration = util.seekTrakDuration(videoTrak, videoTimeScale)
|
|
134
|
-
let audioDuration
|
|
135
|
-
if(!this.videoOnly) {
|
|
136
|
-
audioDuration = util.seekTrakDuration(audioTrak, audioTimeScale)
|
|
137
|
-
}
|
|
172
|
+
let audioDuration = audioTrak ? util.seekTrakDuration(audioTrak, audioTimeScale) : videoDuration
|
|
138
173
|
this.mdatStart = mdat.start
|
|
139
174
|
let vf = this.videoKeyFrames
|
|
140
175
|
let videoKeyFramesLength = vf.length - 1
|
|
@@ -147,52 +182,54 @@ class MP4 {
|
|
|
147
182
|
} else {
|
|
148
183
|
this.timeRage.push([
|
|
149
184
|
item.time.time / videoTimeScale,
|
|
150
|
-
|
|
185
|
+
mvhd.duration / mvhd.timeScale
|
|
151
186
|
])
|
|
152
187
|
}
|
|
153
188
|
})
|
|
154
189
|
this.meta = {
|
|
155
190
|
videoCodec,
|
|
191
|
+
audioCodec,
|
|
156
192
|
createTime: mvhd.createTime,
|
|
157
193
|
modifyTime: mvhd.modifyTime,
|
|
158
|
-
duration:
|
|
194
|
+
duration: mvhd.duration / mvhd.timeScale,
|
|
159
195
|
timeScale: mvhd.timeScale,
|
|
160
196
|
videoDuration,
|
|
161
197
|
videoTimeScale,
|
|
162
|
-
|
|
198
|
+
audioDuration,
|
|
199
|
+
audioTimeScale,
|
|
200
|
+
endTime: Math.min(videoDuration, audioDuration),
|
|
201
|
+
vps,
|
|
163
202
|
sps,
|
|
164
203
|
pps,
|
|
165
204
|
width,
|
|
166
205
|
height,
|
|
167
206
|
profile,
|
|
168
|
-
pixelRatio
|
|
169
|
-
1, 1
|
|
170
|
-
],
|
|
207
|
+
pixelRatio,
|
|
171
208
|
channelCount,
|
|
172
209
|
sampleRate,
|
|
173
|
-
audioConfig: decoderConfig
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
210
|
+
audioConfig: decoderConfig,
|
|
211
|
+
hvc1Data,
|
|
212
|
+
hvcCData,
|
|
213
|
+
ext: {
|
|
214
|
+
videoTrak: this.videoTrak,
|
|
215
|
+
audioTrak: this.audioTrak,
|
|
216
|
+
mdatStart: this.mdatStart,
|
|
217
|
+
timeRage: this.timeRage,
|
|
218
|
+
},
|
|
219
|
+
stss: this._stssObj
|
|
181
220
|
}
|
|
221
|
+
this.emit('metaReady', this.meta)
|
|
182
222
|
}
|
|
183
223
|
|
|
184
224
|
/**
|
|
185
|
-
|
|
186
|
-
|
|
225
|
+
* [init 实例的初始化,主要是获取视频的MOOV元信息]
|
|
226
|
+
*/
|
|
187
227
|
init () {
|
|
188
228
|
let self = this
|
|
189
|
-
self.getData().then(
|
|
229
|
+
self.getData().then(res => {
|
|
190
230
|
let parsed
|
|
191
|
-
|
|
192
231
|
let moovStart = 0
|
|
193
|
-
|
|
194
232
|
let moov
|
|
195
|
-
|
|
196
233
|
let boxes
|
|
197
234
|
try {
|
|
198
235
|
parsed = new Parser(res)
|
|
@@ -217,7 +254,13 @@ class MP4 {
|
|
|
217
254
|
if (nextBox) {
|
|
218
255
|
if (nextBox.type === 'moov') {
|
|
219
256
|
self.getData(moovStart, moovStart + nextBox.size + 28).then(res => {
|
|
220
|
-
let parsed
|
|
257
|
+
let parsed
|
|
258
|
+
// try {
|
|
259
|
+
parsed = new Parser(res)
|
|
260
|
+
// }catch(e) {
|
|
261
|
+
|
|
262
|
+
// }
|
|
263
|
+
|
|
221
264
|
self._boxes = self._boxes.concat(parsed.boxes)
|
|
222
265
|
moov = parsed.boxes.filter(box => box.type === 'moov')
|
|
223
266
|
if (moov.length) {
|
|
@@ -264,7 +307,8 @@ class MP4 {
|
|
|
264
307
|
let stsz = util.findBox(trak, 'stsz') // sample-size
|
|
265
308
|
let stts = util.findBox(trak, 'stts') // sample-time
|
|
266
309
|
let stco = util.findBox(trak, 'stco') // chunk-offset
|
|
267
|
-
let
|
|
310
|
+
let cttsObj = type === 'video' ? this._cttsObj : null;
|
|
311
|
+
let stscObj = type === 'video' ? this._stscVideoObj : this._stscAudioObj;
|
|
268
312
|
let mdatStart = this.mdatStart
|
|
269
313
|
let samples = []
|
|
270
314
|
end = end !== undefined
|
|
@@ -275,8 +319,8 @@ class MP4 {
|
|
|
275
319
|
samples.push({
|
|
276
320
|
idx: item,
|
|
277
321
|
size: stsz.entries[item],
|
|
278
|
-
time: util.seekSampleTime(stts,
|
|
279
|
-
offset: util.seekSampleOffset(stsc, stco, stsz, item, mdatStart)
|
|
322
|
+
time: util.seekSampleTime(stts, cttsObj, item),
|
|
323
|
+
offset: util.seekSampleOffset(stsc, stco, stsz, item, mdatStart, stscObj)
|
|
280
324
|
})
|
|
281
325
|
})
|
|
282
326
|
} else if (end !== 0) {
|
|
@@ -284,16 +328,17 @@ class MP4 {
|
|
|
284
328
|
samples.push({
|
|
285
329
|
idx: i,
|
|
286
330
|
size: stsz.entries[i],
|
|
287
|
-
time: util.seekSampleTime(stts,
|
|
288
|
-
offset: util.seekSampleOffset(stsc, stco, stsz, i, mdatStart)
|
|
331
|
+
time: util.seekSampleTime(stts, cttsObj, i),
|
|
332
|
+
offset: util.seekSampleOffset(stsc, stco, stsz, i, mdatStart, stscObj)
|
|
289
333
|
})
|
|
290
334
|
}
|
|
291
335
|
} else {
|
|
336
|
+
let offset = util.seekSampleOffset(stsc, stco, stsz, start, mdatStart, stscObj)
|
|
292
337
|
samples = {
|
|
293
338
|
idx: start,
|
|
294
339
|
size: stsz.entries[start],
|
|
295
|
-
time: util.seekSampleTime(stts,
|
|
296
|
-
offset:
|
|
340
|
+
time: util.seekSampleTime(stts, cttsObj, start),
|
|
341
|
+
offset: offset
|
|
297
342
|
}
|
|
298
343
|
}
|
|
299
344
|
return samples
|
|
@@ -305,7 +350,33 @@ class MP4 {
|
|
|
305
350
|
}
|
|
306
351
|
let videoTrak = this.videoTrak
|
|
307
352
|
let stss = util.findBox(videoTrak, 'stss')
|
|
353
|
+
let stsc = util.findBox(videoTrak, 'stsc') // chunk~samples
|
|
354
|
+
let ctts = util.findBox(videoTrak, 'ctts') // offset-compositime
|
|
355
|
+
this._cttsObj = null;
|
|
356
|
+
if(ctts){
|
|
357
|
+
this._cttsObj = {};
|
|
358
|
+
let count = 0;
|
|
359
|
+
for(let i = 0; i < ctts.entry.length; i++){
|
|
360
|
+
let entry = ctts.entry[i];
|
|
361
|
+
for(let j = 0; j < entry.count; j++){
|
|
362
|
+
this._cttsObj[count] = entry.offset;
|
|
363
|
+
count += 1;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
this._stscVideoObj = {};
|
|
369
|
+
let sampleCount = 0;
|
|
370
|
+
for(let i = 0; i < stsc.count - 1; i++){
|
|
371
|
+
let entry = stsc.entries[i];
|
|
372
|
+
for(let j = 0; j < entry.chunk_count * entry.samples_per_chunk; j++){
|
|
373
|
+
sampleCount ++;
|
|
374
|
+
this._stscVideoObj[sampleCount] = entry;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
308
378
|
let frames = this.getSamplesByOrders('video', stss.entries.map(item => item - 1))
|
|
379
|
+
this._stssObj = stss;
|
|
309
380
|
this._videoFrames = frames
|
|
310
381
|
return frames
|
|
311
382
|
}
|
|
@@ -317,6 +388,16 @@ class MP4 {
|
|
|
317
388
|
let videoScale = util.findBox(this.videoTrak, 'mdhd').timescale
|
|
318
389
|
let audioScale = util.findBox(this.audioTrak, 'mdhd').timescale
|
|
319
390
|
let audioStts = util.findBox(this.audioTrak, 'stts').entry
|
|
391
|
+
let stsc = util.findBox(this.audioTrak, 'stsc') // chunk~samples
|
|
392
|
+
this._stscAudioObj = {};
|
|
393
|
+
let sampleCount = 0;
|
|
394
|
+
for(let i = 0; i < stsc.count - 1; i++){
|
|
395
|
+
let entry = stsc.entries[i];
|
|
396
|
+
for(let j = 0; j < entry.chunk_count * entry.samples_per_chunk; j++){
|
|
397
|
+
sampleCount ++;
|
|
398
|
+
this._stscAudioObj[sampleCount] = entry;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
320
401
|
let videoFrames = this.videoKeyFrames
|
|
321
402
|
let audioIndex = []
|
|
322
403
|
audioIndex = videoFrames.map(item => {
|
|
@@ -326,29 +407,33 @@ class MP4 {
|
|
|
326
407
|
return this._audioFrames
|
|
327
408
|
}
|
|
328
409
|
|
|
329
|
-
packMeta
|
|
410
|
+
packMeta() {
|
|
330
411
|
if (!this.meta) {
|
|
331
412
|
return
|
|
332
413
|
}
|
|
333
414
|
let buffer = new Buffer()
|
|
334
415
|
buffer.write(FMP4.ftyp())
|
|
335
416
|
buffer.write(FMP4.moov(this.meta))
|
|
336
|
-
this.cache.write(buffer.buffer)
|
|
417
|
+
// this.cache.write(buffer.buffer)
|
|
337
418
|
return buffer.buffer
|
|
338
419
|
}
|
|
339
420
|
|
|
340
|
-
|
|
341
|
-
let
|
|
342
|
-
let fragIndex
|
|
421
|
+
getRangeFromTime(time){
|
|
422
|
+
let fragIndex = this.getFragmentIdx(time);
|
|
423
|
+
let range = this.getFragRange(fragIndex)
|
|
424
|
+
if(range === [0, 0]) return null;
|
|
425
|
+
return {
|
|
426
|
+
range,
|
|
427
|
+
fragIndex,
|
|
428
|
+
}
|
|
429
|
+
}
|
|
343
430
|
|
|
431
|
+
getFragmentIdx(time){
|
|
432
|
+
let timeStart = Math.round(time * this.meta.videoTimeScale)
|
|
433
|
+
let fragIndex
|
|
344
434
|
let videoFrames = this.videoKeyFrames
|
|
345
|
-
let audioFrames
|
|
346
|
-
if(!this.videoOnly) {
|
|
347
|
-
audioFrames = this.audioKeyFrames
|
|
348
|
-
}
|
|
349
435
|
videoFrames.every((item, idx) => {
|
|
350
436
|
let nowTime = item.time.time
|
|
351
|
-
|
|
352
437
|
let nextTime = videoFrames[idx + 1]
|
|
353
438
|
? videoFrames[idx + 1].time.time
|
|
354
439
|
: Number.MAX_SAFE_INTEGER
|
|
@@ -359,10 +444,10 @@ class MP4 {
|
|
|
359
444
|
return true
|
|
360
445
|
}
|
|
361
446
|
})
|
|
362
|
-
if(
|
|
447
|
+
if (this.audioTrak) {
|
|
448
|
+
let audioFrames = this.audioKeyFrames
|
|
363
449
|
audioFrames.every((item, idx) => {
|
|
364
450
|
let nowTime = item.startTime
|
|
365
|
-
|
|
366
451
|
let nextTime = audioFrames[idx + 1]
|
|
367
452
|
? audioFrames[idx + 1].startTime
|
|
368
453
|
: Number.MAX_SAFE_INTEGER
|
|
@@ -374,197 +459,93 @@ class MP4 {
|
|
|
374
459
|
}
|
|
375
460
|
})
|
|
376
461
|
}
|
|
377
|
-
|
|
378
|
-
return Promise.resolve(null)
|
|
379
|
-
} else {
|
|
380
|
-
return this.loadFragment(fragIndex)
|
|
381
|
-
}
|
|
462
|
+
return fragIndex;
|
|
382
463
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
464
|
+
|
|
465
|
+
seek (time) {
|
|
466
|
+
let fragIndex = this.getFragmentIdx(time);
|
|
467
|
+
this.timeRage[fragIndex].downloaded = true;
|
|
468
|
+
return this.loadFragment(fragIndex)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
getFragRange (fragIndex) {
|
|
386
472
|
let videoFrame = this.videoKeyFrames[fragIndex]
|
|
387
|
-
let
|
|
388
|
-
|
|
389
|
-
if(
|
|
390
|
-
audioFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndex].order, 0)
|
|
391
|
-
start = Math.min(
|
|
473
|
+
let start = videoFrame.offset
|
|
474
|
+
let end
|
|
475
|
+
if (this.audioTrak) {
|
|
476
|
+
let audioFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndex].order, 0)
|
|
477
|
+
start = Math.min(start, audioFrame.offset)
|
|
392
478
|
}
|
|
393
479
|
if (fragIndex < this.videoKeyFrames.length - 1) {
|
|
394
480
|
let videoNextFrame = this.videoKeyFrames[fragIndex + 1]
|
|
395
|
-
let audioNextFrame
|
|
396
481
|
end = videoNextFrame.offset
|
|
397
|
-
if(
|
|
398
|
-
audioNextFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndex + 1].order, 0)
|
|
399
|
-
end = Math.max(
|
|
482
|
+
if (this.audioTrak) {
|
|
483
|
+
let audioNextFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndex + 1].order, 0)
|
|
484
|
+
end = Math.max(end, audioNextFrame.offset || 0)
|
|
400
485
|
}
|
|
401
486
|
}
|
|
402
|
-
let self = this
|
|
403
487
|
if (window.isNaN(start) || (end !== undefined && window.isNaN(end))) {
|
|
404
|
-
|
|
405
|
-
return
|
|
406
|
-
}
|
|
407
|
-
if (this.bufferCache.has(fragIndex)) {
|
|
408
|
-
return Promise.resolve(null)
|
|
409
|
-
} else {
|
|
410
|
-
return this.getData(
|
|
411
|
-
start + self.mdatStart, end
|
|
412
|
-
? self.mdatStart + end
|
|
413
|
-
: '').then((dat) => {
|
|
414
|
-
return self.createFragment(new Uint8Array(dat), start, fragIndex)
|
|
415
|
-
})
|
|
488
|
+
this.emit('error', new Errors('parse', '', { line: 366, handle: '[MP4] loadFragment', url: this.url }))
|
|
489
|
+
return [0, 0]
|
|
416
490
|
}
|
|
491
|
+
return [start + this.mdatStart, end ? this.mdatStart + end : '']
|
|
417
492
|
}
|
|
418
|
-
|
|
419
|
-
let buffer = new Buffer()
|
|
420
|
-
buffer.write(FMP4.moof(data))
|
|
421
|
-
buffer.write(FMP4.mdat(data))
|
|
422
|
-
this.cache.write(buffer.buffer)
|
|
423
|
-
return buffer.buffer
|
|
424
|
-
}
|
|
425
|
-
createFragment (mdatData, start, fragIndex) {
|
|
493
|
+
loadFragment (fragIndex) {
|
|
426
494
|
let self = this
|
|
427
|
-
let
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
let _samples = self.getSamplesByOrders('video', framesIndex[fragIndex], framesIndex[fragIndex + 1])
|
|
432
|
-
let samples = _samples.map((item, idx) => {
|
|
433
|
-
return {
|
|
434
|
-
size: item.size,
|
|
435
|
-
duration: item.time.duration,
|
|
436
|
-
offset: item.time.offset,
|
|
437
|
-
buffer: new Uint8Array(mdatData.slice(item.offset - start, item.offset - start + item.size)),
|
|
438
|
-
key: idx === 0
|
|
439
|
-
}
|
|
440
|
-
})
|
|
441
|
-
resBuffers.push(this.addFragment({id: 1, time: _samples[0].time.time, firstFlags: 0x2000000, flags: 0xf01, samples}))
|
|
442
|
-
}
|
|
443
|
-
if(!this.videoOnly) {
|
|
444
|
-
let _samples = this.getSamplesByOrders(
|
|
445
|
-
'audio', this.audioKeyFrames[fragIndex].order, this.audioKeyFrames[fragIndex + 1]
|
|
446
|
-
? this.audioKeyFrames[fragIndex + 1].order
|
|
447
|
-
: undefined)
|
|
448
|
-
let samples = _samples.map((item, idx) => {
|
|
449
|
-
return {
|
|
450
|
-
size: item.size,
|
|
451
|
-
duration: item.time.duration,
|
|
452
|
-
offset: item.time.offset,
|
|
453
|
-
buffer: new Uint8Array(mdatData.slice(item.offset - start, item.offset - start + item.size)),
|
|
454
|
-
key: idx === 0
|
|
455
|
-
}
|
|
456
|
-
})
|
|
457
|
-
resBuffers.push(this.addFragment({id: 2, time: _samples[0].time.time, firstFlags: 0x00, flags: 0x701, samples: samples}))
|
|
495
|
+
let range = this.getFragRange(fragIndex)
|
|
496
|
+
if(range === [0, 0]) {
|
|
497
|
+
debugger
|
|
498
|
+
return false;
|
|
458
499
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
resBuffers.every(item => {
|
|
462
|
-
bufferSize += item.byteLength
|
|
463
|
-
return true
|
|
500
|
+
return this.getData(range[0], range[1]).then((dat) => {
|
|
501
|
+
return self.createFragment(new Uint8Array(dat), range[0] - this.mdatStart, fragIndex)
|
|
464
502
|
})
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
let offset = 0
|
|
468
|
-
resBuffers.every(item => {
|
|
469
|
-
buffer.set(item, offset)
|
|
470
|
-
offset += item.byteLength
|
|
471
|
-
return true
|
|
503
|
+
.then(buf => {
|
|
504
|
+
return buf
|
|
472
505
|
})
|
|
473
|
-
return Promise.resolve(buffer)
|
|
474
506
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
507
|
+
addFragment (data) {
|
|
508
|
+
let buffer = new Buffer()
|
|
509
|
+
return new Promise(resolve => {
|
|
510
|
+
buffer.write(FMP4.moof(data))
|
|
511
|
+
buffer.write(FMP4.mdat(data))
|
|
512
|
+
// this.cache.write(buffer.buffer)
|
|
513
|
+
resolve(buffer.buffer)
|
|
514
|
+
})
|
|
478
515
|
}
|
|
479
|
-
|
|
480
|
-
cut (start, end) {
|
|
516
|
+
getVideoBuffer(mdatData, start, fragIndex) {
|
|
481
517
|
let self = this
|
|
482
|
-
|
|
483
|
-
let
|
|
484
|
-
let
|
|
485
|
-
let
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
let nextTime = videoFrames[idx + 1]
|
|
494
|
-
? videoFrames[idx + 1].time.time
|
|
495
|
-
: Number.MAX_SAFE_INTEGER
|
|
496
|
-
if (nowTime <= timeStart && timeStart < nextTime) {
|
|
497
|
-
fragIndexStart = idx
|
|
498
|
-
return true
|
|
499
|
-
} else if (nowTime <= timeEnd && timeEnd < nextTime) {
|
|
500
|
-
fragIndexEnd = idx
|
|
501
|
-
return false
|
|
502
|
-
} else {
|
|
503
|
-
return true
|
|
504
|
-
}
|
|
505
|
-
})
|
|
506
|
-
audioFrames.every((item, idx) => {
|
|
507
|
-
let nowTime = item.startTime
|
|
508
|
-
|
|
509
|
-
let nextTime = audioFrames[idx + 1]
|
|
510
|
-
? audioFrames[idx + 1].startTime
|
|
511
|
-
: Number.MAX_SAFE_INTEGER
|
|
512
|
-
if (nowTime <= timeStart && timeStart < nextTime) {
|
|
513
|
-
fragIndexStart = Math.min(idx, fragIndexStart)
|
|
514
|
-
return true
|
|
515
|
-
} else if (nowTime <= timeEnd && timeEnd < nextTime) {
|
|
516
|
-
fragIndexEnd = Math.min(idx, fragIndexEnd)
|
|
517
|
-
return false
|
|
518
|
-
} else {
|
|
519
|
-
return true
|
|
518
|
+
let videoFlags = 0xf01
|
|
519
|
+
let framesIndex = self.videoKeyFrames.map((item) => item.idx)
|
|
520
|
+
let _samples = self.getSamplesByOrders('video', framesIndex[fragIndex], framesIndex[fragIndex + 1])
|
|
521
|
+
let samples = _samples.map((item, idx) => {
|
|
522
|
+
return {
|
|
523
|
+
size: item.size,
|
|
524
|
+
duration: item.time.duration,
|
|
525
|
+
offset: item.time.offset,
|
|
526
|
+
buffer: new Uint8Array(mdatData.slice(item.offset - start, item.offset - start + item.size)),
|
|
527
|
+
key: idx === 0,
|
|
528
|
+
idx: item.idx
|
|
520
529
|
}
|
|
521
530
|
})
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
let videoStartFrame = this.videoKeyFrames[fragIndexStart]
|
|
531
|
-
let audioStartFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndexStart].order, 0)
|
|
532
|
-
start = Math.min(videoStartFrame.offset, audioStartFrame.offset)
|
|
533
|
-
let videoEndFrame = this.videoKeyFrames[fragIndexEnd]
|
|
534
|
-
let audioEndFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndexEnd].order, 0)
|
|
535
|
-
end = Math.max(videoEndFrame.offset, audioEndFrame.offset)
|
|
536
|
-
let self = this
|
|
537
|
-
if (window.isNaN(start) || (end !== undefined && window.isNaN(end))) {
|
|
538
|
-
self.emit('error', new Errors('parse', '', { line: 366, handle: '[MP4] loadFragment', url: self.url }))
|
|
539
|
-
return false
|
|
540
|
-
}
|
|
541
|
-
return this.getData(
|
|
542
|
-
start + self.mdatStart, end
|
|
543
|
-
? self.mdatStart + end
|
|
544
|
-
: '').then((dat) => {
|
|
545
|
-
return self.createFragmentForCut(new Uint8Array(dat), start, fragIndexStart, end, fragIndexEnd)
|
|
531
|
+
return this.addFragment({
|
|
532
|
+
id: 1,
|
|
533
|
+
time: _samples[0].time.time,
|
|
534
|
+
firstFlags: 0x2000000,
|
|
535
|
+
flags: videoFlags,
|
|
536
|
+
samples,
|
|
537
|
+
sampleOffset: _samples[0].idx,
|
|
538
|
+
fragIndex
|
|
546
539
|
})
|
|
547
540
|
}
|
|
548
|
-
|
|
541
|
+
getAudioBuffer(mdatData, start, fragIndex) {
|
|
542
|
+
|
|
549
543
|
let self = this
|
|
550
|
-
let
|
|
551
|
-
|
|
552
|
-
let framesIndex = self.videoKeyFrames.map((item) => item.idx)
|
|
553
|
-
let _samples = self.getSamplesByOrders('video', framesIndex[fragIndexStart], framesIndex[fragIndexEnd])
|
|
554
|
-
let samples = _samples.map((item, idx) => {
|
|
555
|
-
return {
|
|
556
|
-
size: item.size,
|
|
557
|
-
duration: item.time.duration,
|
|
558
|
-
offset: item.time.offset,
|
|
559
|
-
buffer: new Uint8Array(mdatData.slice(item.offset - start, item.offset - start + item.size)),
|
|
560
|
-
key: idx === 0
|
|
561
|
-
}
|
|
562
|
-
})
|
|
563
|
-
resBuffers.push(this.addFragment({id: 1, time: 0, firstFlags: 0x2000000, flags: 0xf01, samples}))
|
|
564
|
-
}
|
|
544
|
+
let audioFlags = 0x701
|
|
545
|
+
|
|
565
546
|
let _samples = this.getSamplesByOrders(
|
|
566
|
-
'audio', this.audioKeyFrames[
|
|
567
|
-
? this.audioKeyFrames[
|
|
547
|
+
'audio', this.audioKeyFrames[fragIndex].order, this.audioKeyFrames[fragIndex + 1]
|
|
548
|
+
? this.audioKeyFrames[fragIndex + 1].order
|
|
568
549
|
: undefined)
|
|
569
550
|
let samples = _samples.map((item, idx) => {
|
|
570
551
|
return {
|
|
@@ -575,22 +556,60 @@ class MP4 {
|
|
|
575
556
|
key: idx === 0
|
|
576
557
|
}
|
|
577
558
|
})
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
559
|
+
return this.addFragment({
|
|
560
|
+
id: 2,
|
|
561
|
+
time: _samples[0].time.time,
|
|
562
|
+
firstFlags: 0x00,
|
|
563
|
+
flags: audioFlags,
|
|
564
|
+
samples,
|
|
565
|
+
sampleOffset: _samples[0].idx,
|
|
566
|
+
fragIndex
|
|
584
567
|
})
|
|
585
|
-
|
|
568
|
+
}
|
|
569
|
+
createFragment (mdatData, start, fragIndex) {
|
|
570
|
+
let self = this
|
|
586
571
|
|
|
587
|
-
let
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
572
|
+
let resBuffers = []
|
|
573
|
+
let promises = [
|
|
574
|
+
self.getVideoBuffer(mdatData, start, fragIndex)
|
|
575
|
+
]
|
|
576
|
+
if (this.audioTrak) {
|
|
577
|
+
promises.push(self.getAudioBuffer(mdatData, start, fragIndex))
|
|
578
|
+
}
|
|
579
|
+
return Promise.all(promises)
|
|
580
|
+
.then(buffers => {
|
|
581
|
+
resBuffers.push(buffers[0])
|
|
582
|
+
if (buffers && buffers[1]) {
|
|
583
|
+
resBuffers.push(buffers[1])
|
|
584
|
+
}
|
|
585
|
+
let bufferSize = 0
|
|
586
|
+
resBuffers.every(item => {
|
|
587
|
+
bufferSize += item.byteLength
|
|
588
|
+
return true
|
|
589
|
+
})
|
|
590
|
+
let buffer = new Uint8Array(bufferSize)
|
|
591
|
+
let offset = 0
|
|
592
|
+
resBuffers.every(item => {
|
|
593
|
+
buffer.set(item, offset)
|
|
594
|
+
offset += item.byteLength
|
|
595
|
+
return true
|
|
596
|
+
})
|
|
597
|
+
return buffer
|
|
592
598
|
})
|
|
593
|
-
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
update(url){
|
|
602
|
+
this.url = url;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
destroy(){
|
|
606
|
+
if(this.hasDestroyed){
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
for (let k in this) {
|
|
610
|
+
delete this[k]
|
|
611
|
+
}
|
|
612
|
+
this.hasDestroyed = true;
|
|
594
613
|
}
|
|
595
614
|
}
|
|
596
615
|
|