From 6f5f0eac77ad00804abe6908edc6a1445ce812bd Mon Sep 17 00:00:00 2001 From: mmcw-dev Date: Fri, 7 Dec 2018 19:10:34 -0500 Subject: [PATCH] Script is working for basic patterns --- .AppleDouble/.Parent | Bin 0 -> 741 bytes .gitignore | 1 + README.md | 23 ++++ frameloom | 308 +++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 41 ++++++ package.json | 15 +++ 6 files changed, 388 insertions(+) create mode 100644 .AppleDouble/.Parent create mode 100644 .gitignore create mode 100644 README.md create mode 100755 frameloom create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.AppleDouble/.Parent b/.AppleDouble/.Parent new file mode 100644 index 0000000000000000000000000000000000000000..b5a74465261f8032140f7052d42d36bd2442970a GIT binary patch literal 741 zcmZQz6=P>$V!#BvKp~(w(^IGvGmzc}#GF9P!oa||6)MgFR8tFN3qa(UdV#b85OV{? z4+3eRJOj`yCYX9YAbTTF3}^-eKajl^#BOkL4Fj@I0@)l5o__v7_C+u|IMNTuz6oK6 z)dATLfoztt%#_spoc#P;AZHW|*$_~kBXt>s8-P9l(S$w15r|8TIaFfJ!e%h*2HV_d Ih!EEe06Y^O&j0`b literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf58583 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# frameoloom + +Node script to generate flicker videos by interweaving frames from multiple videos + +-------- + +## Requirements + +This script relies on `ffmpeg` to export and stitch video back together + +Installation instructions for ffmpeg here: https://github.com/adaptlearning/adapt_authoring/wiki/Installing-FFmpeg + +## Installation + +git clone https://github.com/sixteenmillimeter/videoloom.git +cd videoloom +chmod +x videoloom +npm install + + +## Usage + +./videoloom -i /path/to/video1:/path/to/video2 -o /path/to/output diff --git a/frameloom b/frameloom new file mode 100755 index 0000000..75477df --- /dev/null +++ b/frameloom @@ -0,0 +1,308 @@ +#!/usr/bin/env node + +'use strict'; + +const execRaw = require('child_process').exec; +const os = require('os'); +const path = require('path'); +const program = require('commander'); +const fs = require('fs-extra'); + +let TMPDIR = os.tmpdir() || '/tmp'; +let TMPPATH; + +async function exec (cmd) { + return new Promise((resolve, reject) => { + return execRaw(cmd, (err, stdio, stderr) => { + if (err) return reject(err); + return resolve(stdio); + }); + }); +} + +async function clear () { + let exists; + try { + exists = await fs.exists(TMPPATH); + } catch (err) { + console.error(err) + } + + if (exists) { + console.log(`Clearing tmp directory ${TMPPATH}`) + try { + await exec(`rm -r "${TMPPATH}"`) + } catch (err) { + //suppress error + } + } + + try { + await fs.mkdir(TMPPATH) + } catch (Err) { + console.error(err); + } +} + +async function frames (video, order) { + let ext = 'tif'; + let tmpoutput; + let cmd; + + tmpoutput = path.join(TMPPATH, `export-%05d_${order}.${ext}`); + + cmd = `ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"` + + console.log(`Exporting ${video} as single frames...`) + + try { + await exec(cmd) + } catch (err) { + console.error('Error exporting video', err) + return process.exit(3) + } + return path.join(TMPPATH, `export_${order}`); +} + +function zeroPad (i, max = 5) { + let len = (i + '').length; + let str = i + ''; + for (let x = 0; x < max - len; x++) { + str = '0' + str; + } + return str; +} + +async function reorder (pattern, realtime) { + let frames; + let old; + let seqFile; + let seq; + console.log('Weaving frames...') + try { + frames = await fs.readdir(TMPPATH) + } catch (err) { + console.error('Error reading tmp directory', err) + } + + console.dir(frames) + frames = frames.filter (file =>{ + if (file.indexOf('.tif') !== -1) return true; + }); + //other patterns + + try { + seq = await patternSort(frames, pattern, realtime) + } catch (err) { + console.error('Error sorting frames') + } + console.dir(seq) + // +} + +function groupAlt (list, pattern, realtime) { + let groups = []; + let newList = []; + let frameCount = 0; + let oldPath; + let newName; + let newPath; + let ext = path.extname(list[0]); + + for (let g of pattern) { + groups.push([]); + } + for (let i = 0; i < list.length; i++) { + groups[i % pattern.length].push(list[i]); + } + for (let x = 0; x < list.length; x++) { + for (let g of pattern) { + for (let i = 0; i < g; i++) { + + /*oldPath = path.join(TMPPATH, list[i]); + newName = `./render_${zeroPad(frameCount)}${ext}`; + newPath = path.join(TMPPATH, newName); + + console.log(`Renaming ${list[i]} -> ${newName}`); + + try { + //fs.renameSync(oldPath, newPath) + newList.push(newName); + } catch (err) { + console.error(err); + }*/ + + frameCount++ + } + } + } + return newList +} + +async function patternSort (list, pattern, realtime = false) { + let frameCount = 0; + let stepCount; + let step; + let skipCount; + let skip; + let alt; + let ext = path.extname(list[0]); + let oldPath; + let newName; + let newPath; + let newList = []; + + for (let el of pattern) { + if (el !== 1) alt = true; + } + + if (realtime) { + skip = false; + skipCount = pattern.length + 1; + } + + if (!alt) { + for (let i = 0; i < list.length; i++) { + + if (realtime) { + skipCount--; + if (skipCount === 0) { + skip = !skip; + skipCount = pattern.length; + } + } + + oldPath = path.join(TMPPATH, list[i]); + + if (skip) { + console.log(`Skipping ${list[i]}`); + try { + await fs.unlink(oldPath) + } catch (err) { + console.error(err); + } + continue; + } + + newName = `./render_${zeroPad(frameCount)}${ext}`; + newPath = path.join(TMPPATH, newName); + console.log(`Renaming ${list[i]} -> ${newName}`); + + try { + await fs.rename(oldPath, newPath) + newList.push(newName); + } catch (err) { + console.error(err); + } + + frameCount++; + } + } else { + newList = groupAlt(list, pattern, realtime); + } + + return newList +} + +async function render (output) { + let exp = path.join(TMPPATH, `render_%05d.tif`); + let resolution = '1920x1080'; + let h264 = `-vcodec libx264 -g 1 -crf 25 -pix_fmt yuv420p`; + let prores = `-c:v prores -profile:v 3 -c:a pcm_s16le - g 1`; + let format = (output.indexOf('.mov') !== -1) ? prores : h264; + const cmd = `ffmpeg -r 24 -f image2 -s ${resolution} -i ${exp} ${format} -y ${output}`; + + console.log(`Exporting video ${output}`); + console.log(cmd); + + try { + await exec(cmd); + } catch (err) { + console.error(err); + } +} + +async function main (arg) { + let input = arg.input.split(':'); + let output = arg.output; + let pattern = []; + let realtime = false; + console.time('frameloom'); + + if (input.length < 2) { + console.error('Must provide more than 1 input'); + return process.exit(1); + } + + if (!output) { + console.error('Must provide video output path'); + return process.exit(2); + } + + if (arg.pattern) { + pattern = arg.pattern.split(':'); + pattern = pattern.map(el =>{ + return parseInt(el); + }) + } else { + for (let i = 0; i