in spec-main/video-helpers.js [33:247]
function ToWebM (frames) {
const info = checkFrames(frames);
// max duration by cluster in milliseconds
const CLUSTER_MAX_DURATION = 30000;
const EBML = [
{
id: 0x1a45dfa3, // EBML
data: [
{
data: 1,
id: 0x4286 // EBMLVersion
},
{
data: 1,
id: 0x42f7 // EBMLReadVersion
},
{
data: 4,
id: 0x42f2 // EBMLMaxIDLength
},
{
data: 8,
id: 0x42f3 // EBMLMaxSizeLength
},
{
data: 'webm',
id: 0x4282 // DocType
},
{
data: 2,
id: 0x4287 // DocTypeVersion
},
{
data: 2,
id: 0x4285 // DocTypeReadVersion
}
]
},
{
id: 0x18538067, // Segment
data: [
{
id: 0x1549a966, // Info
data: [
{
data: 1e6, // do things in millisecs (num of nanosecs for duration scale)
id: 0x2ad7b1 // TimecodeScale
},
{
data: 'whammy',
id: 0x4d80 // MuxingApp
},
{
data: 'whammy',
id: 0x5741 // WritingApp
},
{
data: doubleToString(info.duration),
id: 0x4489 // Duration
}
]
},
{
id: 0x1654ae6b, // Tracks
data: [
{
id: 0xae, // TrackEntry
data: [
{
data: 1,
id: 0xd7 // TrackNumber
},
{
data: 1,
id: 0x73c5 // TrackUID
},
{
data: 0,
id: 0x9c // FlagLacing
},
{
data: 'und',
id: 0x22b59c // Language
},
{
data: 'V_VP8',
id: 0x86 // CodecID
},
{
data: 'VP8',
id: 0x258688 // CodecName
},
{
data: 1,
id: 0x83 // TrackType
},
{
id: 0xe0, // Video
data: [
{
data: info.width,
id: 0xb0 // PixelWidth
},
{
data: info.height,
id: 0xba // PixelHeight
}
]
}
]
}
]
},
{
id: 0x1c53bb6b, // Cues
data: [
// cue insertion point
]
}
// cluster insertion point
]
}
];
const segment = EBML[1];
const cues = segment.data[2];
// Generate clusters (max duration)
let frameNumber = 0;
let clusterTimecode = 0;
while (frameNumber < frames.length) {
const cuePoint = {
id: 0xbb, // CuePoint
data: [
{
data: Math.round(clusterTimecode),
id: 0xb3 // CueTime
},
{
id: 0xb7, // CueTrackPositions
data: [
{
data: 1,
id: 0xf7 // CueTrack
},
{
data: 0, // to be filled in when we know it
size: 8,
id: 0xf1 // CueClusterPosition
}
]
}
]
};
cues.data.push(cuePoint);
const clusterFrames = [];
let clusterDuration = 0;
do {
clusterFrames.push(frames[frameNumber]);
clusterDuration += frames[frameNumber].duration;
frameNumber++;
} while (frameNumber < frames.length && clusterDuration < CLUSTER_MAX_DURATION);
let clusterCounter = 0;
const cluster = {
id: 0x1f43b675, // Cluster
data: [
{
data: Math.round(clusterTimecode),
id: 0xe7 // Timecode
}
].concat(clusterFrames.map(function (webp) {
const block = makeSimpleBlock({
discardable: 0,
frame: webp.data.slice(4),
invisible: 0,
keyframe: 1,
lacing: 0,
trackNum: 1,
timecode: Math.round(clusterCounter)
});
clusterCounter += webp.duration;
return {
data: block,
id: 0xa3
};
}))
};
// Add cluster to segment
segment.data.push(cluster);
clusterTimecode += clusterDuration;
}
// First pass to compute cluster positions
let position = 0;
for (let i = 0; i < segment.data.length; i++) {
if (i >= 3) {
cues.data[i - 3].data[1].data[1].data = position;
}
const data = generateEBML([segment.data[i]]);
position += data.size || data.byteLength || data.length;
if (i !== 2) { // not cues
// Save results to avoid having to encode everything twice
segment.data[i] = data;
}
}
return generateEBML(EBML);
}