-
-
Save duangsuse/64c9ac7a278da48f4b3de3dafd70e9df to your computer and use it in GitHub Desktop.
Linux Terminal has the capability of displaying 8-bit colour, so why not play something on it?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mkdir -p src/main/java; cp *.java src/main/java | |
python mvn.py trumeet:asciiart:1.0-SNAPSHOT org.jcodec:-,--javase:0.2.3 >pom.xml | |
mvn install | |
you-get https://www.bilibili.com/video/av706 | |
mv *Bad\ Apple*.mp4 BadApple.mp4 | |
export LINES=$LINES COLUMNS=$COLUMNS | |
echo $COLUMNS x $LINES | |
mvn exec:java -Dexec.mainClass=JPlayer1 -Dexec.args=BadApple.mp4 | |
mvn exec:java -Dexec.mainClass=JPlayer -Dexec.args='BadApple_con.mp4 BadApple.mp4.wav' | |
# compile C++ OpenCV ver. | |
head -n 1 asciiv.cpp|tr -d '/' |sh | |
cvlc BadApple.mp4.wav 2&>/dev/null& ./a.out BadApple_con.mp4 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//g++ asciiv.cpp `pkgconf opencv --libs` | |
#include <iostream> | |
#include <string> | |
#include <chrono> | |
#include <thread> //sleep_for | |
#include <opencv2/core/mat.hpp> | |
#include <opencv2/imgproc/imgproc.hpp> //cvtColor, resize | |
#include <opencv2/videoio.hpp> | |
#include <opencv2/highgui/highgui.hpp> | |
#ifdef __unix | |
# include <sys/ioctl.h> //getwinsz | |
#endif | |
using string = std::string; | |
using Clock = std::chrono::high_resolution_clock; | |
using Time = std::chrono::milliseconds; | |
using Mat = cv::Mat; | |
typedef uint8_t Pix; | |
typedef cv::Point3_<Pix> Pix3; // R[GB] channel color used to sto fg/bg ANSI code | |
cv::Size whCon; | |
const char ANSI_E[] = "\x1b["; | |
static inline void writePixel(std::ostream& os, int gray) { | |
int cps[2]; | |
#define cp(fg,bg) cps[0]=fg, cps[1]=bg; break; | |
switch (gray) { | |
case 0 ... 63: cp(30, 40) | |
case 64 ... 126: cp(90, 100) | |
case 127 ... 189: cp(37, 47) | |
case 190 ... 255: cp(97, 107) | |
} | |
#undef cp | |
os<<ANSI_E<<cps[0]<<"m"<<ANSI_E<<cps[1]<<"m "; | |
} | |
static inline Mat pipe(Mat m) { | |
Mat mBW(m.size(), CV_8U); | |
Mat m1(whCon, mBW.type()); | |
cv::cvtColor(m, mBW, CV_BGR2GRAY); | |
cv::resize(mBW, m1, whCon); mBW.release(); | |
return m1; | |
} | |
void writeFrame1(Mat m) { | |
Mat m1 = pipe(m); | |
for (int i=0; i!=m1.rows; i++) { | |
Pix *p=m1.ptr<Pix>(i,0), *p1=p+m1.cols; | |
for (; p!=p1; p++) { writePixel(std::cout, *p); } | |
std::cout<<std::endl; | |
} | |
m1.release(); | |
} | |
static int colorFix = 0; | |
void writeFrame2C(Mat out) { | |
imshow("render", out); if (cv::waitKey(1) == 'p') std::cin >> colorFix; | |
for (int i=0; i!=out.rows; i++) { | |
auto p=out.ptr<Pix3>(i,0), p1=p+out.cols; | |
std::cout<<ANSI_E<<i<<'H'; | |
for (; p!=p1; p++) { std::cout<<ANSI_E<<colorFix+p->y<<"m"<<ANSI_E<<colorFix+p->x<<"m "; } | |
//std::cout<<std::endl; | |
} | |
out.release(); | |
} | |
cv::VideoWriter vwriter; | |
void writeFrame2(Mat m) { | |
Mat m1 = pipe(m); | |
Mat out(m1.size(), CV_8UC3); | |
#define cp(fg,bg) pixel.x=fg; pixel.y=bg; break; | |
out.forEach<Pix3>([&](Pix3& pixel, const int yx[]) -> void { | |
pixel.z = 0; // xyz, BGR order | |
auto px = m1.at<Pix>(yx[0], yx[1]); | |
switch (px) { | |
case 0 ... 63: cp(30, 40) | |
case 64 ... 126: cp(90, 100) | |
case 127 ... 189: cp(37, 47) | |
case 190 ... 255: cp(97, 107) | |
} | |
}); | |
#undef cp | |
m1.release(); | |
vwriter.write(out); | |
writeFrame2C(out); | |
} | |
typedef const char* cstr; | |
int ienv(cstr k, int deft = 0) { auto s = std::getenv(k); return (s!=nullptr)? atoi(s) : deft; } | |
int envFourcc() { cstr s = std::getenv("FOURCC"); if (s==nullptr) s="DIB "; return (strlen(s)==0)? -1 : cv::VideoWriter::fourcc(s[0],s[1],s[2],s[3]); } | |
void playAA(string fp) { | |
cv::VideoCapture vid(fp); | |
cv::namedWindow("frame", CV_WINDOW_AUTOSIZE); | |
double fps = vid.get(CV_CAP_PROP_FPS); | |
#define seek(d) vid.set(CV_CAP_PROP_POS_MSEC, vid.get(CV_CAP_PROP_POS_MSEC)+d*5000) | |
Time ft_ms{static_cast<int>(1000.0/fps)}; | |
// begin refreshing | |
int mech = ienv("curs"); | |
auto writeFrame = (mech==1)? writeFrame2 : (mech==2)? writeFrame2C : writeFrame1; | |
switch (mech) { case 1: vwriter.open("cache.avi", envFourcc(), fps, whCon); case 2: cv::namedWindow("render", CV_WINDOW_NORMAL); } | |
Mat img; | |
std::chrono::_V2::system_clock::time_point t, t1; | |
while (vid.read(img)) { | |
t = Clock::now(); | |
cv::imshow("frame", img); | |
switch (cv::waitKey(1)) { case '=': seek(1); break; case '-': seek(-1); break; case 'q': goto out; } | |
writeFrame(img); img.release(); | |
t1 = Clock::now(); | |
auto dt = (t1 - t); | |
if (dt < ft_ms) { | |
auto delay = ft_ms - dt; | |
std::this_thread::sleep_for(std::chrono::duration_cast<Time>(delay)); | |
} | |
} | |
out: vid.release(); vwriter.release(); | |
#undef seek | |
} | |
int main(int argc, char* argv[]) { | |
#ifdef __unix | |
struct winsize con; ioctl(0, TIOCGWINSZ, &con); | |
whCon = cv::Size(con.ws_col, con.ws_row); | |
#else | |
whCon = cv::Size(ienv("COLUMNS"), ienv("LINES")); | |
#endif | |
std::cout<<"Geo: "<<whCon<<std::endl; | |
for (int i=1; i<argc; i++) playAA(string(argv[i])); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.jcodec.api.FrameGrab; | |
import org.jcodec.common.io.NIOUtils; | |
import org.jcodec.common.model.Picture; | |
import org.jcodec.scale.AWTUtil; | |
import java.awt.image.BufferedImage; | |
import java.io.*; | |
import javax.sound.sampled.AudioInputStream; | |
import javax.sound.sampled.AudioSystem; | |
import javax.sound.sampled.Clip; | |
/** | |
* Linux Terminal has the capability of displaying 8-bit colour, so why not play something on it? | |
* | |
* Libraries: | |
* <pre> | |
* implementation 'org.jcodec:jcodec:0.2.5' | |
* implementation 'org.jcodec:jcodec-javase:0.2.5' | |
* </pre> | |
* | |
* Pre-scaled video required. Separate wav audio required. Scale the video so that the size will not exceed $COLUMNS pixels in width by $LINES pixels in height. | |
* $COLUMNS and $LINES variables required. | |
* WTFPL | |
* | |
* https://gist.github.com/Trumeet | |
* | |
* @author YuutaW | |
*/ | |
public class JPlayer { | |
public static void main(String... args) throws Throwable { | |
final int mspf = 1000 / 30; | |
final int columns = Integer.parseInt(System.getenv("COLUMNS")); | |
final int lines = Integer.parseInt(System.getenv("LINES")); | |
final int speed = Integer.parseInt(System.getenv("SPEED")); | |
final FrameGrab grab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(new File(args[0]))); | |
int[][] framebuffer = new int[columns][lines]; | |
AudioInputStream audioIn = AudioSystem.getAudioInputStream(new File(args[1])); | |
Clip clip = AudioSystem.getClip(); | |
clip.open(audioIn); | |
System.out.print("\033[2J"); | |
System.out.print("Press any key to start..."); | |
System.in.read(); | |
System.out.print("\033[?25l"); | |
Runtime.getRuntime().addShutdownHook(new Thread(() -> { | |
System.out.print("\033[?25h\u001B[0m"); | |
})); | |
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out), | |
2 ^ 32); | |
clip.start(); | |
while(true) { | |
final long start = System.currentTimeMillis(); | |
final Picture picture = grab.getNativeFrame(); | |
if(picture == null) break; | |
final BufferedImage image = AWTUtil.toBufferedImage(picture); | |
for (int row = 0; row < image.getHeight(); row ++) { | |
for (int column = 0; column < image.getWidth(); column ++) { | |
final int gray = image.getRGB(column, row) & 0xFF; | |
if(framebuffer[column][row] != gray) { | |
render(gray, row, column, writer); | |
} | |
framebuffer[column][row] = gray; | |
} | |
} | |
writer.flush(); | |
final long took = System.currentTimeMillis() - start; | |
if(took <= mspf) { | |
Thread.sleep((mspf - took) / speed); | |
} | |
} | |
} | |
private static void render(final int gray, final int line, final int column, | |
Writer writer) throws IOException { | |
int foregroundColorCode; | |
int backgroundColorCode; | |
if(gray <= 63) { | |
// Black. | |
foregroundColorCode = 30; | |
backgroundColorCode = 40; | |
} else if(gray <= 126) { | |
// Dark Gray | |
foregroundColorCode = 90; | |
backgroundColorCode = 100; | |
} else if(gray <= 189) { | |
// Light Gray | |
foregroundColorCode = 37; | |
backgroundColorCode = 47; | |
} else { | |
// White | |
foregroundColorCode = 97; | |
backgroundColorCode = 107; | |
} | |
// We don't use format here, since it's slow. | |
// '[%d;%dH[%dm[%dm ' | |
final StringBuilder stringBuilder = new StringBuilder() | |
.append("\033[") | |
.append(line) | |
.append(";") | |
.append(column) | |
.append("H\u001B[") | |
.append(foregroundColorCode) | |
.append("m\u001b[") | |
.append(backgroundColorCode) | |
.append("m "); | |
writer.write(stringBuilder.toString()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.File; | |
import java.awt.image.BufferedImage; | |
import javax.sound.sampled.AudioSystem; | |
import javax.sound.sampled.Clip; | |
import java.io.Writer; | |
import java.io.OutputStreamWriter; | |
import java.io.BufferedOutputStream; // faster than -Writer | |
import java.io.IOException; | |
import java.util.Arrays; | |
import java.util.HashMap; | |
import java.util.concurrent.atomic.AtomicInteger; // ringbuffer | |
import java.util.concurrent.BlockingQueue; // multi-thread | |
import java.util.concurrent.ArrayBlockingQueue; | |
import static java.lang.System.out; | |
import static org.jcodec.common.io.NIOUtils.readableChannel; | |
import static java.lang.System.nanoTime; | |
import org.jcodec.api.FrameGrab; | |
import org.jcodec.common.DemuxerTrackMeta; | |
import org.jcodec.common.model.Picture; | |
import static org.jcodec.scale.AWTUtil.toBufferedImage; | |
final public class JPlayer1 { | |
static private Utils u = Utils.INSTANCE; | |
enum Mechanism { Buffered, Posited, Sequed, SequedRst } | |
static public void main(String... args) { | |
JPlayer1 pp = new JPlayer1(u.ienv("NCHUNK", 5*60)); | |
pp.speed = u.ienv("SPEED", 1); | |
Mechanism m = Mechs.get(System.getenv("MECH")); | |
if (m != null) pp.mech = m; | |
for (String fp : args) | |
try { pp.play(new File(fp)); } | |
catch (Exception ex) { System.err.println(fp+":"); ex.printStackTrace(); } | |
} | |
static private HashMap<String, Mechanism> Mechs = new HashMap<>(); | |
static { | |
String[] ss = "b p s sr".split(" "); int i=0; | |
for (Mechanism m : Mechanism.class.getEnumConstants()) Mechs.put(ss[i++], m); | |
} | |
public JPlayer1(int chunkSize) { | |
if (chunkSize > 0) chunks = new String[chunkSize]; | |
else if (chunkSize == -1) rendered = new ArrayBlockingQueue(THREAD_CACHE_NCHUNK); | |
else if (chunkSize < 0) { System.err.println("ignore cache size <0: "+chunkSize); } | |
} | |
static int THREAD_CACHE_NCHUNK = 60*30; | |
Mechanism mech = Mechanism.Buffered; long speed = 1; | |
int w, h; | |
FrameGrab vid; double fps; Clip audDev; | |
protected int[] framebuffer/*refresh cache*/; | |
protected String[] chunks; | |
private BlockingQueue<String> rendered; | |
protected AtomicInteger iHead = new AtomicInteger(), iTail = new AtomicInteger(); | |
protected String linesep = System.lineSeparator(); | |
protected java.io.Console con = System.console(); | |
void generateAndOpen(File f_vid) { | |
w = divlow(2, u.ienv("COLUMNS")); h = divlow(2, u.ienv("LINES")); | |
File f_vid1 = u.genIfMissing(ffArgv(f_vid, "", "-y"/*overwrite*/, "-vf", String.format("scale=%d:%d", w,h))); | |
File f_aud = u.genIfMissing(ffArgv(f_vid, ".wav")); | |
try { | |
vid = FrameGrab.createFrameGrab(readableChannel(f_vid1)); | |
DemuxerTrackMeta md = vid.getVideoTrack().getMeta(); | |
fps = md.getTotalFrames()/md.getTotalDuration(); | |
audDev = AudioSystem.getClip(); | |
audDev.open(AudioSystem.getAudioInputStream(f_aud)); | |
} catch (Exception ex) { throw new RuntimeException("no open", ex); } | |
if (mech == Mechanism.Buffered) framebuffer = new int[w*h]; | |
con.printf("%s: geo [%dx%d]", f_vid1.getName(), w,h); | |
} | |
static private int divlow(int n, int x) { return (x/n)*n; } | |
static final int MS_NANOS = 1000000; | |
void play(File f_vid) throws IOException { | |
generateAndOpen(f_vid); | |
long ft_nanos = (long)(1000.0*MS_NANOS / fps); | |
con.printf(", %f fps =%dnanos, mechanism:%s cache %d, %dx", fps, ft_nanos, mech, (chunks!=null)? chunks.length : 0, speed); | |
out.println(); | |
java.io.OutputStream outs = out;//new BufferedOutputStream(out, (2+4)*w*h); | |
Writer o = new OutputStreamWriter(outs)/*buffering not necessary*/; | |
playIntro(o); | |
long t0 = nanoTime(); | |
if (chunks != null) generateChunk(); | |
if (rendered != null) { | |
new Thread(() -> { | |
try { while (true) rendered.put(renderToString(nextFrame())); } | |
catch (InterruptedException|NullPointerException _) { while (!rendered.isEmpty()); exitApp(); } | |
}, "render").start(); | |
} | |
audDev.start(); | |
try { | |
if (rendered == null) while (true) { //< MAIN draw loop | |
if (chunks != null) o.write(chunks[incPtr(iHead)]); | |
else onFrame(o, nextFrame()); | |
o.flush(); con.flush(); | |
if (chunks != null && samePtr(iHead, iTail)/*full*/) { generateChunk(); if (iHead == null) break; } | |
long t1 = nanoTime(); | |
long dt = (t1 - t0); | |
if (dt < ft_nanos) { | |
long delay = (ft_nanos - dt) / speed; | |
long start = nanoTime(); | |
do { con.flush(); if (!samePtr(iHead, iTail)) generateChunk(1); } while (nanoTime() - start < delay); | |
t0 = t1 + delay; | |
} | |
else { t0 = t1; } // count extra time to next frame dt. | |
} | |
else while (true) { // async | |
o.write(rendered.take()); | |
long t1 = nanoTime(); | |
long dt = (t1 - t0); | |
if (dt < ft_nanos) { | |
long delay = (ft_nanos - dt) / speed; //: any code reuse? | |
Thread.sleep(delay/MS_NANOS, (int)(delay%MS_NANOS)); | |
t0 = t1 + delay; | |
} else { t0 = t1; } | |
} | |
} catch (IOException|NullPointerException|InterruptedException _) {} | |
finally { audDev.stop(); audDev.close(); exitApp(); } | |
} | |
void exitApp() { System.exit(0); } // temp. solution for Thread break | |
void generateChunk() { generateChunk(chunks.length); } | |
void generateChunk(int n) { | |
for (int i=0; i<n; i++) { | |
BufferedImage img = nextFrame(); | |
if (img == null) { if (samePtr(iHead, iTail)) iHead = null; return; } | |
chunks[incPtr(iTail)] = renderToString(img); | |
} | |
} | |
String renderToString(BufferedImage img) { | |
Writer o = new java.io.StringWriter(); | |
onFrame(o, img); return o.toString(); | |
} | |
BufferedImage nextFrame() { | |
try { return toBufferedImage(vid.getNativeFrame()); } catch (IOException _) { throw new NullPointerException("done"); } | |
} | |
protected int incPtr(AtomicInteger ai) { return ai.getAndUpdate(i -> (i+1)%chunks.length); } | |
protected boolean samePtr(AtomicInteger a, AtomicInteger b) { return a.get() == b.get(); } | |
protected void onFrame(Writer o, BufferedImage img) { | |
for (int y=0; y<h; y++) for (int x=0; x<w; x++) { | |
int pix = img.getRGB(x,y); | |
int p = y*w+x, gray = pix & 0xFF; | |
switch (mech) { | |
case SequedRst: | |
if (x==0) u.ansi(o, y, 'H'); | |
u.ansi(o, cp_fg[gray],'m', cp_bg[gray], "m "); | |
break; | |
case Sequed: | |
if (x==0) try { o.write(linesep); o.flush(); } catch (IOException _) {} | |
u.ansi(o, cp_fg[gray],'m', cp_bg[gray], "m "); | |
break; | |
case Posited: | |
writePixelTo(o, x,y, gray); break; | |
case Buffered: | |
if (framebuffer[p] != gray) { writePixelTo(o, x,y, gray); framebuffer[p] = gray; } | |
break; | |
} | |
} | |
} | |
static void writePixelTo(Writer o, int x, int y, int gray) { u.ansi(o, y,";",x, 'H', cp_fg[gray],'m', cp_bg[gray], "m "); } | |
protected void playIntro(Writer o) { | |
out.print("Press to start..."); | |
try { System.in.read(); u.ansi(o, 2, 'J'); o.flush(); } | |
catch (IOException _) { throw new RuntimeException("cancelled stdin"); } | |
int code = 25/*hide_cursor*/; | |
u.ansi(o, "?",code, 'l'); //v ok to redo. | |
Runtime.getRuntime().addShutdownHook(new Thread(() -> u.ansi(o, "?",code, 'h', 0,'m'))); | |
} | |
static private String FF_I[] = {"ffmpeg", "-i"}; | |
static String[] ffArgv(File f_in, String out_suffix, String... args) { | |
String[] argv = new String[2+1+args.length+1]; | |
String oname = f_in.getName(); | |
u.arraymv(FF_I, 0, argv, 0); argv[2] = f_in.getPath(); | |
u.arraymv(args, 0, argv, 3); argv[argv.length-1] = (out_suffix=="")? u.mapRSplit(".", oname, 0, s->s+"_con") : oname+out_suffix; | |
return argv; | |
} | |
static private int cp_sw[] = {0,64,127,190,256}; | |
static int[] | |
cp_fg = u.toPrim(u.rangeSwitch(new Integer[]{30,90,37,97}, cp_sw)), | |
cp_bg = u.toPrim(u.rangeSwitch(new Integer[]{40,100,47,107}, cp_sw)); | |
static class Utils { | |
private Utils() {} static public Utils INSTANCE = new Utils(); | |
void ansi(Writer o, Object... cmd) { try { _ansi(o, cmd); } catch (IOException _) {} } // damn Java | |
private void _ansi(Writer o, Object... cmd) throws IOException { | |
o.write("\u001b["); | |
for (int i=0; i<cmd.length; i++) { | |
Object obj = cmd[i]; | |
if (obj instanceof Character&&i!=cmd.length -1) o.write((char)obj+"\u001b["); | |
else o.write(obj.toString()); | |
} | |
} | |
@FunctionalInterface | |
interface SFunc { String transform(String s); } | |
String mapRSplit(String sep, String s, int no, SFunc op) { | |
int iR = s.lastIndexOf(sep); if (iR == -1) return s; | |
return mapSSide(s,iR,no, op); | |
} | |
String mapSSide(String s, int i, int no, SFunc op) { | |
String a = s.substring(0,i), b = s.substring(i); | |
return (no==0)? op.transform(a)+b : a+op.transform(b); | |
} | |
int[] toPrim(Integer[] xs) { | |
int[] xs1 = new int[xs.length]; for (int i=0; i<xs1.length; i++) xs1[i] = xs[i]; | |
return xs1; | |
} | |
<T> T[] rangeSwitch(T[] values, int... stop_indexs) { | |
int[] a = stop_indexs; Arrays.sort(a); | |
int min=a[0], max=a[a.length-1]; | |
@SuppressWarnings("unchecked") | |
T[] res = (T[])java.lang.reflect.Array.newInstance(values[0].getClass(), (max-min)); | |
for (int i=0; i<a.length -1/*avoid a[lastIdx+1]*/; i+=1) { | |
Arrays.fill(res, a[i], a[i+1], values[i]); | |
} | |
return res; | |
} | |
void arraymv(Object[] src, int p, Object dst, int pd) { System.arraycopy(src, p, dst, pd, src.length); } | |
int ienv(String name) { return ienv(name, null); } | |
int ienv(String name, Integer deft) { String s = System.getenv(name); return (s!=null)? Integer.parseInt(s) : (deft!=null)? deft : _ithrow(new IllegalArgumentException("no environ "+name)); } | |
int _ithrow(RuntimeException ex) throws RuntimeException { throw ex; } | |
File genIfMissing(String... argv) { | |
File fOut = new File(argv[argv.length-1]); | |
if (!fOut.exists()) { | |
out.println(Arrays.toString(argv)); | |
int rn; try { rn = Runtime.getRuntime().exec(argv).waitFor(); } | |
catch (IOException|InterruptedException _) { rn = 1; } | |
if (rn != 0) throw new RuntimeException("failed exec "+Arrays.toString(argv)); | |
} | |
return fOut; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!python3 | |
from lxml import etree | |
from lxml.builder import E | |
from itertools import chain | |
def lets(o, *ops): [op(o) for op in ops] | |
def gavTo(e, g,a,v): [e.append(E(t, s)) for t,s in {"groupId":g, "artifactId":a, "version":v}.items()] | |
def gavSplit(s): (g,a,v) = s.split(':'); return [(g,a,v)] if ',' not in a else [(g,aa.replace("-", g.rsplit('.')[-1], 1),v) for aa in a.split(',')] | |
def listE(tag, op, xs): e = E(tag+"s" if tag[-1]!='y' else tag[:-1]+"ies"); [lets(E(tag), lambda ee: op(ee,x), e.append) for x in xs]; return e | |
def proj(coord, deps, javac="1.8"): | |
root = E("project", E("modelVersion", "4.0.0")); gavTo(root, *gavSplit(coord)[0]); gav = lambda e,c: gavTo(e,*c) | |
root.append(E("build", listE("plugin", gav, [("org.apache.maven.plugins", "maven-compiler-plugin", "3.1")] ) )) | |
root.find("build/plugins").getchildren()[0].append(E("configuration", E("source",javac), E("target",javac)) ) | |
root.append(listE("dependency", gav, chain.from_iterable(map(gavSplit, deps)) )); return root | |
from sys import argv | |
if __name__ == "__main__": | |
root = proj(argv[1], argv[2:]) | |
print(etree.tostring(root, pretty_print=True).decode()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from re import sub | |
from lxml import etree, builder | |
E=builder.E | |
def noun2(s): return sub("(y|s)$", lambda m: "ie" if m[1]=="y" else "", s)+"s" | |
def noun2(s): return s[:-1]+"ies" if s[-1]=="y" else s+"s" | |
for it in "word","words","ivy": print(noun2(it)) | |
def also(op, x): op(x); return x | |
def listE(tag, op): e=E(noun2(tag)); op(lambda: also(e.append, E(tag)) ); return e | |
def _s(s): return s.split(" ") | |
def got(o): return lambda *_: o | |
def got2nd(op, x): return lambda a: op(a, x) | |
def _gav(ae,c): e=ae(); [e.append(E(t,v)) for (t,v) in zip(_s("groupId artifactId version"), c)] | |
def gav(ae,s): c=(g,a,v)=s.split(":"); [_gav(ae, (g,aa.replace("-",g[g.rfind(".")+1:],1),v)) for aa in a.split(",")] if "," in a else _gav(ae,c) | |
root = E("project") | |
_gav(got(root), _s("h d c")) | |
root.append(listE("dependency", got2nd(gav,"org.h:-w,w1:d"))) | |
print(etree.tostring(root, pretty_print=1).decode()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from cv2 import VideoCapture, UMat, namedWindow, imshow, waitKey | |
from sys import stdout | |
AE = b"\x1b[" | |
def play(out, vid, ft=b"%d"): | |
unstop, frame = vid.read() | |
while unstop: | |
a = frame | |
imshow("frame", a); waitKey(1) | |
(n, m, nChn) = frame.shape | |
for i in range(n): | |
for s in AE,ft%i,b"H": out.write(s) | |
for j in range(m): | |
c = a[i, j] | |
for s in AE,ft%c[0],b"m",AE,ft%c[1],b"m ": out.write(s) | |
unstop, frame = vid.read() | |
namedWindow("frame", 0b0) | |
play(stdout.buffer, VideoCapture("cache.avi")) |
修改建议 : JPlayer.java 里 getenv("SPEED", deft) 的,提供默认值 1
JPlayer1 里 SPEED 因为 ienv
类型限制不好改 double ,但可以做成 delay * speed/1000
的千分之一比率
不安全的类型转换的,可以检查对应行,加 @SuppressWarnings("unchecked")
https://gist.github.com/duangsuse/64c9ac7a278da48f4b3de3dafd70e9df#file-jplayer1-java-L94
这里有个大问题,下面 finally
里 exitApp()
的会导致视频放不完就自动退出,思考一下什么方法,或者怎么修正就可以把 chunks
里剩余的帧弄完。
(还有一个小问题,可以先试着给 asciiv.cpp 支持 SPEED 环境变量)
我懒得改了,大家既然是学习也不在乎这些精力,非常抱歉 😭。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
mvn exec:java -Dexec.mainClass=JPlayer1 -Dexec.args=BadApple.mp4
54.07s user 8.04s system 119% cpu 51.925 totalmvn exec:java -Dexec.mainClass=JPlayer
42.69s user 7.59s system 120% cpu 41.633 totalYuuta 又赢了。 好了,睡觉吧,争取下次能写出性能更好的版本来 😂
*注: Yuuta 原版是 BufferedWriter+StringBuilder 缓冲的,没有多余 flush ,但我们为了避免帧撕裂 flush 了许多次(有效果的),从某种意义讲我们还是可以更快的
新版支持 MECH(屏幕刷新方式),NCHUNK,SPEED 参数, NCHUNK=0 时禁用 queue rendering, -1 时多线程 queue, 默认环形缓冲区 queue
为测试我给原版支持了 SPEED 参数,设置得足够大使 Thread.sleep 不会运行。