#!/usr/bin/perl ## $Id: vid1,v 1.37 2015/06/04 15:35:41 pdc Exp $ use IO::Select; use IO::Socket; use Time::HiRes qw ( nanosleep ); $lsn = new IO::Socket::INET(Listen => 1, LocalPort => 1234); $s = IO::Select->new(\*STDIN, $lsn); $pause=0; $skip=1; # every 1 $step=-1; # >0 steps to go; =<0 normal $replay=0; # dont read more $D=0; $D0=3; # -d flag $DD=2; our $w; # w * h our $h; our $colorspace='yv12'; our $bufs=700; # ringbuf our $jumpstep=30; our $mb=1024; our $marks=6; # F5..F8, F9-F10 #### $ESC=qr"^\x1b"; # ESC $K=qr"^\x1b\["; # ESC [ $mplayer=0; $HEADER=0; $FH=0; $sync=0; # +bytes $repeat1=1; # repeat frame on pause $lastbuf=0; # last displayed frame - ibuf index $filesaved=0; $nodata=1; # counter (no data on wait cycle) $saveprefix="/usr/obj/pdc/saved-"; sub par1() { par1next: my $p=shift @ARGV; #print STDERR "got: $p\n" if defined($p); if(($o)=$p=~/^-(.+)/) { if ($o=~/^mp?/) { # mplayer #print STDERR "mplayer\n"; $mplayer=1; goto par1next; } elsif ($1=~/^(yv12|yuv4|i420)$i/) { # for mp $colorspace=$1; goto par1next; } elsif ($1=~/^b(\d+)$/) { # buffer $mb=$1; goto par1next; } elsif ($1=~/^S([-\d]+)$/) { # sync $sync=$1; goto par1next; } elsif ($1=~/^H(\d+)$/) { # header $HEADER=$1; goto par1next; } elsif ($1=~/^d(\d+)?$/) { # debug $D=$1; $D=$D0 if !defined($D); goto par1next; } elsif ($1=~/^FH(\d+)$/) { # frame header $FH=$1; goto par1next; } else { print STDERR "!!! Ignoring unknown option $p\n"; goto par1next; } } return $p; } $w=par1;$h=par1;$fn=par1; while(defined(par1)) {}; if(!defined($fn)) { print STDERR "Usage: $0 [options] [options] -m Run mplayer for output -b# Use # mbytes for ring buffer -H# File header - skip # bytes -FH# Frame header - skip # bytes -S# Sync adjustment (skip # bytes after frame) vid1 640 480 -m /tmp/640 vid1 640 480 -b2500 -m /tmp/640 vid1 640 480 -H35 -FH6 -yuv4 -m /tmp/640 vid1 800 600 -b2500 -m /tmp/800 vid1 1280 960 -m /tmp/1280 vid1 1280 960 -b2500 -m /tmp/1280 vid1 1280 960 -H36 -FH6 -yuv4 stream.yuv -m "; sleep 2; exit 1; } $fs=int($w*$h*3/2); $bufs=int($mb*1024*1024/$fs); $buf2=''; $every=0; $inbuf=0; $ibuf=0; # readpos $frames=0; # read frames $pos=0; $drop1=0; # drop tail $head1=0; # drop head our $out=\*STDOUT; @BUFS=(); use integer; $colorspace=~s'yuv4'i420'i; $colorspace=lc $colorspace; sub run_mplayer(){ my $MP="mplayer -vo vdpau:denoise=1,gl,xv, -demuxer rawvideo -rawvideo w=$w:h=$h:$colorspace -fps 60 -"; print STDERR "Starting: $MP\n"; open(O,"|$MP | wc ") || die"cant start mplayer\n"; $out=\*O; select $out; binmode $out; $|=1; }; select $out; binmode $out; $|=1; print STDERR "Buffer $mb mb for $bufs frames; mplayer:$mplayer\n"; select STDERR; $|=1; if($mplayer) { # start mplayer run_mplayer(); } # perldoc perlfaq8 sub cbreak() { # catch single keys system "stty cbreak /dev/tty 2>&1"; # bsd ## # linux } sub info1($) { # 0 or wait sec my $wait=shift; printf STDERR "inbuf $inbuf; pos $pos; lastbuf $lastbuf; pause $pause; replay $replay; skip $skip; step $step; buf %d, buf2 %d; sync=$sync; FH=$FH; H=$HEADER; drop1 $drop1; debug $D; ". join(' ', map("[$MARK[$_]]", 1..$marks)). "; \n", length($buf), length($buf2); sleep($wait) if $wait; } sub fix_sel() { if(!$pause && !$replay && $pos>=0 ) { # read file $s->add(\*I); } else { # skip file io print STDERR " nofile "; $s->remove(\*I); } }; sub pixel($$$$;$n) { # \$buf, $x,$y,$c; width use integer; my($buf,$x,$y,$c,$n)=@_; if($x<0 || $y<0 || $x>=$w || $y>=$h) { #print STDERR " FAIL ($x,$y)=$c "; return; }; if(defined($n)) { substr($$buf,$w*$y+$x,$n,(chr $c)x$n ); } else { substr($$buf,$w*$y+$x,1,chr $c ); } } sub out_frame($$$) { # out, buf, cnt-more my $out=shift; my $buf=shift; my $cnt=shift; my($i,$j,$x,$y); if(++$every>=$skip || $pause && $cnt<2 || $step>0 ) { #print STDOUT $$buf; if($test==1) { # substr EXPR,OFFSET,LENGTH,REPLACEMENT substr($$buf,$w*16,$w,0x0x(int $w) ); for $j (0..22) { for $i (100..200) { $x=$i; $y=$x+$j*7+50; pixel($buf,$x,$y,0x0,2); pixel($buf,$x,400-$y,0xff,3); }; }; } print $out $$buf; #if(1 ||$sync==0) { print $out $$buf } #elsif($sync>0) { print $out $$buf, chr(0)x$sync } #elsif($sync<0) { print $out substr($$buf,0,$fs+$sync) }; $every=0; } }; sub ring_add($) { my $buf=shift; $BUFS[$ibuf++]=$$buf; $lastbuf=$ibuf; if($ibuf>=$bufs){$ibuf=0} else { $inbuf=$ibuf if $ibuf>=$inbuf }; } sub pos2index($) { my $pos=shift; return $ibuf+$pos-1; } sub index2pos($) { my $i=shift; my $pos=$i-$ibuf+1; $pos-=$inbuf if $pos>0; return $pos; } sub ring_save($$$$) { # pos, dir, cnt, file my $pos=shift; my $dir=shift; my $cnt=shift; my $file=shift; my $i=$ibuf+$pos-1; # last print STDERR "ring_save( $pos, $dir, $cnt ) -> $file\n"; open(FILE,">>$file") || do { print STDERR "cant write to $file: $!\n"; return 0; }; $SAVEskip=$skip; $skip=1; # !!! while($cnt-->0) { $i+=$bufs if $i<0; $i-=$bufs if $i>=$bufs; #print STDERR "$inbuf in \@$pos\t$cnt: \tbuf[$i]\n"; print STDERR "\@$pos\t$cnt: \tbuf[$i]\n"; out_frame(\*FILE, \$BUFS[$i], $cnt); $lastbuf=$i; $i+=$dir; }; $skip=$SAVEskip; close FILE; return 1; } sub ring_play($$$) { # pos, dir, cnt my $pos=shift; my $dir=shift; my $cnt=shift; my $i=$ibuf+$pos-1; # last print STDERR "ring_play( $pos, $dir, $cnt )\n"; while($cnt-->0) { $i+=$bufs if $i<0; $i-=$bufs if $i>=$bufs; #print STDERR "$inbuf in \@$pos\t$cnt: \tbuf[$i]\n"; print STDERR "\@$pos\t$cnt: \tbuf[$i]\n"; out_frame($out, \$BUFS[$i], $cnt); $lastbuf=$i; $i+=$dir; } } sub beep() { print STDERR "\007"; } sub fix_pos() { # beep on edge if($pos<=-$inbuf+1) { $pos=-$inbuf+1; beep; #print STDERR "\007"; } if($pos>=0) { $pos=0; beep; #print STDERR "\007"; } } sub cmd1($) { # \$input #my $got=1; my $in=shift; while($$in ne '') { if($$in=~s%^ %%) { # pause (space) $pause=1-$pause; $replay=0; print STDERR "pause: $pause\n"; } elsif($$in=~s%^([0-9])%%) { # speed 1..9 $skip=$1; $skip=10 if !$skip; if($skip>7){ $skip=($skip-7)*8 }; print STDERR "skip: $skip\n"; } elsif($$in=~s%^q%%i) { # quit exit(0); } elsif($$in=~s%^t%%i) { # test $test++; $test=0 if $test>3; print STDERR "TEST $test\n"; } elsif($$in=~s%^M%%i) { # Mplayer restart if($mplayer) { print STDERR "Mplayer restart\n"; close $out; run_mplayer(); }; } elsif($$in=~s%${K}7~%%i) { # home $pos=-$inbuf; fix_pos(); $pause=1; ring_play($pos,-0,1); } elsif($$in=~s%${K}8~%%i) { # end $pos=0; fix_pos(); $pause=1; ring_play($pos,+0,1); } elsif($$in=~s%^(${K}D|\010)%%i) { # <-- BS --$pos; fix_pos(); ring_play($pos,-0,1); $pause=1; } elsif($$in=~s%${K}2~%%i) { # INS (info) info1(2); } elsif($$in=~s%${K}(28|29|3[1-4])~|^\x6([56789A"])%%i) { # shift+F5..F10 - set mark 1..6 my $mark; $mark=$1-27 if defined $1; $mark-- if $mark>2; if (defined $2) { if ($2 eq 'A') { $mark=$marks-1 } # f11 elsif($2 eq '"') { $mark=$marks } # f12 else { $mark=$2-4 } }; $MARK[$mark]=$lastbuf; print STDERR "MARK $mark set = $lastbuf\n"; info1(0); } elsif($$in=~s%${K}(15|17|18|19|20|21)~%%i) { # F5..F10 - goto mark 1..6 my $mark=$1-14; $mark-- if $mark>1; my $target=$MARK[$mark]; if(!defined $target) { beep; next }; $pos=index2pos($target); print STDERR "GOTO MARK $mark = $target / $pos\n"; fix_pos(); $pause=1; ring_play($pos,+0,1); info1(0); } elsif($$in=~s%${K}(13|14)~%%i) { # F3/F4 - view/save between last marks (f9-f10) $pause=1; my $pos1i=$MARK[$marks-1]; my $pos2i=$MARK[$marks]; my $pos1=index2pos $pos1i; my $pos2=index2pos $pos2i; if($pos2<$pos1) { my $a=$pos1; $pos1=$pos2; $pos2=$a; } print STDERR "FRAGMENT A-B: $pos1i-$pos2i -> $pos1..$pos2\n"; if($1==14) { # save my $orig=''; open(PREF,"<$fn.file") && do { my $put=; if($put=~/([a-z\d._-]+avi)/i) { $orig=$1; print STDERR "orig file: $orig\n"; $saveprefix="/usr/obj/pdc/$orig."; } close PREF; }; my $fn=sprintf "$saveprefix$$-\%03d.${w}x$h.$colorspace",$filesaved; ring_save( $pos1, +1, $pos2-$pos1+1, $fn ); print STDERR "File: $fn from $orig\n"; $filesaved++; } else { # play ring_play( $pos1, +1, $pos2-$pos1+1 ); } $pos=$pos2; } #elsif($$in=~s%^(\*)([+*-])%%i) { # Alt +- / +- elsif($$in=~s%^($ESC)?([+-])%%i) { # Alt +- / +- #print STDERR "KEY: $1.$2\n"; my $s=defined($1) ? $w : 1; if($2 eq '+') { $sync+=$s } else{ $sync-=$s }; print STDERR "SYNC $sync\n"; info1(0); } elsif($$in=~s%^(${K}C|\.)%%i) { # --> . right if($pos==0 and $1 eq '.') { # step . (read next if needed) print STDERR "STEP\n"; $pause=0; $replay=0; $step=1; } else { if(!$pos) { beep() }; # print STDERR "*BEEP* \07" }; $pos++; fix_pos(); ring_play($pos,+0,1); $pause=1; } } elsif($$in=~s%${K}A%%i) { # up my $need=$jumpstep; $need=-$pos if $pos+$need>0; ring_play($pos,+1,$need+1); $pos+=$need; fix_pos(); $pause=1; } elsif($$in=~s%${K}B%%i) { # down my $need=$jumpstep; $need=$inbuf+$pos-1 if $pos-$need<-$inbuf+1; ring_play($pos,-1,$need+1); $pos-=$need; fix_pos(); $pause=1; } elsif($$in=~s%^r|${K}13~%%i) { # replay (to end of buf) F3 #ring_play(-$jumpstep,1,$jumpstep+1); $pause=0; $replay=1; if($pos>=0 && $replay) { fix_pos(); $pause=1 }; } elsif($$in=~s%^b%%i) { # replay back ring_play(0,-1,$jumpstep+1); $pause=1; } elsif($$in=~s%^D%%) { # D = drop1 $drop1=1; } elsif($$in=~s%^d%%) { # d = debug $D=$D ^ $DD; } else { # ???? if($$in=~/\n$/s) {; # enter - continue normal play $pause=0; $replay=0; $sync=0; cbreak } elsif($$in =~ /$ESC$/) { print STDERR "ESC.. "; last }; # wait for more data $$in=''; } } } cbreak; $N=0; $reopen=1; while(1){ # print STDERR "<"; if($reopen && !$replay) { print STDERR "Open $fn..."; if( open(I,"+<$fn") ) { print STDERR "\rOPENED $fn\n"; $reopen=0; $readN=0; $head1=$HEADER; # skip header } else { print STDERR "\rcant open $fn!\n"; }; } #fix_sel(); while( (@READ=$s->can_read(0)) ) { #print STDERR "sels: ",$s->count,'+',$file->count,"; "; #print STDERR "ready: ",$#READ+1,"\n"; fix_sel(); # add/remove file for $fh (@READ) { if($fh==\*STDIN) { # terminal input my $in1; sysread(STDIN,$in1,1000); #print STDERR "STDIN... $in\n"; $in.=$in1; cmd1(\$in); } elsif($fh==\*I) { # replay=dont read if($pause || $replay) { next }; if(!$pause and $pos<0) { next }; # play from buf $N=length($buf); $buf1=''; if($N<$fs) { # need to read if(($head1 || $FH) && $readN<1) { $N1=sysread I,$buf1,$head1+$FH; $readN++; print STDERR $readN,".read $N1 = $head1+$FH \tskip header\n" if $D>2; $head1=0; next }; $N1=sysread I,$buf1,$fs; $readN++; print STDERR $readN,".read $N1 \t" if $D>2; if($N1==0) { # eof print STDERR " EOF "; $reopen=1; $s->remove(\*I); close I; #sleep 1; $buf=''; $buf2=''; next; } ; $nodata=0; } #if($drop1 && $buf eq ''){ $buf1=''; $drop1=0; }; $buf.=$buf1; $N=length($buf); if ($N1<$N1old && $drop1) { # possibly last frag $N1old=$N1; print STDERR " smaller - possibly end\n"; $readN=0; $drop1=0; goto fix1; } $N1old=$N1; if($N<$fs+$sync) { print STDERR ".." if $D>2; next }; $readN=0; #if(1 ||$sync==0) { print $out $$buf } if ($sync>0) { $buf.=chr(0)x$sync } elsif($sync<0) { printf STDERR "buf %d=%d -> %d/%d\n",$N,length($buf),$N+$sync,$fs+$sync; substr($buf,$N+$sync,$fs*10,'') }; $N=length($buf); # if($N>=$fs || $sync!=0 ) { # got full frame #if($N!=0 || $sync!=0 ) { # got full frame if($N!=$fs || $drop1) { fix1: print STDERR "!! bad frame $N +",$N-$fs,"!!\n"; if($N>$fs) { # cut excess $buf2=substr($buf,$fs,$fs*10); #if($drop1 && $buf eq ''){ $buf1=''; $drop1=0; print STDERR " DROP TAIL\n" }; substr($buf,$fs,$fs*10,''); } elsif($N<$fs) { # add lost $buf.=chr(0)x($fs-$N); } } $frames++; #=int($N/$fs); print STDERR "=$frames \t " if $D>1; print STDERR " fill buf[$ibuf] " if $D>1; ring_add(\$buf); if($step>0) { # $step--; $pause=1; } out_frame($out, \$buf, 255); $buf=$buf2; $buf2=''; print STDERR "\n" if $D>1; # } # else { # print STDERR "!! Skipping bad frame\n"; # } } elsif($fh==$lsn) { print STDERR "accept...\n"; $new=$lsn->accept; $s->add($new); } else { # socket? print STDERR "other: $fn...\n"; sysread($fh,$in,1000); print STDERR "got: $in\n"; #cmd1(\$in); } } # i/o handles }; # i/o ready #print STDERR ">"; # { nanosleep 100000000 }; if(!$pause and $pos<0) { # replay from buf #print STDERR "play all read...\n"; print STDERR " replay "; $pos++; ring_play($pos,+0,1); if($pos>=0 && $replay) { fix_pos(); $pause=1 }; } else { # normal play fix_sel(); # add/remove file ring_play($pos,+0,1) if $pause && $repeat1; info1(0) if $pause; my $num=scalar $s->can_read(4); # wait 4 sec $nodata++ if !$num; if($nodata && !$pause) { print STDERR "nodata: $nodata; " ; if ($nodata==1) { $buf=''; $buf2=''; print STDERR "RESET BUF ";}; }; } print STDERR "\n" if $D>1; } # exit __END__ http://en.wikipedia.org/wiki/YUV http://en.wikipedia.org/wiki/YUV#Y.27UV420p_.28and_Y.27V12_or_YV12.29 http://upload.wikimedia.org/wikipedia/en/thumb/0/0d/Yuv420.svg/800px-Yuv420.svg.png http://www.fourcc.org/yuv.php http://avisynth.org.ru/docs/russian/faq_yv12.htm http://pastebin.com/KH2ZfEB4 mkfifo /tmp/640 mkfifo /tmp/1280 vid1 640 480 /tmp/640 vid1 640 480 /tmp/640 -m mencoder -ovc raw -of rawvideo -o /tmp/640 c2-111104-0615-Fri.avi mencoder -ovc raw -of rawvideo -o /tmp/1280 c9-111116-0740-Wed.avi mplayer -vo yuv4mpeg:file=/tmp/640 -fps 100 0-111114-0600-Mon.avi mplayer -vo yuv4mpeg:file=/tmp/1280 -fps 100 vid1 640 480 /tmp/640 | mplayer -demuxer rawvideo -rawvideo w=640:h=480:yv12 - | wc vid1 640 480 /tmp/640 | mplayer -demuxer rawvideo -rawvideo w=640:h=480:yv12 -fps 60 - | wc vid1 640 480 /tmp/640 | mplayer -demuxer rawvideo -rawvideo w=640:h=480:yv12 -fps 100 - | wc net.local.stream.sendspace=512000 net.local.stream.recvspace=512000 net.local.stream.sendspace=2048000 net.local.stream.recvspace=2048000 00000000 59 55 56 34 4d 50 45 47 32 20 57 31 32 38 30 20 |YUV4MPEG2 W1280 | 00000010 48 39 36 30 20 46 31 35 3a 31 20 49 70 20 41 31 |H960 F15:1 Ip A1| 00000020 3a 31 0a 46 52 41 4d 45 0a de e4 00 00 05 01 f1 |:1.FRAME.чД....Я| 00000030 eb ec e5 e8 e3 e5 e5 ec f2 e2 00 e5 e4 eb eb eb 00000000 59 55 56 34 4d 50 45 47 32 20 57 36 34 30 20 48 |YUV4MPEG2 W640 H| 00000010 34 38 30 20 46 31 35 3a 31 20 49 70 20 41 31 3a |480 F15:1 Ip A1:| 00000020 31 0a 46 52 41 4d 45 0a 42 00 03 49 00 00 3a 40 |1.FRAME.B..I..:@| 00000030 42 40 43 39 40 3d 3a 3d 40 3e 3f 40 40 40 40 40 cat c2-110920-1740.avi.15173-8.640x480.yv12 | mencoder -ovc x264 -x264encopts tune=stillimage:ratetol=10 -demuxer rawvideo -fps 15 -rawvideo w=640:h=480:yv12 - -o c2-110920-1740.avi.15173-8.640x480.avi c2-110725-0741.keep.avi.94407-000.640x480.yv12 mplayer -demuxer rawvideo -rawvideo w=640:h=480:yv12 c2-110725-0741.keep.avi.94407-012.640x480.yv12 cat c2-110725-0741.keep.avi.*.yv12 | mencoder -ovc x264 -x264encopts tune=stillimage:ratetol=10 -demuxer rawvideo -fps 15 -rawvideo w=640:h=480:yv12 - -o c2-110725-0741.keep.select.avi