귀하의 질문에 흥미가 생겼습니다. 다시 돌아와서 게임을하기로 결정했습니다. 다음은 Perl의 기본 구현입니다. 아마도 휴대용이 아닙니다 (ioctl
), Linux에서만 테스트되었습니다.
기본적인 아이디어는 다음가
- 도 (대기열에 현재 타임 스탬프를 밀어 가능한 입력을 판독 키
- 로서 현재 타임 스탬프가 모든 X의 마이크로
- 저장소 해시 각 입력 청크 어레이)
- 룩업 오래된 타임 스탬프 큐 길이만큼 지연된 경우 해시 데이터를 폐기 + 물품
- 반복
최대 버퍼 크기
저장된 데이터의 최대 크기가있다. 도달 한 경우 추가 데이터는 쓰기 후에 공간을 사용할 수있게 될 때까지 읽지 않습니다.
성능
그것은 당신의 요구 사항 (몇 MB/s의) 충분히 빨리 아마하지 않습니다. 내 최대 처리량은 639Kb/s였습니다 (아래 참조).
테스트
# Measure max throughput:
$ pv < /dev/zero | ./buffer_delay.pl > /dev/null
# Interactive manual test, use two terminal windows:
$ mkfifo data_fifo
terminal-one $ cat > data_fifo
terminal-two $ ./buffer_delay.pl < data_fifo
# now type in terminal-one and see it appear delayed in terminal-two.
# It will be line-buffered because of the terminals, not a limitation
# of buffer_delay.pl
buffer_delay.pl
#!/usr/bin/perl
use strict;
use warnings;
use IO::Select;
use Time::HiRes qw(gettimeofday usleep);
require 'sys/ioctl.ph';
$|++;
my $delay_usec = 3 * 1000000; # (3s) delay in microseconds
my $buffer_size_max = 10 * 1024 * 1024 ; # (10 Mb) max bytes our buffer is allowed to contain.
# When buffer is full, incoming data will not be read
# until space becomes available after writing
my $read_frequency = 10; # Approximate read frequency in Hz (will not be exact)
my %buffer; # the data we are delaying, saved in chunks by timestamp
my @timestamps; # keys to %buffer, used as a queue
my $buffer_size = 0; # num bytes currently in %buffer, compare to $buffer_size_max
my $time_slice = 1000000/$read_frequency; # microseconds, min time for each discrete read-step
my $sel = IO::Select->new([\*STDIN]);
my $overflow_unread = 0; # Num bytes waiting when $buffer_size_max is reached
while (1) {
my $now = sprintf "%d%06d", gettimeofday; # timestamp, used to label incoming chunks
# input available?
if ($overflow_unread || $sel->can_read($time_slice/1000000)) {
# how much?
my $available_bytes;
if ($overflow_unread) {
$available_bytes = $overflow_unread;
}
else {
$available_bytes = pack("L", 0);
ioctl (STDIN, FIONREAD(), $available_bytes);
$available_bytes = unpack("L", $available_bytes);
}
# will it fit?
my $remaining_space = $buffer_size_max - $buffer_size;
my $try_to_read_bytes = $available_bytes;
if ($try_to_read_bytes > $remaining_space) {
$try_to_read_bytes = $remaining_space;
}
# read input
if ($try_to_read_bytes > 0) {
my $input_data;
my $num_read = read (STDIN, $input_data, $try_to_read_bytes);
die "read error: $!" unless defined $num_read;
exit if $num_read == 0; # EOF
$buffer{$now} = $input_data; # save input
push @timestamps, $now; # save the timestamp
$buffer_size += length $input_data;
if ($overflow_unread) {
$overflow_unread -= length $input_data;
}
elsif (length $input_data < $available_bytes) {
$overflow_unread = $available_bytes - length $input_data;
}
}
}
# write + delete any data old enough
my $then = $now - $delay_usec; # when data is old enough
while (scalar @timestamps && $timestamps[0] < $then) {
my $ts = shift @timestamps;
print $buffer{$ts} if defined $buffer{$ts};
$buffer_size -= length $buffer{$ts};
die "Serious problem\n" unless $buffer_size >= 0;
delete $buffer{$ts};
}
# usleep any remaining time up to $time_slice
my $time_left = (sprintf "%d%06d", gettimeofday) - $now;
usleep ($time_slice - $time_left) if $time_slice > $time_left;
}
의 의견과 제안 사항을 아래 게시 주시기 바랍니다! 이 같은 명령 DIT가 존재하지 보였다으로
이러한 필터 명령이 아닌 사소한하지만, 쓰기 상당히 솔직 할 것이다. 나는 그것을하는 기존의 도구에 대해 알지 못한다. –
타이밍이 정확해야하고 입력의 최소 "청크"는 무엇입니까? 시간 0에 바이트 'A'를 입력하고 시간 0.7에 바이트 'B'를 넣었고 시간 1.9에 바이트 'C'를 입력하면 출력을 정확하게 타이밍해야합니까 (5.0, 5.7 및 6.9 초)? 또는 ABC가 7.0 초에 출력 될 수 있습니까? 버퍼의 최대 크기는 얼마입니까? 버퍼가 가득 차기 전에 얼마나 많은 Mb의 데이터가 지연되어야합니까? – grebneke
이상적으로는 거의 정확하게 시간을 측정해야합니다 (예 : 100ms 이내, 4.9 ~ 5.1, 5.6 ~ 5.8, 6.8 ~ 7). 바이트 단위의 버퍼 크기는 다른 매개 변수 여야합니다 ('-d 5000 -s 2m'). – rom1v