From 4ce64c06e9ab0c58545c364f9bf7203d9cd6c371 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 12 Feb 2020 16:57:05 -0800 Subject: [PATCH] MCREC Player and Indexer now support indexes at the end of the file. --- mcrec.js | 71 +++++++++++++++++++++++++--------------- views/default.handlebars | 1 + views/player.handlebars | 51 +++++++++++++++++++++++++---- 3 files changed, 90 insertions(+), 33 deletions(-) diff --git a/mcrec.js b/mcrec.js index 8f0da50a..c1d4cda0 100644 --- a/mcrec.js +++ b/mcrec.js @@ -35,15 +35,17 @@ function startEx(argv) { state.recFileSize = fs.statSync(infile).size; if (state.recFileSize < 32) { log("Invalid file: " + infile); return; } log("Processing file: " + infile + ", " + state.recFileSize + " bytes."); - state.recFile = fs.openSync(infile, 'r'); + state.recFile = fs.openSync(infile, 'r+'); state.indexTime = 10; // Interval between indexes in seconds state.lastIndex = 0; // Last time an index was writen in seconds state.indexes = []; state.width = 0; state.height = 0; state.basePtr = null; - readLastBlock(state, function (state, result) { + readLastBlock(state, function (state, result, time, extras) { if (result == false) { log("Invalid file: " + infile); return; } + if (extras != null) { log("File already indexed: " + infile); return; } + state.lastTimeStamp = time; readNextBlock(state, processBlock); }); } @@ -62,7 +64,7 @@ function createIndex(state, ptr) { } function processBlock(state, block) { - if (block == null) { writeIndexedFile(state, function () { log("Done."); }); return; } + if (block == null) { writeIndex(state, function () { log("Done."); }); return; } var elapseMilliSeconds = 0; if (state.startTime != null) { elapseMilliSeconds = (block.time - state.startTime); } var flagBinary = (block.flags & 1) != 0; @@ -164,29 +166,21 @@ function processBlock(state, block) { readNextBlock(state, processBlock); } -function writeIndexedFile(state, func) { - var outfile = state.recFileName; - if (outfile.endsWith('.mcrec')) { outfile = outfile.substring(0, outfile.length - 6) + '-ndx.mcrec'; } else { outfile += '-ndx.mcrec'; } - if (fs.existsSync(outfile)) { log("File already exists: " + outfile); return; } - log("Writing file: " + outfile); - state.writeFile = fs.openSync(outfile, 'w'); - state.metadata.indexInterval = state.indexTime; - state.metadata.indexStartTime = state.startTime; - state.metadata.indexes = state.indexes; - var firstBlock = JSON.stringify(state.metadata); - recordingEntry(state.writeFile, 1, state.metadataFlags, state.metadataTime, firstBlock, function (state) { - var len = 0, buffer = Buffer.alloc(4096), ptr = state.dataStartPtr; - while (ptr < state.recFileSize) { - len = fs.readSync(state.recFile, buffer, 0, 4096, ptr); - fs.writeSync(state.writeFile, buffer, 0, len); - ptr += len; - } - func(state); - }, state); +function writeIndex(state, func) { + // Add the new indexes in extra metadata at the end of the file. + var extraMetadata = {}; + extraMetadata.indexInterval = state.indexTime; + extraMetadata.indexStartTime = state.startTime; + extraMetadata.indexes = state.indexes; + recordingEntry(state.recFile, 4, 0, state.lastTimeStamp, JSON.stringify(extraMetadata), function (state) { + recordingEntry(state.recFile, 3, 0, state.recFileSize - 32, 'MeshCentralMCNDX', function (state) { + func(state); + }, state); + }, state, state.recFileSize - 32); } // Record a new entry in a recording log -function recordingEntry(fd, type, flags, time, data, func, tag) { +function recordingEntry(fd, type, flags, time, data, func, tag, position) { try { if (typeof data == 'string') { // String write @@ -196,7 +190,11 @@ function recordingEntry(fd, type, flags, time, data, func, tag) { header.writeInt32BE(blockData.length, 4); // Size header.writeIntBE(time, 10, 6); // Time var block = Buffer.concat([header, blockData]); - fs.write(fd, block, 0, block.length, function () { func(tag); }); + if (typeof position == 'number') { + fs.write(fd, block, 0, block.length, position, function () { func(tag); }); + } else { + fs.write(fd, block, 0, block.length, function () { func(tag); }); + } } else { // Binary write 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) { header.writeInt32BE(data.length, 4); // Size header.writeIntBE(time, 10, 6); // Time var block = Buffer.concat([header, data]); - fs.write(fd, block, 0, block.length, function () { func(tag); }); + if (typeof position == 'number') { + fs.write(fd, block, 0, block.length, position, function () { func(tag); }); + } else { + fs.write(fd, block, 0, block.length, function () { func(tag); }); + } } } catch (ex) { console.log(ex); func(state, tag); } } @@ -218,7 +220,24 @@ function readLastBlock(state, func) { var size = buf.readInt32BE(4); var time = (buf.readInt32BE(8) << 32) + buf.readInt32BE(12); var magic = buf.toString('utf8', 16, 32); - func(state, (type == 3) && (size == 16) && (magic == 'MeshCentralMCREC')); + if ((type == 3) && (size == 16) && (magic == 'MeshCentralMCNDX')) { + // Extra metadata present, lets read it. + extraMetadata = null; + var buf2 = Buffer.alloc(16); + fs.read(state.recFile, buf2, 0, 16, time, function (err, bytesRead, buf2) { + var xtype = buf2.readInt16BE(0); + var xflags = buf2.readInt16BE(2); + var xsize = buf2.readInt32BE(4); + var xtime = (buf2.readInt32BE(8) << 32) + buf.readInt32BE(12); + var buf3 = Buffer.alloc(xsize); + fs.read(state.recFile, buf3, 0, xsize, time + 16, function (err, bytesRead, buf3) { + func(state, true, xtime, JSON.parse(buf3.toString())); + }); + }); + } else { + // No extra metadata or fail + func(state, (type == 3) && (size == 16) && (magic == 'MeshCentralMCREC'), time, null); + } }); } diff --git a/views/default.handlebars b/views/default.handlebars index 52fca9e7..c307e0be 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -10978,6 +10978,7 @@ // // Converts string to UTF8 byte array, polyfill for IE. + // Following method is code from Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder if (typeof TextEncoder === 'undefined') { window.TextEncoder=function TextEncoder(){}; TextEncoder.prototype.encode = function encode(str) { diff --git a/views/player.handlebars b/views/player.handlebars index 9c7da1e5..f39fe627 100644 --- a/views/player.handlebars +++ b/views/player.handlebars @@ -28,9 +28,6 @@ -
-
-
@@ -43,6 +40,9 @@
+
+
+
00:00:00
@@ -165,7 +165,24 @@ var flags = ReadShort(this.result, 2); var size = ReadInt(this.result, 4); var time = (ReadInt(this.result, 8) << 32) + ReadInt(this.result, 12); - if ((type == 3) && (size == 16) && (this.result.substring(16, 32) == 'MeshCentralMCREC')) { func(type, flags, time); } else { func(-1); } + var magic = this.result.substring(16, 32); + if ((type == 3) && (size == 16) && (magic == 'MeshCentralMCNDX')) { + // Extra metadata present, lets read it. + var fr2 = new FileReader(); + fr2.onload = function () { + var xtype = ReadShort(this.result, 0); + var xflags = ReadShort(this.result, 2); + var xsize = ReadInt(this.result, 4); + var xtime = (ReadInt(this.result, 8) << 32) + ReadInt(this.result, 12); + var extras = JSON.parse(this.result.substring(16)); + func(type, flags, xtime, extras); // Include extra metadata + } + fr2.readAsBinaryString(recFile.slice(time, recFile.size - 32)); + } else if ((type == 3) && (size == 16) && (magic == 'MeshCentralMCREC')) { + func(type, flags, time); // No extra metadata + } else { + func(-1); // Fail + } }; fr.readAsBinaryString(recFile.slice(recFile.size - 32, recFile.size)); } @@ -183,6 +200,7 @@ if ((type != 1) || (flags != 0)) { cleanup(); return; } try { recFileMetadata = JSON.parse(data) } catch (ex) { cleanup(); return; } if ((recFileMetadata == null) || (recFileMetadata.magic != 'MeshCentralRelaySession') || (recFileMetadata.ver != 1)) { cleanup(); return; } + if (recFileExtras) { for (var i in recFileExtras) { recFileMetadata[i] = recFileExtras[i]; } } var x = ''; x += addInfo("Time", recFileMetadata.time); 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 @@ cleanup(); recFile = files[0]; recFilePtr = 0; - readNextBlock(processFirstBlock); - readLastBlock(function (type, flags, time) { if (type == 3) { recFileEndTime = time; } else { recFileEndTime = 0; } }); + readLastBlock(function (type, flags, time, extras) { + if (type == 3) { + // File is ok + recFileEndTime = time; + recFileExtras = extras; + readNextBlock(processFirstBlock); + } else { + // This is not a good file + recFileEndTime = 0; + } + }); } var dragtimer = null; @@ -399,7 +426,17 @@ recFile = files[0]; recFilePtr = 0; readNextBlock(processFirstBlock); - readLastBlock(function (type, flags, time) { if (type == 3) { recFileEndTime = time; } else { recFileEndTime = 0; } }); + readLastBlock(function (type, flags, time) { + if (type == 3) { + // File is ok + recFileEndTime = time; + recFileExtras = extras; + readNextBlock(processFirstBlock); + } else { + // This is not a good file + recFileEndTime = 0; + } + }); Q('OpenFileButton').blur(); }