xgplayer-mp4 1.2.4 → 2.0.2
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 +5 -4
- 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 -268
- 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,19 +162,14 @@ class MP4 {
|
|
|
125
162
|
}
|
|
126
163
|
}
|
|
127
164
|
})
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.audioTrak = Merge({}, audioTrak)
|
|
134
|
-
}
|
|
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
|
|
135
170
|
let mdat = this._boxes.find(item => item.type === 'mdat')
|
|
136
171
|
let videoDuration = util.seekTrakDuration(videoTrak, videoTimeScale)
|
|
137
|
-
let audioDuration
|
|
138
|
-
if(!this.videoOnly) {
|
|
139
|
-
audioDuration = util.seekTrakDuration(audioTrak, audioTimeScale)
|
|
140
|
-
}
|
|
172
|
+
let audioDuration = audioTrak ? util.seekTrakDuration(audioTrak, audioTimeScale) : videoDuration
|
|
141
173
|
this.mdatStart = mdat.start
|
|
142
174
|
let vf = this.videoKeyFrames
|
|
143
175
|
let videoKeyFramesLength = vf.length - 1
|
|
@@ -150,52 +182,54 @@ class MP4 {
|
|
|
150
182
|
} else {
|
|
151
183
|
this.timeRage.push([
|
|
152
184
|
item.time.time / videoTimeScale,
|
|
153
|
-
|
|
185
|
+
mvhd.duration / mvhd.timeScale
|
|
154
186
|
])
|
|
155
187
|
}
|
|
156
188
|
})
|
|
157
189
|
this.meta = {
|
|
158
190
|
videoCodec,
|
|
191
|
+
audioCodec,
|
|
159
192
|
createTime: mvhd.createTime,
|
|
160
193
|
modifyTime: mvhd.modifyTime,
|
|
161
|
-
duration:
|
|
194
|
+
duration: mvhd.duration / mvhd.timeScale,
|
|
162
195
|
timeScale: mvhd.timeScale,
|
|
163
196
|
videoDuration,
|
|
164
197
|
videoTimeScale,
|
|
165
|
-
|
|
198
|
+
audioDuration,
|
|
199
|
+
audioTimeScale,
|
|
200
|
+
endTime: Math.min(videoDuration, audioDuration),
|
|
201
|
+
vps,
|
|
166
202
|
sps,
|
|
167
203
|
pps,
|
|
168
204
|
width,
|
|
169
205
|
height,
|
|
170
206
|
profile,
|
|
171
|
-
pixelRatio
|
|
172
|
-
1, 1
|
|
173
|
-
],
|
|
207
|
+
pixelRatio,
|
|
174
208
|
channelCount,
|
|
175
209
|
sampleRate,
|
|
176
|
-
audioConfig: decoderConfig
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
|
184
220
|
}
|
|
221
|
+
this.emit('metaReady', this.meta)
|
|
185
222
|
}
|
|
186
223
|
|
|
187
224
|
/**
|
|
188
|
-
|
|
189
|
-
|
|
225
|
+
* [init 实例的初始化,主要是获取视频的MOOV元信息]
|
|
226
|
+
*/
|
|
190
227
|
init () {
|
|
191
228
|
let self = this
|
|
192
|
-
self.getData().then(
|
|
229
|
+
self.getData().then(res => {
|
|
193
230
|
let parsed
|
|
194
|
-
|
|
195
231
|
let moovStart = 0
|
|
196
|
-
|
|
197
232
|
let moov
|
|
198
|
-
|
|
199
233
|
let boxes
|
|
200
234
|
try {
|
|
201
235
|
parsed = new Parser(res)
|
|
@@ -220,7 +254,13 @@ class MP4 {
|
|
|
220
254
|
if (nextBox) {
|
|
221
255
|
if (nextBox.type === 'moov') {
|
|
222
256
|
self.getData(moovStart, moovStart + nextBox.size + 28).then(res => {
|
|
223
|
-
let parsed
|
|
257
|
+
let parsed
|
|
258
|
+
// try {
|
|
259
|
+
parsed = new Parser(res)
|
|
260
|
+
// }catch(e) {
|
|
261
|
+
|
|
262
|
+
// }
|
|
263
|
+
|
|
224
264
|
self._boxes = self._boxes.concat(parsed.boxes)
|
|
225
265
|
moov = parsed.boxes.filter(box => box.type === 'moov')
|
|
226
266
|
if (moov.length) {
|
|
@@ -267,7 +307,8 @@ class MP4 {
|
|
|
267
307
|
let stsz = util.findBox(trak, 'stsz') // sample-size
|
|
268
308
|
let stts = util.findBox(trak, 'stts') // sample-time
|
|
269
309
|
let stco = util.findBox(trak, 'stco') // chunk-offset
|
|
270
|
-
let
|
|
310
|
+
let cttsObj = type === 'video' ? this._cttsObj : null;
|
|
311
|
+
let stscObj = type === 'video' ? this._stscVideoObj : this._stscAudioObj;
|
|
271
312
|
let mdatStart = this.mdatStart
|
|
272
313
|
let samples = []
|
|
273
314
|
end = end !== undefined
|
|
@@ -278,8 +319,8 @@ class MP4 {
|
|
|
278
319
|
samples.push({
|
|
279
320
|
idx: item,
|
|
280
321
|
size: stsz.entries[item],
|
|
281
|
-
time: util.seekSampleTime(stts,
|
|
282
|
-
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)
|
|
283
324
|
})
|
|
284
325
|
})
|
|
285
326
|
} else if (end !== 0) {
|
|
@@ -287,16 +328,17 @@ class MP4 {
|
|
|
287
328
|
samples.push({
|
|
288
329
|
idx: i,
|
|
289
330
|
size: stsz.entries[i],
|
|
290
|
-
time: util.seekSampleTime(stts,
|
|
291
|
-
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)
|
|
292
333
|
})
|
|
293
334
|
}
|
|
294
335
|
} else {
|
|
336
|
+
let offset = util.seekSampleOffset(stsc, stco, stsz, start, mdatStart, stscObj)
|
|
295
337
|
samples = {
|
|
296
338
|
idx: start,
|
|
297
339
|
size: stsz.entries[start],
|
|
298
|
-
time: util.seekSampleTime(stts,
|
|
299
|
-
offset:
|
|
340
|
+
time: util.seekSampleTime(stts, cttsObj, start),
|
|
341
|
+
offset: offset
|
|
300
342
|
}
|
|
301
343
|
}
|
|
302
344
|
return samples
|
|
@@ -308,7 +350,33 @@ class MP4 {
|
|
|
308
350
|
}
|
|
309
351
|
let videoTrak = this.videoTrak
|
|
310
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
|
+
|
|
311
378
|
let frames = this.getSamplesByOrders('video', stss.entries.map(item => item - 1))
|
|
379
|
+
this._stssObj = stss;
|
|
312
380
|
this._videoFrames = frames
|
|
313
381
|
return frames
|
|
314
382
|
}
|
|
@@ -320,6 +388,16 @@ class MP4 {
|
|
|
320
388
|
let videoScale = util.findBox(this.videoTrak, 'mdhd').timescale
|
|
321
389
|
let audioScale = util.findBox(this.audioTrak, 'mdhd').timescale
|
|
322
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
|
+
}
|
|
323
401
|
let videoFrames = this.videoKeyFrames
|
|
324
402
|
let audioIndex = []
|
|
325
403
|
audioIndex = videoFrames.map(item => {
|
|
@@ -329,29 +407,33 @@ class MP4 {
|
|
|
329
407
|
return this._audioFrames
|
|
330
408
|
}
|
|
331
409
|
|
|
332
|
-
packMeta
|
|
410
|
+
packMeta() {
|
|
333
411
|
if (!this.meta) {
|
|
334
412
|
return
|
|
335
413
|
}
|
|
336
414
|
let buffer = new Buffer()
|
|
337
415
|
buffer.write(FMP4.ftyp())
|
|
338
416
|
buffer.write(FMP4.moov(this.meta))
|
|
339
|
-
this.cache.write(buffer.buffer)
|
|
417
|
+
// this.cache.write(buffer.buffer)
|
|
340
418
|
return buffer.buffer
|
|
341
419
|
}
|
|
342
420
|
|
|
343
|
-
|
|
344
|
-
let
|
|
345
|
-
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
|
+
}
|
|
346
430
|
|
|
431
|
+
getFragmentIdx(time){
|
|
432
|
+
let timeStart = Math.round(time * this.meta.videoTimeScale)
|
|
433
|
+
let fragIndex
|
|
347
434
|
let videoFrames = this.videoKeyFrames
|
|
348
|
-
let audioFrames
|
|
349
|
-
if(!this.videoOnly) {
|
|
350
|
-
audioFrames = this.audioKeyFrames
|
|
351
|
-
}
|
|
352
435
|
videoFrames.every((item, idx) => {
|
|
353
436
|
let nowTime = item.time.time
|
|
354
|
-
|
|
355
437
|
let nextTime = videoFrames[idx + 1]
|
|
356
438
|
? videoFrames[idx + 1].time.time
|
|
357
439
|
: Number.MAX_SAFE_INTEGER
|
|
@@ -362,10 +444,10 @@ class MP4 {
|
|
|
362
444
|
return true
|
|
363
445
|
}
|
|
364
446
|
})
|
|
365
|
-
if(
|
|
447
|
+
if (this.audioTrak) {
|
|
448
|
+
let audioFrames = this.audioKeyFrames
|
|
366
449
|
audioFrames.every((item, idx) => {
|
|
367
450
|
let nowTime = item.startTime
|
|
368
|
-
|
|
369
451
|
let nextTime = audioFrames[idx + 1]
|
|
370
452
|
? audioFrames[idx + 1].startTime
|
|
371
453
|
: Number.MAX_SAFE_INTEGER
|
|
@@ -377,197 +459,93 @@ class MP4 {
|
|
|
377
459
|
}
|
|
378
460
|
})
|
|
379
461
|
}
|
|
380
|
-
|
|
381
|
-
return Promise.resolve(null)
|
|
382
|
-
} else {
|
|
383
|
-
return this.loadFragment(fragIndex)
|
|
384
|
-
}
|
|
462
|
+
return fragIndex;
|
|
385
463
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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) {
|
|
389
472
|
let videoFrame = this.videoKeyFrames[fragIndex]
|
|
390
|
-
let
|
|
391
|
-
|
|
392
|
-
if(
|
|
393
|
-
audioFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndex].order, 0)
|
|
394
|
-
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)
|
|
395
478
|
}
|
|
396
479
|
if (fragIndex < this.videoKeyFrames.length - 1) {
|
|
397
480
|
let videoNextFrame = this.videoKeyFrames[fragIndex + 1]
|
|
398
|
-
let audioNextFrame
|
|
399
481
|
end = videoNextFrame.offset
|
|
400
|
-
if(
|
|
401
|
-
audioNextFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndex + 1].order, 0)
|
|
402
|
-
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)
|
|
403
485
|
}
|
|
404
486
|
}
|
|
405
|
-
let self = this
|
|
406
487
|
if (window.isNaN(start) || (end !== undefined && window.isNaN(end))) {
|
|
407
|
-
|
|
408
|
-
return
|
|
409
|
-
}
|
|
410
|
-
if (this.bufferCache.has(fragIndex)) {
|
|
411
|
-
return Promise.resolve(null)
|
|
412
|
-
} else {
|
|
413
|
-
return this.getData(
|
|
414
|
-
start + self.mdatStart, end
|
|
415
|
-
? self.mdatStart + end
|
|
416
|
-
: '').then((dat) => {
|
|
417
|
-
return self.createFragment(new Uint8Array(dat), start, fragIndex)
|
|
418
|
-
})
|
|
488
|
+
this.emit('error', new Errors('parse', '', { line: 366, handle: '[MP4] loadFragment', url: this.url }))
|
|
489
|
+
return [0, 0]
|
|
419
490
|
}
|
|
491
|
+
return [start + this.mdatStart, end ? this.mdatStart + end : '']
|
|
420
492
|
}
|
|
421
|
-
|
|
422
|
-
let buffer = new Buffer()
|
|
423
|
-
buffer.write(FMP4.moof(data))
|
|
424
|
-
buffer.write(FMP4.mdat(data))
|
|
425
|
-
this.cache.write(buffer.buffer)
|
|
426
|
-
return buffer.buffer
|
|
427
|
-
}
|
|
428
|
-
createFragment (mdatData, start, fragIndex) {
|
|
493
|
+
loadFragment (fragIndex) {
|
|
429
494
|
let self = this
|
|
430
|
-
let
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
let _samples = self.getSamplesByOrders('video', framesIndex[fragIndex], framesIndex[fragIndex + 1])
|
|
435
|
-
let samples = _samples.map((item, idx) => {
|
|
436
|
-
return {
|
|
437
|
-
size: item.size,
|
|
438
|
-
duration: item.time.duration,
|
|
439
|
-
offset: item.time.offset,
|
|
440
|
-
buffer: new Uint8Array(mdatData.slice(item.offset - start, item.offset - start + item.size)),
|
|
441
|
-
key: idx === 0
|
|
442
|
-
}
|
|
443
|
-
})
|
|
444
|
-
resBuffers.push(this.addFragment({id: 1, time: _samples[0].time.time, firstFlags: 0x2000000, flags: 0xf01, samples}))
|
|
495
|
+
let range = this.getFragRange(fragIndex)
|
|
496
|
+
if(range === [0, 0]) {
|
|
497
|
+
debugger
|
|
498
|
+
return false;
|
|
445
499
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
'audio', this.audioKeyFrames[fragIndex].order, this.audioKeyFrames[fragIndex + 1]
|
|
449
|
-
? this.audioKeyFrames[fragIndex + 1].order
|
|
450
|
-
: undefined)
|
|
451
|
-
let samples = _samples.map((item, idx) => {
|
|
452
|
-
return {
|
|
453
|
-
size: item.size,
|
|
454
|
-
duration: item.time.duration,
|
|
455
|
-
offset: item.time.offset,
|
|
456
|
-
buffer: new Uint8Array(mdatData.slice(item.offset - start, item.offset - start + item.size)),
|
|
457
|
-
key: idx === 0
|
|
458
|
-
}
|
|
459
|
-
})
|
|
460
|
-
resBuffers.push(this.addFragment({id: 2, time: _samples[0].time.time, firstFlags: 0x00, flags: 0x701, samples: samples}))
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
let bufferSize = 0
|
|
464
|
-
resBuffers.every(item => {
|
|
465
|
-
bufferSize += item.byteLength
|
|
466
|
-
return true
|
|
500
|
+
return this.getData(range[0], range[1]).then((dat) => {
|
|
501
|
+
return self.createFragment(new Uint8Array(dat), range[0] - this.mdatStart, fragIndex)
|
|
467
502
|
})
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
let offset = 0
|
|
471
|
-
resBuffers.every(item => {
|
|
472
|
-
buffer.set(item, offset)
|
|
473
|
-
offset += item.byteLength
|
|
474
|
-
return true
|
|
503
|
+
.then(buf => {
|
|
504
|
+
return buf
|
|
475
505
|
})
|
|
476
|
-
return Promise.resolve(buffer)
|
|
477
506
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
+
})
|
|
481
515
|
}
|
|
482
|
-
|
|
483
|
-
cut (start, end) {
|
|
516
|
+
getVideoBuffer(mdatData, start, fragIndex) {
|
|
484
517
|
let self = this
|
|
485
|
-
|
|
486
|
-
let
|
|
487
|
-
let
|
|
488
|
-
let
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
let nextTime = videoFrames[idx + 1]
|
|
497
|
-
? videoFrames[idx + 1].time.time
|
|
498
|
-
: Number.MAX_SAFE_INTEGER
|
|
499
|
-
if (nowTime <= timeStart && timeStart < nextTime) {
|
|
500
|
-
fragIndexStart = idx
|
|
501
|
-
return true
|
|
502
|
-
} else if (nowTime <= timeEnd && timeEnd < nextTime) {
|
|
503
|
-
fragIndexEnd = idx
|
|
504
|
-
return false
|
|
505
|
-
} else {
|
|
506
|
-
return true
|
|
507
|
-
}
|
|
508
|
-
})
|
|
509
|
-
audioFrames.every((item, idx) => {
|
|
510
|
-
let nowTime = item.startTime
|
|
511
|
-
|
|
512
|
-
let nextTime = audioFrames[idx + 1]
|
|
513
|
-
? audioFrames[idx + 1].startTime
|
|
514
|
-
: Number.MAX_SAFE_INTEGER
|
|
515
|
-
if (nowTime <= timeStart && timeStart < nextTime) {
|
|
516
|
-
fragIndexStart = Math.min(idx, fragIndexStart)
|
|
517
|
-
return true
|
|
518
|
-
} else if (nowTime <= timeEnd && timeEnd < nextTime) {
|
|
519
|
-
fragIndexEnd = Math.min(idx, fragIndexEnd)
|
|
520
|
-
return false
|
|
521
|
-
} else {
|
|
522
|
-
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
|
|
523
529
|
}
|
|
524
530
|
})
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
let videoStartFrame = this.videoKeyFrames[fragIndexStart]
|
|
534
|
-
let audioStartFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndexStart].order, 0)
|
|
535
|
-
start = Math.min(videoStartFrame.offset, audioStartFrame.offset)
|
|
536
|
-
let videoEndFrame = this.videoKeyFrames[fragIndexEnd]
|
|
537
|
-
let audioEndFrame = this.getSamplesByOrders('audio', this.audioKeyFrames[fragIndexEnd].order, 0)
|
|
538
|
-
end = Math.max(videoEndFrame.offset, audioEndFrame.offset)
|
|
539
|
-
let self = this
|
|
540
|
-
if (window.isNaN(start) || (end !== undefined && window.isNaN(end))) {
|
|
541
|
-
self.emit('error', new Errors('parse', '', { line: 366, handle: '[MP4] loadFragment', url: self.url }))
|
|
542
|
-
return false
|
|
543
|
-
}
|
|
544
|
-
return this.getData(
|
|
545
|
-
start + self.mdatStart, end
|
|
546
|
-
? self.mdatStart + end
|
|
547
|
-
: '').then((dat) => {
|
|
548
|
-
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
|
|
549
539
|
})
|
|
550
540
|
}
|
|
551
|
-
|
|
541
|
+
getAudioBuffer(mdatData, start, fragIndex) {
|
|
542
|
+
|
|
552
543
|
let self = this
|
|
553
|
-
let
|
|
554
|
-
|
|
555
|
-
let framesIndex = self.videoKeyFrames.map((item) => item.idx)
|
|
556
|
-
let _samples = self.getSamplesByOrders('video', framesIndex[fragIndexStart], framesIndex[fragIndexEnd])
|
|
557
|
-
let samples = _samples.map((item, idx) => {
|
|
558
|
-
return {
|
|
559
|
-
size: item.size,
|
|
560
|
-
duration: item.time.duration,
|
|
561
|
-
offset: item.time.offset,
|
|
562
|
-
buffer: new Uint8Array(mdatData.slice(item.offset - start, item.offset - start + item.size)),
|
|
563
|
-
key: idx === 0
|
|
564
|
-
}
|
|
565
|
-
})
|
|
566
|
-
resBuffers.push(this.addFragment({id: 1, time: 0, firstFlags: 0x2000000, flags: 0xf01, samples}))
|
|
567
|
-
}
|
|
544
|
+
let audioFlags = 0x701
|
|
545
|
+
|
|
568
546
|
let _samples = this.getSamplesByOrders(
|
|
569
|
-
'audio', this.audioKeyFrames[
|
|
570
|
-
? this.audioKeyFrames[
|
|
547
|
+
'audio', this.audioKeyFrames[fragIndex].order, this.audioKeyFrames[fragIndex + 1]
|
|
548
|
+
? this.audioKeyFrames[fragIndex + 1].order
|
|
571
549
|
: undefined)
|
|
572
550
|
let samples = _samples.map((item, idx) => {
|
|
573
551
|
return {
|
|
@@ -578,22 +556,60 @@ class MP4 {
|
|
|
578
556
|
key: idx === 0
|
|
579
557
|
}
|
|
580
558
|
})
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
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
|
|
587
567
|
})
|
|
588
|
-
|
|
568
|
+
}
|
|
569
|
+
createFragment (mdatData, start, fragIndex) {
|
|
570
|
+
let self = this
|
|
589
571
|
|
|
590
|
-
let
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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
|
|
595
598
|
})
|
|
596
|
-
|
|
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;
|
|
597
613
|
}
|
|
598
614
|
}
|
|
599
615
|
|