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>&checkmark;</b></div>
33 <div id="bigfail" style="display:none;left:calc((100vh / 2))"><b>&#10007;</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