-
Notifications
You must be signed in to change notification settings - Fork 3
/
mkv.js
80 lines (64 loc) · 2.53 KB
/
mkv.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
var mkv = require('matroska')
function onlySeekCuesAndTracks() {
return {
skipTags: {
SimpleBlock: true,
Void: true,
Block: true,
FileData: true,
Cluster: true
}
};
}
function atPath() {
var args = Array.prototype.slice.call(arguments)
var arg // string
var data = args.shift() // object
if (! data) return
while (arg = args.shift()) {
if (! arg) return data;
if (! data.children) return;
data = data.children.filter(function(x) { return x._name === arg })[0]
if (! data) return
}
return data
}
function findById(all, name) {
return all.filter(function(x) { return x._name === name })[0]
}
function getForMkv(url, cb) {
var decoder = new mkv.Decoder(onlySeekCuesAndTracks());
decoder.parseEbmlIDs(url, [ mkv.Schema.byName.Cues, mkv.Schema.byName.Tracks ], function(err, doc) {
if (err) return cb(err);
// First, select the video track
var videoTrackIdx = -1; // initial value
var tracks = atPath(doc, "Segment", "Tracks");
tracks.children.forEach(function(track) {
if (! track.children) return;
// https://matroska.org/technical/specs/index.html#Tracks
var trackNum = findById(track.children, "TrackNumber").getUInt(); // TrackNumber
var trackType = findById(track.children, "TrackType").getUInt(); // TrackType (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control).
if (trackType === 1) videoTrackIdx = trackNum;
});
if (videoTrackIdx === -1) return cb(new Error('no video tracks found'))
// Go through CuePoint(s) and filter out the ones which are from the video track
var cues = atPath(doc, "Segment", "Cues");
if (! (cues && cues.children && cues.children.length)) return cb(new Error("no cues found in doc -> Segment -> Cues"));
cues = cues.children.filter(function(x) { return x._name === "CuePoint" })
if (! cues.length) return cb(new Error("no CuePoints"));
var frames = cues.filter(function(cue) {
// children[1] is CueTrackPositions; first child of that is CueTrack
// we need that to determine if this is a part of the video track
return cue.children[1].children[0].getUInt() === videoTrackIdx
}).map(function(cue) {
// children[0] is CueTime
// judging by this muxer, timestamp is pts: https://www.ffmpeg.org/doxygen/0.6/matroskaenc_8c-source.html#l00373
var t = cue.children[0].getUInt()
return { timestamp: t, pts: t, dts: t }
})
//if (frames[0] && frames[0].timestamp !== 0) frames.unshift({ timestamp: 0 })
cb(null, frames)
// "doc -> Segment -> Cues -> CuePoint []"
})
}
module.exports = getForMkv