themcgovern/content/posts/command-line-call.md
2024-06-23 23:55:59 -05:00

3.3 KiB

+++ title = 'Command Line Call' date = 2024-05-27T20:15:23-05:00 draft = true +++

Calling from the command line

I was thinking about setting up Jitsi recently and have been researching internet audio and video calling.

One to one example code

# Record audio and send it to whatever's listening on 127.0.0.1:6969
arecord -f S16 -r 48000 - | nc -u 127.0.0.1 6969
# Receive audio from port 6969 and play it
nc -lvp 6969 | aplay -f S16 -r 48000 -

Multiple people chat example

Client(s)

# Record audio and send it to whatever's listening on 127.0.0.1:6969
#arecord -f S16 -r 48000 - | nc -u 127.0.0.1 6969
# Receive audio from port 6969 and play it
#nc -u <server> 6970 | aplay -f S16 -r 48000 -
arecord -f S16 -r 48000 - | nc -u <server> 6969 | aplay -f S16 -r 48000 -

Server

#!/usr/bin/env perl
use v5.36;
use IO::Socket::INET;
use Time::HiRes 'gettimeofday';

my $listen_addr = "0.0.0.0";
my $port = 6969;
my $udp_max_datagram_size = 65535;
my $sample_rate = 48000;
my $sample_template = "s";

my $last_time;
my $buffer;

sub fit_together($audio_in) {
    my @temp1 = unpack "$sample_template*", $buffer;
    my @temp2 = unpack "$sample_template*", $audio_in;

    my $time_diff = gettimeofday() - $last_time;
    my $sample_skip = floor($time_diff * $sample_rate);

    @temp1 = @temp1[ $sample_skip .. $#temp1 ];
    while ($#temp1 < $#temp2) {
        push @temp1, 0;
    }
    while ($#temp2 < $#temp1) {
        push @temp2, 0;
    }

    $last_time = gettimeofday();
    return (@temp1, @temp2);
}

sub process_audio_in($peer, $audio_in) {
    if(not $last_time) {
        $last_time = gettimeofday();
        $buffer = $audio_in;
    } else {
        my (@temp1, @temp2) = fit_together($audio_in);
        $buffer = pack "$sample_template*", 
            map { 
                my $addition = $temp1[$_]+$temp2[$_];
                if($addition > $sample_max) {
                    $sample_max
                } elsif($addition < $sample_min) {
                    $sample_min
                } else {
                    $addition
                }
            } 0..$#temp1;
    }
}

my $sock = IO::Socket::INET->new(
    LocalAddr => "$listen_addr:$port",
    Proto => "udp"
) or die "INET Socket no work :( $@";

say "Listening for clients on addr $listen_addr:$port";

while("humanity" != "extinct") {
    my $audio_data_in = eval {
        $sock->recv($udp_max_datagram_size) or die "INET Socket recv() fail: $!";
    };
    if ($@) {
        say "Peer ".$sock->peername." sent invalid request, skipping: $@";
        next;
    }
    process_audio($sock->peername, $audio_data_in);
    eval {
        $sock->send($buffer, $#next_packet) or die "INET Socket recv() fail: $!";
    }
    if ($@) {
        say "Failed to send audio packet to ".$sock->peername.": $@";
    }
}

The issue with this is that it's not private or anonymous. My attempts to tackle these issues is detailed below:

Making it secure

Anonymity and privacy can be easily added by simply running nc with torsocks on the sending end and running nc behind tor on the receiving end. i2p could also probably be used for this.

arecord -f S16 -r 48000 - | torsocks nc example.onion 6969

The audio will be anonymous due to tor's design. The audio will also be encrypted by the hidden service tunnel.

Authentication

TOR Access Keys