!7 import org.jcodec.api.awt.AWTSequenceEncoder8Bit; static int framesToEncode = 100000; p { inputFilePath("Video to modify", voidfunc(final File video) { thread { invertVideo(video); } }); } svoid invertVideo(File video) { File outFile = prepareProgramFile(addSuffix("inverted-" + video.getName(), ".mp4")); final LeftQueue lq = new LeftQueue(video); final RightQueue rq = new RightQueue(outFile); lq.rightQueue = rq; stepThreads(1, lq, rq); } svoid stepThreads(final int interval, Steppable... engines) { final EngineGroup group = new EngineGroup(asList(engines)); new L threads; for (final Steppable s : engines) { Thread thread = new Thread("Stepper Thread") { public void run() ctex { s.step(); while licensed { while (s.shouldStep) { s.shouldStep = false; s.step(); } synchronized(s) { s.wait(interval); } } } }; s.thread = thread; threads.add(thread); } for (Thread t : threads) t.start(); } sclass EngineGroup { L engines; *(L *engines) { for (Steppable engine : engines) engine.group = this; } void shouldStep { for (Steppable engine : engines) engine.shouldStepLocally(); } } abstract sclass Steppable { volatile bool shouldStep; Thread thread; EngineGroup group; void shouldStep() { if (group != null) group.shouldStep(); else shouldStepLocally(); } void shouldStepLocally { shouldStep = true; synchronized(this) { notifyAll(); } } abstract void step(); } // decoding part Steppable > LeftQueue { Iterator stream; RightQueue rightQueue; *(File video) { stream = framesFromVideo_reordering(video); } void step { if (stream != null && rightQueue.needImage()) if (stream.hasNext()) { rightQueue.receive(stream.next()); shouldStep(); } else { stream = null; rightQueue.close(); } } } // encoding part Steppable > RightQueue { File outMP4; AWTSequenceEncoder8Bit enc; volatile BufferedImage img; int frames; volatile bool shouldClose; *(File *outMP4) ctex { enc = AWTSequenceEncoder8Bit.create25Fps(outMP4); enc.getEncoder().setKeyInterval(25); } void receive(BufferedImage img) { if (this.img != null) fail(); this.img = img; shouldStep(); } bool needImage() { ret enc != null && img == null; } void step ctex { if (shouldClose) { shouldClose = false; close(); } if (img != null && enc != null) { img = invertedImage(img); enc.encodeImage(img); img = null; ++frames; print("Frames: " + frames + "/" + framesToEncode); if (frames >= framesToEncode) shouldClose = true; else shouldStep(); } } void close ctex { if (enc != null) { enc.finish(); enc = null; print("Wrote " + f2s(outMP4) + " (" + frames + " frames)"); } } }