MCREC Player and Indexer now support indexes at the end of the file.
Ylian Saint-Hilaire committed
Feb 12, 2020 at 16:57 UTC
4ce64c06e9ab0c58545c364f9bf7203d9cd6c371
3 files changed
+90
-33
mcrec.js
+45
-26
@@ -35,15 +35,17 @@ function startEx(argv) {
35
state.recFileSize = fs.statSync(infile).size;
36
if (state.recFileSize < 32) { log("Invalid file: " + infile); return; }
37
log("Processing file: " + infile + ", " + state.recFileSize + " bytes.");
38
- state.recFile = fs.openSync(infile, 'r');
38
+ state.recFile = fs.openSync(infile, 'r+');
39
state.indexTime = 10; // Interval between indexes in seconds
40
state.lastIndex = 0; // Last time an index was writen in seconds
41
state.indexes = [];
42
state.width = 0;
43
state.height = 0;
44
state.basePtr = null;
45
- readLastBlock(state, function (state, result) {
45
+ readLastBlock(state, function (state, result, time, extras) {
46
if (result == false) { log("Invalid file: " + infile); return; }
47
+ if (extras != null) { log("File already indexed: " + infile); return; }
48
+ state.lastTimeStamp = time;
49
readNextBlock(state, processBlock);
50
});
51
}
@@ -62,7 +64,7 @@ function createIndex(state, ptr) {
64
}
65
66
function processBlock(state, block) {
65
- if (block == null) { writeIndexedFile(state, function () { log("Done."); }); return; }
67
+ if (block == null) { writeIndex(state, function () { log("Done."); }); return; }
68
var elapseMilliSeconds = 0;
69
if (state.startTime != null) { elapseMilliSeconds = (block.time - state.startTime); }
70
var flagBinary = (block.flags & 1) != 0;
@@ -164,29 +166,21 @@ function processBlock(state, block) {
166
readNextBlock(state, processBlock);
167
}
168
167
-function writeIndexedFile(state, func) {
168
- var outfile = state.recFileName;
169
- if (outfile.endsWith('.mcrec')) { outfile = outfile.substring(0, outfile.length - 6) + '-ndx.mcrec'; } else { outfile += '-ndx.mcrec'; }
170
- if (fs.existsSync(outfile)) { log("File already exists: " + outfile); return; }
171
- log("Writing file: " + outfile);
172
- state.writeFile = fs.openSync(outfile, 'w');
173
- state.metadata.indexInterval = state.indexTime;
174
- state.metadata.indexStartTime = state.startTime;
175
- state.metadata.indexes = state.indexes;
176
- var firstBlock = JSON.stringify(state.metadata);
177
- recordingEntry(state.writeFile, 1, state.metadataFlags, state.metadataTime, firstBlock, function (state) {
178
- var len = 0, buffer = Buffer.alloc(4096), ptr = state.dataStartPtr;
179
- while (ptr < state.recFileSize) {
180
- len = fs.readSync(state.recFile, buffer, 0, 4096, ptr);
181
- fs.writeSync(state.writeFile, buffer, 0, len);
182
- ptr += len;
183
- }
184
- func(state);
185
- }, state);
169
+function writeIndex(state, func) {
170
+ // Add the new indexes in extra metadata at the end of the file.
171
+ var extraMetadata = {};
172
+ extraMetadata.indexInterval = state.indexTime;
173
+ extraMetadata.indexStartTime = state.startTime;
174
+ extraMetadata.indexes = state.indexes;
175
+ recordingEntry(state.recFile, 4, 0, state.lastTimeStamp, JSON.stringify(extraMetadata), function (state) {
176
+ recordingEntry(state.recFile, 3, 0, state.recFileSize - 32, 'MeshCentralMCNDX', function (state) {
177
+ func(state);
178
+ }, state);
179
+ }, state, state.recFileSize - 32);
180
}
181
182
// Record a new entry in a recording log
189
-function recordingEntry(fd, type, flags, time, data, func, tag) {
183
+function recordingEntry(fd, type, flags, time, data, func, tag, position) {
184
try {
185
if (typeof data == 'string') {
186
// String write
@@ -196,7 +190,11 @@ function recordingEntry(fd, type, flags, time, data, func, tag) {
190
header.writeInt32BE(blockData.length, 4); // Size
191
header.writeIntBE(time, 10, 6); // Time
192
var block = Buffer.concat([header, blockData]);
199
- fs.write(fd, block, 0, block.length, function () { func(tag); });
193
+ if (typeof position == 'number') {
194
+ fs.write(fd, block, 0, block.length, position, function () { func(tag); });
195
+ } else {
196
+ fs.write(fd, block, 0, block.length, function () { func(tag); });
197
+ }
198
} else {
199
// Binary write
200
var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
@@ -205,7 +203,11 @@ function recordingEntry(fd, type, flags, time, data, func, tag) {
203
header.writeInt32BE(data.length, 4); // Size
204
header.writeIntBE(time, 10, 6); // Time
205
var block = Buffer.concat([header, data]);
208
- fs.write(fd, block, 0, block.length, function () { func(tag); });
206
+ if (typeof position == 'number') {
207
+ fs.write(fd, block, 0, block.length, position, function () { func(tag); });
208
+ } else {
209
+ fs.write(fd, block, 0, block.length, function () { func(tag); });
210
+ }
211
}
212
} catch (ex) { console.log(ex); func(state, tag); }
213
}
@@ -218,7 +220,24 @@ function readLastBlock(state, func) {
220
var size = buf.readInt32BE(4);
221
var time = (buf.readInt32BE(8) << 32) + buf.readInt32BE(12);
222
var magic = buf.toString('utf8', 16, 32);
221
- func(state, (type == 3) && (size == 16) && (magic == 'MeshCentralMCREC'));
223
+ if ((type == 3) && (size == 16) && (magic == 'MeshCentralMCNDX')) {
224
+ // Extra metadata present, lets read it.
225
+ extraMetadata = null;
226
+ var buf2 = Buffer.alloc(16);
227
+ fs.read(state.recFile, buf2, 0, 16, time, function (err, bytesRead, buf2) {
228
+ var xtype = buf2.readInt16BE(0);
229
+ var xflags = buf2.readInt16BE(2);
230
+ var xsize = buf2.readInt32BE(4);
231
+ var xtime = (buf2.readInt32BE(8) << 32) + buf.readInt32BE(12);
232
+ var buf3 = Buffer.alloc(xsize);
233
+ fs.read(state.recFile, buf3, 0, xsize, time + 16, function (err, bytesRead, buf3) {
234
+ func(state, true, xtime, JSON.parse(buf3.toString()));
235
+ });
236
+ });
237
+ } else {
238
+ // No extra metadata or fail
239
+ func(state, (type == 3) && (size == 16) && (magic == 'MeshCentralMCREC'), time, null);
240
+ }
241
});
242
}
243
views/default.handlebars
+1
@@ -10978,6 +10978,7 @@
10978
//
10979
10980
// Converts string to UTF8 byte array, polyfill for IE.
10981
+ // Following method is code from Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
10982
if (typeof TextEncoder === 'undefined') {
10983
window.TextEncoder=function TextEncoder(){};
10984
TextEncoder.prototype.encode = function encode(str) {
views/player.handlebars
+44
-7
@@ -28,9 +28,6 @@
28
<span id="deskstatus"></span>
29
</div>
30
</div>
31
- <div id=deskarea2 style="">
32
- <div class="areaProgress" style="cursor:pointer" onclick="progressBarSeek(event)"><div id="progressbar" style="height:6px;cursor:pointer"></div></div>
33
- </div>
31
<div id=deskarea3x style="max-height:calc(100vh - 58px);height:calc(100vh - 58px);" onclick="togglePause()">
32
<div id="bigok" style="display:none;left:calc((100vh / 2))"><b>✓</b></div>
33
<div id="bigfail" style="display:none;left:calc((100vh / 2))"><b>✗</b></div>
@@ -43,6 +40,9 @@
40
</div>
41
<div id=p11DeskConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=clearConsoleMsg()></div>
42
</div>
43
+ <div id=deskarea2 style="">
44
+ <div class="areaProgress" style="cursor:pointer" onclick="progressBarSeek(event)"><div id="progressbar" style="height:6px;cursor:pointer"></div></div>
45
+ </div>
46
<div id=deskarea4 class="areaFoot">
47
<div class="toright2">
48
<div id="timespan" style="padding-top:4px;padding-right:4px">00:00:00</div>
@@ -165,7 +165,24 @@
165
var flags = ReadShort(this.result, 2);
166
var size = ReadInt(this.result, 4);
167
var time = (ReadInt(this.result, 8) << 32) + ReadInt(this.result, 12);
168
- if ((type == 3) && (size == 16) && (this.result.substring(16, 32) == 'MeshCentralMCREC')) { func(type, flags, time); } else { func(-1); }
168
+ var magic = this.result.substring(16, 32);
169
+ if ((type == 3) && (size == 16) && (magic == 'MeshCentralMCNDX')) {
170
+ // Extra metadata present, lets read it.
171
+ var fr2 = new FileReader();
172
+ fr2.onload = function () {
173
+ var xtype = ReadShort(this.result, 0);
174
+ var xflags = ReadShort(this.result, 2);
175
+ var xsize = ReadInt(this.result, 4);
176
+ var xtime = (ReadInt(this.result, 8) << 32) + ReadInt(this.result, 12);
177
+ var extras = JSON.parse(this.result.substring(16));
178
+ func(type, flags, xtime, extras); // Include extra metadata
179
+ }
180
+ fr2.readAsBinaryString(recFile.slice(time, recFile.size - 32));
181
+ } else if ((type == 3) && (size == 16) && (magic == 'MeshCentralMCREC')) {
182
+ func(type, flags, time); // No extra metadata
183
+ } else {
184
+ func(-1); // Fail
185
+ }
186
};
187
fr.readAsBinaryString(recFile.slice(recFile.size - 32, recFile.size));
188
}
@@ -183,6 +200,7 @@
200
if ((type != 1) || (flags != 0)) { cleanup(); return; }
201
try { recFileMetadata = JSON.parse(data) } catch (ex) { cleanup(); return; }
202
if ((recFileMetadata == null) || (recFileMetadata.magic != 'MeshCentralRelaySession') || (recFileMetadata.ver != 1)) { cleanup(); return; }
203
+ if (recFileExtras) { for (var i in recFileExtras) { recFileMetadata[i] = recFileExtras[i]; } }
204
var x = '';
205
x += addInfo("Time", recFileMetadata.time);
206
if (recFileEndTime != 0) { var secs = Math.floor((recFileEndTime - time) / 1000); x += addInfo("Duration", format("{0} second{1}", secs, (secs > 1) ? 's' : '')); }
@@ -355,8 +373,17 @@
373
cleanup();
374
recFile = files[0];
375
recFilePtr = 0;
358
- readNextBlock(processFirstBlock);
359
- readLastBlock(function (type, flags, time) { if (type == 3) { recFileEndTime = time; } else { recFileEndTime = 0; } });
376
+ readLastBlock(function (type, flags, time, extras) {
377
+ if (type == 3) {
378
+ // File is ok
379
+ recFileEndTime = time;
380
+ recFileExtras = extras;
381
+ readNextBlock(processFirstBlock);
382
+ } else {
383
+ // This is not a good file
384
+ recFileEndTime = 0;
385
+ }
386
+ });
387
}
388
389
var dragtimer = null;
@@ -399,7 +426,17 @@
426
recFile = files[0];
427
recFilePtr = 0;
428
readNextBlock(processFirstBlock);
402
- readLastBlock(function (type, flags, time) { if (type == 3) { recFileEndTime = time; } else { recFileEndTime = 0; } });
429
+ readLastBlock(function (type, flags, time) {
430
+ if (type == 3) {
431
+ // File is ok
432
+ recFileEndTime = time;
433
+ recFileExtras = extras;
434
+ readNextBlock(processFirstBlock);
435
+ } else {
436
+ // This is not a good file
437
+ recFileEndTime = 0;
438
+ }
439
+ });
440
Q('OpenFileButton').blur();
441
}
442