restructuring of files 6 - install script will not function at this point
git-svn-id: svn://192.168.202.10@22 3d104415-ff17-0410-8863-d5cf3c621b8a
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
### AST_VICI_conf.pl
|
||||
###
|
||||
### Copyright (C) 2006 Matt Florell <vicidial@gmail.com> LICENSE: GPLv2
|
||||
###
|
||||
|
||||
### Customized Variables
|
||||
$SIP_user = 'SIP/cc100'; # your phone id
|
||||
$server_ip = '10.10.10.15'; # Asterisk server IP
|
||||
$DB_server = '10.10.10.15'; # MySQL server IP
|
||||
$DB_database = 'asterisk'; # MySQL database name
|
||||
$DB_user = 'idcheck'; # MySQL user
|
||||
$DB_pass = '1234'; # MySQL pass
|
||||
$DB_port = '3306'; # MySQL connection port
|
||||
|
||||
### Constants
|
||||
$record_channel='';
|
||||
$filename='';
|
||||
$recording_id='';
|
||||
$US = '_';
|
||||
$MT[0] = '';
|
||||
|
||||
return 1;
|
||||
|
||||
|
||||
# this subroutine is to be used with the callerID function to customize the
|
||||
# variables that are passed in the URL string and the way that they are passed,
|
||||
# these are the variables you have to work with:
|
||||
# $callerID_areacode
|
||||
# $callerID_prefix
|
||||
# $callerID_last4
|
||||
# $callerID_Time
|
||||
# $callerID_Channel
|
||||
# $callerID_uniqueID
|
||||
# $callerID_phone_ext
|
||||
# $callerID_server_ip
|
||||
# $callerID_extension
|
||||
# $callerID_inbound_number
|
||||
# $callerID_comment_a
|
||||
# $callerID_comment_b
|
||||
# $callerID_comment_c
|
||||
# $callerID_comment_d
|
||||
# $callerID_comment_e
|
||||
|
||||
sub create_callerID_local_query_string
|
||||
{
|
||||
$local_web_callerID_QUERY_STRING ='';
|
||||
$local_web_callerID_QUERY_STRING.="?callerID_areacode=$callerID_areacode";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_prefix=$callerID_prefix";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_last4=$callerID_last4";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_Time=$callerID_Time";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_Channel=$callerID_Channel";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_uniqueID=$callerID_uniqueID";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_phone_ext=$callerID_phone_ext";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_server_ip=$callerID_server_ip";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_extension=$callerID_extension";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_inbound_number=$callerID_inbound_number";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_a=$callerID_comment_a";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_b=$callerID_comment_b";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_c=$callerID_comment_c";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_d=$callerID_comment_d";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_e=$callerID_comment_e";
|
||||
}
|
||||
|
||||
# $lead_id
|
||||
# $vendor_id
|
||||
# $list_id
|
||||
# $phone_code
|
||||
# $phone_number
|
||||
# $title
|
||||
# $first_name
|
||||
# $middle_initial
|
||||
# $last_name
|
||||
# $address1
|
||||
# $address2
|
||||
# $address3
|
||||
# $city
|
||||
# $state
|
||||
# $province
|
||||
# $postal_code
|
||||
# $country_code
|
||||
# $gender
|
||||
# $date_of_birth
|
||||
# $alt_phone
|
||||
# $email
|
||||
# $security
|
||||
# $comments
|
||||
# $user
|
||||
# $pass
|
||||
# $fronter
|
||||
# $closer
|
||||
# $campaign
|
||||
# $group
|
||||
# $SQLdate
|
||||
# $epoch
|
||||
# $uniqueid
|
||||
# $customer_zap_channel
|
||||
# $server_ip
|
||||
# $SIPexten
|
||||
|
||||
sub create_VICIDIAL_query_string
|
||||
{
|
||||
$VICIDIAL_web_QUERY_STRING ='';
|
||||
$VICIDIAL_web_QUERY_STRING.="?lead_id=$lead_id";
|
||||
$VICIDIAL_web_QUERY_STRING.="&vendor_id=$vendor_id";
|
||||
$VICIDIAL_web_QUERY_STRING.="&list_id=$list_id";
|
||||
$VICIDIAL_web_QUERY_STRING.="&phone_code=$phone_code";
|
||||
$VICIDIAL_web_QUERY_STRING.="&phone_number=$phone_number";
|
||||
$VICIDIAL_web_QUERY_STRING.="&title=$title";
|
||||
$VICIDIAL_web_QUERY_STRING.="&first_name=$first_name";
|
||||
$VICIDIAL_web_QUERY_STRING.="&middle_initial=$middle_initial";
|
||||
$VICIDIAL_web_QUERY_STRING.="&last_name=$last_name";
|
||||
$VICIDIAL_web_QUERY_STRING.="&address1=$address1";
|
||||
$VICIDIAL_web_QUERY_STRING.="&address2=$address2";
|
||||
$VICIDIAL_web_QUERY_STRING.="&address3=$address3";
|
||||
$VICIDIAL_web_QUERY_STRING.="&city=$city";
|
||||
$VICIDIAL_web_QUERY_STRING.="&state=$state";
|
||||
$VICIDIAL_web_QUERY_STRING.="&province=$province";
|
||||
$VICIDIAL_web_QUERY_STRING.="&postal_code=$postal_code";
|
||||
$VICIDIAL_web_QUERY_STRING.="&country_code=$country_code";
|
||||
$VICIDIAL_web_QUERY_STRING.="&gender=$gender";
|
||||
$VICIDIAL_web_QUERY_STRING.="&date_of_birth=$date_of_birth";
|
||||
$VICIDIAL_web_QUERY_STRING.="&alt_phone=$alt_phone";
|
||||
$VICIDIAL_web_QUERY_STRING.="&email=$email";
|
||||
$VICIDIAL_web_QUERY_STRING.="&security=$security";
|
||||
$VICIDIAL_web_QUERY_STRING.="&comments=$comments";
|
||||
$VICIDIAL_web_QUERY_STRING.="&user=$user";
|
||||
$VICIDIAL_web_QUERY_STRING.="&pass=$userpass";
|
||||
$VICIDIAL_web_QUERY_STRING.="&fronter=$fronter";
|
||||
$VICIDIAL_web_QUERY_STRING.="&closer=$user";
|
||||
$VICIDIAL_web_QUERY_STRING.="&campaign=$campaign";
|
||||
$VICIDIAL_web_QUERY_STRING.="&group=$group";
|
||||
$VICIDIAL_web_QUERY_STRING.="&channel_group=$channel_group";
|
||||
$VICIDIAL_web_QUERY_STRING.="&SQLdate=$SQLdate";
|
||||
$VICIDIAL_web_QUERY_STRING.="&epoch=$secX";
|
||||
$VICIDIAL_web_QUERY_STRING.="&uniqueid=$uniqueid";
|
||||
$VICIDIAL_web_QUERY_STRING.="&customer_zap_channel=$customer_zap_channel";
|
||||
$VICIDIAL_web_QUERY_STRING.="&server_ip=$server_ip";
|
||||
$VICIDIAL_web_QUERY_STRING.="&SIPexten=$SIPexten";
|
||||
$VICIDIAL_web_QUERY_STRING.="&session_id=$session_id";
|
||||
$VICIDIAL_web_QUERY_STRING.="&phone=$phone_number";
|
||||
$VICIDIAL_web_QUERY_STRING.="&parked_by=$lead_id";
|
||||
$VICIDIAL_web_QUERY_STRING =~ s/ /+/gi;
|
||||
$VICIDIAL_web_QUERY_STRING =~ s/\`|\~|\:|\;|\#|\'|\"|\{|\}|\(|\)|\*|\^|\%|\$|\!|\%|\r|\t|\n//gi;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
ACT|0200|0399
|
||||
NT|0800|0999
|
||||
NSW|1000|2599
|
||||
ACT|2600|2618
|
||||
NSW|2619|2899
|
||||
ACT|2900|2920
|
||||
NSW|2921|2999
|
||||
VIC|3000|3999
|
||||
QLD|4000|4999
|
||||
SA|5000|5999
|
||||
WA|6000|6999
|
||||
TAS|7000|7999
|
||||
VIC|8000|8999
|
||||
QLD|9000|9999
|
||||
@@ -0,0 +1,20 @@
|
||||
package Asterisk;
|
||||
|
||||
require 5.004;
|
||||
|
||||
$VERSION = '0.08';
|
||||
|
||||
sub version { $VERSION; }
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
my $self = {};
|
||||
$self->{configfile} = undef;
|
||||
$self->{config} = {};
|
||||
bless $self, ref $class || $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub DESTROY { }
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,685 @@
|
||||
package Asterisk::AGI;
|
||||
|
||||
require 5.004;
|
||||
|
||||
use Asterisk;
|
||||
|
||||
@ISA = ( 'Asterisk' );
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Asterisk::AGI - Simple Asterisk Gateway Interface Class
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Asterisk::AGI;
|
||||
|
||||
$AGI = new Asterisk::AGI;
|
||||
|
||||
# pull AGI variables into %input
|
||||
|
||||
%input = $AGI->ReadParse();
|
||||
|
||||
# say the number 1984
|
||||
|
||||
$AGI->say_number(1984);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module should make it easier to write scripts that interact with the
|
||||
asterisk open source pbx via AGI (asterisk gateway interface)
|
||||
|
||||
=head1 AGI COMMANDS
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
my $self = {};
|
||||
$self->{'callback'} = undef;
|
||||
$self->{'status'} = undef;
|
||||
$self->{'lastresponse'} = undef;
|
||||
bless $self, ref $class || $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub ReadParse {
|
||||
my ($self, $fh) = @_;
|
||||
|
||||
my %input = ();
|
||||
|
||||
$fh = \*STDIN if (!$fh);
|
||||
|
||||
while (<$fh>) {
|
||||
chomp;
|
||||
last unless length($_);
|
||||
if (/^agi_(\w+)\:\s+(.*)$/) {
|
||||
$input{$1} = $2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (defined($DEBUG)&&($DEBUG>0)) {
|
||||
print STDERR "AGI Environment Dump:\n";
|
||||
foreach $i (sort keys %input) {
|
||||
print STDERR " -- $i = $input{$i}\n";
|
||||
}
|
||||
}
|
||||
|
||||
return %input;
|
||||
}
|
||||
|
||||
sub setcallback {
|
||||
my ($self, $function) = @_;
|
||||
|
||||
if (defined($function) && ref($function) eq 'CODE') {
|
||||
$self->{'callback'} = $function;
|
||||
}
|
||||
}
|
||||
|
||||
sub callback {
|
||||
my ($self, $result) = @_;
|
||||
|
||||
if (defined($self->{'callback'}) && ref($self->{'callback'}) eq 'CODE') {
|
||||
&{$self->{'callback'}}($result);
|
||||
}
|
||||
}
|
||||
|
||||
sub execute {
|
||||
my ($self, $command) = @_;
|
||||
|
||||
$self->_execcommand($command);
|
||||
my $res = $self->_readresponse();
|
||||
|
||||
return $self->_checkresult($res);
|
||||
}
|
||||
|
||||
sub _execcommand {
|
||||
my ($self, $command, $fh) = @_;
|
||||
|
||||
$fh = \*STDOUT if (!$fh);
|
||||
|
||||
select ((select ($fh), $| = 1)[0]);
|
||||
|
||||
return -1 if (!defined($command));
|
||||
|
||||
return print $fh "$command\n";
|
||||
}
|
||||
|
||||
sub _readresponse {
|
||||
my ($self, $fh) = @_;
|
||||
|
||||
my $response = undef;
|
||||
$fh = \*STDIN if (!$fh);
|
||||
$response = <$fh> || return '200 result=-1 (noresponse)';
|
||||
chomp($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
sub _checkresult {
|
||||
my ($self, $response) = @_;
|
||||
|
||||
return undef if (!defined($response));
|
||||
my $result = undef;
|
||||
|
||||
$self->_lastresponse($response);
|
||||
if ($response =~ /^200/) {
|
||||
if ($response =~ /result=(-?[\d*#]+)/) {
|
||||
$result = $1;
|
||||
}
|
||||
} elsif ($response =~ /\(noresponse\)/) {
|
||||
$self->_status('noresponse');
|
||||
} else {
|
||||
print STDERR "Unexpected result '$response'\n" if (defined($DEBUG) && $DEBUG);
|
||||
}
|
||||
print STDERR "_checkresult($response) = $result\n" if (defined($DEBUG) && $DEBUG>3);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub _status {
|
||||
my ($self, $status) = @_;
|
||||
|
||||
if (defined($status)) {
|
||||
$self->{'status'} = $status;
|
||||
} else {
|
||||
return $self->{'status'};
|
||||
}
|
||||
}
|
||||
|
||||
sub _lastresponse {
|
||||
my ($self, $response) = @_;
|
||||
|
||||
if (defined($response)) {
|
||||
$self->{'lastresponse'} = $response;
|
||||
} else {
|
||||
return $self->{'lastresponse'};
|
||||
}
|
||||
}
|
||||
|
||||
=over 4
|
||||
|
||||
=item $AGI->stream_file($filename, $digits)
|
||||
|
||||
Executes AGI Command "STREAM FILE $filename $digits"
|
||||
|
||||
This command instructs Asterisk to play the given sound file and listen for the given dtmf digits. The
|
||||
fileextension must not be used in the filename because Asterisk will find the most appropriate file
|
||||
type.
|
||||
|
||||
Example: $AGI->stream_file('demo-echotest', '0123');
|
||||
|
||||
Returns: -1 on error or hangup,
|
||||
0 if playback completes without a digit being pressed,
|
||||
or the ASCII numerical value of the digit if a digit was pressed
|
||||
|
||||
=cut
|
||||
|
||||
sub stream_file {
|
||||
my ($self, $filename, $digits) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
|
||||
$digits = '""' if (!defined($digits));
|
||||
|
||||
return -1 if (!defined($filename));
|
||||
$ret = $self->execute("STREAM FILE $filename $digits");
|
||||
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
=item $AGI->send_text($text)
|
||||
|
||||
Executes AGI Command "SEND TEXT "$text"
|
||||
|
||||
Sends the given text on a channel. Most channels do not support the transmission of text.
|
||||
|
||||
Example: $AGI->send_text('You've got mail!');
|
||||
|
||||
Returns: -1 on error or hangup,
|
||||
0 if the text was sent or if the channel does not support text transmission.
|
||||
|
||||
=cut
|
||||
|
||||
sub send_text {
|
||||
my ($self, $text) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
|
||||
return $ret if (!defined($text));
|
||||
$ret = $self->execute("SEND TEXT \"$text\"");
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
=item $AGI->send_image($image)
|
||||
|
||||
Executes AGI Command "SEND IMAGE $image
|
||||
|
||||
Sends the given image on a channel. Most channels do not support the transmission of images.
|
||||
|
||||
Example: $AGI->send_image('image.png');
|
||||
|
||||
Returns: -1 on error or hangup,
|
||||
0 if the image was sent or if the channel does not support image transmission.
|
||||
|
||||
=cut
|
||||
|
||||
sub send_image {
|
||||
my ($self, $image) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
return -1 if (!defined($image));
|
||||
|
||||
$ret = $self->execute("SEND IMAGE $image");
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
=item $AGI->say_number($number, $digits)
|
||||
|
||||
Executes AGI Command "SAY NUMBER $number $digits"
|
||||
|
||||
Says the given $number, returning early if any of the $digits are received.
|
||||
|
||||
Example: $AGI->say_number('98765');
|
||||
|
||||
Returns: -1 on error or hangup,
|
||||
0 if playback completes without a digit being pressed,
|
||||
or the ASCII numerical value of the digit of one was pressed.
|
||||
|
||||
=cut
|
||||
|
||||
sub say_number {
|
||||
my ($self, $number, $digits) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
|
||||
$digits = '""' if (!defined($digits));
|
||||
|
||||
return -1 if (!defined($number));
|
||||
$number =~ s/\D//g;
|
||||
$ret = $self->execute("SAY NUMBER $number $digits");
|
||||
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
=item $AGI->say_digits($number, $digits)
|
||||
|
||||
Executes AGI Command "SAY DIGITS $number $digits"
|
||||
|
||||
Says the given digit string $number, returning early if any of the $digits are received.
|
||||
|
||||
Example: $AGI->say_digits('8675309');
|
||||
|
||||
Returns: -1 on error or hangup,
|
||||
0 if playback completes without a digit being pressed,
|
||||
or the ASCII numerical value of the digit of one was pressed.
|
||||
|
||||
=cut
|
||||
|
||||
sub say_digits {
|
||||
my ($self, $number, $digits) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
$digits = '""' if (!defined($digits));
|
||||
|
||||
return -1 if (!defined($number));
|
||||
$number =~ s/\D//g;
|
||||
$ret = $self->execute("SAY DIGITS $number $digits");
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
=item $AGI->answer()
|
||||
|
||||
Executes AGI Command "ANSWER"
|
||||
|
||||
Answers channel if not already in answer state
|
||||
|
||||
Example: $AGI->answer();
|
||||
|
||||
Returns: -1 on channel failure, or
|
||||
0 if successful
|
||||
|
||||
=cut
|
||||
|
||||
sub answer {
|
||||
my ($self) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
$ret = $self->execute('ANSWER');
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
=item $AGI->get_data($filename, $timeout, $maxdigits)
|
||||
|
||||
Executes AGI Command "GET DATA $filename $timeout $maxdigits"
|
||||
|
||||
Streams $filename and returns when $maxdigits have been received or
|
||||
when $timeout has been reached. Timeout is specified in ms
|
||||
|
||||
Example: $AGI->get_data('demo-welcome', 15000, 5);
|
||||
|
||||
=cut
|
||||
|
||||
sub get_data {
|
||||
my ($self, $filename, $timeout, $maxdigits) = @_;
|
||||
|
||||
my $ret = undef;
|
||||
|
||||
return -1 if (!defined($filename));
|
||||
$ret = $self->execute("GET DATA $filename $timeout $maxdigits");
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
=item $AGI->set_callerid($number)
|
||||
|
||||
Executes AGI Command "SET CALLERID $number"
|
||||
|
||||
Changes the callerid of the current channel to <number>
|
||||
|
||||
Example: $AGI->set_callerid('9995551212');
|
||||
|
||||
Returns: Always returns 1
|
||||
|
||||
=cut
|
||||
|
||||
sub set_callerid {
|
||||
my ($self, $number) = @_;
|
||||
|
||||
return if (!defined($number));
|
||||
return $self->execute("SET CALLERID $number");
|
||||
}
|
||||
|
||||
=item $AGI->set_context($context)
|
||||
|
||||
Executes AGI Command "SET CONTEXT $context"
|
||||
|
||||
Changes the context for continuation upon exiting the agi application
|
||||
|
||||
Example: $AGI->set_context('dialout');
|
||||
|
||||
Returns: Always returns 0
|
||||
|
||||
=cut
|
||||
|
||||
sub set_context {
|
||||
my ($self, $context) = @_;
|
||||
|
||||
return -1 if (!defined($context));
|
||||
return $self->execute("SET CONTEXT $context");
|
||||
}
|
||||
|
||||
=item $AGI->set_extension($extension)
|
||||
|
||||
Executes AGI Command "SET EXTENSION $extension"
|
||||
|
||||
Changes the extension for continuation upon exiting the agi application
|
||||
|
||||
Example: $AGI->set_extension('7');
|
||||
|
||||
Returns: Always returns 0
|
||||
|
||||
=cut
|
||||
|
||||
sub set_extension {
|
||||
my ($self, $extension) = @_;
|
||||
|
||||
return -1 if (!defined($extension));
|
||||
return $self->execute("SET EXTENSION $extension");
|
||||
}
|
||||
|
||||
=item $AGI->set_priority($priority)
|
||||
|
||||
Executes AGI Command "SET PRIORITY $priority"
|
||||
|
||||
Changes the priority for continuation upon exiting the agi application
|
||||
|
||||
Example: $AGI->set_priority(1);
|
||||
|
||||
Returns: Always returns 0
|
||||
|
||||
=cut
|
||||
|
||||
sub set_priority {
|
||||
my ($self, $priority) = @_;
|
||||
|
||||
return -1 if (!defined($priority));
|
||||
return $self->execute("SET PRIORITY $priority");
|
||||
}
|
||||
|
||||
sub receive_char {
|
||||
my ($self, $timeout) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
#wait forever if timeout is not set. is this the prefered default?
|
||||
$timeout = 0 if (!defined($timeout));
|
||||
$ret = $self->execute("RECEIVE CHAR $timeout");
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
sub tdd_mode {
|
||||
my ($self, $mode) = @_;
|
||||
|
||||
return 0 if (!defined($mode));
|
||||
return $self->execute("TDD MODE $mode");
|
||||
}
|
||||
|
||||
|
||||
sub wait_for_digit {
|
||||
my ($self, $timeout) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
$timeout = -1 if (!defined($timeout));
|
||||
$ret = $self->execute("WAIT FOR DIGIT $timeout");
|
||||
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub record_file {
|
||||
my ($self, $filename, $format, $digits, $timeout, $beep) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
|
||||
return -1 if (!defined($filename));
|
||||
$digits = '""' if (!defined($digits));
|
||||
$ret = $self->execute("RECORD FILE $filename $format $digits $timeout");
|
||||
|
||||
$self->callback($ret) if ($ret == -1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub set_autohangup {
|
||||
my ($self, $time) = @_;
|
||||
|
||||
$time = 0 if (!defined($time));
|
||||
return $self->execute("SET AUTOHANGUP $time");
|
||||
}
|
||||
|
||||
=item $AGI->hangup($channel)
|
||||
|
||||
Executes AGI Command "HANGUP $channel"
|
||||
|
||||
Hangs up the passed $channel, or the current channel if $channel is not passed.
|
||||
It is left to the AGI script to exit properly, otherwise you could end up with zombies.
|
||||
|
||||
Example: $AGI->hangup();
|
||||
|
||||
Returns: Always returns 1
|
||||
|
||||
=cut
|
||||
|
||||
sub hangup {
|
||||
my ($self, $channel) = @_;
|
||||
|
||||
if ($channel) {
|
||||
return $self->execute("HANGUP $channel");
|
||||
} else {
|
||||
return $self->execute("HANGUP");
|
||||
}
|
||||
}
|
||||
|
||||
=item $AGI->exec($app, $options)
|
||||
|
||||
Executes AGI Command "EXEC $app $options"
|
||||
|
||||
The most powerful AGI command. Executes the given application passing the given options.
|
||||
|
||||
Example: $AGI->exec('Dial', 'Zap/g2/8005551212');
|
||||
|
||||
Returns: -2 on failure to find application, or
|
||||
whatever the given application returns
|
||||
|
||||
=cut
|
||||
|
||||
sub exec {
|
||||
my ($self, $app, $options) = @_;
|
||||
return -1 if (!defined($app));
|
||||
$options = '""' if (!defined($options));
|
||||
return $self->execute("EXEC $app $options");
|
||||
}
|
||||
|
||||
sub channel_status {
|
||||
my ($self, $channel) = @_;
|
||||
|
||||
return $self->execute("CHANNEL STATUS $channel");
|
||||
}
|
||||
|
||||
=item $AGI->set_variable($variable, $value)
|
||||
|
||||
Executes AGI Command "SET VARIABLE $variable $value"
|
||||
|
||||
Sets the channel variable <variablename> to <value>
|
||||
|
||||
Example: $AGI->set_variable('status', 'authorized');
|
||||
|
||||
Returns: Always returns 1
|
||||
|
||||
=cut
|
||||
|
||||
sub set_variable {
|
||||
my ($self, $variable, $value) = @_;
|
||||
|
||||
return $self->execute("SET VARIABLE $variable $value");
|
||||
}
|
||||
|
||||
=item $AGI->get_variable($variable)
|
||||
|
||||
Executes AGI Command "GET VARIABLE $variablename"
|
||||
|
||||
Gets the channel variable <variablename>
|
||||
|
||||
Example: $AGI->get_variable('status');
|
||||
|
||||
Returns: The value of the variable, or undef if variable does not exist
|
||||
|
||||
=cut
|
||||
|
||||
sub get_variable {
|
||||
my ($self, $variable) = @_;
|
||||
|
||||
my $result = undef;
|
||||
|
||||
if ($self->execute("GET VARIABLE $variable")) {
|
||||
my $tempresult = $self->_lastresponse();
|
||||
if ($tempresult =~ /\((.*)\)/) {
|
||||
$result = $1;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
=item $AGI->verbose($message, $level)
|
||||
|
||||
Executes AGI Command "VERBOSE $message $level"
|
||||
|
||||
Logs $message with verboselevel $level
|
||||
|
||||
Example: $AGI->verbose("System Crashed\n", 1);
|
||||
|
||||
Returns: Always returns 1
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
sub verbose {
|
||||
my ($self, $message, $level) = @_;
|
||||
|
||||
return $self->execute("VERBOSE \"$message\" $level");
|
||||
}
|
||||
|
||||
=item $AGI->database_get($family, $key)
|
||||
|
||||
Executes AGI Command "DATABASE GET $family $key"
|
||||
|
||||
Example: $var = $AGI->database_get('test', 'status');
|
||||
|
||||
Returns: The value of the variable, or undef if variable does not exist
|
||||
|
||||
=cut
|
||||
|
||||
sub database_get {
|
||||
my ($self, $family, $key) = @_;
|
||||
|
||||
my $result = undef;
|
||||
|
||||
if ($self->execute("DATABASE GET $family $key")) {
|
||||
my $tempresult = $self->_lastresponse();
|
||||
if ($tempresult =~ /\((.*)\)/) {
|
||||
$result = $1;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
=item $AGI->database_put($family, $key, $value)
|
||||
|
||||
Executes AGI Command "DATABASE PUT $family $key $value"
|
||||
|
||||
Set/modifes database entry <family>/<key> to <value>
|
||||
|
||||
Example: $AGI->database_put('test', 'status', 'authorized');
|
||||
|
||||
Returns: 1 on success, 0 otherwise
|
||||
|
||||
=cut
|
||||
|
||||
sub database_put {
|
||||
my ($self, $family, $key, $value) = @_;
|
||||
|
||||
return $self->execute("DATABASE PUT $family $key $value");
|
||||
}
|
||||
|
||||
=item $AGI->database_del($family, $key)
|
||||
|
||||
Executes AGI Command "DATABASE DEL $family $key"
|
||||
|
||||
Removes database entry <family>/<key>
|
||||
|
||||
Example: $AGI->database_del('test', 'status');
|
||||
|
||||
Returns: 1 on success, 0 otherwise
|
||||
|
||||
=cut
|
||||
|
||||
sub database_del {
|
||||
my ($self, $family, $key) = @_;
|
||||
|
||||
return $self->execute("DATABASE DEL $family $key");
|
||||
}
|
||||
|
||||
=item $AGI->database_deltree($family, $key)
|
||||
|
||||
Executes AGI Command "DATABASE DELTREE $family $key"
|
||||
|
||||
Deletes a family or specific keytree within a family in the Asterisk database
|
||||
|
||||
Example: $AGI->database_deltree('test', 'status');
|
||||
Example: $AGI->database_deltree('test');
|
||||
|
||||
Returns: 1 on success, 0 otherwise
|
||||
|
||||
=cut
|
||||
|
||||
sub database_deltree {
|
||||
my ($self, $family, $key) = @_;
|
||||
|
||||
return $self->execute("DATABASE DELTREE $family $key");
|
||||
}
|
||||
|
||||
sub noop {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->execute("NOOP");
|
||||
}
|
||||
|
||||
sub set_music {
|
||||
my ($self, $mode, $class) = @_;
|
||||
|
||||
return $self->execute("SET MUSIC $mode $class");
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=back
|
||||
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
### AST_VICI_conf.pl
|
||||
###
|
||||
### Copyright (C) 2006 Matt Florell <vicidial@gmail.com> LICENSE: GPLv2
|
||||
###
|
||||
|
||||
### Customized Variables
|
||||
$SIP_user = 'SIP/cc100'; # your phone id
|
||||
$server_ip = '10.10.10.15'; # Asterisk server IP
|
||||
$DB_server = '10.10.10.15'; # MySQL server IP
|
||||
$DB_database = 'asterisk'; # MySQL database name
|
||||
$DB_user = 'idcheck'; # MySQL user
|
||||
$DB_pass = '1234'; # MySQL pass
|
||||
$DB_port = '3306'; # MySQL connection port
|
||||
|
||||
### Constants
|
||||
$record_channel='';
|
||||
$filename='';
|
||||
$recording_id='';
|
||||
$US = '_';
|
||||
$MT[0] = '';
|
||||
|
||||
return 1;
|
||||
|
||||
|
||||
# this subroutine is to be used with the callerID function to customize the
|
||||
# variables that are passed in the URL string and the way that they are passed,
|
||||
# these are the variables you have to work with:
|
||||
# $callerID_areacode
|
||||
# $callerID_prefix
|
||||
# $callerID_last4
|
||||
# $callerID_Time
|
||||
# $callerID_Channel
|
||||
# $callerID_uniqueID
|
||||
# $callerID_phone_ext
|
||||
# $callerID_server_ip
|
||||
# $callerID_extension
|
||||
# $callerID_inbound_number
|
||||
# $callerID_comment_a
|
||||
# $callerID_comment_b
|
||||
# $callerID_comment_c
|
||||
# $callerID_comment_d
|
||||
# $callerID_comment_e
|
||||
|
||||
sub create_callerID_local_query_string
|
||||
{
|
||||
$local_web_callerID_QUERY_STRING ='';
|
||||
$local_web_callerID_QUERY_STRING.="?callerID_areacode=$callerID_areacode";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_prefix=$callerID_prefix";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_last4=$callerID_last4";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_Time=$callerID_Time";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_Channel=$callerID_Channel";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_uniqueID=$callerID_uniqueID";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_phone_ext=$callerID_phone_ext";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_server_ip=$callerID_server_ip";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_extension=$callerID_extension";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_inbound_number=$callerID_inbound_number";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_a=$callerID_comment_a";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_b=$callerID_comment_b";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_c=$callerID_comment_c";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_d=$callerID_comment_d";
|
||||
$local_web_callerID_QUERY_STRING.="&callerID_comment_e=$callerID_comment_e";
|
||||
}
|
||||
|
||||
# $lead_id
|
||||
# $vendor_id
|
||||
# $list_id
|
||||
# $phone_code
|
||||
# $phone_number
|
||||
# $title
|
||||
# $first_name
|
||||
# $middle_initial
|
||||
# $last_name
|
||||
# $address1
|
||||
# $address2
|
||||
# $address3
|
||||
# $city
|
||||
# $state
|
||||
# $province
|
||||
# $postal_code
|
||||
# $country_code
|
||||
# $gender
|
||||
# $date_of_birth
|
||||
# $alt_phone
|
||||
# $email
|
||||
# $security
|
||||
# $comments
|
||||
# $user
|
||||
# $pass
|
||||
# $fronter
|
||||
# $closer
|
||||
# $campaign
|
||||
# $group
|
||||
# $SQLdate
|
||||
# $epoch
|
||||
# $uniqueid
|
||||
# $customer_zap_channel
|
||||
# $server_ip
|
||||
# $SIPexten
|
||||
|
||||
sub create_VICIDIAL_query_string
|
||||
{
|
||||
$VICIDIAL_web_QUERY_STRING ='';
|
||||
$VICIDIAL_web_QUERY_STRING.="?lead_id=$lead_id";
|
||||
$VICIDIAL_web_QUERY_STRING.="&vendor_id=$vendor_id";
|
||||
$VICIDIAL_web_QUERY_STRING.="&list_id=$list_id";
|
||||
$VICIDIAL_web_QUERY_STRING.="&phone_code=$phone_code";
|
||||
$VICIDIAL_web_QUERY_STRING.="&phone_number=$phone_number";
|
||||
$VICIDIAL_web_QUERY_STRING.="&title=$title";
|
||||
$VICIDIAL_web_QUERY_STRING.="&first_name=$first_name";
|
||||
$VICIDIAL_web_QUERY_STRING.="&middle_initial=$middle_initial";
|
||||
$VICIDIAL_web_QUERY_STRING.="&last_name=$last_name";
|
||||
$VICIDIAL_web_QUERY_STRING.="&address1=$address1";
|
||||
$VICIDIAL_web_QUERY_STRING.="&address2=$address2";
|
||||
$VICIDIAL_web_QUERY_STRING.="&address3=$address3";
|
||||
$VICIDIAL_web_QUERY_STRING.="&city=$city";
|
||||
$VICIDIAL_web_QUERY_STRING.="&state=$state";
|
||||
$VICIDIAL_web_QUERY_STRING.="&province=$province";
|
||||
$VICIDIAL_web_QUERY_STRING.="&postal_code=$postal_code";
|
||||
$VICIDIAL_web_QUERY_STRING.="&country_code=$country_code";
|
||||
$VICIDIAL_web_QUERY_STRING.="&gender=$gender";
|
||||
$VICIDIAL_web_QUERY_STRING.="&date_of_birth=$date_of_birth";
|
||||
$VICIDIAL_web_QUERY_STRING.="&alt_phone=$alt_phone";
|
||||
$VICIDIAL_web_QUERY_STRING.="&email=$email";
|
||||
$VICIDIAL_web_QUERY_STRING.="&security=$security";
|
||||
$VICIDIAL_web_QUERY_STRING.="&comments=$comments";
|
||||
$VICIDIAL_web_QUERY_STRING.="&user=$user";
|
||||
$VICIDIAL_web_QUERY_STRING.="&pass=$userpass";
|
||||
$VICIDIAL_web_QUERY_STRING.="&fronter=$fronter";
|
||||
$VICIDIAL_web_QUERY_STRING.="&closer=$user";
|
||||
$VICIDIAL_web_QUERY_STRING.="&campaign=$campaign";
|
||||
$VICIDIAL_web_QUERY_STRING.="&group=$group";
|
||||
$VICIDIAL_web_QUERY_STRING.="&channel_group=$channel_group";
|
||||
$VICIDIAL_web_QUERY_STRING.="&SQLdate=$SQLdate";
|
||||
$VICIDIAL_web_QUERY_STRING.="&epoch=$secX";
|
||||
$VICIDIAL_web_QUERY_STRING.="&uniqueid=$uniqueid";
|
||||
$VICIDIAL_web_QUERY_STRING.="&customer_zap_channel=$customer_zap_channel";
|
||||
$VICIDIAL_web_QUERY_STRING.="&server_ip=$server_ip";
|
||||
$VICIDIAL_web_QUERY_STRING.="&SIPexten=$SIPexten";
|
||||
$VICIDIAL_web_QUERY_STRING.="&session_id=$session_id";
|
||||
$VICIDIAL_web_QUERY_STRING.="&phone=$phone_number";
|
||||
$VICIDIAL_web_QUERY_STRING.="&parked_by=$lead_id";
|
||||
$VICIDIAL_web_QUERY_STRING =~ s/ /+/gi;
|
||||
$VICIDIAL_web_QUERY_STRING =~ s/\`|\~|\:|\;|\#|\'|\"|\{|\}|\(|\)|\*|\^|\%|\$|\!|\%|\r|\t|\n//gi;
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
package Asterisk::Manager;
|
||||
|
||||
require 5.004;
|
||||
|
||||
use Asterisk;
|
||||
use IO::Socket;
|
||||
use Digest::MD5;
|
||||
|
||||
#use strict;
|
||||
#use warnings;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Asterisk::Manager - Asterisk Manager Interface
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Asterisk::Manager;
|
||||
|
||||
my $astman = new Asterisk::Manager;
|
||||
|
||||
$astman->user('username');
|
||||
|
||||
$astman->secret('test');
|
||||
|
||||
$astman->host('localhost');
|
||||
|
||||
$astman->connect || die "Could not connect to " . $astman->host . "!\n";
|
||||
|
||||
$astman->disconnect;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module provides a simple interface to the asterisk manager interface.
|
||||
|
||||
=cut
|
||||
|
||||
my $EOL = "\r\n";
|
||||
my $BLANK = $EOL x 2;
|
||||
|
||||
my $VERSION = '0.01';
|
||||
|
||||
sub version { $VERSION; }
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
|
||||
my $self = {};
|
||||
$self->{_CONNFD} = undef;
|
||||
$self->{_PROTOVERS} = undef;
|
||||
$self->{_ERRORSTR} = undef;
|
||||
$self->{_HOST} = 'localhost';
|
||||
$self->{_PORT} = 5038;
|
||||
$self->{_USER} = undef;
|
||||
$self->{_SECRET} = undef;
|
||||
$self->{_EVENTCB} = {};
|
||||
$self->{_DEBUG} = 0;
|
||||
$self->{_CONNECTED} = 0;
|
||||
bless $self, ref $class || $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub DESTROY { }
|
||||
|
||||
sub user {
|
||||
my ($self, $user) = @_;
|
||||
|
||||
if ($user) {
|
||||
$self->{_USER} = $user;
|
||||
}
|
||||
|
||||
return $self->{_USER};
|
||||
}
|
||||
|
||||
sub secret {
|
||||
my ($self, $secret) = @_;
|
||||
|
||||
if ($secret) {
|
||||
$self->{_SECRET} = $secret;
|
||||
}
|
||||
|
||||
return $self->{_SECRET};
|
||||
}
|
||||
|
||||
sub host {
|
||||
my ($self, $host) = @_;
|
||||
|
||||
if ($host) {
|
||||
$self->{_HOST} = $host;
|
||||
}
|
||||
|
||||
return $self->{_HOST};
|
||||
}
|
||||
|
||||
sub port {
|
||||
my ($self, $port) = @_;
|
||||
|
||||
if ($port) {
|
||||
$self->{_PORT} = $port;
|
||||
}
|
||||
|
||||
return $self->{_PORT};
|
||||
}
|
||||
|
||||
sub connected {
|
||||
my ($self, $connected) = @_;
|
||||
|
||||
if (defined($connected)) {
|
||||
$self->{_CONNECTED} = $connected;
|
||||
}
|
||||
|
||||
return $self->{_CONNECTED};
|
||||
}
|
||||
|
||||
sub error {
|
||||
my ($self, $error) = @_;
|
||||
|
||||
if ($error) {
|
||||
$self->{_ERRORSTR} = $error;
|
||||
}
|
||||
|
||||
return $self->{_ERRORSTR};
|
||||
}
|
||||
|
||||
sub debug {
|
||||
my ($self, $debug) = @_;
|
||||
|
||||
if ($debug) {
|
||||
$self->{_DEBUG} = $debug;
|
||||
}
|
||||
|
||||
return $self->{_DEBUG};
|
||||
}
|
||||
|
||||
sub connfd {
|
||||
my ($self, $connfd) = @_;
|
||||
|
||||
if ($connfd) {
|
||||
$self->{_CONNFD} = $connfd;
|
||||
}
|
||||
|
||||
return $self->{_CONNFD};
|
||||
}
|
||||
|
||||
sub read_response {
|
||||
my ($self, $connfd) = @_;
|
||||
|
||||
my @response;
|
||||
|
||||
if (!$connfd) {
|
||||
$connfd = $self->connfd;
|
||||
}
|
||||
|
||||
while (my $line = <$connfd>) {
|
||||
last if ($line eq $EOL);
|
||||
|
||||
if (wantarray) {
|
||||
$line =~ s/$EOL//g;
|
||||
push(@response, $line) if $line;
|
||||
} else {
|
||||
$response[0] .= $line;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return wantarray ? @response : $response[0];
|
||||
}
|
||||
|
||||
sub connect {
|
||||
my ($self) = @_;
|
||||
|
||||
my $host = $self->host;
|
||||
my $port = $self->port;
|
||||
my $user = $self->user;
|
||||
my $secret = $self->secret;
|
||||
my %resp;
|
||||
|
||||
my $conn = new IO::Socket::INET( Proto => 'tcp',
|
||||
PeerAddr => $host,
|
||||
PeerPort => $port
|
||||
);
|
||||
if (!$conn) {
|
||||
$self->error("Connection refused ($host:$port)\n");
|
||||
return undef;
|
||||
}
|
||||
|
||||
$conn->autoflush(1);
|
||||
|
||||
my $input = <$conn>;
|
||||
$input =~ s/$EOL//g;
|
||||
|
||||
my ($manager, $version) = split('/', $input);
|
||||
|
||||
if ($manager !~ /Asterisk Call Manager/) {
|
||||
return $self->error("Unknown Protocol\n");
|
||||
}
|
||||
|
||||
$self->{_PROTOVERS} = $version;
|
||||
$self->connfd($conn);
|
||||
|
||||
# check if the remote host supports MD5 Challenge authentication
|
||||
my %authresp = $self->sendcommand( Action => 'Challenge',
|
||||
AuthType => 'MD5'
|
||||
);
|
||||
|
||||
if (($authresp{Response} eq 'Success')) {
|
||||
# do md5 login
|
||||
my $md5 = new Digest::MD5;
|
||||
$md5->add($authresp{Challenge});
|
||||
$md5->add($secret);
|
||||
my $digest = $md5->hexdigest;
|
||||
%resp = $self->sendcommand( Action => 'Login',
|
||||
AuthType => 'MD5',
|
||||
Username => $user,
|
||||
Key => $digest
|
||||
);
|
||||
} else {
|
||||
# do plain text login
|
||||
%resp = $self->sendcommand( Action => 'Login',
|
||||
Username => $user,
|
||||
Secret => $secret
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if ( ($resp{Response} ne 'Success') && ($resp{Message} ne 'Authentication accepted') ) {
|
||||
$self->error("Authentication failed for user $user\n");
|
||||
return undef;
|
||||
}
|
||||
|
||||
$self->connected(1);
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
sub astman_h2s {
|
||||
my ($self, %thash) = @_;
|
||||
|
||||
my $tstring = '';
|
||||
|
||||
foreach my $key (keys %thash) {
|
||||
$tstring .= $key . ': ' . $thash{$key} . ${EOL};
|
||||
}
|
||||
|
||||
return $tstring;
|
||||
}
|
||||
|
||||
sub astman_s2h {
|
||||
my ($self, $tstring) = @_;
|
||||
|
||||
my %thash;
|
||||
|
||||
foreach my $line (split(/$EOL/, $tstring)) {
|
||||
if ($line =~ /(\w*):\s*(\w*)/) {
|
||||
$thash{$1} = $2;
|
||||
}
|
||||
}
|
||||
|
||||
return %thash;
|
||||
}
|
||||
|
||||
#$want is how you want the data returned
|
||||
#$want = 0 (default) returns the results in a hash
|
||||
#$want = 1 returns the results in a large string
|
||||
#$want = 2 returns the results in an array
|
||||
sub sendcommand {
|
||||
my ($self, %command, $want) = @_;
|
||||
|
||||
if (!defined($want)) {
|
||||
$want = 0;
|
||||
}
|
||||
|
||||
my $conn = $self->connfd || return;
|
||||
my $cstring = $self->astman_h2s(%command);
|
||||
|
||||
$conn->send("$cstring$EOL");
|
||||
|
||||
if ($want == 1) {
|
||||
my $response = $self->read_response($conn);
|
||||
return $response;
|
||||
}
|
||||
|
||||
my @resp = $self->read_response($conn);
|
||||
|
||||
if ($want == 2) {
|
||||
return @resp;
|
||||
} else {
|
||||
return map { split(': ', $_) } @resp;
|
||||
}
|
||||
}
|
||||
|
||||
sub setcallback {
|
||||
my ($self, $event, $function) = @_;
|
||||
|
||||
if (defined($function) && ref($function) eq 'CODE') {
|
||||
$self->{_EVENTCB}{$event} = $function;
|
||||
}
|
||||
}
|
||||
|
||||
sub eventcallback {
|
||||
my ($self, %resp) = @_;
|
||||
|
||||
my $callback;
|
||||
my $event = $resp{Event};
|
||||
|
||||
return if (!$event);
|
||||
|
||||
if (defined($self->{_EVENTCB}{$event})) {
|
||||
$callback = $self->{_EVENTCB}{$event};
|
||||
} elsif (defined($self->{_EVENTCB}{DEFAULT})) {
|
||||
$callback = $self->{_EVENTCB}{DEFAULT};
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
return &{$callback}(%resp);
|
||||
}
|
||||
|
||||
sub eventloop {
|
||||
my ($self) = @_;
|
||||
|
||||
while (1) {
|
||||
$self->handleevent;
|
||||
}
|
||||
}
|
||||
|
||||
sub handleevent {
|
||||
my ($self) = @_;
|
||||
|
||||
my %resp = map { split(': ', $_); } $self->read_response;
|
||||
$self->eventcallback(%resp);
|
||||
|
||||
return %resp;
|
||||
}
|
||||
|
||||
sub action {
|
||||
my ($self, $command, $wanthash) = @_;
|
||||
|
||||
return if (!$command);
|
||||
|
||||
my $conn = $self->connfd || return;
|
||||
|
||||
print $conn "Action: $command" . $BLANK;
|
||||
my @resp = $self->read_response($conn);
|
||||
|
||||
if ($wanthash) {
|
||||
return map { split(': ', $_) } @resp;
|
||||
} elsif (wantarray) {
|
||||
return @resp;
|
||||
} else {
|
||||
return $resp[0];
|
||||
}
|
||||
}
|
||||
|
||||
sub command {
|
||||
my ($self, $command) = @_;
|
||||
|
||||
return if (!$command);
|
||||
|
||||
return $self->sendcommand('Action' => 'Command',
|
||||
'Command' => $command, 1 );
|
||||
}
|
||||
|
||||
sub monitor {
|
||||
my ($self, $channel, $file) = @_;
|
||||
|
||||
return if (!$channel);
|
||||
|
||||
return $self->sendcommand('Action' => 'Monitor',
|
||||
'Channel' => $channel,
|
||||
'File' => $file, 1 );
|
||||
}
|
||||
|
||||
sub changemonitor {
|
||||
my ($self, $channel, $file) = @_;
|
||||
|
||||
return if (!$channel);
|
||||
|
||||
return $self->sendcommand('Action' => 'ChangeMonitor',
|
||||
'Channel' => $channel,
|
||||
'File' => $file, 1 );
|
||||
}
|
||||
|
||||
sub stopmonitor {
|
||||
my ($self, $channel, $file) = @_;
|
||||
|
||||
return if (!$channel);
|
||||
|
||||
return $self->sendcommand('Action' => 'StopMonitor',
|
||||
'Channel' => $channel,
|
||||
'File' => $file, 1 );
|
||||
}
|
||||
|
||||
|
||||
sub disconnect {
|
||||
my ($self) = @_;
|
||||
|
||||
my $conn = $self->connfd;
|
||||
|
||||
my %resp = $self->sendcommand('Action' => 'Logoff');
|
||||
|
||||
|
||||
if ($resp{Response} eq 'Goodbye') {
|
||||
$self->{_CONNFD} = undef;
|
||||
$self->connected(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,110 @@
|
||||
package Asterisk::Outgoing;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Asterisk::Outgoing - Create outgoing call queue file
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Asterisk::Outgoing;
|
||||
|
||||
my $out = new Asterisk::Outgoing;
|
||||
|
||||
$out->setvariable('Channel', 'Zap/1/');
|
||||
|
||||
$out->setvariable('MaxRetries', 0);
|
||||
|
||||
$out->setvariable('RetryTime', 60);
|
||||
|
||||
$out->setvariable('WaitTime', 60);
|
||||
|
||||
$out->setvariable('Application', 'Playback');
|
||||
|
||||
$out->setvariable('Data', 'beep');
|
||||
|
||||
$out->create_outgoing;
|
||||
|
||||
=cut
|
||||
|
||||
require 5.004;
|
||||
|
||||
use Fcntl ':flock';
|
||||
use Asterisk;
|
||||
@ISA = ('Asterisk');
|
||||
|
||||
$VERSION = '0.01';
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
my $self = {};
|
||||
$self->{OUTDIR} = '/var/spool/asterisk/outgoing';
|
||||
$self->{OUTTIME} = undef;
|
||||
$self->{OUTVARS} = {};
|
||||
$self->{ALLOWEDVARS} = [ 'channel', 'maxretries', 'retrytime', 'waittime', 'context', 'extension', 'priority', 'application', 'data', 'callerid' ];
|
||||
bless $self, ref $class || $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub outdir {
|
||||
my ($self, $dir) = @_;
|
||||
|
||||
if (defined($dir)) {
|
||||
$self->{OUTDIR} = $dir;
|
||||
}
|
||||
|
||||
return $self->{OUTDIR};
|
||||
}
|
||||
|
||||
sub outtime {
|
||||
my ($self, $time) = @_;
|
||||
|
||||
if (defined($time)) {
|
||||
$self->{OUTTIME} = $time;
|
||||
} elsif (!defined($self->{OUTTIME})) {
|
||||
$self->{OUTTIME} = time();
|
||||
}
|
||||
|
||||
return $self->{OUTTIME};
|
||||
}
|
||||
|
||||
sub checkvariable {
|
||||
my ($self, $var) = @_;
|
||||
|
||||
my $ret = 0;
|
||||
|
||||
foreach $allowed (@{$self->{ALLOWEDVARS}}) {
|
||||
if ($allowed =~ /$var/i) {
|
||||
$ret = 1;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub setvariable {
|
||||
my ($self, $var, $value) = @_;
|
||||
|
||||
if ($self->checkvariable($var)) {
|
||||
$self->{OUTVARS}{$var} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
sub create_outgoing {
|
||||
my ($self) = @_;
|
||||
|
||||
my $time = $self->outtime();
|
||||
|
||||
my $outdir = $self->outdir();
|
||||
my $filename = $outdir . '/' . $time . '.outgoing';
|
||||
open(OUTFILE, ">$filename") || return 0;
|
||||
flock(OUTFILE, LOCK_EX);
|
||||
utime($time, $time, $filename);
|
||||
foreach my $var (keys %{$self->{OUTVARS}}) {
|
||||
print OUTFILE "$var: " . $self->{OUTVARS}{$var} . "\n";
|
||||
}
|
||||
flock(OUTFILE, LOCK_UN);
|
||||
close(OUTFILE);
|
||||
utime($time, $time, $filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,61 @@
|
||||
package Asterisk::QCall;
|
||||
|
||||
require 5.004;
|
||||
|
||||
use Fcntl ':flock';
|
||||
use Asterisk;
|
||||
|
||||
$VERSION = '0.01';
|
||||
|
||||
sub version { $VERSION; }
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
my $self = {};
|
||||
$self->{QUEUEDIR} = '/var/spool/asterisk/qcall';
|
||||
$self->{QUEUETIME} = undef;
|
||||
bless $self, ref $class || $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub DESTROY { }
|
||||
|
||||
sub queuedir {
|
||||
my ($self, $dir) = @_;
|
||||
|
||||
if (defined($dir)) {
|
||||
$self->{QUEUEDIR} = $dir;
|
||||
}
|
||||
|
||||
return $self->{QUEUEDIR};
|
||||
}
|
||||
|
||||
sub queuetime {
|
||||
my ($self, $time) = @_;
|
||||
|
||||
if (defined($time)) {
|
||||
$self->{QUEUETIME} = $time;
|
||||
} elsif (!defined($self->{QUEUETIME})) {
|
||||
$self->{QUEUETIME} = time();
|
||||
}
|
||||
|
||||
return $self->{QUEUETIME};
|
||||
}
|
||||
|
||||
sub create_qcall {
|
||||
my ($self, $dialstring, $callerid, $extension, $maxsecs, $identifier, $response) = @_;
|
||||
|
||||
my $time = $self->queuetime();
|
||||
|
||||
my $queuedir = $self->queuedir();
|
||||
my $filename = $queuedir . '/' . $time . '.queue';
|
||||
open(QFILE, ">$filename") || return 0;
|
||||
flock(QFILE, LOCK_EX);
|
||||
print QFILE "$dialstring $callerid $extension $maxsecs $identifier $response";
|
||||
flock(QFILE, LOCK_UN);
|
||||
close(QFILE);
|
||||
my $ret = utime($time, $time, $filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,261 @@
|
||||
package Asterisk::Voicemail;
|
||||
|
||||
require 5.004;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Asterisk::Voicemail - Stuff to deal with asterisk voicemail
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
This is not completed yet
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
description
|
||||
|
||||
=cut
|
||||
|
||||
use Asterisk;
|
||||
|
||||
@ISA = ( 'Asterisk' );
|
||||
|
||||
$VERSION = '0.01';
|
||||
|
||||
$DEBUG = 5;
|
||||
|
||||
sub version { $VERSION; }
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
my $self = {};
|
||||
bless $self, ref $class || $class;
|
||||
# while (my ($key,$value) = each %args) { $self->set($key,$value); }
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub DESTROY { }
|
||||
|
||||
sub spooldirectory {
|
||||
my ($self, $directory) = @_;
|
||||
|
||||
if (defined($directory)) {
|
||||
$self->{'spooldirectory'} = $directory;
|
||||
} else {
|
||||
$self->{'spooldirectory'} = '/var/spool/asterisk/vm' if (!defined($self->{'spooldirectory'}));
|
||||
}
|
||||
|
||||
return $self->{'spooldirectory'};
|
||||
}
|
||||
|
||||
sub sounddirectory {
|
||||
my ($self, $directory) = @_;
|
||||
|
||||
if (defined($directory)) {
|
||||
$self->{'sounddirectory'} = $directory;
|
||||
} else {
|
||||
$self->{'sounddirectory'} = '/var/lib/asterisk/sounds' if (!defined($self->{'sounddirectory'}));
|
||||
}
|
||||
|
||||
return $self->{'sounddirectory'};
|
||||
}
|
||||
|
||||
sub serveremail {
|
||||
my ($self, $email) = @_;
|
||||
|
||||
$self->{'serveremail'} = $email if (defined($email));
|
||||
|
||||
return $self->{'serveremail'};
|
||||
}
|
||||
|
||||
sub format {
|
||||
my ($self, @formats) = @_;
|
||||
|
||||
if (@formats) {
|
||||
$self->{'formats'} = @formats;
|
||||
}
|
||||
|
||||
return $self->{'formats'};
|
||||
}
|
||||
|
||||
sub vmbox {
|
||||
my ($self, $vmbox, $vmpass, $vmfn, $vmemail) = @_;
|
||||
|
||||
if (defined($vmbox) && (defined($vmpass) || defined($vmfn) || defined($vmemail)) ) {
|
||||
$self->{'vmbox'}{$vmbox}{'pass'} = $vmpass if (defined($vmpass));
|
||||
$self->{'vmbox'}{$vmbox}{'fn'} = $vmfn if (defined($vmfn));
|
||||
$self->{'vmbox'}{$vmbox}{'email'} = $vmemail if (defined($vmemail));
|
||||
} elsif (defined($vmbox)) {
|
||||
return ($self->{'vmbox'}{$vmbox}{'pass'},
|
||||
$self->{'vmbox'}{$vmbox}{'fn'},
|
||||
$self->{'vmbox'}{$vmbox}{'email'} );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub getfolders {
|
||||
my ($self, $vmbox) = @_;
|
||||
|
||||
my @folders = ();
|
||||
|
||||
my $spool = $self->spooldirectory();
|
||||
|
||||
foreach $file (<$spool/$vmbox/*>) {
|
||||
if ( -d $file ) {
|
||||
$file =~ s/$spool\/$vmbox\///;
|
||||
push(@folders, $file);
|
||||
}
|
||||
}
|
||||
return @folders;
|
||||
}
|
||||
|
||||
sub configfile {
|
||||
my ($self, $configfile) = @_;
|
||||
|
||||
if (defined($configfile)) {
|
||||
$self->{'configfile'} = $configfile;
|
||||
} else {
|
||||
$self->{'configfile'} = '/etc/asterisk/voicemail.conf' if (!defined($self->{'configfile'}));
|
||||
}
|
||||
|
||||
return $self->{'configfile'};
|
||||
}
|
||||
|
||||
sub readconfig {
|
||||
my ($self) = @_;
|
||||
|
||||
my $context = '';
|
||||
my $line = '';
|
||||
|
||||
my $configfile = $self->configfile();
|
||||
|
||||
open(CF, "<$configfile") || die "Error loading $configfile: $!\n";
|
||||
while ($line = <CF>) {
|
||||
chop($line);
|
||||
|
||||
$line =~ s/;.*$//;
|
||||
$line =~ s/\s*$//;
|
||||
|
||||
if ($line =~ /^;/) {
|
||||
next;
|
||||
} elsif ($line =~ /^\s*$/) {
|
||||
next;
|
||||
} elsif ($line =~ /^\[(\w+)\]$/) {
|
||||
$context = $1;
|
||||
print STDERR "Context: $context\n" if ($DEBUG>3);
|
||||
} elsif ($line =~ /^format\s*[=>]+\s*(.*)/) {
|
||||
my $formats = $1;
|
||||
$self->format(split(/|/, $formats));
|
||||
} elsif ($line =~ /^serveremail\s*[=>]+\s*(.*)/) {
|
||||
$self->serveremail($1);
|
||||
} elsif ($line =~ /^(\d+)\s*[=>]+\s*(.*)/) {
|
||||
my $vmbox = $1;
|
||||
my $vmstr = $2;
|
||||
my ($vmpass, $vmfn, $vmemail) = split(/,/, $vmstr);
|
||||
print STDERR "VM BOX ($vmbox)\n" if ($DEBUG>3);
|
||||
$self->vmbox($vmbox, $vmpass, $vmfn, $vmstr);
|
||||
} else {
|
||||
print STDERR "Unknown line: $line\n" if ($DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
close(CF);
|
||||
}
|
||||
|
||||
sub appendsoundfile {
|
||||
my ($self, $source, $dest) = @_;
|
||||
|
||||
open(IN, "<$source") || return 0;
|
||||
open(OUT, ">>$dest") || return 0;
|
||||
while (<IN>) {
|
||||
print OUT $_;
|
||||
}
|
||||
close(IN);
|
||||
close(OUT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub validmailbox {
|
||||
my ($self, $vmbox) = @_;
|
||||
|
||||
return 1 if ($self->vmbox($vmbox));
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub msgcount {
|
||||
my ($self, $vmbox, $folder) = @_;
|
||||
|
||||
my $count = 0;
|
||||
|
||||
return $count if (!defined($vmbox) || !defined($folder));
|
||||
|
||||
my $spool = $self->spooldirectory() . '/' . $vmbox . '/' . $folder;
|
||||
|
||||
if (opendir(DIR, $spool)) {
|
||||
my @msgs = grep(/^msg.*\.txt$/, readdir(DIR));
|
||||
$count = $#msgs+1;
|
||||
closedir(DIR);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
sub msgcountstr {
|
||||
my ($self, $vmbox, $folder) = @_;
|
||||
|
||||
my $count = $self->msgcount($vmbox, $folder);
|
||||
|
||||
if ($count > 1) {
|
||||
return "$count messages";
|
||||
} elsif ($count > 0) {
|
||||
return "$count message";
|
||||
} else {
|
||||
return "no messages";
|
||||
}
|
||||
}
|
||||
|
||||
sub createdefaultmailbox {
|
||||
my ($self, $vmbox) = @_;
|
||||
|
||||
my $spool = $self->spooldirectory();
|
||||
my $sounddir = $self->sounddirectory();
|
||||
|
||||
my $vmdir = "$spool/$vmbox";
|
||||
|
||||
|
||||
if ( -d $vmdir ) {
|
||||
print STDERR "Directory already exists: $vmdir\n" if ($DEBUG);
|
||||
} else {
|
||||
mkdir($vmdir, 0755) || return 0;
|
||||
mkdir("$vmdir/INBOX", 0755) || return 0;
|
||||
}
|
||||
|
||||
$self->appendsoundfile("$sounddir/vm-theperson.gsm", "$vmdir/unavail.gsm");
|
||||
$self->appendsoundfile("$sounddir/vm-theperson.gsm", "$vmdir/busy.gsm");
|
||||
$self->appendsoundfile("$sounddir/vm-extension.gsm", "$vmdir/greet.gsm");
|
||||
|
||||
foreach $chr (split(//, $vmbox)) {
|
||||
$self->appendsoundfile("$sounddir/digits/$chr.gsm", "$vmdir/unavail.gsm");
|
||||
$self->appendsoundfile("$sounddir/digits/$chr.gsm", "$vmdir/busy.gsm");
|
||||
$self->appendsoundfile("$sounddir/digits/$chr.gsm", "$vmdir/greet.gsm");
|
||||
}
|
||||
|
||||
$self->appendsoundfile("$sounddir/vm-isunavail.gsm", "$vmdir/unavail.gsm");
|
||||
$self->appendsoundfile("$sounddir/vm-isonphone.gsm", "$vmdir/busy.gsm");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub messages {
|
||||
my ($self, $messages, $folder) = @_;
|
||||
|
||||
my $path = $self->spooldirectory() . '/' . $mailbox . '/' . $folder;
|
||||
if (opendir(DIR, $path)) {
|
||||
my @msgs = sort grep(/^msg....\.txt$/, readdir(DIR));
|
||||
closedir(DIR);
|
||||
return map { s/^msg(....)\.txt$/$1/; $_ } @msgs;
|
||||
}
|
||||
return ();
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 311 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 240 B |
Binary file not shown.
|
After Width: | Height: | Size: 230 B |
Binary file not shown.
|
After Width: | Height: | Size: 520 B |
+1015
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,266 @@
|
||||
package Net::HTTP;
|
||||
|
||||
# $Id: HTTP.pm,v 1.43 2002/12/26 09:13:53 gisle Exp $
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION @ISA);
|
||||
|
||||
$VERSION = "1.00";
|
||||
eval { require IO::Socket::INET } || require IO::Socket;
|
||||
require Net::HTTP::Methods;
|
||||
|
||||
@ISA=qw(IO::Socket::INET Net::HTTP::Methods);
|
||||
|
||||
sub configure {
|
||||
my($self, $cnf) = @_;
|
||||
$self->http_configure($cnf);
|
||||
}
|
||||
|
||||
sub http_connect {
|
||||
my($self, $cnf) = @_;
|
||||
$self->SUPER::configure($cnf);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Net::HTTP - Low-level HTTP connection (client)
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Net::HTTP;
|
||||
my $s = Net::HTTP->new(Host => "www.perl.com) || die $@;
|
||||
$s->write_request(GET => "/", 'User-Agent' => "Mozilla/5.0");
|
||||
my($code, $mess, %h) = $s->read_response_headers;
|
||||
|
||||
while (1) {
|
||||
my $buf;
|
||||
my $n = $s->read_entity_body($buf, 1024);
|
||||
die "read failed: $!" unless defined $n;
|
||||
last unless $n;
|
||||
print $buf;
|
||||
}
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The C<Net::HTTP> class is a low-level HTTP client. An instance of the
|
||||
C<Net::HTTP> class represents a connection to an HTTP server. The
|
||||
HTTP protocol is described in RFC 2616. The C<Net::HTTP> class
|
||||
support C<HTTP/1.0> and C<HTTP/1.1>.
|
||||
|
||||
C<Net::HTTP> is a sub-class of C<IO::Socket::INET>. You can mix the
|
||||
methods described below with reading and writing from the socket
|
||||
directly. This is not necessary a good idea, unless you know what you
|
||||
are doing.
|
||||
|
||||
The following methods are provided (in addition to those of
|
||||
C<IO::Socket::INET>):
|
||||
|
||||
=over
|
||||
|
||||
=item $s = Net::HTTP->new( %options )
|
||||
|
||||
The C<Net::HTTP> constructor method takes the same options as
|
||||
C<IO::Socket::INET>'s as well as these:
|
||||
|
||||
Host: Initial host attribute value
|
||||
KeepAlive: Initial keep_alive attribute value
|
||||
SendTE: Initial send_te attribute_value
|
||||
HTTPVersion: Initial http_version attribute value
|
||||
PeerHTTPVersion: Initial peer_http_version attribute value
|
||||
MaxLineLength: Initial max_line_length attribute value
|
||||
MaxHeaderLines: Initial max_header_lines attribute value
|
||||
|
||||
The C<Host> option is also the default for C<IO::Socket::INET>'s
|
||||
C<PeerAddr>. The C<PeerPort> defaults to 80 if not provided.
|
||||
|
||||
The C<Listen> option provided by C<IO::Socket::INET>'s constructor
|
||||
method is not allowed.
|
||||
|
||||
If unable to connect to the given HTTP server then the constructor
|
||||
returns C<undef> and $@ contains the reason. After a successful
|
||||
connect, a C<Net:HTTP> object is returned.
|
||||
|
||||
=item $s->host
|
||||
|
||||
Get/set the default value of the C<Host> header to send. The $host
|
||||
should not be set to an empty string (or C<undef>).
|
||||
|
||||
=item $s->keep_alive
|
||||
|
||||
Get/set the I<keep-alive> value. If this value is TRUE then the
|
||||
request will be sent with headers indicating that the server should try
|
||||
to keep the connection open so that multiple requests can be sent.
|
||||
|
||||
The actual headers set will depend on the value of the C<http_version>
|
||||
and C<peer_http_version> attributes.
|
||||
|
||||
=item $s->send_te
|
||||
|
||||
Get/set the a value indicating if the request will be sent with a "TE"
|
||||
header to indicate the transfer encodings that the server can chose to
|
||||
use. If the C<Compress::Zlib> module is installed then this will
|
||||
annouce that this client accept both the I<deflate> and I<gzip>
|
||||
encodings.
|
||||
|
||||
=item $s->http_version
|
||||
|
||||
Get/set the HTTP version number that this client should announce.
|
||||
This value can only be set to "1.0" or "1.1". The default is "1.1".
|
||||
|
||||
=item $s->peer_http_version
|
||||
|
||||
Get/set the protocol version number of our peer. This value will
|
||||
initially be "1.0", but will be updated by a successful
|
||||
read_response_headers() method call.
|
||||
|
||||
=item $s->max_line_length
|
||||
|
||||
Get/set a limit on the length of response line and response header
|
||||
lines. The default is 4096. A value of 0 means no limit.
|
||||
|
||||
=item $s->max_header_length
|
||||
|
||||
Get/set a limit on the number of headers lines that a response can
|
||||
have. The default is 128. A value of 0 means no limit.
|
||||
|
||||
=item $s->format_request($method, $uri, %headers, [$content])
|
||||
|
||||
Format a request message and return it as a string. If the headers do
|
||||
not include a C<Host> header, then a header is inserted with the value
|
||||
of the C<host> attribute. Headers like C<Connection> and
|
||||
C<Keep-Alive> might also be added depending on the status of the
|
||||
C<keep_alive> attribute.
|
||||
|
||||
If $content is given (and it is non-empty), then a C<Content-Length>
|
||||
header is automatically added unless it was already present.
|
||||
|
||||
=item $s->write_request($method, $uri, %headers, [$content])
|
||||
|
||||
Format and send a request message. Arguments are the same as for
|
||||
format_request(). Returns true if successful.
|
||||
|
||||
=item $s->format_chunk( $data )
|
||||
|
||||
Returns the string to be written for the given chunk of data.
|
||||
|
||||
=item $s->write_chunk($data)
|
||||
|
||||
Will write a new chunk of request entity body data. This method
|
||||
should only be used if the C<Transfer-Encoding> header with a value of
|
||||
C<chunked> was sent in the request. Note, writing zero-length data is
|
||||
a no-op. Use the write_chunk_eof() method to signal end of entity
|
||||
body data.
|
||||
|
||||
Returns true if successful.
|
||||
|
||||
=item $s->format_chunk_eof( %trailers )
|
||||
|
||||
Returns the string to be written for signaling EOF when a
|
||||
C<Transfer-Encoding> of C<chunked> is used.
|
||||
|
||||
=item $s->write_chunk_eof( %trailers )
|
||||
|
||||
Will write eof marker for chunked data and optional trailers. Note
|
||||
that trailers should not really be used unless is was signaled
|
||||
with a C<Trailer> header.
|
||||
|
||||
Returns true if successful.
|
||||
|
||||
=item ($code, $mess, %headers) = $s->read_response_headers( %opts )
|
||||
|
||||
Read response headers from server and return it. The $code is the 3
|
||||
digit HTTP status code (see L<HTTP::Status>) and $mess is the textual
|
||||
message that came with it. Headers are then returned as key/value
|
||||
pairs. Since key letter casing is not normalized and the same key can
|
||||
even occur multiple times, assigning these values directly to a hash
|
||||
is not wise. Only the $code is returned if this method is called in
|
||||
scalar context.
|
||||
|
||||
As a side effect this method updates the 'peer_http_version'
|
||||
attribute.
|
||||
|
||||
Options might be passed in as key/value pairs. There are currently
|
||||
only two options supported; C<laxed> and C<junk_out>.
|
||||
|
||||
The C<laxed> option will make read_response_headers() more forgiving
|
||||
towards servers that have not learned how to speak HTTP properly. The
|
||||
C<laxed> option is a boolean flag, and is enabled by passing in a TRUE
|
||||
value. The C<junk_out> option can be used to capture bad header lines
|
||||
when C<laxed> is enabled. The value should be an array reference.
|
||||
Bad header lines will be pushed onto the array.
|
||||
|
||||
The C<laxed> option must be specified in order to communicate with
|
||||
pre-HTTP/1.0 servers that don't describe the response outcome or the
|
||||
data they send back with a header block. For these servers
|
||||
peer_http_version is set to "0.9" and this method returns (200,
|
||||
"Assumed OK").
|
||||
|
||||
The method will raise an exception (die) if the server does not speak
|
||||
proper HTTP or if the C<max_line_length> or C<max_header_length>
|
||||
limits are reached. If the C<laxed> option is turned on and
|
||||
C<max_line_length> and C<max_header_length> checks are turned off,
|
||||
then no exception will be raised and this method will always
|
||||
return a response code.
|
||||
|
||||
=item $n = $s->read_entity_body($buf, $size);
|
||||
|
||||
Reads chunks of the entity body content. Basically the same interface
|
||||
as for read() and sysread(), but the buffer offset argument is not
|
||||
supported yet. This method should only be called after a successful
|
||||
read_response_headers() call.
|
||||
|
||||
The return value will be C<undef> on read errors, 0 on EOF, -1 if no data
|
||||
could be returned this time, otherwise the number of bytes assgined
|
||||
to $buf. The $buf set to "" when the return value is -1.
|
||||
|
||||
This method will raise exceptions (die) if the server does not speak
|
||||
proper HTTP. This can only happen when reading chunked data.
|
||||
|
||||
=item %headers = $s->get_trailers
|
||||
|
||||
After read_entity_body() has returned 0 to indicate end of the entity
|
||||
body, you might call this method to pick up any trailers.
|
||||
|
||||
=item $s->_rbuf
|
||||
|
||||
Get/set the read buffer content. The read_response_headers() and
|
||||
read_entity_body() methods use an internal buffer which they will look
|
||||
for data before they actually sysread more from the socket itself. If
|
||||
they read too much, the remaining data will be left in this buffer.
|
||||
|
||||
=item $s->_rbuf_length
|
||||
|
||||
Returns the number of bytes in the read buffer. This should always be
|
||||
the same as:
|
||||
|
||||
length($s->_rbuf)
|
||||
|
||||
but might be more efficient.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SUBCLASSING
|
||||
|
||||
The read_response_headers() and read_entity_body() will invoke the
|
||||
sysread() method when they need more data. Subclasses might want to
|
||||
override this method to contol how reading takes place.
|
||||
|
||||
The object itself is a glob. Subclasses should avoid using hash key
|
||||
names prefixed with C<http_> and C<io_>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<LWP>, L<IO::Socket::INET>, L<Net::HTTP::NB>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2001-2003 Gisle Aas.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the same terms as Perl itself.
|
||||
|
||||
=cut
|
||||
@@ -0,0 +1,520 @@
|
||||
package Net::HTTP::Methods;
|
||||
|
||||
# $Id: Methods.pm,v 1.12 2002/12/26 09:13:55 gisle Exp $
|
||||
|
||||
require 5.005; # 4-arg substr
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION);
|
||||
|
||||
$VERSION = "1.00";
|
||||
|
||||
my $CRLF = "\015\012"; # "\r\n" is not portable
|
||||
|
||||
sub new {
|
||||
my($class, %cnf) = @_;
|
||||
require Symbol;
|
||||
my $self = bless Symbol::gensym(), $class;
|
||||
return $self->http_configure(\%cnf);
|
||||
}
|
||||
|
||||
sub http_configure {
|
||||
my($self, $cnf) = @_;
|
||||
|
||||
die "Listen option not allowed" if $cnf->{Listen};
|
||||
my $host = delete $cnf->{Host};
|
||||
my $peer = $cnf->{PeerAddr} || $cnf->{PeerHost};
|
||||
if ($host) {
|
||||
$cnf->{PeerAddr} = $host unless $peer;
|
||||
}
|
||||
else {
|
||||
$host = $peer;
|
||||
$host =~ s/:.*//;
|
||||
}
|
||||
$cnf->{PeerPort} = $self->http_default_port unless $cnf->{PeerPort};
|
||||
$cnf->{Proto} = 'tcp';
|
||||
|
||||
my $keep_alive = delete $cnf->{KeepAlive};
|
||||
my $http_version = delete $cnf->{HTTPVersion};
|
||||
$http_version = "1.1" unless defined $http_version;
|
||||
my $peer_http_version = delete $cnf->{PeerHTTPVersion};
|
||||
$peer_http_version = "1.0" unless defined $peer_http_version;
|
||||
my $send_te = delete $cnf->{SendTE};
|
||||
my $max_line_length = delete $cnf->{MaxLineLength};
|
||||
$max_line_length = 4*1024 unless defined $max_line_length;
|
||||
my $max_header_lines = delete $cnf->{MaxHeaderLines};
|
||||
$max_header_lines = 128 unless defined $max_header_lines;
|
||||
|
||||
return undef unless $self->http_connect($cnf);
|
||||
|
||||
unless ($host =~ /:/) {
|
||||
my $p = $self->peerport;
|
||||
$host .= ":$p";
|
||||
}
|
||||
$self->host($host);
|
||||
$self->keep_alive($keep_alive);
|
||||
$self->send_te($send_te);
|
||||
$self->http_version($http_version);
|
||||
$self->peer_http_version($peer_http_version);
|
||||
$self->max_line_length($max_line_length);
|
||||
$self->max_header_lines($max_header_lines);
|
||||
|
||||
${*$self}{'http_buf'} = "";
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub http_default_port {
|
||||
80;
|
||||
}
|
||||
|
||||
# set up property accessors
|
||||
for my $method (qw(host keep_alive send_te max_line_length max_header_lines peer_http_version)) {
|
||||
my $prop_name = "http_" . $method;
|
||||
no strict 'refs';
|
||||
*$method = sub {
|
||||
my $self = shift;
|
||||
my $old = ${*$self}{$prop_name};
|
||||
${*$self}{$prop_name} = shift if @_;
|
||||
return $old;
|
||||
};
|
||||
}
|
||||
|
||||
# we want this one to be a bit smarter
|
||||
sub http_version {
|
||||
my $self = shift;
|
||||
my $old = ${*$self}{'http_version'};
|
||||
if (@_) {
|
||||
my $v = shift;
|
||||
$v = "1.0" if $v eq "1"; # float
|
||||
unless ($v eq "1.0" or $v eq "1.1") {
|
||||
require Carp;
|
||||
Carp::croak("Unsupported HTTP version '$v'");
|
||||
}
|
||||
${*$self}{'http_version'} = $v;
|
||||
}
|
||||
$old;
|
||||
}
|
||||
|
||||
sub format_request {
|
||||
my $self = shift;
|
||||
my $method = shift;
|
||||
my $uri = shift;
|
||||
|
||||
my $content = (@_ % 2) ? pop : "";
|
||||
|
||||
for ($method, $uri) {
|
||||
require Carp;
|
||||
Carp::croak("Bad method or uri") if /\s/ || !length;
|
||||
}
|
||||
|
||||
push(@{${*$self}{'http_request_method'}}, $method);
|
||||
my $ver = ${*$self}{'http_version'};
|
||||
my $peer_ver = ${*$self}{'http_peer_http_version'} || "1.0";
|
||||
|
||||
my @h;
|
||||
my @connection;
|
||||
my %given = (host => 0, "content-length" => 0, "te" => 0);
|
||||
while (@_) {
|
||||
my($k, $v) = splice(@_, 0, 2);
|
||||
my $lc_k = lc($k);
|
||||
if ($lc_k eq "connection") {
|
||||
push(@connection, split(/\s*,\s*/, $v));
|
||||
next;
|
||||
}
|
||||
if (exists $given{$lc_k}) {
|
||||
$given{$lc_k}++;
|
||||
}
|
||||
push(@h, "$k: $v");
|
||||
}
|
||||
|
||||
if (length($content) && !$given{'content-length'}) {
|
||||
push(@h, "Content-Length: " . length($content));
|
||||
}
|
||||
|
||||
my @h2;
|
||||
if ($given{te}) {
|
||||
push(@connection, "TE") unless grep lc($_) eq "te", @connection;
|
||||
}
|
||||
elsif ($self->send_te && zlib_ok()) {
|
||||
# gzip is less wanted since the Compress::Zlib interface for
|
||||
# it does not really allow chunked decoding to take place easily.
|
||||
push(@h2, "TE: deflate,gzip;q=0.3");
|
||||
push(@connection, "TE");
|
||||
}
|
||||
|
||||
unless (grep lc($_) eq "close", @connection) {
|
||||
if ($self->keep_alive) {
|
||||
if ($peer_ver eq "1.0") {
|
||||
# from looking at Netscape's headers
|
||||
push(@h2, "Keep-Alive: 300");
|
||||
unshift(@connection, "Keep-Alive");
|
||||
}
|
||||
}
|
||||
else {
|
||||
push(@connection, "close") if $ver ge "1.1";
|
||||
}
|
||||
}
|
||||
push(@h2, "Connection: " . join(", ", @connection)) if @connection;
|
||||
push(@h2, "Host: ${*$self}{'http_host'}") unless $given{host};
|
||||
|
||||
return join($CRLF, "$method $uri HTTP/$ver", @h2, @h, "", $content);
|
||||
}
|
||||
|
||||
|
||||
sub write_request {
|
||||
my $self = shift;
|
||||
$self->print($self->format_request(@_));
|
||||
}
|
||||
|
||||
sub format_chunk {
|
||||
my $self = shift;
|
||||
return $_[0] unless defined($_[0]) && length($_[0]);
|
||||
return sprintf("%x", length($_[0])) . $CRLF . $_[0] . $CRLF;
|
||||
}
|
||||
|
||||
sub write_chunk {
|
||||
my $self = shift;
|
||||
return 1 unless defined($_[0]) && length($_[0]);
|
||||
$self->print(sprintf("%x", length($_[0])) . $CRLF . $_[0] . $CRLF);
|
||||
}
|
||||
|
||||
sub format_chunk_eof {
|
||||
my $self = shift;
|
||||
my @h;
|
||||
while (@_) {
|
||||
push(@h, sprintf "%s: %s$CRLF", splice(@_, 0, 2));
|
||||
}
|
||||
return join("", "0$CRLF", @h, $CRLF);
|
||||
}
|
||||
|
||||
sub write_chunk_eof {
|
||||
my $self = shift;
|
||||
$self->print($self->format_chunk_eof(@_));
|
||||
}
|
||||
|
||||
|
||||
sub my_read {
|
||||
die if @_ > 3;
|
||||
my $self = shift;
|
||||
my $len = $_[1];
|
||||
for (${*$self}{'http_buf'}) {
|
||||
if (length) {
|
||||
$_[0] = substr($_, 0, $len, "");
|
||||
return length($_[0]);
|
||||
}
|
||||
else {
|
||||
return $self->sysread($_[0], $len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub my_readline {
|
||||
my $self = shift;
|
||||
for (${*$self}{'http_buf'}) {
|
||||
my $max_line_length = ${*$self}{'http_max_line_length'};
|
||||
my $pos;
|
||||
while (1) {
|
||||
# find line ending
|
||||
$pos = index($_, "\012");
|
||||
last if $pos >= 0;
|
||||
die "Line too long (limit is $max_line_length)"
|
||||
if $max_line_length && length($_) > $max_line_length;
|
||||
|
||||
# need to read more data to find a line ending
|
||||
my $n = $self->sysread($_, 1024, length);
|
||||
if (!$n) {
|
||||
return undef unless length;
|
||||
return substr($_, 0, length, "");
|
||||
}
|
||||
}
|
||||
die "Line too long ($pos; limit is $max_line_length)"
|
||||
if $max_line_length && $pos > $max_line_length;
|
||||
|
||||
my $line = substr($_, 0, $pos+1, "");
|
||||
$line =~ s/(\015?\012)\z// || die "Assert";
|
||||
return wantarray ? ($line, $1) : $line;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub _rbuf {
|
||||
my $self = shift;
|
||||
if (@_) {
|
||||
for (${*$self}{'http_buf'}) {
|
||||
my $old;
|
||||
$old = $_ if defined wantarray;
|
||||
$_ = shift;
|
||||
return $old;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ${*$self}{'http_buf'};
|
||||
}
|
||||
}
|
||||
|
||||
sub _rbuf_length {
|
||||
my $self = shift;
|
||||
return length ${*$self}{'http_buf'};
|
||||
}
|
||||
|
||||
|
||||
sub _read_header_lines {
|
||||
my $self = shift;
|
||||
my $junk_out = shift;
|
||||
|
||||
my @headers;
|
||||
my $line_count = 0;
|
||||
my $max_header_lines = ${*$self}{'http_max_header_lines'};
|
||||
while (my $line = my_readline($self)) {
|
||||
if ($line =~ /^(\S+)\s*:\s*(.*)/s) {
|
||||
push(@headers, $1, $2);
|
||||
}
|
||||
elsif (@headers && $line =~ s/^\s+//) {
|
||||
$headers[-1] .= " " . $line;
|
||||
}
|
||||
elsif ($junk_out) {
|
||||
push(@$junk_out, $line);
|
||||
}
|
||||
else {
|
||||
die "Bad header: '$line'\n";
|
||||
}
|
||||
if ($max_header_lines) {
|
||||
$line_count++;
|
||||
if ($line_count >= $max_header_lines) {
|
||||
die "Too many header lines (limit is $max_header_lines)";
|
||||
}
|
||||
}
|
||||
}
|
||||
return @headers;
|
||||
}
|
||||
|
||||
|
||||
sub read_response_headers {
|
||||
my($self, %opt) = @_;
|
||||
my $laxed = $opt{laxed};
|
||||
|
||||
my($status, $eol) = my_readline($self);
|
||||
unless (defined $status) {
|
||||
die "EOF instead of response status line" unless $laxed;
|
||||
# assume HTTP/0.9
|
||||
${*$self}{'http_peer_http_version'} = "0.9";
|
||||
${*$self}{'http_status'} = "200";
|
||||
return 200 unless wantarray;
|
||||
return (200, "EOF");
|
||||
}
|
||||
|
||||
my($peer_ver, $code, $message) = split(/\s+/, $status, 3);
|
||||
if (!$peer_ver || $peer_ver !~ s,^HTTP/,, || $code !~ /^[1-5]\d\d$/) {
|
||||
die "Bad response status line: '$status'" unless $laxed;
|
||||
# assume HTTP/0.9
|
||||
${*$self}{'http_peer_http_version'} = "0.9";
|
||||
${*$self}{'http_status'} = "200";
|
||||
substr(${*$self}{'http_buf'}, 0, 0) = $status . $eol;
|
||||
return 200 unless wantarray;
|
||||
return (200, "Assumed OK");
|
||||
};
|
||||
|
||||
${*$self}{'http_peer_http_version'} = $peer_ver;
|
||||
${*$self}{'http_status'} = $code;
|
||||
|
||||
my $junk_out;
|
||||
if ($laxed) {
|
||||
$junk_out = $opt{junk_out} || [];
|
||||
}
|
||||
my @headers = $self->_read_header_lines($junk_out);
|
||||
|
||||
# pick out headers that read_entity_body might need
|
||||
my @te;
|
||||
my $content_length;
|
||||
for (my $i = 0; $i < @headers; $i += 2) {
|
||||
my $h = lc($headers[$i]);
|
||||
if ($h eq 'transfer-encoding') {
|
||||
push(@te, $headers[$i+1]);
|
||||
}
|
||||
elsif ($h eq 'content-length') {
|
||||
$content_length = $headers[$i+1];
|
||||
}
|
||||
}
|
||||
${*$self}{'http_te'} = join(",", @te);
|
||||
${*$self}{'http_content_length'} = $content_length;
|
||||
${*$self}{'http_first_body'}++;
|
||||
delete ${*$self}{'http_trailers'};
|
||||
return $code unless wantarray;
|
||||
return ($code, $message, @headers);
|
||||
}
|
||||
|
||||
|
||||
sub read_entity_body {
|
||||
my $self = shift;
|
||||
my $buf_ref = \$_[0];
|
||||
my $size = $_[1];
|
||||
die "Offset not supported yet" if $_[2];
|
||||
|
||||
my $chunked;
|
||||
my $bytes;
|
||||
|
||||
if (${*$self}{'http_first_body'}) {
|
||||
${*$self}{'http_first_body'} = 0;
|
||||
delete ${*$self}{'http_chunked'};
|
||||
delete ${*$self}{'http_bytes'};
|
||||
my $method = shift(@{${*$self}{'http_request_method'}});
|
||||
my $status = ${*$self}{'http_status'};
|
||||
if ($method eq "HEAD" || $status =~ /^(?:1|[23]04)/) {
|
||||
# these responses are always empty
|
||||
$bytes = 0;
|
||||
}
|
||||
elsif (my $te = ${*$self}{'http_te'}) {
|
||||
my @te = split(/\s*,\s*/, lc($te));
|
||||
die "Chunked must be last Transfer-Encoding '$te'"
|
||||
unless pop(@te) eq "chunked";
|
||||
|
||||
for (@te) {
|
||||
if ($_ eq "deflate" && zlib_ok()) {
|
||||
#require Compress::Zlib;
|
||||
my $i = Compress::Zlib::inflateInit();
|
||||
die "Can't make inflator" unless $i;
|
||||
$_ = sub { scalar($i->inflate($_[0])) }
|
||||
}
|
||||
elsif ($_ eq "gzip" && zlib_ok()) {
|
||||
#require Compress::Zlib;
|
||||
my @buf;
|
||||
$_ = sub {
|
||||
push(@buf, $_[0]);
|
||||
return Compress::Zlib::memGunzip(join("", @buf)) if $_[1];
|
||||
return "";
|
||||
};
|
||||
}
|
||||
elsif ($_ eq "identity") {
|
||||
$_ = sub { $_[0] };
|
||||
}
|
||||
else {
|
||||
die "Can't handle transfer encoding '$te'";
|
||||
}
|
||||
}
|
||||
|
||||
@te = reverse(@te);
|
||||
|
||||
${*$self}{'http_te2'} = @te ? \@te : "";
|
||||
$chunked = -1;
|
||||
}
|
||||
elsif (defined(my $content_length = ${*$self}{'http_content_length'})) {
|
||||
$bytes = $content_length;
|
||||
}
|
||||
else {
|
||||
# XXX Multi-Part types are self delimiting, but RFC 2616 says we
|
||||
# only has to deal with 'multipart/byteranges'
|
||||
|
||||
# Read until EOF
|
||||
}
|
||||
}
|
||||
else {
|
||||
$chunked = ${*$self}{'http_chunked'};
|
||||
$bytes = ${*$self}{'http_bytes'};
|
||||
}
|
||||
|
||||
if (defined $chunked) {
|
||||
# The state encoded in $chunked is:
|
||||
# $chunked == 0: read CRLF after chunk, then chunk header
|
||||
# $chunked == -1: read chunk header
|
||||
# $chunked > 0: bytes left in current chunk to read
|
||||
|
||||
if ($chunked <= 0) {
|
||||
my $line = my_readline($self);
|
||||
if ($chunked == 0) {
|
||||
die "Missing newline after chunk data: '$line'" unless $line eq "";
|
||||
$line = my_readline($self);
|
||||
}
|
||||
my $chunk_len = $line;
|
||||
$chunk_len =~ s/;.*//; # ignore potential chunk parameters
|
||||
unless ($chunk_len =~ /^([\da-fA-F]+)\s*$/) {
|
||||
die "Bad chunk-size in HTTP response: $line";
|
||||
}
|
||||
$chunked = hex($1);
|
||||
if ($chunked == 0) {
|
||||
${*$self}{'http_trailers'} = [$self->_read_header_lines];
|
||||
$$buf_ref = "";
|
||||
|
||||
my $n = 0;
|
||||
if (my $transforms = delete ${*$self}{'http_te2'}) {
|
||||
for (@$transforms) {
|
||||
$$buf_ref = &$_($$buf_ref, 1);
|
||||
}
|
||||
$n = length($$buf_ref);
|
||||
}
|
||||
|
||||
# in case somebody tries to read more, make sure we continue
|
||||
# to return EOF
|
||||
delete ${*$self}{'http_chunked'};
|
||||
${*$self}{'http_bytes'} = 0;
|
||||
|
||||
return $n;
|
||||
}
|
||||
}
|
||||
|
||||
my $n = $chunked;
|
||||
$n = $size if $size && $size < $n;
|
||||
$n = my_read($self, $$buf_ref, $n);
|
||||
return undef unless defined $n;
|
||||
|
||||
${*$self}{'http_chunked'} = $chunked - $n;
|
||||
|
||||
if ($n > 0) {
|
||||
if (my $transforms = ${*$self}{'http_te2'}) {
|
||||
for (@$transforms) {
|
||||
$$buf_ref = &$_($$buf_ref, 0);
|
||||
}
|
||||
$n = length($$buf_ref);
|
||||
$n = -1 if $n == 0;
|
||||
}
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
elsif (defined $bytes) {
|
||||
unless ($bytes) {
|
||||
$$buf_ref = "";
|
||||
return 0;
|
||||
}
|
||||
my $n = $bytes;
|
||||
$n = $size if $size && $size < $n;
|
||||
$n = my_read($self, $$buf_ref, $n);
|
||||
return undef unless defined $n;
|
||||
${*$self}{'http_bytes'} = $bytes - $n;
|
||||
return $n;
|
||||
}
|
||||
else {
|
||||
# read until eof
|
||||
$size ||= 8*1024;
|
||||
return my_read($self, $$buf_ref, $size);
|
||||
}
|
||||
}
|
||||
|
||||
sub get_trailers {
|
||||
my $self = shift;
|
||||
@{${*$self}{'http_trailers'} || []};
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
my $zlib_ok;
|
||||
|
||||
sub zlib_ok {
|
||||
return $zlib_ok if defined $zlib_ok;
|
||||
|
||||
# Try to load Compress::Zlib.
|
||||
local $@;
|
||||
local $SIG{__DIE__};
|
||||
$zlib_ok = 0;
|
||||
|
||||
eval {
|
||||
require Compress::Zlib;
|
||||
Compress::Zlib->VERSION(1.10);
|
||||
$zlib_ok++;
|
||||
};
|
||||
|
||||
return $zlib_ok;
|
||||
}
|
||||
|
||||
} # BEGIN
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,106 @@
|
||||
package Net::HTTP::NB;
|
||||
|
||||
# $Id: NB.pm,v 1.5 2001/08/28 03:03:42 gisle Exp $
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION @ISA);
|
||||
|
||||
$VERSION = "0.02";
|
||||
require Net::HTTP;
|
||||
@ISA=qw(Net::HTTP);
|
||||
|
||||
sub sysread {
|
||||
my $self = $_[0];
|
||||
if (${*$self}{'httpnb_read_count'}++) {
|
||||
${*$self}{'http_buf'} = ${*$self}{'httpnb_save'};
|
||||
die "Multi-read\n";
|
||||
}
|
||||
my $buf;
|
||||
my $offset = $_[3] || 0;
|
||||
my $n = sysread($self, $_[1], $_[2], $offset);
|
||||
${*$self}{'httpnb_save'} .= substr($_[1], $offset);
|
||||
return $n;
|
||||
}
|
||||
|
||||
sub read_response_headers {
|
||||
my $self = shift;
|
||||
${*$self}{'httpnb_read_count'} = 0;
|
||||
${*$self}{'httpnb_save'} = ${*$self}{'http_buf'};
|
||||
my @h = eval { $self->SUPER::read_response_headers(@_) };
|
||||
if ($@) {
|
||||
return if $@ eq "Multi-read\n";
|
||||
die;
|
||||
}
|
||||
return @h;
|
||||
}
|
||||
|
||||
sub read_entity_body {
|
||||
my $self = shift;
|
||||
${*$self}{'httpnb_read_count'} = 0;
|
||||
${*$self}{'httpnb_save'} = ${*$self}{'http_buf'};
|
||||
# XXX I'm not so sure this does the correct thing in case of
|
||||
# transfer-encoding tranforms
|
||||
my $n = eval { $self->SUPER::read_entity_body(@_); };
|
||||
if ($@) {
|
||||
$_[0] = "";
|
||||
return -1;
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Net::HTTP::NB - Non-blocking HTTP client
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Net::HTTP::NB;
|
||||
my $s = Net::HTTP::NB->new(Host => "www.perl.com) || die $@;
|
||||
$s->write_request(GET => "/");
|
||||
|
||||
use IO::Select;
|
||||
my $sel = IO::Select->new($s);
|
||||
|
||||
READ_HEADER: {
|
||||
die "Header timeout" unless $sel->can_read(10);
|
||||
my($code, $mess, %h) = $s->read_response_headers;
|
||||
redo READ_HEADER unless $code;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
die "Body timeout" unless $sel->can_read(10);
|
||||
my $buf;
|
||||
my $n = $s->read_entity_body($buf, 1024);
|
||||
last unless $n;
|
||||
print $buf;
|
||||
}
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Same interface as C<Net::HTTP> but it will never try multiple reads
|
||||
when the read_response_headers() or read_entity_body() methods are
|
||||
invoked. This make it possible to multiplex multiple Net::HTTP::NB
|
||||
using select without risk blocking.
|
||||
|
||||
If read_response_headers() did not see enough data to complete the
|
||||
headers an empty list is returned.
|
||||
|
||||
If read_entity_body() did not see new entity data in its read
|
||||
the value -1 is returned.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Net::HTTP>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2001 Gisle Aas.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the same terms as Perl itself.
|
||||
|
||||
=cut
|
||||
@@ -0,0 +1,55 @@
|
||||
package Net::HTTPS;
|
||||
|
||||
# $Id: HTTPS.pm,v 1.3 2002/12/23 18:16:29 gisle Exp $
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION $SSL_SOCKET_CLASS @ISA);
|
||||
|
||||
$VERSION = "1.00";
|
||||
|
||||
# Figure out which SSL implementation to use
|
||||
if ($IO::Socket::SSL::VERSION) {
|
||||
$SSL_SOCKET_CLASS = "IO::Socket::SSL"; # it was already loaded
|
||||
}
|
||||
else {
|
||||
eval { require Net::SSL; }; # from Crypt-SSLeay
|
||||
if ($@) {
|
||||
my $old_errsv = $@;
|
||||
eval {
|
||||
require IO::Socket::SSL;
|
||||
};
|
||||
if ($@) {
|
||||
$old_errsv =~ s/\s\(\@INC contains:.*\)/)/g;
|
||||
die $old_errsv . $@;
|
||||
}
|
||||
$SSL_SOCKET_CLASS = "IO::Socket::SSL";
|
||||
}
|
||||
else {
|
||||
$SSL_SOCKET_CLASS = "Net::SSL";
|
||||
}
|
||||
}
|
||||
|
||||
require Net::HTTP::Methods;
|
||||
|
||||
@ISA=($SSL_SOCKET_CLASS, 'Net::HTTP::Methods');
|
||||
|
||||
sub configure {
|
||||
my($self, $cnf) = @_;
|
||||
$self->http_configure($cnf);
|
||||
}
|
||||
|
||||
sub http_connect {
|
||||
my($self, $cnf) = @_;
|
||||
$self->SUPER::configure($cnf);
|
||||
}
|
||||
|
||||
sub http_default_port {
|
||||
443;
|
||||
}
|
||||
|
||||
# The underlying SSLeay classes fails to work if the socket is
|
||||
# placed in non-blocking mode. This override of the blocking
|
||||
# method makes sure it stays the way it was created.
|
||||
sub blocking { } # noop
|
||||
|
||||
1;
|
||||
+1015
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,255 @@
|
||||
package Net::SSH;
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION @ISA @EXPORT_OK $ssh $equalspace $DEBUG @ssh_options);
|
||||
use Exporter;
|
||||
use IO::File;
|
||||
use IPC::Open2;
|
||||
use IPC::Open3;
|
||||
|
||||
@ISA = qw(Exporter);
|
||||
@EXPORT_OK = qw( ssh issh ssh_cmd sshopen2 sshopen3 );
|
||||
$VERSION = '0.07';
|
||||
|
||||
$DEBUG = 0;
|
||||
|
||||
$ssh = "ssh";
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Net::SSH - Perl extension for secure shell
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Net::SSH qw(ssh issh sshopen2 sshopen3);
|
||||
|
||||
ssh('user@hostname', $command);
|
||||
|
||||
issh('user@hostname', $command);
|
||||
|
||||
ssh_cmd('user@hostname', $command);
|
||||
ssh_cmd( {
|
||||
user => 'user',
|
||||
host => 'host.name',
|
||||
command => 'command',
|
||||
args => [ '-arg1', '-arg2' ],
|
||||
stdin_string => "string\n",
|
||||
} );
|
||||
|
||||
sshopen2('user@hostname', $reader, $writer, $command);
|
||||
|
||||
sshopen3('user@hostname', $writer, $reader, $error, $command);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Simple wrappers around ssh commands.
|
||||
|
||||
For an all-perl implementation that does not require the system B<ssh> command,
|
||||
see L<Net::SSH::Perl> instead.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=over 4
|
||||
|
||||
=item ssh [USER@]HOST, COMMAND [, ARGS ... ]
|
||||
|
||||
Calls ssh in batch mode.
|
||||
|
||||
=cut
|
||||
|
||||
sub ssh {
|
||||
my($host, @command) = @_;
|
||||
@ssh_options = &_ssh_options unless @ssh_options;
|
||||
my @cmd = ($ssh, @ssh_options, $host, @command);
|
||||
warn "[Net::SSH::ssh] executing ". join(' ', @cmd). "\n"
|
||||
if $DEBUG;
|
||||
system(@cmd);
|
||||
}
|
||||
|
||||
=item issh [USER@]HOST, COMMAND [, ARGS ... ]
|
||||
|
||||
Prints the ssh command to be executed, waits for the user to confirm, and
|
||||
(optionally) executes the command.
|
||||
|
||||
=cut
|
||||
|
||||
sub issh {
|
||||
my($host, @command) = @_;
|
||||
my @cmd = ($ssh, $host, @command);
|
||||
print join(' ', @cmd), "\n";
|
||||
if ( &_yesno ) {
|
||||
system(@cmd);
|
||||
}
|
||||
}
|
||||
|
||||
=item ssh_cmd [USER@]HOST, COMMAND [, ARGS ... ]
|
||||
|
||||
=item ssh_cmd OPTIONS_HASHREF
|
||||
|
||||
Calls ssh in batch mode. Throws a fatal error if data occurs on the command's
|
||||
STDERR. Returns any data from the command's STDOUT.
|
||||
|
||||
If using the hashref-style of passing arguments, possible keys are:
|
||||
|
||||
user (optional)
|
||||
host (requried)
|
||||
command (required)
|
||||
args (optional, arrayref)
|
||||
stdin_string (optional) - written to the command's STDIN
|
||||
|
||||
=cut
|
||||
|
||||
sub ssh_cmd {
|
||||
my($host, $stdin_string, @command);
|
||||
if ( ref($_[0]) ) {
|
||||
my $opt = shift;
|
||||
$host = $opt->{host};
|
||||
$host = $opt->{user}. '@'. $host if exists $opt->{user};
|
||||
@command = ( $opt->{command} );
|
||||
push @command, @{ $opt->{args} } if exists $opt->{args};
|
||||
$stdin_string = $opt->{stdin_string};
|
||||
} else {
|
||||
($host, @command) = @_;
|
||||
undef $stdin_string;
|
||||
}
|
||||
|
||||
my $reader = IO::File->new();
|
||||
my $writer = IO::File->new();
|
||||
my $error = IO::File->new();
|
||||
|
||||
sshopen3( $host, $writer, $reader, $error, @command ) or die $!;
|
||||
|
||||
print $writer $stdin_string if defined $stdin_string;
|
||||
close $writer;
|
||||
|
||||
local $/ = undef;
|
||||
my $output_stream = <$reader>;
|
||||
my $error_stream = <$error>;
|
||||
|
||||
if ( length $error_stream ) {
|
||||
die "[Net:SSH::ssh_cmd] STDERR $error_stream";
|
||||
}
|
||||
|
||||
return $output_stream;
|
||||
|
||||
}
|
||||
|
||||
=item sshopen2 [USER@]HOST, READER, WRITER, COMMAND [, ARGS ... ]
|
||||
|
||||
Connects the supplied filehandles to the ssh process (in batch mode).
|
||||
|
||||
=cut
|
||||
|
||||
sub sshopen2 {
|
||||
my($host, $reader, $writer, @command) = @_;
|
||||
@ssh_options = &_ssh_options unless @ssh_options;
|
||||
open2($reader, $writer, $ssh, @ssh_options, $host, @command);
|
||||
}
|
||||
|
||||
=item sshopen3 HOST, WRITER, READER, ERROR, COMMAND [, ARGS ... ]
|
||||
|
||||
Connects the supplied filehandles to the ssh process (in batch mode).
|
||||
|
||||
=cut
|
||||
|
||||
sub sshopen3 {
|
||||
my($host, $writer, $reader, $error, @command) = @_;
|
||||
@ssh_options = &_ssh_options unless @ssh_options;
|
||||
open3($writer, $reader, $error, $ssh, @ssh_options, $host, @command);
|
||||
}
|
||||
|
||||
sub _yesno {
|
||||
print "Proceed [y/N]:";
|
||||
my $x = scalar(<STDIN>);
|
||||
$x =~ /^y/i;
|
||||
}
|
||||
|
||||
sub _ssh_options {
|
||||
my $reader = IO::File->new();
|
||||
my $writer = IO::File->new();
|
||||
my $error = IO::File->new();
|
||||
open3($writer, $reader, $error, $ssh, '-V');
|
||||
my $ssh_version = <$error>;
|
||||
chomp($ssh_version);
|
||||
if ( $ssh_version =~ /.*OpenSSH[-|_](\w+)\./ && $1 == 1 ) {
|
||||
$equalspace = " ";
|
||||
} else {
|
||||
$equalspace = "=";
|
||||
}
|
||||
my @options = ( '-o', 'BatchMode'.$equalspace.'yes' );
|
||||
if ( $ssh_version =~ /.*OpenSSH[-|_](\w+)\./ && $1 > 1 ) {
|
||||
unshift @options, '-T';
|
||||
}
|
||||
@options;
|
||||
}
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXAMPLE
|
||||
|
||||
use Net::SSH qw(sshopen2);
|
||||
use strict;
|
||||
|
||||
my $user = "username";
|
||||
my $host = "hostname";
|
||||
my $cmd = "command";
|
||||
|
||||
sshopen2("$user\@$host", *READER, *WRITER, "$cmd") || die "ssh: $!";
|
||||
|
||||
while (<READER>) {
|
||||
chomp();
|
||||
print "$_\n";
|
||||
}
|
||||
|
||||
close(READER);
|
||||
close(WRITER);
|
||||
|
||||
=head1 FREQUENTLY ASKED QUESTIONS
|
||||
|
||||
Q: How do you supply a password to connect with ssh within a perl script
|
||||
using the Net::SSH module?
|
||||
|
||||
A: You don't. Use RSA or DSA keys. See the ssh-keygen(1) manpage.
|
||||
|
||||
Q: My script is "leaking" ssh processes.
|
||||
|
||||
A: See L<perlfaq8/"How do I avoid zombies on a Unix system">, L<IPC::Open2>,
|
||||
L<IPC::Open3> and L<perlfunc/waitpid>.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Ivan Kohler <ivan-netssh_pod@420.am>
|
||||
|
||||
John Harrison <japh@in-ta.net> contributed an example for the documentation.
|
||||
|
||||
Martin Langhoff <martin@cwa.co.nz> contributed the ssh_cmd command, and
|
||||
Jeff Finucane <jeff@cmh.net> updated it and took care of the 0.04 release.
|
||||
|
||||
Anthony Awtrey <tony@awtrey.com> contributed a fix for those still using
|
||||
OpenSSH v1.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (c) 2002 Ivan Kohler.
|
||||
Copyright (c) 2002 Freeside Internet Services, LLC
|
||||
All rights reserved.
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the same terms as Perl itself.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Not OO.
|
||||
|
||||
Look at IPC::Session (also fsh)
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
For an all-perl implementation that does not require the system B<ssh> command,
|
||||
see L<Net::SSH::Perl> instead.
|
||||
|
||||
ssh-keygen(1), ssh(1), L<IO::File>, L<IPC::Open2>, L<IPC::Open3>
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+5252
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,359 @@
|
||||
package Time::HiRes;
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION $XS_VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD);
|
||||
|
||||
require Exporter;
|
||||
require DynaLoader;
|
||||
|
||||
@ISA = qw(Exporter DynaLoader);
|
||||
|
||||
@EXPORT = qw( );
|
||||
@EXPORT_OK = qw (usleep sleep ualarm alarm gettimeofday time tv_interval
|
||||
getitimer setitimer
|
||||
ITIMER_REAL ITIMER_VIRTUAL ITIMER_PROF ITIMER_REALPROF
|
||||
d_usleep d_ualarm d_gettimeofday d_getitimer d_setitimer
|
||||
d_nanosleep);
|
||||
|
||||
$VERSION = '1.59';
|
||||
$XS_VERSION = $VERSION;
|
||||
$VERSION = eval $VERSION;
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $constname;
|
||||
($constname = $AUTOLOAD) =~ s/.*:://;
|
||||
die "&Time::HiRes::constant not defined" if $constname eq 'constant';
|
||||
my ($error, $val) = constant($constname);
|
||||
if ($error) { die $error; }
|
||||
{
|
||||
no strict 'refs';
|
||||
*$AUTOLOAD = sub { $val };
|
||||
}
|
||||
goto &$AUTOLOAD;
|
||||
}
|
||||
|
||||
bootstrap Time::HiRes;
|
||||
|
||||
# Preloaded methods go here.
|
||||
|
||||
sub tv_interval {
|
||||
# probably could have been done in C
|
||||
my ($a, $b) = @_;
|
||||
$b = [gettimeofday()] unless defined($b);
|
||||
(${$b}[0] - ${$a}[0]) + ((${$b}[1] - ${$a}[1]) / 1_000_000);
|
||||
}
|
||||
|
||||
# Autoload methods go after =cut, and are processed by the autosplit program.
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Time::HiRes - High resolution alarm, sleep, gettimeofday, interval timers
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
|
||||
|
||||
usleep ($microseconds);
|
||||
|
||||
ualarm ($microseconds);
|
||||
ualarm ($microseconds, $interval_microseconds);
|
||||
|
||||
$t0 = [gettimeofday];
|
||||
($seconds, $microseconds) = gettimeofday;
|
||||
|
||||
$elapsed = tv_interval ( $t0, [$seconds, $microseconds]);
|
||||
$elapsed = tv_interval ( $t0, [gettimeofday]);
|
||||
$elapsed = tv_interval ( $t0 );
|
||||
|
||||
use Time::HiRes qw ( time alarm sleep );
|
||||
|
||||
$now_fractions = time;
|
||||
sleep ($floating_seconds);
|
||||
alarm ($floating_seconds);
|
||||
alarm ($floating_seconds, $floating_interval);
|
||||
|
||||
use Time::HiRes qw( setitimer getitimer
|
||||
ITIMER_REAL ITIMER_VIRTUAL ITIMER_PROF ITIMER_REALPROF );
|
||||
|
||||
setitimer ($which, $floating_seconds, $floating_interval );
|
||||
getitimer ($which);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The C<Time::HiRes> module implements a Perl interface to the C<usleep>,
|
||||
C<ualarm>, C<gettimeofday>, and C<setitimer>/C<getitimer> system calls, in other
|
||||
words, high resolution time and timers. See the L</EXAMPLES> section below
|
||||
and the test scripts for usage; see your system documentation for the
|
||||
description of the underlying C<nanosleep> or C<usleep>, C<ualarm>,
|
||||
C<gettimeofday>, and C<setitimer>/C<getitimer> calls.
|
||||
|
||||
If your system lacks C<gettimeofday()> or an emulation of it you don't
|
||||
get C<gettimeofday()> or the one-argument form of C<tv_interval()>. If your system lacks all of
|
||||
C<nanosleep()>, C<usleep()>, and C<select()>, you don't get
|
||||
C<Time::HiRes::usleep()> or C<Time::HiRes::sleep()>. If your system lacks both
|
||||
C<ualarm()> and C<setitimer()> you don't get
|
||||
C<Time::HiRes::ualarm()> or C<Time::HiRes::alarm()>.
|
||||
|
||||
If you try to import an unimplemented function in the C<use> statement
|
||||
it will fail at compile time.
|
||||
|
||||
If your subsecond sleeping is implemented with C<nanosleep()> instead of
|
||||
C<usleep()>, you can mix subsecond sleeping with signals since
|
||||
C<nanosleep()> does not use signals. This, however is unportable, and you
|
||||
should first check for the truth value of C<&Time::HiRes::d_nanosleep> to
|
||||
see whether you have nanosleep, and then carefully read your
|
||||
C<nanosleep()> C API documentation for any peculiarities. (There is no
|
||||
separate interface to call C<nanosleep()>; just use C<Time::HiRes::sleep()>
|
||||
or C<Time::HiRes::usleep()> with small enough values.)
|
||||
|
||||
Unless using C<nanosleep> for mixing sleeping with signals, give
|
||||
some thought to whether Perl is the tool you should be using for work
|
||||
requiring nanosecond accuracies.
|
||||
|
||||
The following functions can be imported from this module.
|
||||
No functions are exported by default.
|
||||
|
||||
=over 4
|
||||
|
||||
=item gettimeofday ()
|
||||
|
||||
In array context returns a two-element array with the seconds and
|
||||
microseconds since the epoch. In scalar context returns floating
|
||||
seconds like C<Time::HiRes::time()> (see below).
|
||||
|
||||
=item usleep ( $useconds )
|
||||
|
||||
Sleeps for the number of microseconds specified. Returns the number
|
||||
of microseconds actually slept. Can sleep for more than one second,
|
||||
unlike the C<usleep> system call. See also C<Time::HiRes::sleep()> below.
|
||||
|
||||
=item ualarm ( $useconds [, $interval_useconds ] )
|
||||
|
||||
Issues a C<ualarm> call; the C<$interval_useconds> is optional and
|
||||
will be zero if unspecified, resulting in C<alarm>-like behaviour.
|
||||
|
||||
=item tv_interval
|
||||
|
||||
tv_interval ( $ref_to_gettimeofday [, $ref_to_later_gettimeofday] )
|
||||
|
||||
Returns the floating seconds between the two times, which should have
|
||||
been returned by C<gettimeofday()>. If the second argument is omitted,
|
||||
then the current time is used.
|
||||
|
||||
=item time ()
|
||||
|
||||
Returns a floating seconds since the epoch. This function can be
|
||||
imported, resulting in a nice drop-in replacement for the C<time>
|
||||
provided with core Perl; see the L</EXAMPLES> below.
|
||||
|
||||
B<NOTE 1>: This higher resolution timer can return values either less
|
||||
or more than the core C<time()>, depending on whether your platform
|
||||
rounds the higher resolution timer values up, down, or to the nearest second
|
||||
to get the core C<time()>, but naturally the difference should be never
|
||||
more than half a second.
|
||||
|
||||
B<NOTE 2>: Since Sunday, September 9th, 2001 at 01:46:40 AM GMT, when
|
||||
the C<time()> seconds since epoch rolled over to 1_000_000_000, the
|
||||
default floating point format of Perl and the seconds since epoch have
|
||||
conspired to produce an apparent bug: if you print the value of
|
||||
C<Time::HiRes::time()> you seem to be getting only five decimals, not six
|
||||
as promised (microseconds). Not to worry, the microseconds are there
|
||||
(assuming your platform supports such granularity in first place).
|
||||
What is going on is that the default floating point format of Perl
|
||||
only outputs 15 digits. In this case that means ten digits before the
|
||||
decimal separator and five after. To see the microseconds you can use
|
||||
either C<printf>/C<sprintf> with C<"%.6f">, or the C<gettimeofday()> function in
|
||||
list context, which will give you the seconds and microseconds as two
|
||||
separate values.
|
||||
|
||||
=item sleep ( $floating_seconds )
|
||||
|
||||
Sleeps for the specified amount of seconds. Returns the number of
|
||||
seconds actually slept (a floating point value). This function can be
|
||||
imported, resulting in a nice drop-in replacement for the C<sleep>
|
||||
provided with perl, see the L</EXAMPLES> below.
|
||||
|
||||
=item alarm ( $floating_seconds [, $interval_floating_seconds ] )
|
||||
|
||||
The C<SIGALRM> signal is sent after the specified number of seconds.
|
||||
Implemented using C<ualarm()>. The C<$interval_floating_seconds> argument
|
||||
is optional and will be zero if unspecified, resulting in C<alarm()>-like
|
||||
behaviour. This function can be imported, resulting in a nice drop-in
|
||||
replacement for the C<alarm> provided with perl, see the L</EXAMPLES> below.
|
||||
|
||||
B<NOTE 1>: With some operating system and Perl release combinations
|
||||
C<SIGALRM> restarts C<select()>, instead of interuping it.
|
||||
This means that an C<alarm()> followed by a C<select()>
|
||||
may together take the sum of the times specified for the the
|
||||
C<alarm()> and the C<select()>, not just the time of the C<alarm()>.
|
||||
|
||||
=item setitimer ( $which, $floating_seconds [, $interval_floating_seconds ] )
|
||||
|
||||
Start up an interval timer: after a certain time, a signal arrives,
|
||||
and more signals may keep arriving at certain intervals. To disable a
|
||||
timer, use C<$floating_seconds> of zero. If the C<$interval_floating_seconds>
|
||||
is set to zero (or unspecified), the timer is disabled B<after> the
|
||||
next delivered signal.
|
||||
|
||||
Use of interval timers may interfere with C<alarm()>, C<sleep()>,
|
||||
and C<usleep()>. In standard-speak the "interaction is unspecified",
|
||||
which means that I<anything> may happen: it may work, it may not.
|
||||
|
||||
In scalar context, the remaining time in the timer is returned.
|
||||
|
||||
In list context, both the remaining time and the interval are returned.
|
||||
|
||||
There are usually three or four interval timers available: the C<$which>
|
||||
can be C<ITIMER_REAL>, C<ITIMER_VIRTUAL>, C<ITIMER_PROF>, or C<ITIMER_REALPROF>.
|
||||
Note that which ones are available depends: true UNIX platforms usually
|
||||
have the first three, but (for example) Win32 and Cygwin have only
|
||||
C<ITIMER_REAL>, and only Solaris seems to have C<ITIMER_REALPROF> (which is
|
||||
used to profile multithreaded programs).
|
||||
|
||||
C<ITIMER_REAL> results in C<alarm()>-like behavior. Time is counted in
|
||||
I<real time>; that is, wallclock time. C<SIGALRM> is delivered when
|
||||
the timer expires.
|
||||
|
||||
C<ITIMER_VIRTUAL> counts time in (process) I<virtual time>; that is, only
|
||||
when the process is running. In multiprocessor/user/CPU systems this
|
||||
may be more or less than real or wallclock time. (This time is also
|
||||
known as the I<user time>.) C<SIGVTALRM> is delivered when the timer expires.
|
||||
|
||||
C<ITIMER_PROF> counts time when either the process virtual time or when
|
||||
the operating system is running on behalf of the process (such as I/O).
|
||||
(This time is also known as the I<system time>.) (The sum of user
|
||||
time and system time is known as the I<CPU time>.) C<SIGPROF> is
|
||||
delivered when the timer expires. C<SIGPROF> can interrupt system calls.
|
||||
|
||||
The semantics of interval timers for multithreaded programs are
|
||||
system-specific, and some systems may support additional interval
|
||||
timers. See your C<setitimer()> documentation.
|
||||
|
||||
=item getitimer ( $which )
|
||||
|
||||
Return the remaining time in the interval timer specified by C<$which>.
|
||||
|
||||
In scalar context, the remaining time is returned.
|
||||
|
||||
In list context, both the remaining time and the interval are returned.
|
||||
The interval is always what you put in using C<setitimer()>.
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);
|
||||
|
||||
$microseconds = 750_000;
|
||||
usleep $microseconds;
|
||||
|
||||
# signal alarm in 2.5s & every .1s thereafter
|
||||
ualarm 2_500_000, 100_000;
|
||||
|
||||
# get seconds and microseconds since the epoch
|
||||
($s, $usec) = gettimeofday;
|
||||
|
||||
# measure elapsed time
|
||||
# (could also do by subtracting 2 gettimeofday return values)
|
||||
$t0 = [gettimeofday];
|
||||
# do bunch of stuff here
|
||||
$t1 = [gettimeofday];
|
||||
# do more stuff here
|
||||
$t0_t1 = tv_interval $t0, $t1;
|
||||
|
||||
$elapsed = tv_interval ($t0, [gettimeofday]);
|
||||
$elapsed = tv_interval ($t0); # equivalent code
|
||||
|
||||
#
|
||||
# replacements for time, alarm and sleep that know about
|
||||
# floating seconds
|
||||
#
|
||||
use Time::HiRes;
|
||||
$now_fractions = Time::HiRes::time;
|
||||
Time::HiRes::sleep (2.5);
|
||||
Time::HiRes::alarm (10.6666666);
|
||||
|
||||
use Time::HiRes qw ( time alarm sleep );
|
||||
$now_fractions = time;
|
||||
sleep (2.5);
|
||||
alarm (10.6666666);
|
||||
|
||||
# Arm an interval timer to go off first at 10 seconds and
|
||||
# after that every 2.5 seconds, in process virtual time
|
||||
|
||||
use Time::HiRes qw ( setitimer ITIMER_VIRTUAL time );
|
||||
|
||||
$SIG{VTALRM} = sub { print time, "\n" };
|
||||
setitimer(ITIMER_VIRTUAL, 10, 2.5);
|
||||
|
||||
=head1 C API
|
||||
|
||||
In addition to the perl API described above, a C API is available for
|
||||
extension writers. The following C functions are available in the
|
||||
modglobal hash:
|
||||
|
||||
name C prototype
|
||||
--------------- ----------------------
|
||||
Time::NVtime double (*)()
|
||||
Time::U2time void (*)(UV ret[2])
|
||||
|
||||
Both functions return equivalent information (like C<gettimeofday>)
|
||||
but with different representations. The names C<NVtime> and C<U2time>
|
||||
were selected mainly because they are operating system independent.
|
||||
(C<gettimeofday> is Unix-centric, though some platforms like VMS have
|
||||
emulations for it.)
|
||||
|
||||
Here is an example of using C<NVtime> from C:
|
||||
|
||||
double (*myNVtime)();
|
||||
SV **svp = hv_fetch(PL_modglobal, "Time::NVtime", 12, 0);
|
||||
if (!svp) croak("Time::HiRes is required");
|
||||
if (!SvIOK(*svp)) croak("Time::NVtime isn't a function pointer");
|
||||
myNVtime = INT2PTR(double(*)(), SvIV(*svp));
|
||||
printf("The current time is: %f\n", (*myNVtime)());
|
||||
|
||||
=head1 DIAGNOSTICS
|
||||
|
||||
=head2 negative time not invented yet
|
||||
|
||||
You tried to use a negative time argument.
|
||||
|
||||
=head2 internal error: useconds < 0 (unsigned ... signed ...)
|
||||
|
||||
Something went horribly wrong-- the number of microseconds that cannot
|
||||
become negative just became negative. Maybe your compiler is broken?
|
||||
|
||||
=head1 CAVEATS
|
||||
|
||||
Notice that the core C<time()> maybe rounding rather than truncating.
|
||||
What this means is that the core C<time()> may be reporting the time
|
||||
as one second later than C<gettimeofday()> and C<Time::HiRes::time()>.
|
||||
|
||||
Adjusting the system clock (either manually or by services like ntp)
|
||||
may cause problems, especially for long running programs that assume
|
||||
a monotonously increasing time (note that all platforms do not adjust
|
||||
time as gracefully as UNIX ntp does). For example in Win32 (and derived
|
||||
platforms like Cygwin and MinGW) the Time::HiRes::time() may temporarily
|
||||
drift off from the system clock (and the original time()) by up to 0.5
|
||||
seconds. Time::HiRes will notice this eventually and recalibrate.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
D. Wegscheid <wegscd@whirlpool.com>
|
||||
R. Schertler <roderick@argon.org>
|
||||
J. Hietaniemi <jhi@iki.fi>
|
||||
G. Aas <gisle@aas.no>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 1996-2002 Douglas E. Wegscheid. All rights reserved.
|
||||
|
||||
Copyright (c) 2002,2003,2004 Jarkko Hietaniemi. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself.
|
||||
|
||||
=cut
|
||||
@@ -0,0 +1,369 @@
|
||||
package Time::HiRes;
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION $XS_VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD);
|
||||
|
||||
require Exporter;
|
||||
require DynaLoader;
|
||||
|
||||
@ISA = qw(Exporter DynaLoader);
|
||||
|
||||
@EXPORT = qw( );
|
||||
@EXPORT_OK = qw (usleep sleep ualarm alarm gettimeofday time tv_interval
|
||||
getitimer setitimer
|
||||
ITIMER_REAL ITIMER_VIRTUAL ITIMER_PROF ITIMER_REALPROF
|
||||
d_usleep d_ualarm d_gettimeofday d_getitimer d_setitimer
|
||||
d_nanosleep);
|
||||
|
||||
$VERSION = '1.65';
|
||||
$XS_VERSION = $VERSION;
|
||||
$VERSION = eval $VERSION;
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $constname;
|
||||
($constname = $AUTOLOAD) =~ s/.*:://;
|
||||
die "&Time::HiRes::constant not defined" if $constname eq 'constant';
|
||||
my ($error, $val) = constant($constname);
|
||||
if ($error) { die $error; }
|
||||
{
|
||||
no strict 'refs';
|
||||
*$AUTOLOAD = sub { $val };
|
||||
}
|
||||
goto &$AUTOLOAD;
|
||||
}
|
||||
|
||||
bootstrap Time::HiRes;
|
||||
|
||||
# Preloaded methods go here.
|
||||
|
||||
sub tv_interval {
|
||||
# probably could have been done in C
|
||||
my ($a, $b) = @_;
|
||||
$b = [gettimeofday()] unless defined($b);
|
||||
(${$b}[0] - ${$a}[0]) + ((${$b}[1] - ${$a}[1]) / 1_000_000);
|
||||
}
|
||||
|
||||
# Autoload methods go after =cut, and are processed by the autosplit program.
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Time::HiRes - High resolution alarm, sleep, gettimeofday, interval timers
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
|
||||
|
||||
usleep ($microseconds);
|
||||
|
||||
ualarm ($microseconds);
|
||||
ualarm ($microseconds, $interval_microseconds);
|
||||
|
||||
$t0 = [gettimeofday];
|
||||
($seconds, $microseconds) = gettimeofday;
|
||||
|
||||
$elapsed = tv_interval ( $t0, [$seconds, $microseconds]);
|
||||
$elapsed = tv_interval ( $t0, [gettimeofday]);
|
||||
$elapsed = tv_interval ( $t0 );
|
||||
|
||||
use Time::HiRes qw ( time alarm sleep );
|
||||
|
||||
$now_fractions = time;
|
||||
sleep ($floating_seconds);
|
||||
alarm ($floating_seconds);
|
||||
alarm ($floating_seconds, $floating_interval);
|
||||
|
||||
use Time::HiRes qw( setitimer getitimer
|
||||
ITIMER_REAL ITIMER_VIRTUAL ITIMER_PROF ITIMER_REALPROF );
|
||||
|
||||
setitimer ($which, $floating_seconds, $floating_interval );
|
||||
getitimer ($which);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The C<Time::HiRes> module implements a Perl interface to the
|
||||
C<usleep>, C<ualarm>, C<gettimeofday>, and C<setitimer>/C<getitimer>
|
||||
system calls, in other words, high resolution time and timers. See the
|
||||
L</EXAMPLES> section below and the test scripts for usage; see your
|
||||
system documentation for the description of the underlying
|
||||
C<nanosleep> or C<usleep>, C<ualarm>, C<gettimeofday>, and
|
||||
C<setitimer>/C<getitimer> calls.
|
||||
|
||||
If your system lacks C<gettimeofday()> or an emulation of it you don't
|
||||
get C<gettimeofday()> or the one-argument form of C<tv_interval()>.
|
||||
If your system lacks all of C<nanosleep()>, C<usleep()>, and
|
||||
C<select()>, you don't get C<Time::HiRes::usleep()> or
|
||||
C<Time::HiRes::sleep()>. If your system lacks both C<ualarm()> and
|
||||
C<setitimer()> you don't get C<Time::HiRes::ualarm()> or
|
||||
C<Time::HiRes::alarm()>.
|
||||
|
||||
If you try to import an unimplemented function in the C<use> statement
|
||||
it will fail at compile time.
|
||||
|
||||
If your subsecond sleeping is implemented with C<nanosleep()> instead
|
||||
of C<usleep()>, you can mix subsecond sleeping with signals since
|
||||
C<nanosleep()> does not use signals. This, however, is not portable,
|
||||
and you should first check for the truth value of
|
||||
C<&Time::HiRes::d_nanosleep> to see whether you have nanosleep, and
|
||||
then carefully read your C<nanosleep()> C API documentation for any
|
||||
peculiarities. (There is no separate interface to call
|
||||
C<nanosleep()>; just use C<Time::HiRes::sleep()> or
|
||||
C<Time::HiRes::usleep()> with small enough values.)
|
||||
|
||||
Unless using C<nanosleep> for mixing sleeping with signals, give
|
||||
some thought to whether Perl is the tool you should be using for
|
||||
work requiring nanosecond accuracies.
|
||||
|
||||
The following functions can be imported from this module.
|
||||
No functions are exported by default.
|
||||
|
||||
=over 4
|
||||
|
||||
=item gettimeofday ()
|
||||
|
||||
In array context returns a two-element array with the seconds and
|
||||
microseconds since the epoch. In scalar context returns floating
|
||||
seconds like C<Time::HiRes::time()> (see below).
|
||||
|
||||
=item usleep ( $useconds )
|
||||
|
||||
Sleeps for the number of microseconds specified. Returns the number
|
||||
of microseconds actually slept. Can sleep for more than one second,
|
||||
unlike the C<usleep> system call. See also C<Time::HiRes::sleep()> below.
|
||||
|
||||
=item ualarm ( $useconds [, $interval_useconds ] )
|
||||
|
||||
Issues a C<ualarm> call; the C<$interval_useconds> is optional and
|
||||
will be zero if unspecified, resulting in C<alarm>-like behaviour.
|
||||
|
||||
Note that the interaction between alarms and sleeps are unspecified.
|
||||
|
||||
=item tv_interval
|
||||
|
||||
tv_interval ( $ref_to_gettimeofday [, $ref_to_later_gettimeofday] )
|
||||
|
||||
Returns the floating seconds between the two times, which should have
|
||||
been returned by C<gettimeofday()>. If the second argument is omitted,
|
||||
then the current time is used.
|
||||
|
||||
=item time ()
|
||||
|
||||
Returns a floating seconds since the epoch. This function can be
|
||||
imported, resulting in a nice drop-in replacement for the C<time>
|
||||
provided with core Perl; see the L</EXAMPLES> below.
|
||||
|
||||
B<NOTE 1>: This higher resolution timer can return values either less
|
||||
or more than the core C<time()>, depending on whether your platform
|
||||
rounds the higher resolution timer values up, down, or to the nearest second
|
||||
to get the core C<time()>, but naturally the difference should be never
|
||||
more than half a second.
|
||||
|
||||
B<NOTE 2>: Since Sunday, September 9th, 2001 at 01:46:40 AM GMT, when
|
||||
the C<time()> seconds since epoch rolled over to 1_000_000_000, the
|
||||
default floating point format of Perl and the seconds since epoch have
|
||||
conspired to produce an apparent bug: if you print the value of
|
||||
C<Time::HiRes::time()> you seem to be getting only five decimals, not
|
||||
six as promised (microseconds). Not to worry, the microseconds are
|
||||
there (assuming your platform supports such granularity in the first
|
||||
place). What is going on is that the default floating point format of
|
||||
Perl only outputs 15 digits. In this case that means ten digits
|
||||
before the decimal separator and five after. To see the microseconds
|
||||
you can use either C<printf>/C<sprintf> with C<"%.6f">, or the
|
||||
C<gettimeofday()> function in list context, which will give you the
|
||||
seconds and microseconds as two separate values.
|
||||
|
||||
=item sleep ( $floating_seconds )
|
||||
|
||||
Sleeps for the specified amount of seconds. Returns the number of
|
||||
seconds actually slept (a floating point value). This function can
|
||||
be imported, resulting in a nice drop-in replacement for the C<sleep>
|
||||
provided with perl, see the L</EXAMPLES> below.
|
||||
|
||||
Note that the interaction between alarms and sleeps are unspecified.
|
||||
|
||||
=item alarm ( $floating_seconds [, $interval_floating_seconds ] )
|
||||
|
||||
The C<SIGALRM> signal is sent after the specified number of seconds.
|
||||
Implemented using C<ualarm()>. The C<$interval_floating_seconds> argument
|
||||
is optional and will be zero if unspecified, resulting in C<alarm()>-like
|
||||
behaviour. This function can be imported, resulting in a nice drop-in
|
||||
replacement for the C<alarm> provided with perl, see the L</EXAMPLES> below.
|
||||
|
||||
B<NOTE 1>: With some combinations of operating systems and Perl
|
||||
releases C<SIGALRM> restarts C<select()>, instead of interrupting it.
|
||||
This means that an C<alarm()> followed by a C<select()> may together
|
||||
take the sum of the times specified for the the C<alarm()> and the
|
||||
C<select()>, not just the time of the C<alarm()>.
|
||||
|
||||
Note that the interaction between alarms and sleeps are unspecified.
|
||||
|
||||
=item setitimer ( $which, $floating_seconds [, $interval_floating_seconds ] )
|
||||
|
||||
Start up an interval timer: after a certain time, a signal arrives,
|
||||
and more signals may keep arriving at certain intervals. To disable
|
||||
an "itimer", use C<$floating_seconds> of zero. If the
|
||||
C<$interval_floating_seconds> is set to zero (or unspecified), the
|
||||
timer is disabled B<after> the next delivered signal.
|
||||
|
||||
Use of interval timers may interfere with C<alarm()>, C<sleep()>,
|
||||
and C<usleep()>. In standard-speak the "interaction is unspecified",
|
||||
which means that I<anything> may happen: it may work, it may not.
|
||||
|
||||
In scalar context, the remaining time in the timer is returned.
|
||||
|
||||
In list context, both the remaining time and the interval are returned.
|
||||
|
||||
There are usually three or four interval timers available: the
|
||||
C<$which> can be C<ITIMER_REAL>, C<ITIMER_VIRTUAL>, C<ITIMER_PROF>, or
|
||||
C<ITIMER_REALPROF>. Note that which ones are available depends: true
|
||||
UNIX platforms usually have the first three, but (for example) Win32
|
||||
and Cygwin have only C<ITIMER_REAL>, and only Solaris seems to have
|
||||
C<ITIMER_REALPROF> (which is used to profile multithreaded programs).
|
||||
|
||||
C<ITIMER_REAL> results in C<alarm()>-like behavior. Time is counted in
|
||||
I<real time>; that is, wallclock time. C<SIGALRM> is delivered when
|
||||
the timer expires.
|
||||
|
||||
C<ITIMER_VIRTUAL> counts time in (process) I<virtual time>; that is,
|
||||
only when the process is running. In multiprocessor/user/CPU systems
|
||||
this may be more or less than real or wallclock time. (This time is
|
||||
also known as the I<user time>.) C<SIGVTALRM> is delivered when the
|
||||
timer expires.
|
||||
|
||||
C<ITIMER_PROF> counts time when either the process virtual time or when
|
||||
the operating system is running on behalf of the process (such as I/O).
|
||||
(This time is also known as the I<system time>.) (The sum of user
|
||||
time and system time is known as the I<CPU time>.) C<SIGPROF> is
|
||||
delivered when the timer expires. C<SIGPROF> can interrupt system calls.
|
||||
|
||||
The semantics of interval timers for multithreaded programs are
|
||||
system-specific, and some systems may support additional interval
|
||||
timers. See your C<setitimer()> documentation.
|
||||
|
||||
=item getitimer ( $which )
|
||||
|
||||
Return the remaining time in the interval timer specified by C<$which>.
|
||||
|
||||
In scalar context, the remaining time is returned.
|
||||
|
||||
In list context, both the remaining time and the interval are returned.
|
||||
The interval is always what you put in using C<setitimer()>.
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);
|
||||
|
||||
$microseconds = 750_000;
|
||||
usleep $microseconds;
|
||||
|
||||
# signal alarm in 2.5s & every .1s thereafter
|
||||
ualarm 2_500_000, 100_000;
|
||||
|
||||
# get seconds and microseconds since the epoch
|
||||
($s, $usec) = gettimeofday;
|
||||
|
||||
# measure elapsed time
|
||||
# (could also do by subtracting 2 gettimeofday return values)
|
||||
$t0 = [gettimeofday];
|
||||
# do bunch of stuff here
|
||||
$t1 = [gettimeofday];
|
||||
# do more stuff here
|
||||
$t0_t1 = tv_interval $t0, $t1;
|
||||
|
||||
$elapsed = tv_interval ($t0, [gettimeofday]);
|
||||
$elapsed = tv_interval ($t0); # equivalent code
|
||||
|
||||
#
|
||||
# replacements for time, alarm and sleep that know about
|
||||
# floating seconds
|
||||
#
|
||||
use Time::HiRes;
|
||||
$now_fractions = Time::HiRes::time;
|
||||
Time::HiRes::sleep (2.5);
|
||||
Time::HiRes::alarm (10.6666666);
|
||||
|
||||
use Time::HiRes qw ( time alarm sleep );
|
||||
$now_fractions = time;
|
||||
sleep (2.5);
|
||||
alarm (10.6666666);
|
||||
|
||||
# Arm an interval timer to go off first at 10 seconds and
|
||||
# after that every 2.5 seconds, in process virtual time
|
||||
|
||||
use Time::HiRes qw ( setitimer ITIMER_VIRTUAL time );
|
||||
|
||||
$SIG{VTALRM} = sub { print time, "\n" };
|
||||
setitimer(ITIMER_VIRTUAL, 10, 2.5);
|
||||
|
||||
=head1 C API
|
||||
|
||||
In addition to the perl API described above, a C API is available for
|
||||
extension writers. The following C functions are available in the
|
||||
modglobal hash:
|
||||
|
||||
name C prototype
|
||||
--------------- ----------------------
|
||||
Time::NVtime double (*)()
|
||||
Time::U2time void (*)(UV ret[2])
|
||||
|
||||
Both functions return equivalent information (like C<gettimeofday>)
|
||||
but with different representations. The names C<NVtime> and C<U2time>
|
||||
were selected mainly because they are operating system independent.
|
||||
(C<gettimeofday> is Unix-centric, though some platforms like VMS have
|
||||
emulations for it.)
|
||||
|
||||
Here is an example of using C<NVtime> from C:
|
||||
|
||||
double (*myNVtime)();
|
||||
SV **svp = hv_fetch(PL_modglobal, "Time::NVtime", 12, 0);
|
||||
if (!svp) croak("Time::HiRes is required");
|
||||
if (!SvIOK(*svp)) croak("Time::NVtime isn't a function pointer");
|
||||
myNVtime = INT2PTR(double(*)(), SvIV(*svp));
|
||||
printf("The current time is: %f\n", (*myNVtime)());
|
||||
|
||||
=head1 DIAGNOSTICS
|
||||
|
||||
=head2 negative time not invented yet
|
||||
|
||||
You tried to use a negative time argument.
|
||||
|
||||
=head2 internal error: useconds < 0 (unsigned ... signed ...)
|
||||
|
||||
Something went horribly wrong-- the number of microseconds that cannot
|
||||
become negative just became negative. Maybe your compiler is broken?
|
||||
|
||||
=head1 CAVEATS
|
||||
|
||||
Notice that the core C<time()> maybe rounding rather than truncating.
|
||||
What this means is that the core C<time()> may be reporting the time
|
||||
as one second later than C<gettimeofday()> and C<Time::HiRes::time()>.
|
||||
|
||||
Adjusting the system clock (either manually or by services like ntp)
|
||||
may cause problems, especially for long running programs that assume
|
||||
a monotonously increasing time (note that all platforms do not adjust
|
||||
time as gracefully as UNIX ntp does). For example in Win32 (and derived
|
||||
platforms like Cygwin and MinGW) the Time::HiRes::time() may temporarily
|
||||
drift off from the system clock (and the original time()) by up to 0.5
|
||||
seconds. Time::HiRes will notice this eventually and recalibrate.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
D. Wegscheid <wegscd@whirlpool.com>
|
||||
R. Schertler <roderick@argon.org>
|
||||
J. Hietaniemi <jhi@iki.fi>
|
||||
G. Aas <gisle@aas.no>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 1996-2002 Douglas E. Wegscheid. All rights reserved.
|
||||
|
||||
Copyright (c) 2002,2003,2004 Jarkko Hietaniemi. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself.
|
||||
|
||||
=cut
|
||||
@@ -0,0 +1,351 @@
|
||||
package Time::HiRes;
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION $XS_VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD);
|
||||
|
||||
require Exporter;
|
||||
require DynaLoader;
|
||||
|
||||
@ISA = qw(Exporter DynaLoader);
|
||||
|
||||
@EXPORT = qw( );
|
||||
@EXPORT_OK = qw (usleep sleep ualarm alarm gettimeofday time tv_interval
|
||||
getitimer setitimer
|
||||
ITIMER_REAL ITIMER_VIRTUAL ITIMER_PROF ITIMER_REALPROF
|
||||
d_usleep d_ualarm d_gettimeofday d_getitimer d_setitimer
|
||||
d_nanosleep);
|
||||
|
||||
$VERSION = '1.55';
|
||||
$XS_VERSION = $VERSION;
|
||||
$VERSION = eval $VERSION;
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $constname;
|
||||
($constname = $AUTOLOAD) =~ s/.*:://;
|
||||
die "&Time::HiRes::constant not defined" if $constname eq 'constant';
|
||||
my ($error, $val) = constant($constname);
|
||||
if ($error) { die $error; }
|
||||
{
|
||||
no strict 'refs';
|
||||
*$AUTOLOAD = sub { $val };
|
||||
}
|
||||
goto &$AUTOLOAD;
|
||||
}
|
||||
|
||||
bootstrap Time::HiRes;
|
||||
|
||||
# Preloaded methods go here.
|
||||
|
||||
sub tv_interval {
|
||||
# probably could have been done in C
|
||||
my ($a, $b) = @_;
|
||||
$b = [gettimeofday()] unless defined($b);
|
||||
(${$b}[0] - ${$a}[0]) + ((${$b}[1] - ${$a}[1]) / 1_000_000);
|
||||
}
|
||||
|
||||
# Autoload methods go after =cut, and are processed by the autosplit program.
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Time::HiRes - High resolution alarm, sleep, gettimeofday, interval timers
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
|
||||
|
||||
usleep ($microseconds);
|
||||
|
||||
ualarm ($microseconds);
|
||||
ualarm ($microseconds, $interval_microseconds);
|
||||
|
||||
$t0 = [gettimeofday];
|
||||
($seconds, $microseconds) = gettimeofday;
|
||||
|
||||
$elapsed = tv_interval ( $t0, [$seconds, $microseconds]);
|
||||
$elapsed = tv_interval ( $t0, [gettimeofday]);
|
||||
$elapsed = tv_interval ( $t0 );
|
||||
|
||||
use Time::HiRes qw ( time alarm sleep );
|
||||
|
||||
$now_fractions = time;
|
||||
sleep ($floating_seconds);
|
||||
alarm ($floating_seconds);
|
||||
alarm ($floating_seconds, $floating_interval);
|
||||
|
||||
use Time::HiRes qw( setitimer getitimer
|
||||
ITIMER_REAL ITIMER_VIRTUAL ITIMER_PROF ITIMER_REALPROF );
|
||||
|
||||
setitimer ($which, $floating_seconds, $floating_interval );
|
||||
getitimer ($which);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The C<Time::HiRes> module implements a Perl interface to the C<usleep>,
|
||||
C<ualarm>, C<gettimeofday>, and C<setitimer>/C<getitimer> system calls, in other
|
||||
words, high resolution time and timers. See the L</EXAMPLES> section below
|
||||
and the test scripts for usage; see your system documentation for the
|
||||
description of the underlying C<nanosleep> or C<usleep>, C<ualarm>,
|
||||
C<gettimeofday>, and C<setitimer>/C<getitimer> calls.
|
||||
|
||||
If your system lacks C<gettimeofday()> or an emulation of it you don't
|
||||
get C<gettimeofday()> or the one-argument form of C<tv_interval()>. If your system lacks all of
|
||||
C<nanosleep()>, C<usleep()>, and C<select()>, you don't get
|
||||
C<Time::HiRes::usleep()> or C<Time::HiRes::sleep()>. If your system lacks both
|
||||
C<ualarm()> and C<setitimer()> you don't get
|
||||
C<Time::HiRes::ualarm()> or C<Time::HiRes::alarm()>.
|
||||
|
||||
If you try to import an unimplemented function in the C<use> statement
|
||||
it will fail at compile time.
|
||||
|
||||
If your subsecond sleeping is implemented with C<nanosleep()> instead of
|
||||
C<usleep()>, you can mix subsecond sleeping with signals since
|
||||
C<nanosleep()> does not use signals. This, however is unportable, and you
|
||||
should first check for the truth value of C<&Time::HiRes::d_nanosleep> to
|
||||
see whether you have nanosleep, and then carefully read your
|
||||
C<nanosleep()> C API documentation for any peculiarities. (There is no
|
||||
separate interface to call C<nanosleep()>; just use C<Time::HiRes::sleep()>
|
||||
or C<Time::HiRes::usleep()> with small enough values.)
|
||||
|
||||
Unless using C<nanosleep> for mixing sleeping with signals, give
|
||||
some thought to whether Perl is the tool you should be using for work
|
||||
requiring nanosecond accuracies.
|
||||
|
||||
The following functions can be imported from this module.
|
||||
No functions are exported by default.
|
||||
|
||||
=over 4
|
||||
|
||||
=item gettimeofday ()
|
||||
|
||||
In array context returns a two-element array with the seconds and
|
||||
microseconds since the epoch. In scalar context returns floating
|
||||
seconds like C<Time::HiRes::time()> (see below).
|
||||
|
||||
=item usleep ( $useconds )
|
||||
|
||||
Sleeps for the number of microseconds specified. Returns the number
|
||||
of microseconds actually slept. Can sleep for more than one second,
|
||||
unlike the C<usleep> system call. See also C<Time::HiRes::sleep()> below.
|
||||
|
||||
=item ualarm ( $useconds [, $interval_useconds ] )
|
||||
|
||||
Issues a C<ualarm> call; the C<$interval_useconds> is optional and
|
||||
will be zero if unspecified, resulting in C<alarm>-like behaviour.
|
||||
|
||||
=item tv_interval
|
||||
|
||||
tv_interval ( $ref_to_gettimeofday [, $ref_to_later_gettimeofday] )
|
||||
|
||||
Returns the floating seconds between the two times, which should have
|
||||
been returned by C<gettimeofday()>. If the second argument is omitted,
|
||||
then the current time is used.
|
||||
|
||||
=item time ()
|
||||
|
||||
Returns a floating seconds since the epoch. This function can be
|
||||
imported, resulting in a nice drop-in replacement for the C<time>
|
||||
provided with core Perl; see the L</EXAMPLES> below.
|
||||
|
||||
B<NOTE 1>: This higher resolution timer can return values either less
|
||||
or more than the core C<time()>, depending on whether your platform
|
||||
rounds the higher resolution timer values up, down, or to the nearest second
|
||||
to get the core C<time()>, but naturally the difference should be never
|
||||
more than half a second.
|
||||
|
||||
B<NOTE 2>: Since Sunday, September 9th, 2001 at 01:46:40 AM GMT, when
|
||||
the C<time()> seconds since epoch rolled over to 1_000_000_000, the
|
||||
default floating point format of Perl and the seconds since epoch have
|
||||
conspired to produce an apparent bug: if you print the value of
|
||||
C<Time::HiRes::time()> you seem to be getting only five decimals, not six
|
||||
as promised (microseconds). Not to worry, the microseconds are there
|
||||
(assuming your platform supports such granularity in first place).
|
||||
What is going on is that the default floating point format of Perl
|
||||
only outputs 15 digits. In this case that means ten digits before the
|
||||
decimal separator and five after. To see the microseconds you can use
|
||||
either C<printf>/C<sprintf> with C<"%.6f">, or the C<gettimeofday()> function in
|
||||
list context, which will give you the seconds and microseconds as two
|
||||
separate values.
|
||||
|
||||
=item sleep ( $floating_seconds )
|
||||
|
||||
Sleeps for the specified amount of seconds. Returns the number of
|
||||
seconds actually slept (a floating point value). This function can be
|
||||
imported, resulting in a nice drop-in replacement for the C<sleep>
|
||||
provided with perl, see the L</EXAMPLES> below.
|
||||
|
||||
=item alarm ( $floating_seconds [, $interval_floating_seconds ] )
|
||||
|
||||
The C<SIGALRM> signal is sent after the specified number of seconds.
|
||||
Implemented using C<ualarm()>. The C<$interval_floating_seconds> argument
|
||||
is optional and will be zero if unspecified, resulting in C<alarm()>-like
|
||||
behaviour. This function can be imported, resulting in a nice drop-in
|
||||
replacement for the C<alarm> provided with perl, see the L</EXAMPLES> below.
|
||||
|
||||
B<NOTE 1>: With some operating system and Perl release combinations
|
||||
C<SIGALRM> restarts C<select()>, instead of interuping it.
|
||||
This means that an C<alarm()> followed by a C<select()>
|
||||
may together take the sum of the times specified for the the
|
||||
C<alarm()> and the C<select()>, not just the time of the C<alarm()>.
|
||||
|
||||
=item setitimer ( $which, $floating_seconds [, $interval_floating_seconds ] )
|
||||
|
||||
Start up an interval timer: after a certain time, a signal arrives,
|
||||
and more signals may keep arriving at certain intervals. To disable a
|
||||
timer, use C<$floating_seconds> of zero. If the C<$interval_floating_seconds>
|
||||
is set to zero (or unspecified), the timer is disabled B<after> the
|
||||
next delivered signal.
|
||||
|
||||
Use of interval timers may interfere with C<alarm()>, C<sleep()>,
|
||||
and C<usleep()>. In standard-speak the "interaction is unspecified",
|
||||
which means that I<anything> may happen: it may work, it may not.
|
||||
|
||||
In scalar context, the remaining time in the timer is returned.
|
||||
|
||||
In list context, both the remaining time and the interval are returned.
|
||||
|
||||
There are usually three or four interval timers available: the C<$which>
|
||||
can be C<ITIMER_REAL>, C<ITIMER_VIRTUAL>, C<ITIMER_PROF>, or C<ITIMER_REALPROF>.
|
||||
Note that which ones are available depends: true UNIX platforms usually
|
||||
have the first three, but (for example) Win32 and Cygwin have only
|
||||
C<ITIMER_REAL>, and only Solaris seems to have C<ITIMER_REALPROF> (which is
|
||||
used to profile multithreaded programs).
|
||||
|
||||
C<ITIMER_REAL> results in C<alarm()>-like behavior. Time is counted in
|
||||
I<real time>; that is, wallclock time. C<SIGALRM> is delivered when
|
||||
the timer expires.
|
||||
|
||||
C<ITIMER_VIRTUAL> counts time in (process) I<virtual time>; that is, only
|
||||
when the process is running. In multiprocessor/user/CPU systems this
|
||||
may be more or less than real or wallclock time. (This time is also
|
||||
known as the I<user time>.) C<SIGVTALRM> is delivered when the timer expires.
|
||||
|
||||
C<ITIMER_PROF> counts time when either the process virtual time or when
|
||||
the operating system is running on behalf of the process (such as I/O).
|
||||
(This time is also known as the I<system time>.) (The sum of user
|
||||
time and system time is known as the I<CPU time>.) C<SIGPROF> is
|
||||
delivered when the timer expires. C<SIGPROF> can interrupt system calls.
|
||||
|
||||
The semantics of interval timers for multithreaded programs are
|
||||
system-specific, and some systems may support additional interval
|
||||
timers. See your C<setitimer()> documentation.
|
||||
|
||||
=item getitimer ( $which )
|
||||
|
||||
Return the remaining time in the interval timer specified by C<$which>.
|
||||
|
||||
In scalar context, the remaining time is returned.
|
||||
|
||||
In list context, both the remaining time and the interval are returned.
|
||||
The interval is always what you put in using C<setitimer()>.
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);
|
||||
|
||||
$microseconds = 750_000;
|
||||
usleep $microseconds;
|
||||
|
||||
# signal alarm in 2.5s & every .1s thereafter
|
||||
ualarm 2_500_000, 100_000;
|
||||
|
||||
# get seconds and microseconds since the epoch
|
||||
($s, $usec) = gettimeofday;
|
||||
|
||||
# measure elapsed time
|
||||
# (could also do by subtracting 2 gettimeofday return values)
|
||||
$t0 = [gettimeofday];
|
||||
# do bunch of stuff here
|
||||
$t1 = [gettimeofday];
|
||||
# do more stuff here
|
||||
$t0_t1 = tv_interval $t0, $t1;
|
||||
|
||||
$elapsed = tv_interval ($t0, [gettimeofday]);
|
||||
$elapsed = tv_interval ($t0); # equivalent code
|
||||
|
||||
#
|
||||
# replacements for time, alarm and sleep that know about
|
||||
# floating seconds
|
||||
#
|
||||
use Time::HiRes;
|
||||
$now_fractions = Time::HiRes::time;
|
||||
Time::HiRes::sleep (2.5);
|
||||
Time::HiRes::alarm (10.6666666);
|
||||
|
||||
use Time::HiRes qw ( time alarm sleep );
|
||||
$now_fractions = time;
|
||||
sleep (2.5);
|
||||
alarm (10.6666666);
|
||||
|
||||
# Arm an interval timer to go off first at 10 seconds and
|
||||
# after that every 2.5 seconds, in process virtual time
|
||||
|
||||
use Time::HiRes qw ( setitimer ITIMER_VIRTUAL time );
|
||||
|
||||
$SIG{VTALRM} = sub { print time, "\n" };
|
||||
setitimer(ITIMER_VIRTUAL, 10, 2.5);
|
||||
|
||||
=head1 C API
|
||||
|
||||
In addition to the perl API described above, a C API is available for
|
||||
extension writers. The following C functions are available in the
|
||||
modglobal hash:
|
||||
|
||||
name C prototype
|
||||
--------------- ----------------------
|
||||
Time::NVtime double (*)()
|
||||
Time::U2time void (*)(UV ret[2])
|
||||
|
||||
Both functions return equivalent information (like C<gettimeofday>)
|
||||
but with different representations. The names C<NVtime> and C<U2time>
|
||||
were selected mainly because they are operating system independent.
|
||||
(C<gettimeofday> is Unix-centric, though some platforms like VMS have
|
||||
emulations for it.)
|
||||
|
||||
Here is an example of using C<NVtime> from C:
|
||||
|
||||
double (*myNVtime)();
|
||||
SV **svp = hv_fetch(PL_modglobal, "Time::NVtime", 12, 0);
|
||||
if (!svp) croak("Time::HiRes is required");
|
||||
if (!SvIOK(*svp)) croak("Time::NVtime isn't a function pointer");
|
||||
myNVtime = INT2PTR(double(*)(), SvIV(*svp));
|
||||
printf("The current time is: %f\n", (*myNVtime)());
|
||||
|
||||
=head1 DIAGNOSTICS
|
||||
|
||||
=head2 negative time not invented yet
|
||||
|
||||
You tried to use a negative time argument.
|
||||
|
||||
=head2 internal error: useconds < 0 (unsigned ... signed ...)
|
||||
|
||||
Something went horribly wrong-- the number of microseconds that cannot
|
||||
become negative just became negative. Maybe your compiler is broken?
|
||||
|
||||
=head1 CAVEATS
|
||||
|
||||
Notice that the core C<time()> maybe rounding rather than truncating.
|
||||
What this means is that the core C<time()> may be reporting the time as one second
|
||||
later than C<gettimeofday()> and C<Time::HiRes::time()>.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
D. Wegscheid <wegscd@whirlpool.com>
|
||||
R. Schertler <roderick@argon.org>
|
||||
J. Hietaniemi <jhi@iki.fi>
|
||||
G. Aas <gisle@aas.no>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 1996-2002 Douglas E. Wegscheid. All rights reserved.
|
||||
|
||||
Copyright (c) 2002,2003,2004 Jarkko Hietaniemi. All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself.
|
||||
|
||||
=cut
|
||||
@@ -0,0 +1,289 @@
|
||||
package Time::Local;
|
||||
|
||||
require Exporter;
|
||||
use Carp;
|
||||
use Config;
|
||||
use strict;
|
||||
use integer;
|
||||
|
||||
use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK );
|
||||
$VERSION = '1.07';
|
||||
@ISA = qw( Exporter );
|
||||
@EXPORT = qw( timegm timelocal );
|
||||
@EXPORT_OK = qw( timegm_nocheck timelocal_nocheck );
|
||||
|
||||
my @MonthDays = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
||||
|
||||
# Determine breakpoint for rolling century
|
||||
my $ThisYear = (localtime())[5];
|
||||
my $Breakpoint = ($ThisYear + 50) % 100;
|
||||
my $NextCentury = $ThisYear - $ThisYear % 100;
|
||||
$NextCentury += 100 if $Breakpoint < 50;
|
||||
my $Century = $NextCentury - 100;
|
||||
my $SecOff = 0;
|
||||
|
||||
my (%Options, %Cheat);
|
||||
|
||||
my $MaxInt = ((1<<(8 * $Config{intsize} - 2))-1)*2 + 1;
|
||||
my $MaxDay = int(($MaxInt-43200)/86400)-1;
|
||||
|
||||
# Determine the EPOC day for this machine
|
||||
my $Epoc = 0;
|
||||
if ($^O eq 'vos') {
|
||||
# work around posix-977 -- VOS doesn't handle dates in
|
||||
# the range 1970-1980.
|
||||
$Epoc = _daygm((0, 0, 0, 1, 0, 70, 4, 0));
|
||||
}
|
||||
elsif ($^O eq 'MacOS') {
|
||||
no integer;
|
||||
|
||||
$MaxDay *=2 if $^O eq 'MacOS'; # time_t unsigned ... quick hack?
|
||||
# MacOS time() is seconds since 1 Jan 1904, localtime
|
||||
# so we need to calculate an offset to apply later
|
||||
$Epoc = 693901;
|
||||
$SecOff = timelocal(localtime(0)) - timelocal(gmtime(0));
|
||||
$Epoc += _daygm(gmtime(0));
|
||||
}
|
||||
else {
|
||||
$Epoc = _daygm(gmtime(0));
|
||||
}
|
||||
|
||||
%Cheat=(); # clear the cache as epoc has changed
|
||||
|
||||
sub _daygm {
|
||||
$_[3] + ($Cheat{pack("ss",@_[4,5])} ||= do {
|
||||
my $month = ($_[4] + 10) % 12;
|
||||
my $year = $_[5] + 1900 - $month/10;
|
||||
365*$year + $year/4 - $year/100 + $year/400 + ($month*306 + 5)/10 - $Epoc
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
sub _timegm {
|
||||
my $sec = $SecOff + $_[0] + 60 * $_[1] + 3600 * $_[2];
|
||||
|
||||
no integer;
|
||||
|
||||
$sec + 86400 * &_daygm;
|
||||
}
|
||||
|
||||
|
||||
sub timegm {
|
||||
my ($sec,$min,$hour,$mday,$month,$year) = @_;
|
||||
|
||||
if ($year >= 1000) {
|
||||
$year -= 1900;
|
||||
}
|
||||
elsif ($year < 100 and $year >= 0) {
|
||||
$year += ($year > $Breakpoint) ? $Century : $NextCentury;
|
||||
}
|
||||
|
||||
unless ($Options{no_range_check}) {
|
||||
if (abs($year) >= 0x7fff) {
|
||||
$year += 1900;
|
||||
croak "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)";
|
||||
}
|
||||
|
||||
croak "Month '$month' out of range 0..11" if $month > 11 or $month < 0;
|
||||
|
||||
my $md = $MonthDays[$month];
|
||||
++$md unless $month != 1 or $year % 4 or !($year % 400);
|
||||
|
||||
croak "Day '$mday' out of range 1..$md" if $mday > $md or $mday < 1;
|
||||
croak "Hour '$hour' out of range 0..23" if $hour > 23 or $hour < 0;
|
||||
croak "Minute '$min' out of range 0..59" if $min > 59 or $min < 0;
|
||||
croak "Second '$sec' out of range 0..59" if $sec > 59 or $sec < 0;
|
||||
}
|
||||
|
||||
my $days = _daygm(undef, undef, undef, $mday, $month, $year);
|
||||
|
||||
unless ($Options{no_range_check} or abs($days) < $MaxDay) {
|
||||
$year += 1900;
|
||||
croak "Cannot handle date ($sec, $min, $hour, $mday, $month, $year)";
|
||||
}
|
||||
|
||||
$sec += $SecOff + 60*$min + 3600*$hour;
|
||||
|
||||
no integer;
|
||||
|
||||
$sec + 86400*$days;
|
||||
}
|
||||
|
||||
|
||||
sub timegm_nocheck {
|
||||
local $Options{no_range_check} = 1;
|
||||
&timegm;
|
||||
}
|
||||
|
||||
|
||||
sub timelocal {
|
||||
no integer;
|
||||
my $ref_t = &timegm;
|
||||
my $loc_t = _timegm(localtime($ref_t));
|
||||
|
||||
# Is there a timezone offset from GMT or are we done
|
||||
my $zone_off = $ref_t - $loc_t
|
||||
or return $loc_t;
|
||||
|
||||
# Adjust for timezone
|
||||
$loc_t = $ref_t + $zone_off;
|
||||
|
||||
# Are we close to a DST change or are we done
|
||||
my $dst_off = $ref_t - _timegm(localtime($loc_t))
|
||||
or return $loc_t;
|
||||
|
||||
# Adjust for DST change
|
||||
$loc_t += $dst_off;
|
||||
|
||||
# for a negative offset from GMT, and if the original date
|
||||
# was a non-extent gap in a forward DST jump, we should
|
||||
# now have the wrong answer - undo the DST adjust;
|
||||
|
||||
return $loc_t if $zone_off <= 0;
|
||||
|
||||
my ($s,$m,$h) = localtime($loc_t);
|
||||
$loc_t -= $dst_off if $s != $_[0] || $m != $_[1] || $h != $_[2];
|
||||
|
||||
$loc_t;
|
||||
}
|
||||
|
||||
|
||||
sub timelocal_nocheck {
|
||||
local $Options{no_range_check} = 1;
|
||||
&timelocal;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Time::Local - efficiently compute time from local and GMT time
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
$time = timelocal($sec,$min,$hour,$mday,$mon,$year);
|
||||
$time = timegm($sec,$min,$hour,$mday,$mon,$year);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
These routines are the inverse of built-in perl functions localtime()
|
||||
and gmtime(). They accept a date as a six-element array, and return
|
||||
the corresponding time(2) value in seconds since the system epoch
|
||||
(Midnight, January 1, 1970 UTC on Unix, for example). This value can
|
||||
be positive or negative, though POSIX only requires support for
|
||||
positive values, so dates before the system's epoch may not work on
|
||||
all operating systems.
|
||||
|
||||
It is worth drawing particular attention to the expected ranges for
|
||||
the values provided. The value for the day of the month is the actual day
|
||||
(ie 1..31), while the month is the number of months since January (0..11).
|
||||
This is consistent with the values returned from localtime() and gmtime().
|
||||
|
||||
The timelocal() and timegm() functions perform range checking on the
|
||||
input $sec, $min, $hour, $mday, and $mon values by default. If you'd
|
||||
rather they didn't, you can explicitly import the timelocal_nocheck()
|
||||
and timegm_nocheck() functions.
|
||||
|
||||
use Time::Local 'timelocal_nocheck';
|
||||
|
||||
{
|
||||
# The 365th day of 1999
|
||||
print scalar localtime timelocal_nocheck 0,0,0,365,0,99;
|
||||
|
||||
# The twenty thousandth day since 1970
|
||||
print scalar localtime timelocal_nocheck 0,0,0,20000,0,70;
|
||||
|
||||
# And even the 10,000,000th second since 1999!
|
||||
print scalar localtime timelocal_nocheck 10000000,0,0,1,0,99;
|
||||
}
|
||||
|
||||
Your mileage may vary when trying these with minutes and hours,
|
||||
and it doesn't work at all for months.
|
||||
|
||||
Strictly speaking, the year should also be specified in a form consistent
|
||||
with localtime(), i.e. the offset from 1900.
|
||||
In order to make the interpretation of the year easier for humans,
|
||||
however, who are more accustomed to seeing years as two-digit or four-digit
|
||||
values, the following conventions are followed:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Years greater than 999 are interpreted as being the actual year,
|
||||
rather than the offset from 1900. Thus, 1963 would indicate the year
|
||||
Martin Luther King won the Nobel prize, not the year 2863.
|
||||
|
||||
=item *
|
||||
|
||||
Years in the range 100..999 are interpreted as offset from 1900,
|
||||
so that 112 indicates 2012. This rule also applies to years less than zero
|
||||
(but see note below regarding date range).
|
||||
|
||||
=item *
|
||||
|
||||
Years in the range 0..99 are interpreted as shorthand for years in the
|
||||
rolling "current century," defined as 50 years on either side of the current
|
||||
year. Thus, today, in 1999, 0 would refer to 2000, and 45 to 2045,
|
||||
but 55 would refer to 1955. Twenty years from now, 55 would instead refer
|
||||
to 2055. This is messy, but matches the way people currently think about
|
||||
two digit dates. Whenever possible, use an absolute four digit year instead.
|
||||
|
||||
=back
|
||||
|
||||
The scheme above allows interpretation of a wide range of dates, particularly
|
||||
if 4-digit years are used.
|
||||
|
||||
Please note, however, that the range of dates that can be actually be handled
|
||||
depends on the size of an integer (time_t) on a given platform.
|
||||
Currently, this is 32 bits for most systems, yielding an approximate range
|
||||
from Dec 1901 to Jan 2038.
|
||||
|
||||
Both timelocal() and timegm() croak if given dates outside the supported
|
||||
range.
|
||||
|
||||
=head1 IMPLEMENTATION
|
||||
|
||||
These routines are quite efficient and yet are always guaranteed to agree
|
||||
with localtime() and gmtime(). We manage this by caching the start times
|
||||
of any months we've seen before. If we know the start time of the month,
|
||||
we can always calculate any time within the month. The start times
|
||||
are calculated using a mathematical formula. Unlike other algorithms
|
||||
that do multiple calls to gmtime().
|
||||
|
||||
timelocal() is implemented using the same cache. We just assume that we're
|
||||
translating a GMT time, and then fudge it when we're done for the timezone
|
||||
and daylight savings arguments. Note that the timezone is evaluated for
|
||||
each date because countries occasionally change their official timezones.
|
||||
Assuming that localtime() corrects for these changes, this routine will
|
||||
also be correct.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
The whole scheme for interpreting two-digit years can be considered a bug.
|
||||
|
||||
The proclivity to croak() is probably a bug.
|
||||
|
||||
=head1 SUPPORT
|
||||
|
||||
Support for this module is provided via the perl5-porters@perl.org
|
||||
email list. See http://lists.perl.org/ for more details.
|
||||
|
||||
Please submit bugs using the RT system at bugs.perl.org, the perlbug
|
||||
script, or as a last resort, to the perl5-porters@perl.org list.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
This module is based on a Perl 4 library, timelocal.pl, that was
|
||||
included with Perl 4.036, and was most likely written by Tom
|
||||
Christiansen.
|
||||
|
||||
The current version was written by Graham Barr.
|
||||
|
||||
It is now being maintained separately from the Perl core by Dave
|
||||
Rolsky, <autarch@urth.org>.
|
||||
|
||||
=cut
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package Time::gmtime;
|
||||
use strict;
|
||||
use 5.006_001;
|
||||
|
||||
use Time::tm;
|
||||
|
||||
our(@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
|
||||
BEGIN {
|
||||
use Exporter ();
|
||||
@ISA = qw(Exporter Time::tm);
|
||||
@EXPORT = qw(gmtime gmctime);
|
||||
@EXPORT_OK = qw(
|
||||
$tm_sec $tm_min $tm_hour $tm_mday
|
||||
$tm_mon $tm_year $tm_wday $tm_yday
|
||||
$tm_isdst
|
||||
);
|
||||
%EXPORT_TAGS = ( FIELDS => [ @EXPORT_OK, @EXPORT ] );
|
||||
$VERSION = 1.02;
|
||||
}
|
||||
use vars @EXPORT_OK;
|
||||
|
||||
sub populate (@) {
|
||||
return unless @_;
|
||||
my $tmob = Time::tm->new();
|
||||
@$tmob = (
|
||||
$tm_sec, $tm_min, $tm_hour, $tm_mday,
|
||||
$tm_mon, $tm_year, $tm_wday, $tm_yday,
|
||||
$tm_isdst )
|
||||
= @_;
|
||||
return $tmob;
|
||||
}
|
||||
|
||||
sub gmtime (;$) { populate CORE::gmtime(@_ ? shift : time)}
|
||||
sub gmctime (;$) { scalar CORE::gmtime(@_ ? shift : time)}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Time::gmtime - by-name interface to Perl's built-in gmtime() function
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Time::gmtime;
|
||||
$gm = gmtime();
|
||||
printf "The day in Greenwich is %s\n",
|
||||
(qw(Sun Mon Tue Wed Thu Fri Sat Sun))[ gm->wday() ];
|
||||
|
||||
use Time::gmtime w(:FIELDS;
|
||||
printf "The day in Greenwich is %s\n",
|
||||
(qw(Sun Mon Tue Wed Thu Fri Sat Sun))[ gm_wday() ];
|
||||
|
||||
$now = gmctime();
|
||||
|
||||
use Time::gmtime;
|
||||
use File::stat;
|
||||
$date_string = gmctime(stat($file)->mtime);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module's default exports override the core gmtime() function,
|
||||
replacing it with a version that returns "Time::tm" objects.
|
||||
This object has methods that return the similarly named structure field
|
||||
name from the C's tm structure from F<time.h>; namely sec, min, hour,
|
||||
mday, mon, year, wday, yday, and isdst.
|
||||
|
||||
You may also import all the structure fields directly into your namespace
|
||||
as regular variables using the :FIELDS import tag. (Note that this
|
||||
still overrides your core functions.) Access these fields as variables
|
||||
named with a preceding C<tm_> in front their method names. Thus,
|
||||
C<$tm_obj-E<gt>mday()> corresponds to $tm_mday if you import the fields.
|
||||
|
||||
The gmctime() function provides a way of getting at the
|
||||
scalar sense of the original CORE::gmtime() function.
|
||||
|
||||
To access this functionality without the core overrides,
|
||||
pass the C<use> an empty import list, and then access
|
||||
function functions with their full qualified names.
|
||||
On the other hand, the built-ins are still available
|
||||
via the C<CORE::> pseudo-package.
|
||||
|
||||
=head1 NOTE
|
||||
|
||||
While this class is currently implemented using the Class::Struct
|
||||
module to build a struct-like class, you shouldn't rely upon this.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Tom Christiansen
|
||||
@@ -0,0 +1,86 @@
|
||||
package Time::localtime;
|
||||
use strict;
|
||||
use 5.006_001;
|
||||
|
||||
use Time::tm;
|
||||
|
||||
our(@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
|
||||
BEGIN {
|
||||
use Exporter ();
|
||||
@ISA = qw(Exporter Time::tm);
|
||||
@EXPORT = qw(localtime ctime);
|
||||
@EXPORT_OK = qw(
|
||||
$tm_sec $tm_min $tm_hour $tm_mday
|
||||
$tm_mon $tm_year $tm_wday $tm_yday
|
||||
$tm_isdst
|
||||
);
|
||||
%EXPORT_TAGS = ( FIELDS => [ @EXPORT_OK, @EXPORT ] );
|
||||
$VERSION = 1.02;
|
||||
}
|
||||
use vars @EXPORT_OK;
|
||||
|
||||
sub populate (@) {
|
||||
return unless @_;
|
||||
my $tmob = Time::tm->new();
|
||||
@$tmob = (
|
||||
$tm_sec, $tm_min, $tm_hour, $tm_mday,
|
||||
$tm_mon, $tm_year, $tm_wday, $tm_yday,
|
||||
$tm_isdst )
|
||||
= @_;
|
||||
return $tmob;
|
||||
}
|
||||
|
||||
sub localtime (;$) { populate CORE::localtime(@_ ? shift : time)}
|
||||
sub ctime (;$) { scalar CORE::localtime(@_ ? shift : time) }
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Time::localtime - by-name interface to Perl's built-in localtime() function
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Time::localtime;
|
||||
printf "Year is %d\n", localtime->year() + 1900;
|
||||
|
||||
$now = ctime();
|
||||
|
||||
use Time::localtime;
|
||||
use File::stat;
|
||||
$date_string = ctime(stat($file)->mtime);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module's default exports override the core localtime() function,
|
||||
replacing it with a version that returns "Time::tm" objects.
|
||||
This object has methods that return the similarly named structure field
|
||||
name from the C's tm structure from F<time.h>; namely sec, min, hour,
|
||||
mday, mon, year, wday, yday, and isdst.
|
||||
|
||||
You may also import all the structure fields directly into your namespace
|
||||
as regular variables using the :FIELDS import tag. (Note that this still
|
||||
overrides your core functions.) Access these fields as
|
||||
variables named with a preceding C<tm_> in front their method names.
|
||||
Thus, C<$tm_obj-E<gt>mday()> corresponds to $tm_mday if you import
|
||||
the fields.
|
||||
|
||||
The ctime() function provides a way of getting at the
|
||||
scalar sense of the original CORE::localtime() function.
|
||||
|
||||
To access this functionality without the core overrides,
|
||||
pass the C<use> an empty import list, and then access
|
||||
function functions with their full qualified names.
|
||||
On the other hand, the built-ins are still available
|
||||
via the C<CORE::> pseudo-package.
|
||||
|
||||
=head1 NOTE
|
||||
|
||||
While this class is currently implemented using the Class::Struct
|
||||
module to build a struct-like class, you shouldn't rely upon this.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Tom Christiansen
|
||||
@@ -0,0 +1,33 @@
|
||||
package Time::tm;
|
||||
use strict;
|
||||
|
||||
our $VERSION = '1.00';
|
||||
|
||||
use Class::Struct qw(struct);
|
||||
struct('Time::tm' => [
|
||||
map { $_ => '$' } qw{ sec min hour mday mon year wday yday isdst }
|
||||
]);
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Time::tm - internal object used by Time::gmtime and Time::localtime
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Don't use this module directly.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is used internally as a base class by Time::localtime And
|
||||
Time::gmtime functions. It creates a Time::tm struct object which is
|
||||
addressable just like's C's tm structure from F<time.h>; namely with sec,
|
||||
min, hour, mday, mon, year, wday, yday, and isdst.
|
||||
|
||||
This class is an internal interface only.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Tom Christiansen
|
||||
@@ -0,0 +1,51 @@
|
||||
MA|01000|02799
|
||||
RI|02800|02999
|
||||
NH|03000|03899
|
||||
ME|03900|04999
|
||||
VT|05000|05999
|
||||
CT|06000|06999
|
||||
NJ|07000|08999
|
||||
NY|10000|14999
|
||||
PA|15000|19699
|
||||
DE|19700|19999
|
||||
DC|20000|20599
|
||||
MD|20600|21999
|
||||
VA|22000|24699
|
||||
WV|24700|26999
|
||||
NC|27000|28999
|
||||
SC|29000|29999
|
||||
GA|30000|31999
|
||||
FL|32000|34999
|
||||
AL|35000|36999
|
||||
TN|37000|38599
|
||||
MS|38600|39999
|
||||
KY|40000|42999
|
||||
OH|43000|45999
|
||||
IN|46000|47999
|
||||
MI|48000|49999
|
||||
IA|50000|52999
|
||||
WI|53000|54999
|
||||
MN|55000|56999
|
||||
SD|57000|57999
|
||||
ND|58000|58999
|
||||
MT|59000|59999
|
||||
IL|60000|62999
|
||||
MO|63000|65999
|
||||
KS|66000|67999
|
||||
NE|68000|69999
|
||||
LA|70000|71599
|
||||
AR|71600|72999
|
||||
OK|73000|74999
|
||||
TX|75000|79999
|
||||
CO|80000|81999
|
||||
WY|82000|83199
|
||||
ID|83200|83999
|
||||
UT|84000|84999
|
||||
AZ|85000|86999
|
||||
NM|87000|88899
|
||||
NV|88900|89999
|
||||
CA|90000|96699
|
||||
HI|96700|96999
|
||||
OR|97000|97999
|
||||
WA|98000|99499
|
||||
AK|99500|99999
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
Õ8z*½PScOȪP,ór)" ×␍‰¼+àEÓ<7‘Ôøj2ýîÕhØÕÑ#8誙éBØ,ƒ65ŸÂCb�(ÔøRºý¡aá.4³ Áµ‘•™âáäWFR2¹Á) ±‰Õ8z.ý¿¤hÜÍK]…º%Mq]¤œ°Í·M]ƒ%tñ•Ôøyî½\ƒá‘,Yµc:â•SÄÏd<,ióŸ£UäôÔøK:ô»B.e©Z%¢�Vn*àóä¡�Q´Eã!tä+DìÔùC5Ñ�WETÁbíÁ´øo‡k]¡ŽbÉŸ�›”c�ÔøCz´ãÁÕ”ª.”ãÂ4û6±£_ÂM¬è]]�¸æDóÔøbv¼]áÅ–)`]¢kéÊœ_�uYª®©]ÁUU2FòÔør.½]á·Mt÷…ÃZÇlöd]£C]6¦ø]‚Y[Ê.jÔøR÷=_aëº_ ‚ÜÊÜâÁ1©Z…šâÁÌ]…æßÔør2ýÕ¢)6A›]âÉ]¢(,Qáœ]�™”\gSÕ4Âaõ‹¦&¬Q2cÊ>”¬vø\ud’I$P`ë®ÚÝu
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
ÕxZ²ýP4e5cU\#n7#¤¸ŽHVŽêcØÍŒÉÕxC»=æâ¸�Ö¹A`ø‘a肹�¹µBÁ+3+Õx;ÿ4ÍQ‘ªÓ‡Á¨x‹¸å¡7␍TÖÞ_¡ª°’› ÕxJöüÍB<]ÁÚ%]á‡Ð¸�
|
||||
]ât×1ã]"0s Õwb2ý¡"Ù�hÏÏ£&ñ� 0_CÚ%‚Ø,Ï#ÁdAœÕxZ·=a#^TI¿XV¥ØÕ¡±s6©IsBt—IrŸÕx%~´¶Â— Žb�!aÏ8s‡ËaÓÍ8cFå )“8ëÕxZvý�¢E„XÝÌ]#yMÓ…¥02•¨Ï":#ƒ8tÕxZvü�"%Ñ|©ƒ_âÓ,äϢø“)·YŒè×ÕxJúôåA¥Ú}’·¡¼m¡Ìe·�Ñ+6µj_¡Æ-gF|ÕxK>üaâ-
|
||||
ì®ØwCY␍Œè—¡J,ƒFsÏÁÍ®ZÕx<>ô_"q-³£µ )²‹ Ÿ¡*Ƭså ÷␍Ps‹Õv‘æµ_åÃc•Ûã„íÓg‹Î²rG$R 埗Yµ
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
Υχ;ϋ=PW1W#Xυ[r)ΆdΡ[K±δ%7®
|
||||
�Υψ$ώτ£c±#2¤κ΅+)U…�΅BV4FΒ¬£Α›S΄½ΥχC{4[γH�npή¥¤κ•·”£βΨbΒ��Οƒ`εgΈβΥχC?=εγW␍n(*ΉΆEΝn,©ΉΒΖ{Qα–εΑαΞ6¦qΥχR·=Ή!ηΖ®\Οb#΅V‰)Ή�«°Λ£ΐ…h~ ΥχR»=£ �²΄Ρ“£΅η␍�\%�Ά.ΜΠkε‚nΝΜ\–ΥχR»=εΑ—pzΫΟαµr’δ>���ά™=£΅$uΘμΥχR»=Ο�fξG…Ο ΘµjΥ&Οa·ΣyΟΰΧΥχCΏ<ΟΒ·+61γ�‚�aΝΧΟ£pτK¦σΉΆβιηΥχ;ΎτWCΡlR�΅™ƒLΤΙJΧε$�Έ=R£Αf¤‡�¶Υψ%>³γ##xw…bΆ�ΙLMδα“Ω}‰�Οbj&‡V¤ΥψϊμΉΰΉκ:3 ¥αΤέk¶Ί�α+[µ? ΅ΰ®k�SΥψ%vσ£A¥bY—ΨΝ��•9‹‰λ7q·©ΰέ‘Ψε
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
Õ8‚®ýPRáW¸é\-Ñ�¸£¬eŒ•lñìG'„XVÔÔùbúýî¤Õ,‹µbÓ*V èÂÔrK"“bTÕJ¤°Ôú5~³ÕaÈ‹fÅ9áZÚÉTŽÈ�¥`Í•ááI”ŒZ^Õ8‚nýYäÃZ2�!QÄhzã˜äk¥Ì*Š·‘ƒ›"xS�Õ8‚nô“£ªâ漓‚Ƹ®�‘ÃÓ*Ž‹"“‚fs�ŒãÔùSzó‘CdÖƒHÝΡµQTëÎÁº�mqÓÂZ•£sÔùbû=iB
|
||||
ª‘·V“bc« #“�meTQ¢ÅJeÔùS;4‘¡Š²ŽU�Q¡\ÔÆH'k‚JéXž»á+˜ÊÓÕ8‚2¼U"àê#ÒçÃØ9е�kÃÚ:�'œQ„"‘IÔø‚.ü‘�S”Ìc§"7ÊоÏRërŒâ“Á%ÚœÎÄÔúD~óÕV΢Ée’¢�VãÒÁ¸Õqräã!±�·EÔùK¾üáÁ¡i}9EiÁšë™UEã¡Lyª¥ê“áXz«9#Õ3âáõ—ȰržQ¨ Y”ÍsÌq[‘HܘaMòí2
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
Õxj·=P3cWc•T qFÜš$·D�V•ìD%C�H�ÕxS<UÖkoà“Â6±RˆÙU¢t¤.5\U¾”¡Æ+Õxj·=§B¾”ÅÄd§�‰`ɼTUÁ 9D§!ÂòR‰YÕwrw=Ï�RQÅSÂq�9aÖUÂ:²Žšè‘‚c›xIÕxS?=UàÇË�7!Tá¹mfžÌ‚B¤¥šåaâ²*ÕxK¾óS¡l£¶¸U¡,•jd¹U€‹MÉt¯U�“íZfÕxCþ²y"£0•œÓ‘Á›YñLËUÁ#Ùœ;SÁÊr§¸Õx<¾óUÁa\=GÆåáE*U·OUádÞÅhUá>&‡T»ÕxD?4åAáœ9EŒ©Á'Ú¸½UÁÅ2r[S�ªyÇqÕxb·<©ÂH×maQb␍ÅZ%ãB#h�S¢H;K)Õxbû=mAâ¹K&²«¡µÊÐ^WW¡›CÈjg“¡¦ú’ŠäÕxbû=§¡óu¹UÁ5q^ƒå�ŠÓ©ÆÝSÂ;LfŸÕw’.ýåÂÙ�PÞS¡Ó¥ÊÜ��Fär6zœA[´»M
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
уВD;3Pв2FЙXL╓Qе▌EFфFFyДD╛ Аж÷уВ-~╙Ц└╖(я;Em╕3з╣<\ёт╛K≈i╧da1s␍уЬ5~ЙЕyjX╝W╖Q▄╪Wбб≥┼хлД╒╓ЬV┐уЬ=?4ЕBрЦ2%XW╒j▌fRyUB·]iПМ╔б┴▒пн²уЬD=Еб'╒╣:еWB╔ax╫Г"tМ≈)Г║╨,Nа уВS<обьжNH9W└ыF┴ХsЦ╓*╚▒еF▐Ц╥(н/уВZШ<Wд+!ИJ²о┌*╠nEфЦ╒c√6BПоаБЭvs3уВb╩=ЕаФэБ:╓mbй]Jя-ЕЙV≈┐╧┌f535aуВZШ=о║Hl╤║зоБbСO╘Е│kJZ╦WбХu.а╒уВS=WcX°ё8j≥ц≥XПлгW╓ЗZ'кcёZ√fж╨уЬ<Ъ=WB╛$jdКД┌S╛X]на▀JяZ√ЦбРoSуЬ.>УЕ║W╓╡XW│ТlK%aЕ║.у▄ТЯ╧бл╘ь╜ж4╧Й╬Ел+MЛv_dх≈к╠.┘кЯ#╣IZAьЪ⌡Y╣
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
# $Id: Makefile,v 1.9 2005/10/27 17:53:35 stevek Exp $
|
||||
|
||||
#
|
||||
# Makefile, based on the Asterisk Makefile, Coypright (C) 1999, Mark Spencer
|
||||
#
|
||||
# Copyright (C) 2002,2003 Junghanns.NET GmbH
|
||||
#
|
||||
# Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
#
|
||||
# This program is free software and may be modified and
|
||||
# distributed under the terms of the GNU Public License.
|
||||
#
|
||||
|
||||
.EXPORT_ALL_VARIABLES:
|
||||
|
||||
#
|
||||
# app_conference defines which can be passed on the command-line
|
||||
#
|
||||
|
||||
DESTDIR :=
|
||||
INSTALL_MODULES_DIR := /usr/lib/asterisk/modules
|
||||
INSTALL_CONFIG_DIR := /etc/asterisk
|
||||
|
||||
ASTERISK_INCLUDE_DIR := /usr/include/asterisk
|
||||
|
||||
# turn app_conference debugging on or off ( 0 == OFF, 1 == ON )
|
||||
APP_CONFERENCE_DEBUG := 1
|
||||
|
||||
# 0 = OFF 1 = astdsp 2 = speex
|
||||
SILDET := 2
|
||||
|
||||
#
|
||||
# app_conference objects to build
|
||||
#
|
||||
|
||||
OBJS = app_conference.o conference.o member.o frame.o cli.o
|
||||
SHAREDOS = app_conference.so
|
||||
|
||||
#
|
||||
# standard compile settings
|
||||
#
|
||||
|
||||
PROC = $(shell uname -m)
|
||||
INSTALL = install
|
||||
CC = gcc
|
||||
|
||||
# skip asterisk includes if they are in /usr/include/asterisk
|
||||
ifneq ($(shell dirname $(ASTERISK_INCLUDE_DIR)), /usr/include)
|
||||
INCLUDE = -I$(ASTERISK_INCLUDE_DIR)
|
||||
endif
|
||||
|
||||
LIBS = -ldl -lpthread -lm
|
||||
DEBUG := -g
|
||||
|
||||
CFLAGS += -pipe -fPIC -Wall -Wmissing-prototypes -Wmissing-declarations $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE
|
||||
|
||||
CFLAGS += $(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi)
|
||||
CFLAGS += -DCRYPTO
|
||||
|
||||
ifeq ($(APP_CONFERENCE_DEBUG), 1)
|
||||
CFLAGS += -DAPP_CONFERENCE_DEBUG
|
||||
endif
|
||||
|
||||
#
|
||||
# additional flag values for silence detection
|
||||
#
|
||||
|
||||
ifeq ($(SILDET), 2)
|
||||
OBJS += libspeex/preprocess.o libspeex/misc.o libspeex/smallft.o
|
||||
CFLAGS += -Ilibspeex -DSILDET=2
|
||||
endif
|
||||
|
||||
ifeq ($(SILDET), 1)
|
||||
CFLAGS += -DSILDET=1
|
||||
endif
|
||||
|
||||
OSARCH=$(shell uname -s)
|
||||
ifeq (${OSARCH},Darwin)
|
||||
SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace
|
||||
else
|
||||
SOLINK=-shared -Xlinker -x
|
||||
endif
|
||||
|
||||
#
|
||||
# targets
|
||||
#
|
||||
|
||||
all: $(SHAREDOS)
|
||||
|
||||
clean:
|
||||
rm -f *.so *.o $(OBJS)
|
||||
|
||||
app_conference.so : $(OBJS)
|
||||
$(CC) -shared -Xlinker -x -o $@ $(OBJS)
|
||||
|
||||
vad_test: vad_test.o libspeex/preprocess.o libspeex/misc.o libspeex/smallft.o
|
||||
$(CC) $(PROFILE) -o $@ $^ -lm
|
||||
|
||||
install: all
|
||||
if [ ! -d ${DESTDIR}${INSTALL_MODULES_DIR} ]; then \
|
||||
$(INSTALL) -m 755 -d ${DESTDIR}${INSTALL_MODULES_DIR} ; \
|
||||
fi
|
||||
for x in $(SHAREDOS); do \
|
||||
$(INSTALL) -m 755 $$x ${DESTDIR}$(INSTALL_MODULES_DIR) ; \
|
||||
done
|
||||
cp -f *.gsm /var/lib/asterisk/sounds
|
||||
cp -f *.wav /var/lib/asterisk/sounds
|
||||
|
||||
|
||||
#config: all
|
||||
# if [ ! -d ${DESTDIR}${INSTALL_CONFIG_DIR} ]; then \
|
||||
# $(INSTALL) -m 755 -d ${DESTDIR}${INSTALL_CONFIG_DIR} ; \
|
||||
# fi
|
||||
# $(INSTALL) -m 640 conf.conf ${DESTDIR}${INSTALL_CONFIG_DIR}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Õ8šê½PRá•=:¢^
|
||||
àÜr(Ûžf�MÑ[àE8‡Á‘Õ8¢®½áƒv†G—(QE!”fÝ…ã¢Q[…¢¬O*Ö©Ôúrû<ÓHWSœ€áw“tÖ8ÜÁµDÅÆxí"IGDèÔüL4Ÿ�Ô:RŠÄ«ÁU�X¹…¢Éj¸£]‚H䋨êÔûL~ü¹âãrœãA�␍fa¡»áÖ®2·…_Á(º®9FÔù‚·<Qñ*1‚A$ŽHä_¡Ÿ+ŽUT»¡œ.Š5›Õ8¢n½a#6©y>Ü—ã�‰áL]ëRQh_¢¹ÃÓÔø‚²³…7#uG©B.Vƒ³i]‚žM†cj…¢E2•šÅÔù[vª…A¥ÑåDy«œ”Âac]ÂÖloŒÛá‚HtR©FÔùzöô‡�—ÃæHy!ÕŠ¦ð«AqŽ8¨ã!Q‡È¤Ôûc=á¡á6�áB)S�dÞU9Æáñ"<55ÅÔûc¿=ÕÁŒÛ‰ømábÛÂSc_�žÜ©äëáÂH¬UÉDÔù{7=_!1˜Ö¥•!‰S©Ô왩¸ãn:5P!kuÚëæ
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,341 @@
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,121 @@
|
||||
# $Id: Makefile,v 1.9 2005/10/27 17:53:35 stevek Exp $
|
||||
|
||||
#
|
||||
# Makefile, based on the Asterisk Makefile, Coypright (C) 1999, Mark Spencer
|
||||
#
|
||||
# Copyright (C) 2002,2003 Junghanns.NET GmbH
|
||||
#
|
||||
# Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
#
|
||||
# This program is free software and may be modified and
|
||||
# distributed under the terms of the GNU Public License.
|
||||
#
|
||||
|
||||
.EXPORT_ALL_VARIABLES:
|
||||
|
||||
#
|
||||
# app_conference defines which can be passed on the command-line
|
||||
#
|
||||
|
||||
INSTALL_PREFIX := /usr
|
||||
INSTALL_MODULES_DIR := $(INSTALL_PREFIX)/lib/asterisk/modules
|
||||
|
||||
ASTERISK_INCLUDE_DIR := $(HOME)/local/asterisk/asterisk/include
|
||||
|
||||
# turn app_conference debugging on or off ( 0 == OFF, 1 == ON )
|
||||
APP_CONFERENCE_DEBUG := 1
|
||||
|
||||
# 0 = OFF 1 = astdsp 2 = speex
|
||||
SILDET := 2
|
||||
|
||||
#
|
||||
# app_conference objects to build
|
||||
#
|
||||
|
||||
OBJS = app_conference.o conference.o member.o frame.o cli.o
|
||||
SHAREDOS = app_conference.so
|
||||
|
||||
#
|
||||
# standard compile settings
|
||||
#
|
||||
|
||||
PROC = $(shell uname -m)
|
||||
INSTALL = install
|
||||
CC = gcc
|
||||
|
||||
INCLUDE = -I$(ASTERISK_INCLUDE_DIR)
|
||||
LIBS = -ldl -lpthread -lm
|
||||
DEBUG := -g
|
||||
|
||||
CFLAGS = -pipe -fPIC -Wall -Wmissing-prototypes -Wmissing-declarations $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE
|
||||
#CFLAGS += -O2
|
||||
#CFLAGS += -O3 -march=pentium3 -msse -mfpmath=sse,387 -ffast-math
|
||||
# PERF: below is 10% faster than -O2 or -O3 alone.
|
||||
#CFLAGS += -O3 -ffast-math -funroll-loops
|
||||
# below is another 5% faster or so.
|
||||
CFLAGS += -O3 -ffast-math -funroll-all-loops -fprefetch-loop-arrays -fsingle-precision-constant
|
||||
|
||||
# this is fun for PPC
|
||||
#CFLAGS += -mcpu=7450 -faltivec -mabi=altivec -mdynamic-no-pic
|
||||
|
||||
# this is fun for x86
|
||||
CFLAGS += -march=pentium3 -msse -mfpmath=sse,387
|
||||
|
||||
|
||||
# adding -msse -mfpmath=sse has little effect.
|
||||
#CFLAGS += -O3 -msse -mfpmath=sse
|
||||
#CFLAGS += $(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi)
|
||||
CFLAGS += $(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi)
|
||||
CFLAGS += -DCRYPTO
|
||||
|
||||
ifeq ($(APP_CONFERENCE_DEBUG), 1)
|
||||
CFLAGS += -DAPP_CONFERENCE_DEBUG
|
||||
endif
|
||||
|
||||
#
|
||||
# additional flag values for silence detection
|
||||
#
|
||||
|
||||
ifeq ($(SILDET), 2)
|
||||
OBJS += libspeex/preprocess.o libspeex/misc.o libspeex/smallft.o
|
||||
CFLAGS += -Ilibspeex -DSILDET=2
|
||||
endif
|
||||
|
||||
ifeq ($(SILDET), 1)
|
||||
CFLAGS += -DSILDET=1
|
||||
endif
|
||||
|
||||
OSARCH=$(shell uname -s)
|
||||
ifeq (${OSARCH},Darwin)
|
||||
SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace
|
||||
else
|
||||
SOLINK=-shared -Xlinker -x
|
||||
endif
|
||||
|
||||
#
|
||||
# targets
|
||||
#
|
||||
|
||||
all: $(SHAREDOS)
|
||||
|
||||
clean:
|
||||
rm -f *.so *.o $(OBJS)
|
||||
|
||||
app_conference.so : $(OBJS)
|
||||
$(CC) -pg -shared -Xlinker -x -o $@ $(OBJS)
|
||||
|
||||
vad_test: vad_test.o libspeex/preprocess.o libspeex/misc.o libspeex/smallft.o
|
||||
$(CC) $(PROFILE) -o $@ $^ -lm
|
||||
|
||||
install: all
|
||||
for x in $(SHAREDOS); do $(INSTALL) -m 755 $$x $(INSTALL_MODULES_DIR) ; done
|
||||
cp -f *.gsm /var/lib/asterisk/sounds
|
||||
cp -f *.wav /var/lib/asterisk/sounds
|
||||
|
||||
# /var/horizon/mojo/lib/horizoncmd restart asterisk
|
||||
# make sure you restart asterisk after make install
|
||||
|
||||
# config: all
|
||||
# cp conf.conf /etc/asterisk/
|
||||
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
app_conference is a channel-independent conference application.
|
||||
|
||||
Design goals
|
||||
It has several design goals which are different than meetme:
|
||||
|
||||
* It does not require a zap channel for timing.
|
||||
* It is very efficient when used with channels which support DTX (silence
|
||||
detection/discontinuous transmission).
|
||||
|
||||
* It can do VAD on channels which do not support DTX (although this
|
||||
is more expensive than just mixing them, but less expensive then
|
||||
encoding; therefore it might still be a win).
|
||||
* It presents messages on the Monitor interface for determine which
|
||||
speakers are active.
|
||||
|
||||
|
||||
I believe that people other than myself are using this, but my environment is
|
||||
pretty rigid, and cases outside of this aren't tested:
|
||||
|
||||
* Some callers come from the PSTN via IAX2 from a box with Zaptel cards,
|
||||
via uLaw.
|
||||
* Other callers come from iaxclient based softphones using speex
|
||||
(was GSM) via IAX2 (with DTX).
|
||||
* Some conferences have a single SIP channel which listens in GSM.
|
||||
|
||||
|
||||
Mixing design
|
||||
|
||||
* Minimize encoding/decoding, minimize mixing.
|
||||
* Minimize generational loss from trancoding.
|
||||
* Usual cases are handled very efficiently:
|
||||
o One speaker: That speaker's frame is sent directly to each
|
||||
participant which uses the same codec. It is trancoded
|
||||
_once_ for each additional codec type used by participants.
|
||||
o Two speakers: Each speaker gets the other speaker's frames.
|
||||
The two speaker's frames are decoded and mixed, and then
|
||||
encoded _once_ for each codec type used by participants.
|
||||
|
||||
|
||||
License
|
||||
Naturally, app_conference is GPL. The CVS also includes parts of libspeex,
|
||||
which is distributed under it's a BSD-style license.
|
||||
|
||||
Getting app_conference
|
||||
app_conference is available from the 'iaxclient' project on sourceforge:
|
||||
|
||||
* CVS browse: http://iaxclient.cvs.sourceforge.net/iaxclient/app_conference/
|
||||
* Project page: http://sourceforge.net/projects/iaxclient/
|
||||
|
||||
|
||||
Compiling app_conference
|
||||
|
||||
* just like any other asterisk application.
|
||||
* Some makefile fiddling may be necessary
|
||||
|
||||
|
||||
Using app_conference
|
||||
There is no configuration file. Conferences are created on-the-fly.
|
||||
|
||||
Dialplan syntax: Conference(ConferenceName|Flags|Priority[|VADSTART|VADCONTINUE])
|
||||
|
||||
* ConferenceName: Whatever you want to name the conference
|
||||
* Flags one of more of the following:
|
||||
o M: Moderator (presently same as speaker)
|
||||
o S: Speaker
|
||||
o L: Listener
|
||||
o T: "Telephone caller" (just for stats?).
|
||||
o V: Do VAD on this caller
|
||||
o D: Use Denoise filter on this caller.
|
||||
o d: Send manager events when DTMF is received.
|
||||
o q: Do not play enter or exit sounds.
|
||||
o i: use inband dtmf broadcast.
|
||||
o t: use rfc dtmf signal broadcast.
|
||||
* Priority: Currently ignored; was to be a "speaking priority" so a
|
||||
higher priority caller could "override" others.
|
||||
* VADSTART: Optional: "probability" to use to detect start of speech.
|
||||
* VADCONTINUE: Optional: "probability" to use to detect continuation
|
||||
of speech.
|
||||
|
||||
|
||||
Features / CLI/Manager Commands
|
||||
|
||||
* conference debug <conference_name> [ on | off ]
|
||||
enable debugging for a conference
|
||||
|
||||
* conference show stats show conference stats
|
||||
|
||||
* conference play sound <channel-id> <sound-file> [mute]
|
||||
play a sound to a conference member, optionally muting them.
|
||||
|
||||
Manager Events
|
||||
app_conference generates several detailed manager events so that applications
|
||||
interfacing with the manager API can monitor conferences:
|
||||
|
||||
* ConferenceState: sent as members begin/end speaking.
|
||||
Channel: The channel
|
||||
State: "speaking" or "silent"
|
||||
|
||||
* ConferenceDTMF: sent when conference members send DTMF to the conference
|
||||
Channel: The channel
|
||||
Key: The DTMF key send [0-9*#]
|
||||
|
||||
* ConferenceSoundComplete: send when the conference has finished playing
|
||||
a sound to a user
|
||||
Channel: The channel
|
||||
Sound: The first 255 bytes of the file requested in conference play
|
||||
sound CLI/Mgr command.
|
||||
|
||||
|
||||
Benchmarking
|
||||
It would be nice to have solid benchmarks to present, but a good size
|
||||
machine should be able to handle many callers when either (a) they are
|
||||
using DTX, or (b) they are listen-only. It's used often with hundreds of
|
||||
simultaneous callers.
|
||||
|
||||
Discussion
|
||||
The iaxclient-devel mailing list is probably as good a place as any to discuss.
|
||||
|
||||
|
||||
---
|
||||
|
||||
app_conference is brought to you by the letter q, and the number e
|
||||
|
||||
|
||||
Matt Florell Changes for VD_app_conference tarball version. In order to use this
|
||||
as seemlessly as possible for VICIDIAL it was necessary to modify the code. This
|
||||
module can now be used in place of using meetme for VICIDIAL. This is
|
||||
experimental, please test thoroughly before putting it in production.
|
||||
|
||||
CHANGES MADE FOR VICIDIAL:
|
||||
|
||||
- Default to member type of S instead of L
|
||||
- Commented out many repetative NOTICEs and ERRORs
|
||||
- Added entry and exit sounds except for Local/786 and Local/8500998 (VDsilent and DTMFapp)
|
||||
- Added conf-onlyperson mesage to first conference participant
|
||||
- Included enter.gsm and leave.gsm files as well as DTMF wav and gsm sounds
|
||||
- Removed restart from "make install" and added audio file copying
|
||||
- If no 'd' option is set and DTMF frames are detected, the DTMF audio will be played to the member
|
||||
- Added 'q' flag to prevent playing of entry/exit sounds
|
||||
- Added 'i' flag to enable playing of DTMF sounds inband to all members
|
||||
- Added 't' flag to enable sending of RFC DTMF signals to all members
|
||||
- Changed delimiter to pipe | from slash / to be more compliant with meetme standards
|
||||
|
||||
|
||||
NOTE TO 64-BIT LINUX USERS:
|
||||
If you have problems with 64-bit Linux please use the included 64_Makefile instead of the default one.
|
||||
|
||||
COMPILE NOTES:
|
||||
|
||||
Some Makefile alterations may be necessary for your system:
|
||||
|
||||
From Alek Barkley:
|
||||
I am running FC4 with latest updates ( 2.6.16 kernel )
|
||||
My CPU is AMD K6 and GCC 4.0.2.
|
||||
|
||||
I edited Makefile in app_conference distribution.
|
||||
To enter CPU architecture in my case -march=k6
|
||||
========================================================
|
||||
PROC = k6
|
||||
# this is fun for x86
|
||||
#CFLAGS += -march=pentium3 -msse -mfpmath=sse,387
|
||||
CFLAGS += -march=k6
|
||||
===========================================================
|
||||
|
||||
|
||||
VD_app_conference CHANGELOG
|
||||
- Release 0.4 - First VICIDIAL-centric release
|
||||
- Release 0.5 - Fixed the dtmf broadcast logic and a star/hash bug
|
||||
- Added 64-bit makefile thanks to Jon Hood
|
||||
- Release 0.6 - Added Makefile notes and changed some Makefile flags
|
||||
@@ -0,0 +1,86 @@
|
||||
VICIDIAL and app_conference
|
||||
(1.2 tree) EXPERIMENTAL!!!
|
||||
|
||||
app_conference offers the plusses of being able to do conferencing
|
||||
without any zaptel hardware or timers as well as doing native codec
|
||||
streaming, meaning no manditory downsampling to slin like meetme.
|
||||
These two features alone mean a rather significant performance boost
|
||||
to companies who only use VOIP for agents and trunks as well as not
|
||||
requiring the cost of zaptel hardware or the nightmare of getting
|
||||
ztdummy to work on some systems.
|
||||
|
||||
As of right now, I have a test system fully up and running using
|
||||
app_conference with VICIDIAL on Asterisk 1.2.8. I have made several
|
||||
modifications to the app_conference code to make it more of a drop-in
|
||||
replacement for meetme so that it does not require any changes to the
|
||||
VICIDIAL codebase. I have added entry and exit sounds(slightly
|
||||
different from meetme's) as well as "only one in this conference"
|
||||
message in addition to changing the default member type to speaker(S).
|
||||
So far I have tested with IAX and SIP channels only and everything
|
||||
works just like on meetme but I have not done any capacity testing or
|
||||
tested it in production yet.
|
||||
|
||||
If you want to use app_conference instead of meetme for VICIDIAL then follow these instructions:
|
||||
|
||||
cd /usr/src/asterisk
|
||||
wget http://www.eflo.net/files/VD_app_conference_0.6.zip
|
||||
unzip VD_app_conference_0.6.zip
|
||||
cd app_conference
|
||||
make clean
|
||||
make
|
||||
make install
|
||||
|
||||
|
||||
In your extensions.conf file you would replace these lines:
|
||||
exten => 8600051,1,Meetme,8600051
|
||||
exten => 8600052,1,Meetme,8600052
|
||||
...continue through 8600100...
|
||||
exten => 78600051,1,Meetme,8600051|q
|
||||
exten => 78600052,1,Meetme,8600052|q
|
||||
...continue through 78600100...
|
||||
exten => 68600051,1,Meetme,8600051|mq
|
||||
exten => 68600052,1,Meetme,8600052|mq
|
||||
...continue through 68600100...
|
||||
|
||||
|
||||
with these lines:
|
||||
exten => 8600051,1,Conference(8600051)
|
||||
exten => 8600052,1,Conference(8600052)
|
||||
...continue through 8600100...
|
||||
exten => 78600051,1,Conference(8600051|q)
|
||||
exten => 78600052,1,Conference(8600052|q)
|
||||
...continue through 78600100...
|
||||
exten => 68600051,1,Conference(8600051|Lq)
|
||||
exten => 68600052,1,Conference(8600052|Lq)
|
||||
...continue through 68600100...
|
||||
|
||||
NOTES:
|
||||
Here are the possible feature flags for this version of app_conference:
|
||||
o M: Moderator (presently same as speaker) [default]
|
||||
o S: Speaker
|
||||
o L: Listener
|
||||
o T: "Telephone caller" (just for stats?).
|
||||
o V: Do VAD on this caller
|
||||
o D: Use Denoise filter on this caller.
|
||||
o d: Send manager events when DTMF is received.
|
||||
o q: Do not play enter or exit sounds.
|
||||
o i: use inband dtmf broadcast.
|
||||
o t: use rfc dtmf signal broadcast.
|
||||
|
||||
COMPILE NOTES:
|
||||
|
||||
Some Makefile alterations may be necessary for your system:
|
||||
|
||||
From Alek Barkley:
|
||||
I am running FC4 with latest updates ( 2.6.16 kernel )
|
||||
My CPU is AMD K6 and GCC 4.0.2.
|
||||
|
||||
I edited Makefile in app_conference distribution.
|
||||
To enter CPU architecture in my case -march=k6
|
||||
========================================================
|
||||
PROC = k6
|
||||
# this is fun for x86
|
||||
#CFLAGS += -march=pentium3 -msse -mfpmath=sse,387
|
||||
CFLAGS += -march=k6
|
||||
===========================================================
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
|
||||
// $Id: app_conference.c,v 1.22 2005/10/26 21:02:07 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "app_conference.h"
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
|
||||
a conference has n + 1 threads, where n is the number of
|
||||
members and 1 is a conference thread which sends audio
|
||||
back to the members.
|
||||
|
||||
each member thread reads frames from the channel and
|
||||
add's them to the member's frame queue.
|
||||
|
||||
the conference thread reads frames from each speaking members
|
||||
queue, mixes them, and then re-queues them for the member thread
|
||||
to send back to the user.
|
||||
|
||||
*/
|
||||
|
||||
//
|
||||
// app_conference text descriptions
|
||||
//
|
||||
|
||||
static char *tdesc = "Channel Independent Conference Application" ;
|
||||
static char *app = "Conference" ;
|
||||
static char *synopsis = "Channel Independent Conference" ;
|
||||
static char *descrip = " Conference(): returns 0\n"
|
||||
"if the user exits with the '#' key, or -1 if the user hangs up.\n" ;
|
||||
|
||||
//
|
||||
// functions defined in asterisk/module.h
|
||||
//
|
||||
|
||||
STANDARD_LOCAL_USER ;
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
int unload_module( void )
|
||||
{
|
||||
ast_log( LOG_NOTICE, "unloading app_conference module\n" ) ;
|
||||
|
||||
STANDARD_HANGUP_LOCALUSERS ; // defined in asterisk/module.h
|
||||
|
||||
// register conference cli functions
|
||||
unregister_conference_cli() ;
|
||||
|
||||
return ast_unregister_application( app ) ;
|
||||
}
|
||||
|
||||
int load_module( void )
|
||||
{
|
||||
ast_log( LOG_NOTICE, "loading app_conference module [ $Revision: 1.22 $ ]\n" ) ;
|
||||
|
||||
// intialize conference
|
||||
init_conference() ;
|
||||
|
||||
// register conference cli functions
|
||||
register_conference_cli() ;
|
||||
|
||||
return ast_register_application( app, app_conference_main, synopsis, descrip ) ;
|
||||
}
|
||||
|
||||
char *description( void )
|
||||
{
|
||||
return tdesc ;
|
||||
}
|
||||
|
||||
int usecount( void )
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT( res ) ; // defined in asterisk/module.h
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY ;
|
||||
}
|
||||
|
||||
//
|
||||
// main app_conference function
|
||||
//
|
||||
|
||||
int app_conference_main( struct ast_channel* chan, void* data )
|
||||
{
|
||||
int res = 0 ;
|
||||
struct localuser *u ;
|
||||
|
||||
// defined in asterisk/module.h
|
||||
LOCAL_USER_ADD( u ) ;
|
||||
|
||||
// call member thread function
|
||||
res = member_exec( chan, data ) ;
|
||||
|
||||
// defined in asterisk/module.h
|
||||
LOCAL_USER_REMOVE( u ) ;
|
||||
|
||||
return res ;
|
||||
}
|
||||
|
||||
//
|
||||
// utility functions
|
||||
//
|
||||
|
||||
// now returns milliseconds
|
||||
long usecdiff( struct timeval* timeA, struct timeval* timeB )
|
||||
{
|
||||
long a_secs = timeA->tv_sec - timeB->tv_sec ;
|
||||
long b_secs = (long)( timeA->tv_usec / 1000 ) - (long)( timeB->tv_usec / 1000 ) ;
|
||||
long u_secs = ( a_secs * 1000 ) + b_secs ;
|
||||
return u_secs ;
|
||||
}
|
||||
|
||||
// increment a timeval by ms milliseconds
|
||||
void add_milliseconds( struct timeval* tv, long ms )
|
||||
{
|
||||
// add the microseconds to the microseconds field
|
||||
tv->tv_usec += ( ms * 1000 ) ;
|
||||
|
||||
// calculate the number of seconds to increment
|
||||
long s = ( tv->tv_usec / 1000000 ) ;
|
||||
|
||||
// adjust the microsends field
|
||||
if ( s > 0 ) tv->tv_usec -= ( s * 1000000 ) ;
|
||||
|
||||
// increment the seconds field
|
||||
tv->tv_sec += s ;
|
||||
|
||||
return ;
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
|
||||
// $Id: app_conference.h,v 1.11 2005/12/16 22:31:58 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_CONF_H
|
||||
#define _ASTERISK_CONF_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* asterisk includes */
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/frame.h>
|
||||
#include <asterisk/manager.h>
|
||||
#include <asterisk/dsp.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/cli.h>
|
||||
|
||||
/* standard includes */
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#if (SILDET == 2)
|
||||
#include "libspeex/speex_preprocess.h"
|
||||
#endif
|
||||
|
||||
//
|
||||
// app_conference defines
|
||||
//
|
||||
|
||||
// debug logging level
|
||||
|
||||
// LOG_NOTICE for debugging, LOG_DEBUG for production
|
||||
#ifdef APP_CONFERENCE_DEBUG
|
||||
#define AST_CONF_DEBUG LOG_NOTICE
|
||||
#else
|
||||
#define AST_CONF_DEBUG LOG_DEBUG
|
||||
#endif
|
||||
|
||||
//
|
||||
// feature defines
|
||||
//
|
||||
|
||||
// number of times the last non-silent frame should be
|
||||
// repeated after silence starts
|
||||
//#define AST_CONF_CACHE_LAST_FRAME 1
|
||||
|
||||
//
|
||||
// debug defines
|
||||
//
|
||||
|
||||
#define DEBUG_USE_TIMELOG
|
||||
|
||||
#define DEBUG_FRAME_TIMESTAMPS
|
||||
|
||||
// #define DEBUG_OUTPUT_PCM
|
||||
|
||||
//
|
||||
// !!! THESE CONSTANTS SHOULD BE CLEANED UP AND CLARIFIED !!!
|
||||
//
|
||||
|
||||
//
|
||||
// sample information for AST_FORMAT_SLINEAR format
|
||||
//
|
||||
|
||||
#define AST_CONF_SAMPLE_RATE 8000
|
||||
#define AST_CONF_SAMPLE_SIZE 16
|
||||
#define AST_CONF_FRAME_INTERVAL 20
|
||||
|
||||
//
|
||||
// so, since we cycle approximately every 20ms,
|
||||
// we can compute the following values:
|
||||
//
|
||||
// 160 samples per 20 ms frame -or-
|
||||
// ( 8000 samples-per-second * ( 20 ms / 1000 ms-per-second ) ) = 160 samples
|
||||
//
|
||||
// 320 bytes ( 2560 bits ) of data 20 ms frame -or-
|
||||
// ( 160 samples * 16 bits-per-sample / 8 bits-per-byte ) = 320 bytes
|
||||
//
|
||||
|
||||
// 160 samples 16-bit signed linear
|
||||
#define AST_CONF_BLOCK_SAMPLES 160
|
||||
|
||||
// 2 bytes per sample ( i.e. 16-bit )
|
||||
#define AST_CONF_BYTES_PER_SAMPLE 2
|
||||
|
||||
// 320 bytes for each 160 sample frame of 16-bit audio
|
||||
#define AST_CONF_FRAME_DATA_SIZE 320
|
||||
|
||||
// 1000 ms-per-second / 20 ms-per-frame = 50 frames-per-second
|
||||
#define AST_CONF_FRAMES_PER_SECOND ( 1000 / AST_CONF_FRAME_INTERVAL )
|
||||
|
||||
|
||||
//
|
||||
// buffer and queue values
|
||||
//
|
||||
|
||||
// account for friendly offset when allocating buffer for frame
|
||||
#define AST_CONF_BUFFER_SIZE ( AST_CONF_FRAME_DATA_SIZE + AST_FRIENDLY_OFFSET )
|
||||
|
||||
// maximum number of frames queued per member
|
||||
#define AST_CONF_MAX_QUEUE 100
|
||||
|
||||
// minimum number of frames queued per member
|
||||
#define AST_CONF_MIN_QUEUE 0
|
||||
|
||||
// number of queued frames before we start dropping
|
||||
#define AST_CONF_QUEUE_DROP_THRESHOLD 4
|
||||
|
||||
// number of milliseconds between frame drops
|
||||
#define AST_CONF_QUEUE_DROP_TIME_LIMIT 750
|
||||
|
||||
//
|
||||
// timer and sleep values
|
||||
//
|
||||
|
||||
// milliseconds we're willing to wait for a channel
|
||||
// event before we check for outgoing frames
|
||||
#define AST_CONF_WAITFOR_LATENCY 40
|
||||
|
||||
// milliseconds to sleep before trying to process frames
|
||||
#define AST_CONF_CONFERENCE_SLEEP 40
|
||||
|
||||
// milliseconds to wait between state notification updates
|
||||
#define AST_CONF_NOTIFICATION_SLEEP 500
|
||||
|
||||
//
|
||||
// warning threshold values
|
||||
//
|
||||
|
||||
// number of frames behind before warning
|
||||
#define AST_CONF_OUTGOING_FRAMES_WARN 50
|
||||
|
||||
// number of milliseconds off AST_CONF_FRAME_INTERVAL before warning
|
||||
#define AST_CONF_INTERVAL_WARNING 1000
|
||||
|
||||
//
|
||||
// silence detection values
|
||||
//
|
||||
|
||||
// toggle silence detection
|
||||
#define ENABLE_SILENCE_DETECTION 1
|
||||
|
||||
// silence threshold
|
||||
#define AST_CONF_SILENCE_THRESHOLD 128
|
||||
|
||||
// speech tail (delay before dropping silent frames, in ms.
|
||||
// #define AST_CONF_SPEECH_TAIL 180
|
||||
|
||||
// number of frames to ignore speex_preprocess() after speech detected
|
||||
#define AST_CONF_SKIP_SPEEX_PREPROCESS 20
|
||||
|
||||
// our speex probability values
|
||||
#define AST_CONF_PROB_START 0.05
|
||||
#define AST_CONF_PROB_CONTINUE 0.02
|
||||
|
||||
//
|
||||
// format translation values
|
||||
//
|
||||
|
||||
// AST_FORMAT_MAX_AUDIO is 1 << 15, so we support 0..15
|
||||
#define AC_SUPPORTED_FORMATS 16
|
||||
#define AC_SLINEAR_INDEX 6
|
||||
|
||||
//
|
||||
// app_conference functions
|
||||
//
|
||||
|
||||
// main module function
|
||||
int app_conference_main( struct ast_channel* chan, void* data ) ;
|
||||
|
||||
// utility functions
|
||||
long usecdiff( struct timeval* timeA, struct timeval* timeB ) ;
|
||||
void add_milliseconds( struct timeval* tv, long ms ) ;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
|
||||
// $Id: cli.c,v 1.6 2005/12/21 21:34:50 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
//
|
||||
// debug functions
|
||||
//
|
||||
|
||||
static char conference_debug_usage[] =
|
||||
"usage: conference debug <conference_name> [ on | off ]\n"
|
||||
" enable debugging for a conference\n"
|
||||
;
|
||||
|
||||
static struct ast_cli_entry cli_debug = {
|
||||
{ "conference", "debug", NULL },
|
||||
conference_debug,
|
||||
"enable debugging for a conference",
|
||||
conference_debug_usage
|
||||
} ;
|
||||
|
||||
|
||||
int conference_debug( int fd, int argc, char *argv[] )
|
||||
{
|
||||
if ( argc < 3 )
|
||||
return RESULT_SHOWUSAGE ;
|
||||
|
||||
// get the conference name
|
||||
const char* name = argv[2] ;
|
||||
|
||||
// get the new state
|
||||
int state = 0 ;
|
||||
|
||||
if ( argc == 3 )
|
||||
{
|
||||
// no state specified, so toggle it
|
||||
state = -1 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( strncasecmp( name, "YES", 4 ) == 0 )
|
||||
state = 1 ;
|
||||
else if ( strncasecmp( name, "NO", 3 ) == 0 )
|
||||
state = 0 ;
|
||||
else
|
||||
return RESULT_SHOWUSAGE ;
|
||||
}
|
||||
|
||||
int new_state = set_conference_debugging( name, state ) ;
|
||||
|
||||
if ( new_state == 1 )
|
||||
{
|
||||
ast_cli( fd, "enabled conference debugging, name => %s, new_state => %d\n",
|
||||
name, new_state ) ;
|
||||
}
|
||||
else if ( new_state == 0 )
|
||||
{
|
||||
ast_cli( fd, "disabled conference debugging, name => %s, new_state => %d\n",
|
||||
name, new_state ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
// error setting state
|
||||
ast_cli( fd, "\nunable to set debugging state, name => %s\n\n", name ) ;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS ;
|
||||
}
|
||||
|
||||
//
|
||||
// stats functions
|
||||
//
|
||||
|
||||
static char conference_show_stats_usage[] =
|
||||
"usage: conference show stats\n"
|
||||
" display stats for active conferences.\n"
|
||||
;
|
||||
|
||||
static struct ast_cli_entry cli_show_stats = {
|
||||
{ "conference", "show", "stats", NULL },
|
||||
conference_show_stats,
|
||||
"show conference stats",
|
||||
conference_show_stats_usage
|
||||
} ;
|
||||
|
||||
int conference_show_stats( int fd, int argc, char *argv[] )
|
||||
{
|
||||
if ( argc < 3 )
|
||||
return RESULT_SHOWUSAGE ;
|
||||
|
||||
// get count of active conferences
|
||||
int count = get_conference_count() ;
|
||||
|
||||
ast_cli( fd, "\n\nCONFERENCE STATS, ACTIVE( %d )\n\n", count ) ;
|
||||
|
||||
// if zero, go no further
|
||||
if ( count <= 0 )
|
||||
return RESULT_SUCCESS ;
|
||||
|
||||
//
|
||||
// get the conference stats
|
||||
//
|
||||
|
||||
// array of stats structs
|
||||
ast_conference_stats stats[ count ] ;
|
||||
|
||||
// get stats structs
|
||||
count = get_conference_stats( stats, count ) ;
|
||||
|
||||
// make sure we were able to fetch some
|
||||
if ( count <= 0 )
|
||||
{
|
||||
ast_cli( fd, "!!! error fetching conference stats, available => %d !!!\n", count ) ;
|
||||
return RESULT_SUCCESS ;
|
||||
}
|
||||
|
||||
//
|
||||
// output the conference stats
|
||||
//
|
||||
|
||||
// output header
|
||||
ast_cli( fd, "%-20.20s %-40.40s %-40.40s\n", "Name", "Connection Type", "Member Type" ) ;
|
||||
ast_cli( fd, "%-20.20s %-40.40s %-40.40s\n", "----", "---------------", "-----------" ) ;
|
||||
|
||||
ast_conference_stats* s = NULL ;
|
||||
|
||||
char ct[64] ;
|
||||
char mt[64] ;
|
||||
|
||||
int i;
|
||||
for ( i = 0 ; i < count ; ++i )
|
||||
{
|
||||
s = &(stats[i]) ;
|
||||
|
||||
// format connection type
|
||||
snprintf( ct, 40, "phone( %d ), iax( %d ), sip( %d )",
|
||||
s->phone, s->iaxclient, s->sip ) ;
|
||||
|
||||
// format memeber type
|
||||
snprintf( mt, 40, "moderators( %d ), listeners( %d )",
|
||||
s->moderators, s->listeners ) ;
|
||||
|
||||
// output this conferences stats
|
||||
ast_cli( fd, "%-20.20s %-40.40s %-40.40s\n", (char*)( &(s->name) ), ct, mt ) ;
|
||||
}
|
||||
|
||||
ast_cli( fd, "\n" ) ;
|
||||
|
||||
//
|
||||
// drill down to specific stats
|
||||
//
|
||||
|
||||
if ( argc == 4 )
|
||||
{
|
||||
// show stats for a particular conference
|
||||
conference_show_stats_name( fd, argv[3] ) ;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS ;
|
||||
}
|
||||
|
||||
int conference_show_stats_name( int fd, const char* name )
|
||||
{
|
||||
// not implemented yet
|
||||
return RESULT_SUCCESS ;
|
||||
}
|
||||
|
||||
//
|
||||
// play sound
|
||||
//
|
||||
|
||||
static char conference_play_sound_usage[] =
|
||||
"usage: conference play sound <channel-id> <sound-file> [mute]\n"
|
||||
" play sound <sound-file> to conference member <channel-id>.\n"
|
||||
" mute the channel if 'mute' is present.\n"
|
||||
;
|
||||
|
||||
static struct ast_cli_entry cli_play_sound = {
|
||||
{ "conference", "play", "sound", NULL },
|
||||
conference_play_sound,
|
||||
"play a sound to a conference member",
|
||||
conference_play_sound_usage
|
||||
} ;
|
||||
|
||||
int conference_play_sound( int fd, int argc, char *argv[] )
|
||||
{
|
||||
char *channel, *file;
|
||||
int mute = 0;
|
||||
struct ast_conf_member *member;
|
||||
struct ast_conf_soundq *newsound;
|
||||
struct ast_conf_soundq **q;
|
||||
|
||||
if ( argc < 5 )
|
||||
return RESULT_SHOWUSAGE ;
|
||||
|
||||
channel = argv[3];
|
||||
file = argv[4];
|
||||
|
||||
if(argc > 5 && !strcmp(argv[5], "mute"))
|
||||
mute = 1;
|
||||
|
||||
|
||||
member = find_member(channel, 1);
|
||||
if(!member) {
|
||||
ast_cli(fd, "Member %s not found\n", channel);
|
||||
return RESULT_FAILURE;
|
||||
}
|
||||
|
||||
newsound = calloc(1,sizeof(struct ast_conf_soundq));
|
||||
newsound->stream = ast_openstream(member->chan, file, NULL);
|
||||
if(!newsound->stream) {
|
||||
ast_cli(fd, "Sound %s not found\n", file);
|
||||
free(newsound);
|
||||
ast_mutex_unlock(&member->lock);
|
||||
return RESULT_FAILURE;
|
||||
}
|
||||
member->chan->stream = NULL;
|
||||
|
||||
newsound->muted = mute;
|
||||
ast_copy_string(newsound->name, file, sizeof(newsound->name));
|
||||
|
||||
// append sound to the end of the list.
|
||||
for(q=&member->soundq; *q; q = &((*q)->next)) ;;
|
||||
|
||||
*q = newsound;
|
||||
|
||||
ast_mutex_unlock(&member->lock);
|
||||
|
||||
ast_cli( fd, "Playing sound %s to member %s %s\n",
|
||||
file, channel, mute ? "with mute" : "");
|
||||
|
||||
|
||||
return RESULT_SUCCESS ;
|
||||
}
|
||||
|
||||
//
|
||||
// stop sounds
|
||||
//
|
||||
|
||||
static char conference_stop_sounds_usage[] =
|
||||
"usage: conference stop sounds <channel-id>\n"
|
||||
" stop sounds for conference member <channel-id>.\n"
|
||||
;
|
||||
|
||||
static struct ast_cli_entry cli_stop_sounds = {
|
||||
{ "conference", "stop", "sounds", NULL },
|
||||
conference_stop_sounds,
|
||||
"stop sounds for a conference member",
|
||||
conference_stop_sounds_usage
|
||||
} ;
|
||||
|
||||
int conference_stop_sounds( int fd, int argc, char *argv[] )
|
||||
{
|
||||
char *channel;
|
||||
struct ast_conf_member *member;
|
||||
struct ast_conf_soundq *sound;
|
||||
struct ast_conf_soundq *next;
|
||||
|
||||
if ( argc < 4 )
|
||||
return RESULT_SHOWUSAGE ;
|
||||
|
||||
channel = argv[3];
|
||||
|
||||
member = find_member(channel, 1);
|
||||
if(!member) {
|
||||
ast_cli(fd, "Member %s not found\n", channel);
|
||||
return RESULT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// clear all sounds
|
||||
sound = member->soundq;
|
||||
member->soundq = NULL;
|
||||
|
||||
while(sound) {
|
||||
next = sound->next;
|
||||
ast_closestream(sound->stream);
|
||||
free(sound);
|
||||
sound = next;
|
||||
}
|
||||
|
||||
// reset write format, since we're done playing the sound
|
||||
if ( ast_set_write_format( member->chan, member->write_format ) < 0 )
|
||||
{
|
||||
ast_log( LOG_ERROR, "unable to set write format to %d\n",
|
||||
member->write_format ) ;
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&member->lock);
|
||||
|
||||
ast_cli( fd, "Stopped sounds to member %s\n", channel);
|
||||
|
||||
|
||||
return RESULT_SUCCESS ;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// cli initialization function
|
||||
//
|
||||
|
||||
void register_conference_cli( void )
|
||||
{
|
||||
ast_cli_register( &cli_debug ) ;
|
||||
ast_cli_register( &cli_show_stats ) ;
|
||||
ast_cli_register( &cli_play_sound ) ;
|
||||
ast_cli_register( &cli_stop_sounds ) ;
|
||||
}
|
||||
|
||||
void unregister_conference_cli( void )
|
||||
{
|
||||
ast_cli_unregister( &cli_debug ) ;
|
||||
ast_cli_unregister( &cli_show_stats ) ;
|
||||
ast_cli_unregister( &cli_play_sound ) ;
|
||||
ast_cli_unregister( &cli_stop_sounds ) ;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
// $Id: cli.h,v 1.3 2005/10/26 21:02:07 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _APP_CONF_CLI_H
|
||||
#define _APP_CONF_CLI_H
|
||||
|
||||
//
|
||||
// includes
|
||||
//
|
||||
|
||||
#include "app_conference.h"
|
||||
#include "common.h"
|
||||
|
||||
//
|
||||
// function declarations
|
||||
//
|
||||
|
||||
int conference_show_stats( int fd, int argc, char *argv[] ) ;
|
||||
int conference_show_stats_name( int fd, const char* name ) ;
|
||||
|
||||
|
||||
int conference_debug( int fd, int argc, char *argv[] ) ;
|
||||
int conference_no_debug( int fd, int argc, char *argv[] ) ;
|
||||
|
||||
int conference_play_sound( int fd, int argc, char *argv[] ) ;
|
||||
int conference_stop_sounds( int fd, int argc, char *argv[] ) ;
|
||||
|
||||
void register_conference_cli( void ) ;
|
||||
void unregister_conference_cli( void ) ;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
// $Id: common.h,v 1.3 2005/10/26 21:02:07 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _APP_CONF_COMMON_H
|
||||
#define _APP_CONF_COMMON_H
|
||||
|
||||
// typedef includes
|
||||
#include "conf_frame.h"
|
||||
|
||||
// function includesee
|
||||
#include "conference.h"
|
||||
#include "member.h"
|
||||
#include "frame.h"
|
||||
#include "cli.h"
|
||||
|
||||
|
||||
/* Utility functions */
|
||||
|
||||
/* LOG the time taken to execute a function (like lock acquisition */
|
||||
#if 1
|
||||
#define TIMELOG(func,min,message) \
|
||||
do { \
|
||||
struct timeval t1, t2; \
|
||||
int diff; \
|
||||
gettimeofday(&t1,NULL); \
|
||||
func; \
|
||||
gettimeofday(&t2,NULL); \
|
||||
if((diff = usecdiff(&t2, &t1)) > min) \
|
||||
ast_log( AST_CONF_DEBUG, "TimeLog: %s: %d ms\n", message, diff); \
|
||||
} while (0)
|
||||
#else
|
||||
#define TIMELOG(func,min,message) func
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
// $Id: conf_frame.h,v 1.3 2005/10/26 21:02:07 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _APP_CONF_STRUCTS_H
|
||||
#define _APP_CONF_STRUCTS_H
|
||||
|
||||
//
|
||||
// includes
|
||||
//
|
||||
|
||||
#include "app_conference.h"
|
||||
#include "common.h"
|
||||
|
||||
//
|
||||
// struct declarations
|
||||
//
|
||||
|
||||
typedef struct conf_frame
|
||||
{
|
||||
// frame audio data
|
||||
struct ast_frame* fr ;
|
||||
|
||||
// array of converted versions for listeners
|
||||
struct ast_frame* converted[ AC_SUPPORTED_FORMATS ] ;
|
||||
|
||||
// pointer to the frame's owner
|
||||
struct ast_conf_member* member ; // who sent this frame
|
||||
|
||||
// frame meta data
|
||||
// struct timeval timestamp ;
|
||||
// unsigned long cycleid ;
|
||||
// int priority ;
|
||||
|
||||
// linked-list pointers
|
||||
struct conf_frame* next ;
|
||||
struct conf_frame* prev ;
|
||||
|
||||
// should this frame be preserved
|
||||
short static_frame ;
|
||||
|
||||
// pointer to mixing buffer
|
||||
char* mixed_buffer ;
|
||||
} conf_frame ;
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,121 @@
|
||||
|
||||
// $Id: conference.h,v 1.6 2005/10/26 21:02:07 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _APP_CONF_CONFERENCE_H
|
||||
#define _APP_CONF_CONFERENCE_H
|
||||
|
||||
//
|
||||
// includes
|
||||
//
|
||||
|
||||
#include "app_conference.h"
|
||||
#include "common.h"
|
||||
|
||||
//
|
||||
// struct declarations
|
||||
//
|
||||
|
||||
typedef struct ast_conference_stats
|
||||
{
|
||||
// conference name ( copied for ease of use )
|
||||
char name[128] ;
|
||||
|
||||
// type of connection
|
||||
unsigned short phone ;
|
||||
unsigned short iaxclient ;
|
||||
unsigned short sip ;
|
||||
|
||||
// type of users
|
||||
unsigned short moderators ;
|
||||
unsigned short listeners ;
|
||||
|
||||
// accounting data
|
||||
unsigned long frames_in ;
|
||||
unsigned long frames_out ;
|
||||
unsigned long frames_mixed ;
|
||||
|
||||
struct timeval time_entered ;
|
||||
|
||||
} ast_conference_stats ;
|
||||
|
||||
|
||||
struct ast_conference
|
||||
{
|
||||
// conference name
|
||||
char name[128] ;
|
||||
|
||||
// single-linked list of members in conference
|
||||
struct ast_conf_member* memberlist ;
|
||||
int membercount ;
|
||||
|
||||
// conference thread id
|
||||
pthread_t conference_thread ;
|
||||
|
||||
// conference data mutex
|
||||
ast_mutex_t lock ;
|
||||
|
||||
// pointer to next conference in single-linked list
|
||||
struct ast_conference* next ;
|
||||
|
||||
// pointer to translation paths
|
||||
struct ast_trans_pvt* from_slinear_paths[ AC_SUPPORTED_FORMATS ] ;
|
||||
|
||||
// conference stats
|
||||
ast_conference_stats stats ;
|
||||
|
||||
// keep track of current delivery time
|
||||
struct timeval delivery_time ;
|
||||
|
||||
// 1 => on, 0 => off
|
||||
short debug_flag ;
|
||||
} ;
|
||||
|
||||
//
|
||||
// function declarations
|
||||
//
|
||||
|
||||
struct ast_conference* start_conference( struct ast_conf_member* member ) ;
|
||||
|
||||
void conference_exec( struct ast_conference* conf ) ;
|
||||
|
||||
struct ast_conference* find_conf( const char* name ) ;
|
||||
struct ast_conference* create_conf( char* name, struct ast_conf_member* member ) ;
|
||||
void remove_conf( struct ast_conference* conf ) ;
|
||||
|
||||
// find a particular member, locking if requested.
|
||||
struct ast_conf_member *find_member ( char *chan, int lock) ;
|
||||
|
||||
|
||||
int queue_frame_for_listener( struct ast_conference* conf, struct ast_conf_member* member, conf_frame* frame ) ;
|
||||
int queue_frame_for_speaker( struct ast_conference* conf, struct ast_conf_member* member, conf_frame* frame ) ;
|
||||
int queue_silent_frame( struct ast_conference* conf, struct ast_conf_member* member ) ;
|
||||
|
||||
void add_member( struct ast_conf_member* member, struct ast_conference* conf ) ;
|
||||
int remove_member( struct ast_conf_member* member, struct ast_conference* conf ) ;
|
||||
int count_member( struct ast_conf_member* member, struct ast_conference* conf, short add_member ) ;
|
||||
|
||||
// called by app_confernce.c:load_module()
|
||||
void init_conference( void ) ;
|
||||
|
||||
int get_conference_count( void ) ;
|
||||
int get_conference_stats( ast_conference_stats* stats, int requested ) ;
|
||||
int get_conference_stats_by_name( ast_conference_stats* stats, const char* name ) ;
|
||||
|
||||
int set_conference_debugging( const char* name, int state ) ;
|
||||
|
||||
#endif
|
||||
Binary file not shown.
@@ -0,0 +1,614 @@
|
||||
|
||||
// $Id: frame.c,v 1.7 2005/10/27 17:53:35 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "frame.h"
|
||||
|
||||
conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count )
|
||||
{
|
||||
if ( frames_in == NULL )
|
||||
return NULL ;
|
||||
|
||||
conf_frame* frames_out = NULL ;
|
||||
|
||||
if ( speaker_count > 1 )
|
||||
{
|
||||
if ( speaker_count == 2 && listener_count == 0 )
|
||||
{
|
||||
// optimize here also?
|
||||
frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
// mix spoken frames for sending
|
||||
// ( note: this call also releases us from free'ing spoken_frames )
|
||||
frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ;
|
||||
}
|
||||
}
|
||||
else if ( speaker_count == 1 )
|
||||
{
|
||||
// pass-through frames
|
||||
frames_out = mix_single_speaker( frames_in ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no frames to send, leave frames_out null
|
||||
}
|
||||
|
||||
return frames_out ;
|
||||
}
|
||||
|
||||
conf_frame* mix_single_speaker( conf_frame* frames_in )
|
||||
{
|
||||
#ifdef APP_CONFERENCE_DEBUG
|
||||
// ast_log( AST_CONF_DEBUG, "returning single spoken frame\n" ) ;
|
||||
|
||||
//
|
||||
// check input
|
||||
//
|
||||
|
||||
if ( frames_in == NULL )
|
||||
{
|
||||
ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
if ( frames_in->fr == NULL )
|
||||
{
|
||||
ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null data\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
if ( frames_in->member == NULL )
|
||||
{
|
||||
ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null member\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
#endif // APP_CONFERENCE_DEBUG
|
||||
|
||||
//
|
||||
// 'mix' the frame
|
||||
//
|
||||
|
||||
// copy orignal frame to converted array so listeners don't need to re-encode it
|
||||
frames_in->converted[ frames_in->member->read_format_index ] = ast_frdup( frames_in->fr ) ;
|
||||
|
||||
// convert frame to slinear, if we have a path
|
||||
frames_in->fr = convert_frame_to_slinear(
|
||||
frames_in->member->to_slinear,
|
||||
frames_in->fr
|
||||
) ;
|
||||
|
||||
// set the frame's member to null ( i.e. all listeners )
|
||||
frames_in->member = NULL ;
|
||||
|
||||
return frames_in ;
|
||||
}
|
||||
|
||||
// {
|
||||
//
|
||||
// a little optimization for testing only:
|
||||
// when two speakers ( of the same type ) and no listeners
|
||||
// are in a conference, we just swamp the frame's member pointers
|
||||
//
|
||||
/*
|
||||
if (
|
||||
listeners == 0
|
||||
&& speakers == 2
|
||||
&& cf_spokenFrames->member->read_format == cf_spokenFrames->next->member->write_format
|
||||
&& cf_spokenFrames->member->write_format == cf_spokenFrames->next->member->read_format
|
||||
)
|
||||
{
|
||||
struct ast_conf_member* m = NULL ;
|
||||
m = cf_spokenFrames->member ;
|
||||
cf_spokenFrames->member = cf_spokenFrames->next->member ;
|
||||
cf_spokenFrames->next->member = m ;
|
||||
return cf_spokenFrames ;
|
||||
}
|
||||
*/
|
||||
// }
|
||||
|
||||
void set_conf_frame_delivery( conf_frame* frame, struct timeval time )
|
||||
{
|
||||
for ( ; frame != NULL ; frame = frame->next )
|
||||
{
|
||||
if ( frame->fr != NULL )
|
||||
{
|
||||
// copy passed timeval to frame's delivery timeval
|
||||
frame->fr->delivery = time ;
|
||||
}
|
||||
}
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
conf_frame* mix_multiple_speakers(
|
||||
conf_frame* frames_in,
|
||||
int speakers,
|
||||
int listeners
|
||||
)
|
||||
{
|
||||
#ifdef APP_CONFERENCE_DEBUG
|
||||
//
|
||||
// check input
|
||||
//
|
||||
|
||||
// no frames to mix
|
||||
if ( ( frames_in == NULL ) || ( frames_in->fr == NULL ) )
|
||||
{
|
||||
ast_log( AST_CONF_DEBUG, "passed spoken frame list was NULL\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
// if less than two speakers, then no frames to mix
|
||||
if ( speakers < 2 )
|
||||
{
|
||||
ast_log( AST_CONF_DEBUG, "mix_multiple_speakers() called with less than two speakers\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
#endif // APP_CONFERENCE_DEBUG
|
||||
|
||||
//
|
||||
// at this point we know that there is more than one frame,
|
||||
// and that the frames need to be converted to pcm to be mixed
|
||||
//
|
||||
// now, if there are only two frames and two members,
|
||||
// we can swap them. ( but we'll get to that later. )
|
||||
//
|
||||
|
||||
//
|
||||
// loop through the spoken frames, making a list of spoken members,
|
||||
// and converting gsm frames to slinear frames so we can mix them.
|
||||
//
|
||||
|
||||
// pointer to the spoken frames list
|
||||
conf_frame* cf_spoken = frames_in ;
|
||||
|
||||
// pointer to the new list of mixed frames
|
||||
conf_frame* cf_sendFrames = NULL ;
|
||||
|
||||
while ( cf_spoken != NULL )
|
||||
{
|
||||
//
|
||||
// while we're looping through the spoken frames, we'll
|
||||
// convert the frame to a format suitable for mixing
|
||||
//
|
||||
// if the frame fails to convert, drop it and treat
|
||||
// the speaking member like a listener by not adding
|
||||
// them to the cf_sendFrames list
|
||||
//
|
||||
|
||||
if ( cf_spoken->member == NULL )
|
||||
{
|
||||
ast_log( LOG_WARNING, "unable to determine frame member\n" ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ast_log( AST_CONF_DEBUG, "converting frame to slinear, channel => %s\n", cf_spoken->member->channel_name ) ;
|
||||
cf_spoken->fr = convert_frame_to_slinear(
|
||||
cf_spoken->member->to_slinear,
|
||||
cf_spoken->fr
|
||||
) ;
|
||||
|
||||
if ( cf_spoken->fr == NULL )
|
||||
{
|
||||
ast_log( LOG_WARNING, "unable to convert frame to slinear\n" ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new conf frame with last frame as 'next'
|
||||
cf_sendFrames = create_conf_frame( cf_spoken->member, cf_sendFrames, NULL ) ;
|
||||
}
|
||||
}
|
||||
|
||||
// point to the next spoken frame
|
||||
cf_spoken = cf_spoken->next ;
|
||||
}
|
||||
|
||||
// if necessary, add a frame with a null member pointer.
|
||||
// this frame will hold the audio mixed for all listeners
|
||||
if ( listeners > 0 )
|
||||
{
|
||||
cf_sendFrames = create_conf_frame( NULL, cf_sendFrames, NULL ) ;
|
||||
}
|
||||
|
||||
//
|
||||
// mix the audio
|
||||
//
|
||||
|
||||
// convenience pointer that skips over the friendly offset
|
||||
char* cp_listenerData ;
|
||||
|
||||
// pointer to the send frames list
|
||||
conf_frame* cf_send = NULL ;
|
||||
|
||||
for ( cf_send = cf_sendFrames ; cf_send != NULL ; cf_send = cf_send->next )
|
||||
{
|
||||
// allocate a mix buffer which fill large enough memory to
|
||||
// hold a frame, and reset it's memory so we don't get noise
|
||||
char* cp_listenerBuffer = malloc( AST_CONF_BUFFER_SIZE ) ;
|
||||
memset( cp_listenerBuffer, 0x0, AST_CONF_BUFFER_SIZE ) ;
|
||||
|
||||
// point past the friendly offset right to the data
|
||||
cp_listenerData = cp_listenerBuffer + AST_FRIENDLY_OFFSET ;
|
||||
|
||||
// reset the spoken list pointer
|
||||
cf_spoken = frames_in ;
|
||||
|
||||
// really mix the audio
|
||||
for ( ; cf_spoken != NULL ; cf_spoken = cf_spoken->next )
|
||||
{
|
||||
//
|
||||
// if the members are equal, and they
|
||||
// are not null, do not mix them.
|
||||
//
|
||||
if (
|
||||
( cf_send->member == cf_spoken->member )
|
||||
&& ( cf_send->member != NULL )
|
||||
)
|
||||
{
|
||||
// don't mix this frame
|
||||
}
|
||||
else if ( cf_spoken->fr == NULL )
|
||||
{
|
||||
ast_log( LOG_WARNING, "unable to mix conf_frame with null ast_frame\n" ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
// mix the new frame in with the existing buffer
|
||||
mix_slinear_frames( cp_listenerData, (char*)( cf_spoken->fr->data ), cf_spoken->fr->samples ) ;
|
||||
}
|
||||
}
|
||||
|
||||
// copy a pointer to the frame data to the conf_frame
|
||||
cf_send->mixed_buffer = cp_listenerData ;
|
||||
}
|
||||
|
||||
//
|
||||
// copy the mixed buffer to a new frame
|
||||
//
|
||||
|
||||
// reset the send list pointer
|
||||
cf_send = cf_sendFrames ;
|
||||
|
||||
while ( cf_send != NULL )
|
||||
{
|
||||
cf_send->fr = create_slinear_frame( cf_send->mixed_buffer ) ;
|
||||
cf_send = cf_send->next ;
|
||||
}
|
||||
|
||||
//
|
||||
// clean up the spoken frames we were passed
|
||||
// ( caller will only be responsible for free'ing returns frames )
|
||||
//
|
||||
|
||||
// reset the spoken list pointer
|
||||
cf_spoken = frames_in ;
|
||||
|
||||
while ( cf_spoken != NULL )
|
||||
{
|
||||
// delete the frame
|
||||
cf_spoken = delete_conf_frame( cf_spoken ) ;
|
||||
}
|
||||
|
||||
// return the list of frames for sending
|
||||
return cf_sendFrames ;
|
||||
}
|
||||
|
||||
|
||||
struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr )
|
||||
{
|
||||
// check for null frame
|
||||
if ( fr == NULL )
|
||||
{
|
||||
ast_log( LOG_ERROR, "unable to translate null frame to slinear\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
// we don't need to duplicate this frame since
|
||||
// the normal translation would free it anyway, so
|
||||
// we'll just pretend we free'd and malloc'd a new one.
|
||||
if ( fr->subclass == AST_FORMAT_SLINEAR )
|
||||
return fr ;
|
||||
|
||||
// check for null translator ( after we've checked that we need to translate )
|
||||
if ( trans == NULL )
|
||||
{
|
||||
// ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ;
|
||||
return fr ;
|
||||
}
|
||||
|
||||
// return the converted frame
|
||||
return convert_frame( trans, fr ) ;
|
||||
}
|
||||
|
||||
struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr )
|
||||
{
|
||||
// check for null translator ( after we've checked that we need to translate )
|
||||
if ( trans == NULL )
|
||||
{
|
||||
// ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ;
|
||||
return fr ;
|
||||
}
|
||||
|
||||
// check for null frame
|
||||
if ( fr == NULL )
|
||||
{
|
||||
ast_log( LOG_ERROR, "unable to translate null slinear frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
// if the frame is not slinear, return an error
|
||||
if ( fr->subclass != AST_FORMAT_SLINEAR )
|
||||
{
|
||||
ast_log( LOG_ERROR, "unable to translate non-slinear frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
// return the converted frame
|
||||
return convert_frame( trans, fr ) ;
|
||||
}
|
||||
|
||||
struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr )
|
||||
{
|
||||
if ( trans == NULL )
|
||||
{
|
||||
ast_log( LOG_WARNING, "unable to convert frame with null translator\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
if ( fr == NULL )
|
||||
{
|
||||
ast_log( LOG_WARNING, "unable to convert null frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
// convert the frame
|
||||
struct ast_frame* translated_frame = ast_translate( trans, fr, 1 ) ;
|
||||
|
||||
// check for errors
|
||||
if ( translated_frame == NULL )
|
||||
{
|
||||
ast_log( LOG_ERROR, "unable to translate frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
// return the translated frame
|
||||
return translated_frame ;
|
||||
}
|
||||
|
||||
conf_frame* delete_conf_frame( conf_frame* cf )
|
||||
{
|
||||
// check for null frames
|
||||
if ( cf == NULL )
|
||||
{
|
||||
ast_log( AST_CONF_DEBUG, "unable to delete null conf frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
// check for frame marked as static
|
||||
if ( cf->static_frame == 1 )
|
||||
return NULL ;
|
||||
|
||||
if ( cf->fr != NULL )
|
||||
{
|
||||
ast_frfree( cf->fr ) ;
|
||||
cf->fr = NULL ;
|
||||
}
|
||||
|
||||
// make sure converted frames are set to null
|
||||
int c;
|
||||
for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
|
||||
{
|
||||
if ( cf->converted[ c ] != NULL )
|
||||
{
|
||||
ast_frfree( cf->converted[ c ] ) ;
|
||||
cf->converted[ c ] = NULL ;
|
||||
}
|
||||
}
|
||||
|
||||
// get a pointer to the next frame
|
||||
// in the list so we can return it
|
||||
conf_frame* nf = cf->next ;
|
||||
|
||||
free( cf ) ;
|
||||
cf = NULL ;
|
||||
|
||||
return nf ;
|
||||
}
|
||||
|
||||
conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr )
|
||||
{
|
||||
// pointer to list of mixed frames
|
||||
conf_frame* cf = malloc( sizeof( struct conf_frame ) ) ;
|
||||
|
||||
if ( cf == NULL )
|
||||
{
|
||||
ast_log( LOG_ERROR, "unable to allocate memory for conf frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
//
|
||||
// init with some defaults
|
||||
//
|
||||
|
||||
// make sure converted frames are set to null
|
||||
// for ( int c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
|
||||
// {
|
||||
// cf->converted[ c ] = NULL ;
|
||||
// }
|
||||
|
||||
memset( (struct ast_frame*)( cf->converted ), 0x0, ( sizeof( struct ast_frame* ) * AC_SUPPORTED_FORMATS ) ) ;
|
||||
|
||||
cf->member = member ;
|
||||
// cf->priority = 0 ;
|
||||
|
||||
cf->prev = NULL ;
|
||||
cf->next = next ;
|
||||
|
||||
cf->static_frame = 0 ;
|
||||
|
||||
// establish relationship to 'next'
|
||||
if ( next != NULL ) next->prev = cf ;
|
||||
|
||||
// this holds the ast_frame pointer
|
||||
cf->fr = ( fr == NULL ) ? NULL : ast_frdup( ( struct ast_frame* )( fr ) ) ;
|
||||
|
||||
// this holds the temporu mix buffer
|
||||
cf->mixed_buffer = NULL ;
|
||||
|
||||
return cf ;
|
||||
}
|
||||
|
||||
conf_frame* copy_conf_frame( conf_frame* src )
|
||||
{
|
||||
//
|
||||
// check inputs
|
||||
//
|
||||
|
||||
if ( src == NULL )
|
||||
{
|
||||
ast_log( AST_CONF_DEBUG, "unable to copy null conf frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
//
|
||||
// copy the frame
|
||||
//
|
||||
|
||||
struct conf_frame *cfr = NULL ;
|
||||
|
||||
// create a new conf frame
|
||||
cfr = create_conf_frame( src->member, NULL, src->fr ) ;
|
||||
|
||||
if ( cfr == NULL )
|
||||
{
|
||||
ast_log( AST_CONF_DEBUG, "unable to create new conf frame for copy\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
return cfr ;
|
||||
}
|
||||
|
||||
//
|
||||
// slinear frame functions
|
||||
//
|
||||
|
||||
struct ast_frame* create_slinear_frame( char* data )
|
||||
{
|
||||
struct ast_frame* f ;
|
||||
|
||||
f = calloc( 1, sizeof( struct ast_frame ) ) ;
|
||||
if ( f == NULL )
|
||||
{
|
||||
ast_log( LOG_ERROR, "unable to allocate memory for slinear frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
f->frametype = AST_FRAME_VOICE ;
|
||||
f->subclass = AST_FORMAT_SLINEAR ;
|
||||
f->samples = AST_CONF_BLOCK_SAMPLES ;
|
||||
f->offset = AST_FRIENDLY_OFFSET ;
|
||||
f->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_DATA ;
|
||||
|
||||
f->datalen = AST_CONF_FRAME_DATA_SIZE ;
|
||||
f->data = data ;
|
||||
|
||||
f->src = NULL ;
|
||||
|
||||
return f ;
|
||||
}
|
||||
|
||||
void mix_slinear_frames( char *dst, const char *src, int samples )
|
||||
{
|
||||
if ( dst == NULL ) return ;
|
||||
if ( src == NULL ) return ;
|
||||
|
||||
int i, val ;
|
||||
|
||||
for ( i = 0 ; i < samples ; ++i )
|
||||
{
|
||||
val = ( (short*)dst )[i] + ( (short*)src )[i] ;
|
||||
|
||||
if ( val > 0x7fff )
|
||||
{
|
||||
( (short*)dst )[i] = 0x7fff - 1 ;
|
||||
continue ;
|
||||
}
|
||||
else if ( val < -0x7fff )
|
||||
{
|
||||
( (short*)dst )[i] = -0x7fff + 1 ;
|
||||
continue ;
|
||||
}
|
||||
else
|
||||
{
|
||||
( (short*)dst )[i] = val ;
|
||||
continue ;
|
||||
}
|
||||
}
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
//
|
||||
// silent frame functions
|
||||
//
|
||||
|
||||
conf_frame* get_silent_frame( void )
|
||||
{
|
||||
static conf_frame* static_silent_frame = NULL ;
|
||||
|
||||
// we'll let this leak until the application terminates
|
||||
if ( static_silent_frame == NULL )
|
||||
{
|
||||
// ast_log( AST_CONF_DEBUG, "creating cached silent frame\n" ) ;
|
||||
struct ast_frame* fr = get_silent_slinear_frame() ;
|
||||
|
||||
static_silent_frame = create_conf_frame( NULL, NULL, fr ) ;
|
||||
|
||||
if ( static_silent_frame == NULL )
|
||||
{
|
||||
ast_log( LOG_WARNING, "unable to create cached silent frame\n" ) ;
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
// init the 'converted' slinear silent frame
|
||||
static_silent_frame->converted[ AC_SLINEAR_INDEX ] = get_silent_slinear_frame() ;
|
||||
|
||||
// mark frame as static so it's not deleted
|
||||
static_silent_frame->static_frame = 1 ;
|
||||
}
|
||||
|
||||
return static_silent_frame ;
|
||||
}
|
||||
|
||||
struct ast_frame* get_silent_slinear_frame( void )
|
||||
{
|
||||
static struct ast_frame* f = NULL ;
|
||||
|
||||
// we'll let this leak until the application terminates
|
||||
if ( f == NULL )
|
||||
{
|
||||
char* data = malloc( AST_CONF_BUFFER_SIZE ) ;
|
||||
memset( data, 0x0, AST_CONF_BUFFER_SIZE ) ;
|
||||
f = create_slinear_frame( data ) ;
|
||||
}
|
||||
|
||||
return ast_frdup( f ) ;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
// $Id: frame.h,v 1.6 2005/10/26 21:02:07 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003, 2004 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _APP_CONF_FRAME_H
|
||||
#define _APP_CONF_FRAME_H
|
||||
|
||||
//
|
||||
// includes
|
||||
//
|
||||
|
||||
#include "app_conference.h"
|
||||
#include "common.h"
|
||||
|
||||
//
|
||||
// function declarations
|
||||
//
|
||||
|
||||
// mixing
|
||||
conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count ) ;
|
||||
|
||||
conf_frame* mix_multiple_speakers( conf_frame* frames_in, int speakers, int listeners ) ;
|
||||
conf_frame* mix_single_speaker( conf_frame* frames_in ) ;
|
||||
|
||||
// frame creation and deletion
|
||||
conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr ) ;
|
||||
conf_frame* delete_conf_frame( conf_frame* cf ) ;
|
||||
conf_frame* copy_conf_frame( conf_frame* src ) ;
|
||||
|
||||
// convert frame functions
|
||||
struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ;
|
||||
struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) ;
|
||||
struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr ) ;
|
||||
|
||||
// slinear frame functions
|
||||
struct ast_frame* create_slinear_frame( char* data ) ;
|
||||
void mix_slinear_frames( char* dst, const char* src, int samples ) ;
|
||||
|
||||
// silent frame functions
|
||||
conf_frame* get_silent_frame( void ) ;
|
||||
struct ast_frame* get_silent_slinear_frame( void ) ;
|
||||
|
||||
// set delivery timestamp for frames
|
||||
void set_conf_frame_delivery( conf_frame* frame, struct timeval time ) ;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
Õø\}PcÈ£P tÛ†§¬D#E)¤™î#àÑi(*ÕøEÿ3yÂBãF:W£Ì×2›w¢ÓÔWâåÔ¢6èÕøTþéyÁªu–ÃÆW¢I+‘sdWÁ󣩿øy¢®su»Õ÷k¿2yƒ©©áËc„tä9,,WÂÌk|UTWãu#’.1Õøcÿ}yQÒÅã(î é¬d£Úº\2Åï!•␍‚£QÕø\?;wÁÃaÔòñW‚nf�y¢.–7©FW‚•š©ó)Õ÷{?<y�踥¾:y‚ŒkmņWÃYk‹™¢,s�ņոk¿3WávÛ§'“WA«k5ËÍAó0”u*ï!¼eS␍ÕøM¾êy¡«ªcÍyâJ㫉Šw�qÇk
|
||||
°wÂ9uáÓÕø\3y¡C†§£Ò»"Xy�Ö�™¡wr:ªïA¬Ýk‹MÕøT¾©™ÁᜑVxï¡ä꙾,WâHr\ʧw¢6é°ó]Õ¸>úYW!àíµQënµSíB*ÜqU—U¡ºy˜×Õö²³>ÓÇX¦ŸWQŽDâ¿íÇmÈÊ!ï÷ûýö
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
/misc.c/1.1/Thu Mar 25 15:10:17 2004//
|
||||
/misc.h/1.1/Thu Mar 25 15:10:17 2004//
|
||||
/preprocess.c/1.4/Fri Mar 25 20:26:26 2005//
|
||||
/smallft.c/1.3/Fri Mar 25 20:26:26 2005//
|
||||
/smallft.h/1.2/Wed Jul 7 13:39:42 2004//
|
||||
/speex_preprocess.h/1.1/Thu Mar 25 15:10:17 2004//
|
||||
D
|
||||
@@ -0,0 +1 @@
|
||||
app_conference/libspeex
|
||||
@@ -0,0 +1 @@
|
||||
:pserver:anonymous@iaxclient.cvs.sourceforge.net:/cvsroot/iaxclient
|
||||
@@ -0,0 +1,145 @@
|
||||
/* Copyright (C) 2002 Jean-Marc Valin
|
||||
File: mics.c
|
||||
Various utility routines for Speex
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Xiph.org Foundation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "misc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef RELEASE
|
||||
void print_vec(float *vec, int len, char *name)
|
||||
{
|
||||
int i;
|
||||
printf ("%s ", name);
|
||||
for (i=0;i<len;i++)
|
||||
printf (" %f", vec[i]);
|
||||
printf ("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int be_int(unsigned int i)
|
||||
{
|
||||
unsigned int ret=i;
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
ret = i>>24;
|
||||
ret += (i>>8)&0x0000ff00;
|
||||
ret += (i<<8)&0x00ff0000;
|
||||
ret += (i<<24);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int le_int(unsigned int i)
|
||||
{
|
||||
unsigned int ret=i;
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
ret = i>>24;
|
||||
ret += (i>>8)&0x0000ff00;
|
||||
ret += (i<<8)&0x00ff0000;
|
||||
ret += (i<<24);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned short be_short(unsigned short s)
|
||||
{
|
||||
unsigned short ret=s;
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
ret = s>>8;
|
||||
ret += s<<8;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned short le_short(unsigned short s)
|
||||
{
|
||||
unsigned short ret=s;
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
ret = s>>8;
|
||||
ret += s<<8;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *speex_alloc (int size)
|
||||
{
|
||||
return calloc(size,1);
|
||||
}
|
||||
|
||||
void *speex_realloc (void *ptr, int size)
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
void speex_free (void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void *speex_move (void *dest, void *src, int n)
|
||||
{
|
||||
return memmove(dest,src,n);
|
||||
}
|
||||
|
||||
void speex_error(char *str)
|
||||
{
|
||||
fprintf (stderr, "Fatal error: %s\n", str);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void speex_warning(char *str)
|
||||
{
|
||||
fprintf (stderr, "warning: %s\n", str);
|
||||
}
|
||||
|
||||
void speex_warning_int(char *str, int val)
|
||||
{
|
||||
fprintf (stderr, "warning: %s %d\n", str, val);
|
||||
}
|
||||
|
||||
void speex_rand_vec(float std, spx_sig_t *data, int len)
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<len;i++)
|
||||
data[i]+=SIG_SCALING*3*std*((((float)rand())/RAND_MAX)-.5);
|
||||
}
|
||||
|
||||
float speex_rand(float std)
|
||||
{
|
||||
return 3*std*((((float)rand())/RAND_MAX)-.5);
|
||||
}
|
||||
|
||||
void _speex_putc(int ch, void *file)
|
||||
{
|
||||
FILE *f = (FILE *)file;
|
||||
fputc(ch, f);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/* Copyright (C) 2002 Jean-Marc Valin */
|
||||
/**
|
||||
@file misc.h
|
||||
@brief Various compatibility routines for Speex
|
||||
*/
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Xiph.org Foundation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
#ifndef VERSION
|
||||
#define VERSION "speex-1.1"
|
||||
#endif
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
|
||||
typedef signed short spx_word16_t;
|
||||
typedef signed int spx_word32_t;
|
||||
typedef spx_word32_t spx_mem_t;
|
||||
typedef spx_word16_t spx_coef_t;
|
||||
typedef spx_word16_t spx_lsp_t;
|
||||
typedef spx_word32_t spx_sig_t;
|
||||
|
||||
#define LPC_SCALING 8192.
|
||||
#define SIG_SCALING 16384
|
||||
#define LSP_SCALING 8192.
|
||||
|
||||
#define LPC_SHIFT 13
|
||||
#define SIG_SHIFT 14
|
||||
|
||||
#define PSHR(a,shift) (((a)+(1<<((shift)-1))) >> (shift))
|
||||
#define SHR(a,shift) ((a) >> (shift))
|
||||
#define SHL(a,shift) ((a) << (shift))
|
||||
|
||||
/* result fits in 16 bits */
|
||||
#define MULT16_16_16(a,b) (((short)(a))*(b))
|
||||
|
||||
#define MULT16_16(a,b) (((signed int)(a))*(b))
|
||||
#define MULT16_32_Q13(a,b) (((a)*((b)>>13)) + ((a)*((signed int)((b)&0x00001fff))>>13))
|
||||
#define MULT16_32_Q14(a,b) (((a)*((b)>>14)) + ((a)*((signed int)((b)&0x00003fff))>>14))
|
||||
#define MULT16_32_Q15(a,b) (((a)*((b)>>15)) + ((a)*((signed int)((b)&0x00007fff))>>15))
|
||||
|
||||
#define MULT16_16_Q13(a,b) (((signed int)(a))*(b)>>13)
|
||||
#define MULT16_16_Q14(a,b) (((signed int)(a))*(b)>>14)
|
||||
#define MULT16_16_Q15(a,b) (((signed int)(a))*(b)>>15)
|
||||
|
||||
#define MULT16_16_P14(a,b) ((8192+((signed int)(a))*(b))>>14)
|
||||
|
||||
|
||||
#define DIV32_16(a,b) (((signed int)(a))/(b))
|
||||
|
||||
#else
|
||||
|
||||
typedef float spx_mem_t;
|
||||
typedef float spx_coef_t;
|
||||
typedef float spx_lsp_t;
|
||||
typedef float spx_sig_t;
|
||||
typedef float spx_word16_t;
|
||||
typedef float spx_word32_t;
|
||||
|
||||
#define LPC_SCALING 1.
|
||||
#define SIG_SCALING 1.
|
||||
#define LSP_SCALING 1.
|
||||
|
||||
#define LPC_SHIFT 0
|
||||
#define SIG_SHIFT 0
|
||||
|
||||
#define PSHR(a,shift) (a)
|
||||
#define SHR(a,shift) (a)
|
||||
#define SHL(a,shift) (a)
|
||||
#define MULT16_16_16(a,b) ((a)*(b))
|
||||
#define MULT16_16(a,b) ((a)*(b))
|
||||
|
||||
#define MULT16_32_Q13(a,b) ((a)*(b))
|
||||
#define MULT16_32_Q14(a,b) ((a)*(b))
|
||||
#define MULT16_32_Q15(a,b) ((a)*(b))
|
||||
|
||||
#define MULT16_16_Q13(a,b) ((a)*(b))
|
||||
#define MULT16_16_Q14(a,b) ((a)*(b))
|
||||
#define MULT16_16_Q15(a,b) ((a)*(b))
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef RELEASE
|
||||
void print_vec(float *vec, int len, char *name);
|
||||
#endif
|
||||
|
||||
unsigned int be_int(unsigned int i);
|
||||
unsigned int le_int(unsigned int i);
|
||||
|
||||
|
||||
unsigned short be_short(unsigned short s);
|
||||
unsigned short le_short(unsigned short s);
|
||||
|
||||
/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_free */
|
||||
void *speex_alloc (int size);
|
||||
|
||||
/** Speex wrapper for realloc. To do your own dynamic allocation, all you need to do is replace this function, speex_alloc and speex_free */
|
||||
void *speex_realloc (void *ptr, int size);
|
||||
|
||||
/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_alloc */
|
||||
void speex_free (void *ptr);
|
||||
|
||||
/** Speex wrapper for mem_move */
|
||||
void *speex_move (void *dest, void *src, int n);
|
||||
|
||||
void speex_error(char *str);
|
||||
|
||||
void speex_warning(char *str);
|
||||
|
||||
void speex_warning_int(char *str, int val);
|
||||
|
||||
void speex_rand_vec(float std, spx_sig_t *data, int len);
|
||||
|
||||
float speex_rand(float std);
|
||||
|
||||
void _speex_putc(int ch, void *file);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
/********************************************************************
|
||||
* *
|
||||
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
|
||||
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
|
||||
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
|
||||
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
|
||||
* *
|
||||
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
|
||||
* by the XIPHOPHORUS Company http://www.xiph.org/ *
|
||||
* *
|
||||
********************************************************************
|
||||
|
||||
function: fft transform
|
||||
last mod: $Id: smallft.h,v 1.2 2004/07/07 13:39:42 stevek Exp $
|
||||
|
||||
********************************************************************/
|
||||
|
||||
#ifndef _V_SMFT_H_
|
||||
#define _V_SMFT_H_
|
||||
|
||||
/*#include "vorbis/codec.h"*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct drft_lookup{
|
||||
int n;
|
||||
float *trigcache;
|
||||
int *splitcache;
|
||||
};
|
||||
|
||||
extern void drft_forward(struct drft_lookup *l,float *data);
|
||||
extern void drft_backward(struct drft_lookup *l,float *data);
|
||||
extern void drft_init(struct drft_lookup *l,int n);
|
||||
extern void drft_clear(struct drft_lookup *l);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,139 @@
|
||||
/* Copyright (C) 2003 Epic Games
|
||||
Written by Jean-Marc Valin
|
||||
|
||||
File: speex_preprocess.h
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct drft_lookup;
|
||||
|
||||
typedef struct SpeexPreprocessState {
|
||||
int frame_size; /**< Number of samples processed each time */
|
||||
int ps_size; /**< Number of points in the power spectrum */
|
||||
int sampling_rate; /**< Sampling rate of the input/output */
|
||||
|
||||
/* parameters */
|
||||
int denoise_enabled;
|
||||
int agc_enabled;
|
||||
float agc_level;
|
||||
int vad_enabled;
|
||||
|
||||
// probabilities to check speech_prob against
|
||||
float speech_prob_start ;
|
||||
float speech_prob_continue ;
|
||||
|
||||
float *frame; /**< Processing frame (2*ps_size) */
|
||||
float *ps; /**< Current power spectrum */
|
||||
float *gain2; /**< Adjusted gains */
|
||||
float *window; /**< Analysis/Synthesis window */
|
||||
float *noise; /**< Noise estimate */
|
||||
float *old_ps; /**< Power spectrum for last frame */
|
||||
float *gain; /**< Ephraim Malah gain */
|
||||
float *prior; /**< A-priori SNR */
|
||||
float *post; /**< A-posteriori SNR */
|
||||
|
||||
float *S; /**< Smoothed power spectrum */
|
||||
float *Smin; /**< See Cohen paper */
|
||||
float *Stmp; /**< See Cohen paper */
|
||||
float *update_prob; /**< Propability of speech presence for noise update */
|
||||
|
||||
float *zeta; /**< Smoothed a priori SNR */
|
||||
float Zpeak;
|
||||
float Zlast;
|
||||
|
||||
float *loudness_weight; /**< Perceptual loudness curve */
|
||||
|
||||
float *echo_noise;
|
||||
|
||||
float *noise_bands;
|
||||
float *noise_bands2;
|
||||
int noise_bandsN;
|
||||
float *speech_bands;
|
||||
float *speech_bands2;
|
||||
int speech_bandsN;
|
||||
|
||||
float *inbuf; /**< Input buffer (overlapped analysis) */
|
||||
float *outbuf; /**< Output buffer (for overlap and add) */
|
||||
|
||||
float speech_prob;
|
||||
int last_speech;
|
||||
float loudness; /**< loudness estimate */
|
||||
float loudness2; /**< loudness estimate */
|
||||
int nb_adapt; /**< Number of frames used for adaptation so far */
|
||||
int nb_loudness_adapt; /**< Number of frames used for loudness adaptation so far */
|
||||
int consec_noise; /**< Number of consecutive noise frames */
|
||||
int nb_preprocess; /**< Number of frames processed so far */
|
||||
struct drft_lookup *fft_lookup; /**< Lookup table for the FFT */
|
||||
|
||||
} SpeexPreprocessState;
|
||||
|
||||
/** Creates a new preprocessing state */
|
||||
SpeexPreprocessState *speex_preprocess_state_init(int frame_size, int sampling_rate);
|
||||
|
||||
/** Destroys a denoising state */
|
||||
void speex_preprocess_state_destroy(SpeexPreprocessState *st);
|
||||
|
||||
/** Preprocess a frame */
|
||||
int speex_preprocess(SpeexPreprocessState *st, short *x, float *noise);
|
||||
|
||||
/** Preprocess a frame */
|
||||
void speex_preprocess_estimate_update(SpeexPreprocessState *st, short *x, float *noise);
|
||||
|
||||
/** Used like the ioctl function to control the preprocessor parameters */
|
||||
int speex_preprocess_ctl(SpeexPreprocessState *st, int request, void *ptr);
|
||||
|
||||
#define SPEEX_PROB_START 0.35
|
||||
#define SPEEX_PROB_CONTINUE 0.1
|
||||
|
||||
#define SPEEX_PREPROCESS_SET_DENOISE 0
|
||||
#define SPEEX_PREPROCESS_GET_DENOISE 1
|
||||
|
||||
#define SPEEX_PREPROCESS_SET_AGC 2
|
||||
#define SPEEX_PREPROCESS_GET_AGC 3
|
||||
|
||||
#define SPEEX_PREPROCESS_SET_VAD 4
|
||||
#define SPEEX_PREPROCESS_GET_VAD 5
|
||||
|
||||
#define SPEEX_PREPROCESS_SET_AGC_LEVEL 6
|
||||
#define SPEEX_PREPROCESS_GET_AGC_LEVEL 7
|
||||
|
||||
#define SPEEX_PREPROCESS_SET_PROB_START 8
|
||||
#define SPEEX_PREPROCESS_GET_PROB_START 9
|
||||
|
||||
#define SPEEX_PREPROCESS_SET_PROB_CONTINUE 10
|
||||
#define SPEEX_PREPROCESS_GET_PROB_CONTINUE 11
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
||||
|
||||
// $Id: member.h,v 1.7 2005/11/02 22:42:42 stevek Exp $
|
||||
|
||||
/*
|
||||
* app_conference
|
||||
*
|
||||
* A channel independent conference application for Asterisk
|
||||
*
|
||||
* Copyright (C) 2002, 2003 Junghanns.NET GmbH
|
||||
* Copyright (C) 2003-2005 HorizonLive.com, Inc.
|
||||
*
|
||||
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
|
||||
* Steve Kann <stevek@stevek.com>
|
||||
*
|
||||
* This program may be modified and distributed under the
|
||||
* terms of the GNU Public License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _APP_CONF_MEMBER_H
|
||||
#define _APP_CONF_MEMBER_H
|
||||
|
||||
//
|
||||
// includes
|
||||
//
|
||||
|
||||
#include "app_conference.h"
|
||||
#include "common.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
|
||||
//
|
||||
// struct declarations
|
||||
//
|
||||
|
||||
struct ast_conf_soundq
|
||||
{
|
||||
char name[256];
|
||||
struct ast_filestream *stream; // the stream
|
||||
int muted; // should incoming audio be muted while we play?
|
||||
struct ast_conf_soundq *next;
|
||||
};
|
||||
|
||||
struct ast_conf_member
|
||||
{
|
||||
ast_mutex_t lock ; // member data mutex
|
||||
|
||||
struct ast_channel* chan ; // member's channel
|
||||
char* channel_name ; // member's channel name
|
||||
|
||||
// values passed to create_member () via *data
|
||||
int priority ; // highest priority gets the channel
|
||||
char* flags ; // raw member-type flags
|
||||
char type ; // L = ListenOnly, M = Moderator, S = Standard (Listen/Talk)
|
||||
char* id ; // member id
|
||||
|
||||
// determine by flags and channel name
|
||||
char connection_type ; // T = telephone, X = iaxclient, S = sip
|
||||
|
||||
// vad voice probability thresholds
|
||||
float vad_prob_start ;
|
||||
float vad_prob_continue ;
|
||||
|
||||
// ready flag
|
||||
short ready_for_outgoing ;
|
||||
|
||||
// input frame queue
|
||||
conf_frame* inFrames ;
|
||||
conf_frame* inFramesTail ;
|
||||
unsigned int inFramesCount ;
|
||||
|
||||
// input/output smoother
|
||||
struct ast_smoother *inSmoother;
|
||||
struct ast_packer *outPacker;
|
||||
int smooth_size_in;
|
||||
int smooth_size_out;
|
||||
int smooth_multiple;
|
||||
|
||||
// frames needed by conference_exec
|
||||
unsigned int inFramesNeeded ;
|
||||
|
||||
// used when caching last frame
|
||||
conf_frame* inFramesLast ;
|
||||
unsigned int inFramesRepeatLast ;
|
||||
unsigned short okayToCacheLast ;
|
||||
|
||||
// output frame queue
|
||||
conf_frame* outFrames ;
|
||||
conf_frame* outFramesTail ;
|
||||
unsigned int outFramesCount ;
|
||||
|
||||
// time we last dropped a frame
|
||||
struct timeval last_in_dropped ;
|
||||
struct timeval last_out_dropped ;
|
||||
|
||||
// ( not currently used )
|
||||
// int samplesperframe ;
|
||||
|
||||
// used for determining need to mix frames
|
||||
// and for management interface notification
|
||||
short speaking_state_prev ;
|
||||
short speaking_state_notify ;
|
||||
short speaking_state ;
|
||||
|
||||
// pointer to next member in single-linked list
|
||||
struct ast_conf_member* next ;
|
||||
|
||||
// accounting values
|
||||
unsigned long frames_in ;
|
||||
unsigned long frames_in_dropped ;
|
||||
unsigned long frames_out ;
|
||||
unsigned long frames_out_dropped ;
|
||||
|
||||
// for counting sequentially dropped frames
|
||||
unsigned int sequential_drops ;
|
||||
unsigned long since_dropped ;
|
||||
|
||||
// start time
|
||||
struct timeval time_entered ;
|
||||
struct timeval lastsent_timeval ;
|
||||
|
||||
// flag indicating we should remove this member
|
||||
short remove_flag ;
|
||||
|
||||
#if ( SILDET == 2 )
|
||||
// pointer to speex preprocessor dsp
|
||||
SpeexPreprocessState *dsp ;
|
||||
// number of frames to ignore speex_preprocess()
|
||||
int ignore_speex_count;
|
||||
#else
|
||||
// placeholder when preprocessing is not enabled
|
||||
void* dsp ;
|
||||
#endif
|
||||
|
||||
// audio format this member is using
|
||||
int write_format ;
|
||||
int read_format ;
|
||||
|
||||
int write_format_index ;
|
||||
int read_format_index ;
|
||||
|
||||
// member frame translators
|
||||
struct ast_trans_pvt* to_slinear ;
|
||||
struct ast_trans_pvt* from_slinear ;
|
||||
|
||||
// For playing sounds
|
||||
struct ast_conf_soundq *soundq;
|
||||
|
||||
#ifdef DEBUG_OUTPUT_PCM
|
||||
FILE *incoming_fh;
|
||||
#endif
|
||||
|
||||
int send_dtmf:1;
|
||||
int quiet_entry_exit:1;
|
||||
int inband_dtmf:1;
|
||||
int rfc_dtmf:1;
|
||||
} ;
|
||||
|
||||
struct conf_member
|
||||
{
|
||||
struct ast_conf_member* realmember ;
|
||||
struct conf_member* next ;
|
||||
} ;
|
||||
|
||||
//
|
||||
// function declarations
|
||||
//
|
||||
|
||||
int member_exec( struct ast_channel* chan, void* data ) ;
|
||||
|
||||
int basic_play_sound ( char *channel, char *file ) ;
|
||||
|
||||
struct ast_conf_member* create_member( struct ast_channel* chan, const char* data ) ;
|
||||
struct ast_conf_member* delete_member( struct ast_conf_member* member ) ;
|
||||
|
||||
// incoming queue
|
||||
int queue_incoming_frame( struct ast_conf_member* member, struct ast_frame* fr ) ;
|
||||
conf_frame* get_incoming_frame( struct ast_conf_member* member ) ;
|
||||
|
||||
// outgoing queue
|
||||
int queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ;
|
||||
int __queue_outgoing_frame( struct ast_conf_member* member, const struct ast_frame* fr, struct timeval delivery ) ;
|
||||
conf_frame* get_outgoing_frame( struct ast_conf_member* member ) ;
|
||||
|
||||
void send_state_change_notifications( struct ast_conf_member* member ) ;
|
||||
|
||||
//
|
||||
// meta-info accessors functions
|
||||
//
|
||||
|
||||
short memberIsPhoneClient( struct ast_conf_member* member ) ;
|
||||
short memberIsIaxClient( struct ast_conf_member* member ) ;
|
||||
short memberIsSIPClient( struct ast_conf_member* member ) ;
|
||||
|
||||
short memberIsModerator( struct ast_conf_member* member ) ;
|
||||
short memberIsListener( struct ast_conf_member* member ) ;
|
||||
|
||||
//
|
||||
// packer functions
|
||||
//
|
||||
|
||||
struct ast_packer;
|
||||
|
||||
extern struct ast_packer *ast_packer_new(int bytes);
|
||||
extern void ast_packer_set_flags(struct ast_packer *packer, int flags);
|
||||
extern int ast_packer_get_flags(struct ast_packer *packer);
|
||||
extern void ast_packer_free(struct ast_packer *s);
|
||||
extern void ast_packer_reset(struct ast_packer *s, int bytes);
|
||||
extern int ast_packer_feed(struct ast_packer *s, const struct ast_frame *f);
|
||||
extern struct ast_frame *ast_packer_read(struct ast_packer *s);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
Õ:“nôPSaÌtÖªj*PÕR6ÛžD ␍IÁ‰àDCtbëÕ<kºêáCÈk<¸Ö»Â¬£ª5†¹#BâtdêíB¸1xVÕÔûk¶©…#Ì‹'ŒÝbgMKŒ©yâÃÅiµ)ï¢èåO¤Ôû{6ªw!·Sã§(ï¢ñœ‰È«»¢VÕ6Ž\yB8×7Õ<cú©Á›K£êñ�¾©Ž©ˆÃFÖ¬£»‚V¸±È£ÔûkviïB³[�Q\›C6êxfæßB¾,6)‡»"˜ã°ñZÕ;{:ª½Âäèh¤½Â—5ÓÃw£QœtÖ²»¡ÌmÉÔü\:¢íâž,R)ía×VN©ÝaES¦¬=ïÁÑÆVI#Ôûkv©»Á·†Oi‚aÅÑ”y¡Ñ[�özïÁJ•VÝÕ<szª½‚„Ž4¢½¢V±Y,ׇ+£¥S«#7"â·"Ôücú¡ï¡6åIw!¶Ý׫A(s5Y␍íTâ\Ê;Ôûs6ª»�ª×8ÖÜïÂÈyu8ÞwÁŽÜÊCÅ«â:ªã·•Õ4ë©õ{ÃH±<i†¿Ç
|
||||
ÜnCŒWí8âª:˜!]®×yì
|
||||
Binary file not shown.
Reference in New Issue
Block a user