teetime: Added file format FourCC: TTM1.

This commit is contained in:
Ole Tange 2023-04-11 00:38:50 +02:00
parent 3d2355f59d
commit 5ab7ad938f

View file

@ -44,7 +44,7 @@ Play back I<factor> times faster. 1.0 = actual speed.
=item B<-i> =item B<-i>
Read I<file> as input. Read I<file> as input. Use - to read from standard input.
=item B<--maxwait> I<maxwait> =item B<--maxwait> I<maxwait>
@ -53,19 +53,39 @@ Read I<file> as input.
Wait at most I<maxwait> seconds. Wait at most I<maxwait> seconds.
=back =back
=head1 EXAMPLES =head1 EXAMPLES
(sleep 0.5; echo After 0.5s; sleep 1.5; echo After 2s) | teetime myfile (sleep 0.5; echo After 0.5s; sleep 1.5; echo After 2s) |
(sleep 0.5; echo After 2.5s; sleep 1.5; echo After 4s) | teetime -a myfile teetime myfile.tt
teetime -i myfile (sleep 0.5; echo After 2.5s; sleep 1.5; echo After 4s) |
teetime -a myfile.tt
teetime -i myfile.tt
Play file using stdin:
cat myfile.tt | teetime -i -
Play it faster:
teetime -f 2 -i myfile.tt
=head1 File format
The .tt-format is simply:
Version: "TTM1"
Repeat: (
Wait ms: Unsigned 32-bit
Length of string: Unsigned 32-bit
String: string
)
=head1 AUTHOR =head1 AUTHOR
Copyright (C) 2020 Ole Tange, Copyright (C) 2020-2023 Ole Tange,
http://ole.tange.dk and Free Software Foundation, Inc. http://ole.tange.dk and Free Software Foundation, Inc.
@ -99,16 +119,39 @@ B<tee>
=cut =cut
sub bashtest1 {
q{
rand -s 8 | head -c 1G | teetime - |mbuffer -q | teetime -i - | pv | md5sum
rand -s 8 | head -c 1G | pv | md5sum
};
}
sub bashtest2 {
q{
doit() {
diff <(rand -s $1 |
head -c$1 |
md5sum) <(rand -s $1 |
teetime - |
teetime -i - |
head -c$1 |
md5sum);
}
export -f doit
find-first-fail -v doit
};
}
use strict; use strict;
use Getopt::Long; use Getopt::Long;
sub now { sub now {
# Returns time since epoch as in seconds with 3 decimals # Returns time since epoch in ms
# Uses: # Uses:
# @Global::use # @Global::use
# Returns: # Returns:
# $time = time now with millisecond accuracy # $time = in milliseconds
if(not $Global::use{"Time::HiRes"}) { if(not $Global::use{"Time::HiRes"}) {
if(eval "use Time::HiRes qw ( time );") { if(eval "use Time::HiRes qw ( time );") {
eval "sub TimeHiRestime { return Time::HiRes::time };"; eval "sub TimeHiRestime { return Time::HiRes::time };";
@ -122,22 +165,34 @@ sub now {
} }
sub readstdin { sub readstdin {
# Read stdin and save it in .tt-format
# If no file: save to stdout, but do not tee output
my $file = shift; my $file = shift;
my $last_time = now(); my $last_time = now();
my $rin = ''; my $rin = '';
my $in; my $in;
my $twogb = 2**31;
my ($read, $time, $delta); my ($read, $time, $delta);
open(my $fh, ($opt::append ? ">>" : ">"), $file) || die; my $fh;
my $save_to_stdout;
if(not defined $file or $file eq "-") {
$save_to_stdout = 1;
}
if($save_to_stdout) {
$fh = *STDOUT;
$| = 1;
} else {
open($fh, ($opt::append ? ">>" : ">"), $file) || die;
}
vec($rin, fileno(STDIN), 1) = 1; vec($rin, fileno(STDIN), 1) = 1;
# print FourCC file identifier
print $fh "TTM1";
while(1) { while(1) {
select($rin,undef,undef,undef); select($rin,undef,undef,undef);
$read = sysread(STDIN,$in,$twogb); $read = sysread(STDIN,$in,1000000);
$time = now(); $time = now();
$delta = $time - $last_time; $delta = $time - $last_time;
print $fh pack("L*",$delta,length $in),$in; print $fh pack("L*",$delta,length $in),$in;
print STDOUT $in; if(not $save_to_stdout) { print STDOUT $in; }
$last_time = $time; $last_time = $time;
if(not $read) { if(not $read) {
# Select says there is something to read, # Select says there is something to read,
@ -162,17 +217,39 @@ sub min(@) {
} }
sub readfile { sub readfile {
# Input is in .tt-format
my $file = shift; my $file = shift;
open(my $fh, "<", $file) || die; my $fh;
while(1) { if($file eq "-") {
$fh = *STDIN;
} else {
open($fh, "<", $file) || die;
}
my $fileformat;
if(sysread($fh,$fileformat,4)) {
if($fileformat eq "TTM1") {
read_ttm1($fh);
} else {
error("Unsupported file format: $fileformat");
}
}
}
sub read_ttm1 {
my $fh = shift;
my $in; my $in;
if(sysread($fh,$in,8)) { my $nread;
while(1) {
if($nread = sysread($fh,$in,8)) {
if($nread != 8) { die; }
# time in ms, length in bytes # time in ms, length in bytes
my ($delta,$length) = unpack("L*",$in); my ($delta,$length) = unpack("L*",$in);
$delta = min($delta/$opt::factor,$opt::maxwait*1000); $delta = min($delta/$opt::factor,$opt::maxwait*1000);
select(undef,undef,undef,$delta/1000); select(undef,undef,undef,$delta/1000);
sysread($fh,$in,$length); while($nread = sysread($fh,$in,$length)) {
$length -= $nread;
print $in; print $in;
}
} else { } else {
# Blocking sysread says there is something read, # Blocking sysread says there is something read,
# but there is not => eof # but there is not => eof
@ -206,7 +283,7 @@ sub help() {
"teetime [-m max] [-f factor] -i file", "teetime [-m max] [-f factor] -i file",
"", "",
"-a append to file", "-a append to file",
"-f playback speed", "-f playback speed factor",
"-i read from file", "-i read from file",
"-m max wait seconds", "-m max wait seconds",
"", "",
@ -214,6 +291,19 @@ sub help() {
"",); "",);
} }
sub error(@) {
my @w = @_;
my $prog = $Global::progname || "teetime";
status(map { ($prog.": Error: ". $_); } @w);
}
sub status(@) {
my @w = @_;
my $fh = *STDERR;
print $fh map { ($_, "\n") } @w;
flush $fh;
}
sub debug { sub debug {
$opt::D or return; $opt::D or return;
@_ = grep { defined $_ ? $_ : "" } @_; @_ = grep { defined $_ ? $_ : "" } @_;
@ -252,5 +342,3 @@ if(GetOptions("debug|D=s" => \$opt::D,
help(); help();
exit(1); exit(1);
} }