@@ -1,201 +0,0 @@ | |||
import * as std from "std"; | |||
function usage(msg) | |||
{ | |||
if ( msg ) | |||
print(msg); | |||
print("usage: ./qjs " + scriptArgs[0] + " [-p] -i <input.json> -s <script.js> -o <output.json>"); | |||
return 1; | |||
} | |||
//--------------------------------------------------------------------- | |||
// no lf, no space | |||
const pflags_def = [ [ false, false ] ]; | |||
function get_pflags(jso) | |||
{ | |||
if ( jso === "fcode" || jso === "bcode" ) | |||
{ | |||
return [ | |||
[ true, false ] | |||
]; | |||
} | |||
else if ( jso === "forward" || jso === "backward" ) | |||
{ | |||
return [ | |||
[ false, false ], | |||
[ true, false ], | |||
[ true, true ] | |||
]; | |||
} | |||
return pflags_def; | |||
} | |||
function print_lf(o_file, level) | |||
{ | |||
o_file.puts('\n'); | |||
o_file.printf("%*s", level * 2, ""); | |||
} | |||
function print_space(o_file) | |||
{ | |||
o_file.puts(' '); | |||
} | |||
function output_lf(o_file, pflags, level) | |||
{ | |||
const json_pflags_no_lf = pflags[0]; | |||
const json_pflags_no_space = pflags[1]; | |||
if ( !json_pflags_no_lf ) | |||
print_lf(o_file, level); | |||
else if ( !json_pflags_no_space ) | |||
print_space(o_file); | |||
} | |||
function json_print_element(o_file, jso, pflags, level) | |||
{ | |||
if ( typeof jso === "object" ) | |||
{ | |||
if ( jso === null ) | |||
{ | |||
o_file.puts("null"); | |||
} | |||
else if ( Array.isArray(jso) ) | |||
{ | |||
const pflags0 = pflags[0]; | |||
const pflags1 = pflags.slice(1); | |||
o_file.puts('['); | |||
for ( let i = 0; i < jso.length; i++ ) | |||
{ | |||
if ( i != 0 ) | |||
o_file.puts(','); | |||
output_lf(o_file, pflags0, level+1); | |||
json_print_element(o_file, jso[i], pflags1, level+1); | |||
} | |||
output_lf(o_file, pflags0, level); | |||
o_file.puts(']'); | |||
} | |||
else | |||
{ | |||
let i = 0; | |||
o_file.puts('{'); | |||
for ( const key in jso ) | |||
{ | |||
if ( i++ != 0 ) | |||
o_file.puts(','); | |||
print_lf(o_file, level+1); | |||
o_file.puts(JSON.stringify(key)); | |||
o_file.puts(':'); | |||
json_print_element(o_file, jso[key], get_pflags(key), level+1); | |||
} | |||
print_lf(o_file, level); | |||
o_file.puts('}'); | |||
} | |||
} | |||
else | |||
{ | |||
o_file.puts(JSON.stringify(jso)); | |||
} | |||
} | |||
function json_fputs(o_file, jso) | |||
{ | |||
json_print_element(o_file, jso, pflags_def, 0); | |||
o_file.puts('\n'); | |||
} | |||
/////////////////////////////////////////////////////////////////////// | |||
// global variables for the script | |||
var json_stream = null; | |||
var json_frame = null; | |||
function run_script(s_path, i_path, o_path, do_pretty) | |||
{ | |||
// load input JSON | |||
print("[+] loading input JSON file '" + i_path + "'") | |||
let i_file = std.open(i_path, "r"); | |||
if ( !i_file ) | |||
return usage("could not open input file '" + i_path + "'"); | |||
let str = i_file.readAsString(); | |||
let json_root = JSON.parse(str); | |||
if ( !json_root ) | |||
return usage("error parsing input file '" + i_path + "'"); | |||
i_file.close(); | |||
// will error out on exception | |||
print("[+] running script '" + s_path + "' on JSON data") | |||
std.loadScript(s_path); | |||
// for each stream (normally there is only one stream of interest) | |||
let streams = json_root["streams"]; | |||
for ( let i = 0; i < streams.length; i++ ) | |||
{ | |||
let stream = streams[i]; | |||
json_stream = stream; | |||
// for each frame in the stream | |||
let frames = stream["frames"]; | |||
for ( let j = 0; j < frames.length; j++ ) | |||
{ | |||
let frame = frames[j]; | |||
json_frame = frame; | |||
glitch_frame(frame); | |||
} | |||
} | |||
// open input JSON | |||
print("[+] writing " + (do_pretty ? "prettified " : "") + "output JSON file '" + o_path + "'") | |||
let o_file = std.open(o_path, "w+"); | |||
if ( !o_file ) | |||
return usage("could not open output file '" + o_path + "'"); | |||
if ( do_pretty ) | |||
json_fputs(o_file, json_root); | |||
else | |||
o_file.puts(JSON.stringify(json_root)); | |||
o_file.close(); | |||
return 0; | |||
} | |||
function main(argc, argv) | |||
{ | |||
let i_path = null; | |||
let o_path = null; | |||
let s_path = null; | |||
let do_pretty = false; | |||
for ( let i = 1; i < argc; ) | |||
{ | |||
let opt = argv[i++]; | |||
if ( opt == "-i" ) | |||
{ | |||
if ( i == argc ) | |||
return usage("parameter missing for option '-i'"); | |||
i_path = argv[i++]; | |||
} | |||
else if ( opt == "-o" ) | |||
{ | |||
if ( i == argc ) | |||
return usage("parameter missing for option '-o'"); | |||
o_path = argv[i++]; | |||
} | |||
else if ( opt == "-s" ) | |||
{ | |||
if ( i == argc ) | |||
return usage("parameter missing for option '-s'"); | |||
s_path = argv[i++]; | |||
} | |||
else if ( opt == "-p" ) | |||
{ | |||
do_pretty = true; | |||
} | |||
else | |||
{ | |||
return usage("unknown option '" + opt + "'"); | |||
} | |||
} | |||
if ( !s_path || !i_path || !o_path ) | |||
return usage(); | |||
return run_script(s_path, i_path, o_path, do_pretty); | |||
} | |||
main(scriptArgs.length, scriptArgs, this); |
@@ -1,131 +0,0 @@ | |||
#!/usr/bin/env python | |||
# run script on frames from input file: | |||
# ffglitch -i <file> -f <features> -s <script> -o <file> | |||
import argparse | |||
import os | |||
import subprocess | |||
import sys | |||
import tempfile | |||
import hashlib | |||
# Load json library (try first with fastest one) | |||
try: | |||
import orjson | |||
_json = orjson | |||
_dumps_ftype = 'wb' | |||
except: | |||
import json | |||
_json = json | |||
_dumps_ftype = 'w' | |||
parser = argparse.ArgumentParser() | |||
parser.add_argument('-i', metavar="<file>", dest='input', required=True, help='input media file') | |||
parser.add_argument('-f', metavar="<feature>", dest='feature', required=True, help='select feature to glitch') | |||
parser.add_argument('-s', metavar="<file>", dest='script', required=True, help='script to glitch frames') | |||
parser.add_argument('-o', metavar="<file>", dest='output', required=True, help='output media file') | |||
parser.add_argument('-v', action="store_true", dest='verbose', required=False, help='verbose output') | |||
parser.add_argument('-k', action="store_true", dest='keep', required=False, help='do not delete temporary JSON file') | |||
args = parser.parse_args() | |||
# Handle verbosity | |||
if args.verbose: | |||
stderr = sys.stdout | |||
else: | |||
stderr = open(os.devnull, 'w') | |||
# Generate input json file name | |||
json_in = os.path.splitext(args.input)[0] + ".json" | |||
# Check that input file name does not end in .json | |||
if json_in == args.input: | |||
raise ValueError('Input file name must not end in .json') | |||
# Function to get sha1sum of file | |||
def calc_sha1sum(filename): | |||
h = hashlib.sha1() | |||
b = bytearray(128*1024) | |||
mv = memoryview(b) | |||
with open(filename, 'rb', buffering=0) as f: | |||
for n in iter(lambda : f.readinto(mv), 0): | |||
h.update(mv[:n]) | |||
return h.hexdigest() | |||
# Try to read input json file | |||
json_root = None | |||
try: | |||
with open(json_in, 'r') as f: | |||
print("[+] checking existing JSON file '%s'" % json_in) | |||
json_root = _json.loads(f.read()) | |||
# check features | |||
features = json_root["features"] | |||
if len(features) != 1 or features[0] != args.feature: | |||
json_root = None | |||
print("[-] feature '%s' not found in '%s'" % (args.feature, json_in)) | |||
# check sha1sum | |||
sha1sum = json_root["sha1sum"] | |||
if len(sha1sum) != 40 or sha1sum != calc_sha1sum(args.input): | |||
json_root = None | |||
print("[-] sha1sum mismatch for '%s' in '%s'" % (args.input, json_in)) | |||
if json_root is not None: | |||
print("[+] OK") | |||
except IOError: | |||
pass | |||
ffedit_path = None | |||
def run_ffedit(cmd): | |||
global ffedit_path | |||
if ffedit_path == None: | |||
dir_path = os.path.dirname(os.path.realpath(__file__)) | |||
ffedit_path = [os.path.join(dir_path, "ffedit")] | |||
cmd = ffedit_path + cmd | |||
if args.verbose: | |||
print("[v] $ %s" % ' '.join(cmd)) | |||
return subprocess.check_output(cmd, stderr=stderr) | |||
# First pass (export data) | |||
if json_root is None: | |||
print("[+] exporting feature '%s' to '%s'" % (args.feature, json_in)) | |||
run_ffedit([args.input, "-f", args.feature, "-e", json_in]) | |||
with open(json_in, 'r') as f: | |||
json_root = _json.loads(f.read()) | |||
# Run script on JSON data. | |||
print("[+] running script '%s' on JSON data" % args.script) | |||
with open(args.script) as infile: | |||
exec(infile.read()) | |||
json_stream = None | |||
json_frame = None | |||
# for each stream (normally there is only one stream of interest) | |||
for stream in json_root["streams"]: | |||
json_stream = stream | |||
# for each frame in the stream | |||
for frame in stream["frames"]: | |||
json_frame = frame | |||
# if ffedit exported motion vectors | |||
if args.feature in frame: | |||
glitch_frame(frame[args.feature]) | |||
# Dump modified JSON data to temporary file. | |||
json_fd, json_out = tempfile.mkstemp(prefix='ffglitch_', suffix='.json') | |||
json_fp = os.fdopen(json_fd, _dumps_ftype) | |||
print("[+] dumping modified data to '%s'" % json_out) | |||
json_fp.write(_json.dumps(json_root)) | |||
json_fp.close() | |||
# Second pass (apply data). | |||
print("[+] applying modified data to '%s'" % args.output) | |||
ok = False | |||
try: | |||
run_ffedit([args.input, "-f", args.feature, "-a", json_out, args.output]) | |||
ok = True | |||
except subprocess.CalledProcessError as grepexc: | |||
vstr = " (rerun FFglitch with '-v')" if not args.verbose else "" | |||
print("[-] something went wrong%s" % vstr) | |||
# Remove temporary JSON file. | |||
if ok and not args.keep: | |||
os.unlink(json_out) | |||
else: | |||
print("[+] not deleting temporary file '%s'" % json_out) |
@@ -1,44 +0,0 @@ | |||
<Technical info about this build>: | |||
FFglitch 0.9.4 | |||
macOS | |||
64-bit | |||
You can get the source code here: | |||
http://ffglitch.org/pub/src/ffglitch-0.9.4.tar.xz | |||
FFglitch also includes the following libraries: | |||
quickjs 2021-03-27 <https://bellard.org/quickjs> | |||
zlib 1.2.11 <http://zlib.net> | |||
Xvid 1.3.6 <https://labs.xvid.com> | |||
<What?> | |||
FFglitch is a multimedia bitstream editor, based on the open-source | |||
project FFmpeg <http://ffmpeg.org>. | |||
For more information, go to <http://ffglitch.org>. | |||
<Who?> | |||
Hi, I’m Ramiro Polla <https://github.com/ramiropolla>. | |||
On my free time I like to work on useless projects. | |||
<GPL boilerplate>: | |||
Copyright (C) 2018-2021 Ramiro Polla | |||
This program is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU General Public License | |||
as published by the Free Software Foundation; either version 2 | |||
of the License, or (at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |