@@ -27,21 +27,34 @@ public class VideoPlayer {
2727 private final File source ;
2828 private volatile int width ;
2929 private volatile int height ;
30+ private volatile ScanType scanType ;
3031 private volatile Asciifier asciifier ;
3132
3233 private volatile boolean running = false ;
3334 private volatile boolean paused = false ;
3435
3536 private Thread thread ;
3637
37- public VideoPlayer (OutputStream outputStream , File source , int width , int height , Asciifier asciifier ) {
38+ private long frameCount = 0 ;
39+ private long lastFrameWriteDelayMicros = 0 ;
40+
41+ public VideoPlayer (OutputStream outputStream , File source , int width , int height , ScanType scanType , Asciifier asciifier ) {
3842 this .outputStream = outputStream ;
3943 this .source = source ;
4044 this .width = width ;
4145 this .height = height ;
46+ this .scanType = scanType ;
4247 this .asciifier = asciifier ;
4348 }
4449
50+ public ScanType getScanType () {
51+ return scanType ;
52+ }
53+
54+ public void setScanType (ScanType scanType ) {
55+ this .scanType = scanType ;
56+ }
57+
4558 public void setResolution (int width , int height ) {
4659 this .width = width ;
4760 this .height = height ;
@@ -180,15 +193,9 @@ private void run() {
180193 BufferedImage image = converter .convert (imageFrame );
181194 imageFrame .close ();
182195
183- String prefix = "" ;
184- if (!asciifier .isColor ()) {
185- prefix += ConsoleUtils .getForegroundResetCode ();
186- }
187- if (!asciifier .isFullPixel ()) {
188- prefix += ConsoleUtils .getBackgroundResetCode ();
189- }
190196
191- String text = prefix + ConsoleUtils .getResetCursorPositionEscapeCode () + asciifier .createFrame (image );
197+
198+ String [] text = asciifier .createFrame (image ) ;
192199
193200 atomicQueueSize .incrementAndGet ();
194201 imageExecutor .submit (() -> {
@@ -203,7 +210,7 @@ private void run() {
203210 if (delayMicros < 0 && queueSize > 1 ) return ; // we're behind! skip the frame.
204211
205212 // recalculate delta
206- delayMicros = imageFrame .timestamp - playbackTimer .elapsedMicros ();
213+ delayMicros = imageFrame .timestamp - playbackTimer .elapsedMicros () - lastFrameWriteDelayMicros ;
207214 // if video is faster than audio
208215 if (delayMicros > 0 ) {
209216 // wait for audio to catch up with the video
@@ -228,8 +235,25 @@ private void run() {
228235 }
229236 }
230237
231- writer .write (text );
232- writer .flush ();
238+ long startTime = System .nanoTime ();
239+
240+ String prefix = "" ;
241+ if (!asciifier .isColor ()) {
242+ prefix += ConsoleUtils .getForegroundResetCode ();
243+ }
244+ if (!asciifier .isFullPixel ()) {
245+ prefix += ConsoleUtils .getBackgroundResetCode ();
246+ }
247+
248+ writer .write (prefix + ConsoleUtils .getResetCursorPositionEscapeCode ());
249+
250+ writeFrame (writer , text );
251+
252+ long endTime = System .nanoTime ();
253+
254+ lastFrameWriteDelayMicros = TimeUnit .NANOSECONDS .toMicros (endTime - startTime );
255+
256+ frameCount ++;
233257 });
234258 });
235259 } else if (frame .samples != null ) { // if frame is audio frame
@@ -303,6 +327,50 @@ private void run() {
303327 paused = false ;
304328 }
305329
330+ private void writeFrame (PrintWriter writer , String [] frame ) {
331+ switch (scanType ) {
332+ case INTERLACED -> writeInterlaced (writer , frame , false );
333+ case INTERLACED_NO_PREV -> writeInterlaced (writer , frame , true );
334+ case PROGRESSIVE -> writeProgressive (writer , frame );
335+ default -> throw new IllegalStateException ("Unexpected value: " + scanType );
336+ }
337+ }
338+
339+ private void writeInterlaced (PrintWriter writer , String [] frame , boolean clearPrevious ) {
340+ boolean even = (frameCount % 2 ) == 0 ;
341+
342+ for (int i = 0 ; i < frame .length ; i ++) {
343+ boolean lineEven = (i % 2 == 0 );
344+
345+ boolean shouldNewLine = !((i + 1 ) % frame .length == 0 );
346+
347+ if (even == lineEven ) {
348+ writer .write (frame [i ]);
349+ if (shouldNewLine ) {
350+ writer .write ("\n " );
351+ }
352+ } else if (shouldNewLine ) {
353+ if (!clearPrevious ) {
354+ writer .write ("\033 [B" );
355+ } else {
356+ writer .write (ConsoleUtils .getBackgroundResetCode () + "\033 [2K\n " );
357+ }
358+ }
359+ }
360+
361+ writer .flush ();
362+ }
363+
364+ private void writeProgressive (PrintWriter writer , String [] frame ) {
365+ for (int i = 0 ; i < frame .length ; i ++) {
366+ writer .write (frame [i ]);
367+ if (!((i + 1 ) % frame .length == 0 )) {
368+ writer .write ("\n " );
369+ }
370+ }
371+ writer .flush ();
372+ }
373+
306374 private void createThread () {
307375 if (running ) {
308376 throw new IllegalStateException ("This getVideos player is already running!" );
0 commit comments