This commit is contained in:
Daniil Gentili 2020-10-27 20:53:13 +01:00
parent 806f50bc2d
commit 8164279427
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
5 changed files with 112 additions and 66 deletions

View File

@ -187,6 +187,9 @@ class Ogg
for ($x = 0; $x < $count; $x++) {
$sizes[]= $this->readLen($content, $offset);
}
if (!$selfDelimited) {
$sizes []= ($len - ($offset + $padding));
}
} else { // CBR
$size = $selfDelimited
? $this->readLen($content, $offset)
@ -241,6 +244,7 @@ class Ogg
while (true) {
$init = yield $this->stream->bufferRead(4+23);
if (empty($init)) {
$this->emitter->complete();
return false; // EOF
}
if (\substr($init, 0, 4) !== self::CAPTURE_PATTERN) {

View File

@ -19,6 +19,7 @@ use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\Ogg\Ogg;
use danog\MadelineProto\VoIP\Endpoint;
use function Amp\delay;
use function Amp\File\open;
if (\extension_loaded('php-libtgvoip')) {
@ -30,7 +31,7 @@ class VoIP
use \danog\MadelineProto\VoIP\MessageHandler;
use \danog\MadelineProto\VoIP\AckHandler;
const PHP_LIBTGVOIP_VERSION = '1.1.2';
const PHP_LIBTGVOIP_VERSION = '1.5.0';
const STATE_CREATED = 0;
const STATE_WAIT_INIT = 1;
const STATE_WAIT_INIT_ACK = 2;
@ -99,13 +100,13 @@ class VoIP
const PROTO_ID = 'GrVP';
const PROTOCOL_VERSION = 3;
const MIN_PROTOCOL_VERSION = 3;
const PROTOCOL_VERSION = 9;
const MIN_PROTOCOL_VERSION = 9;
const STREAM_TYPE_AUDIO = 1;
const STREAM_TYPE_VIDEO = 2;
const CODEC_OPUS = 1;
const CODEC_OPUS = 'SUPO';
private $TLID_DECRYPTED_AUDIO_BLOCK;
@ -137,7 +138,7 @@ class VoIP
private bool $creator;
private PermAuthKey $authKey;
private int $peerVersion;
private int $peerVersion = 0;
/**
* @var Endpoint[]
@ -149,24 +150,39 @@ class VoIP
private $datacenter;
public function __construct(bool $creator, int $otherID, $callID, MTProto $MadelineProto, $callState, $protocol)
public function __construct(bool $creator, int $otherID, MTProto $MadelineProto, $callState)
{
$this->creator = $creator;
$this->otherID = $otherID;
$this->callID = $callID;
$this->MadelineProto = $MadelineProto;
//$this->callID = $callID;
$this->madeline = $this->MadelineProto = $MadelineProto;
$this->callState = $callState;
$this->protocol = $protocol;
//$this->protocol = $protocol;
$this->TLID_REFLECTOR_SELF_INFO = \strrev(\hex2bin(self::TLID_REFLECTOR_SELF_INFO_HEX));
$this->TLID_REFLECTOR_PEER_INFO = \strrev(\hex2bin(self::TLID_REFLECTOR_PEER_INFO_HEX));
$this->TLID_DECRYPTED_AUDIO_BLOCK = \strrev(\hex2bin(self::TLID_DECRYPTED_AUDIO_BLOCK_HEX));
$this->TLID_SIMPLE_AUDIO_BLOCK = \strrev(\hex2bin(self::TLID_SIMPLE_AUDIO_BLOCK_HEX));
}
public static function getConnectionMaxLayer(): int
{
return 92;
}
public function deInitVoIPController()
{
}
public function getDebugString(): string
{
return '';
}
public function setCall($callID)
{
$this->callID = $callID;
}
public function setVisualization($visualization)
{
$this->visualization = $visualization;
@ -210,24 +226,34 @@ class VoIP
public function startTheMagic()
{
foreach ($this->sockets as $socket) {
Tools::callFork(function () use ($socket) {
while ($payload = $this->recv_message($socket)) {
Tools::callFork((function () {
$this->authKey = new PermAuthKey();
$this->authKey->setAuthKey($this->configuration['auth_key']);
foreach ($this->configuration['endpoints'] as $endpoint) {
$this->sockets['v6 '.$endpoint['id']] = new Endpoint('['.$endpoint['ipv6'].']', $endpoint['port'], $endpoint['peer_tag'], true, $this);
$this->sockets['v4 '.$endpoint['id']] = new Endpoint($endpoint['ip'], $endpoint['port'], $endpoint['peer_tag'], true, $this);
}
foreach ($this->sockets as $socket) {
yield from $socket->connect();
}
$this->init_all();
Tools::callFork((function () use ($socket) {
while ($payload = yield from $this->recv_message($socket)) {
Tools::callFork($this->handlePacket($socket, $payload));
}
});
}
})());
})());
return $this;
}
public function handlePacket($datacenter, $packet)
{
\var_dump($packet);
//\var_dump($packet);
switch ($packet['_']) {
case self::PKT_INIT:
$this->voip_state = self::STATE_WAIT_INIT_ACK;
//$this->voip_state = self::STATE_WAIT_INIT_ACK;
$this->send_message(['_' => self::PKT_INIT_ACK, 'protocol' => self::PROTOCOL_VERSION, 'min_protocol' => self::MIN_PROTOCOL_VERSION, 'all_streams' => [['id' => 0, 'type' => self::STREAM_TYPE_AUDIO, 'codec' => self::CODEC_OPUS, 'frame_duration' => 60, 'enabled' => 1]]], $datacenter);
break;
case self::PKT_INIT_ACK:
if ($this->voip_state !== self::STATE_ESTABLISHED) {
$this->voip_state = self::STATE_ESTABLISHED;
@ -237,21 +263,25 @@ class VoIP
$ogg = yield from Ogg::init($stream, 60000);
$it = $ogg->getEmitter()->iterate();
Tools::callFork($ogg->read());
Tools::callFork(function () use ($it) {
Tools::callFork((function () use ($it, $datacenter) {
$timestamp = 0;
$t = microtime(true);
$frames = [];
while (yield $it->advance()) {
$elapsed = microtime(true) - $t;
$t = microtime(true);
$frames []= $it->getCurrent();
}
foreach ($frames as $frame) {
$t = (microtime(true) / 1000) + 60;
yield $this->send_message(['_' => self::PKT_STREAM_DATA, 'stream_id' => 0, 'data' => $frame, 'timestamp' => $timestamp], $datacenter);
yield new Delayed((int) ($t - (microtime(true) / 1000)));
yield new Delayed((int) (60000 - $elapsed / 1000));
yield $this->send_message(['_' => self::PKT_STREAM_DATA, 'stream_id' => 0, 'data' => $it->getCurrent(), 'timestamp' => $this->timestamp], $datacenter);
$timestamp += 60;
}
});
})());
}
break;
case self::PKT_INIT_ACK:
break;
}
}
public $timestamp = 0;
@ -321,23 +351,11 @@ class VoIP
public function parseConfig()
{
$this->authKey = new PermAuthKey();
$this->authKey->setAuthKey($this->configuration['auth_key']);
if (\count($this->configuration['endpoints'])) {
foreach ($this->configuration['endpoints'] as $endpoint) {
$this->sockets['v6 '.$endpoint['id']] = new Endpoint($endpoint['ipv6'], $endpoint['port'], $endpoint['peer_tag'], true, $this);
$this->sockets['v4 '.$endpoint['id']] = new Endpoint($endpoint['ip'], $endpoint['port'], $endpoint['peer_tag'], true, $this);
}
foreach ($this->sockets as $socket) {
yield from $socket->connect();
}
}
}
private function init_all()
{
$test = $this->connection_settings['all']['test_mode'] ? 'test' : 'main';
foreach ($this->datacenter->sockets as $dc_id => $socket) {
foreach ($this->sockets as $socket) {
$this->send_message(['_' => self::PKT_INIT, 'protocol' => self::PROTOCOL_VERSION, 'min_protocol' => self::MIN_PROTOCOL_VERSION, 'audio_streams' => [self::CODEC_OPUS], 'video_streams' => []], $socket);
$this->voip_state = self::STATE_WAIT_INIT;
}

View File

@ -137,7 +137,7 @@ trait AuthKeyHandler
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
Crypt::checkG($g_b, $dh_config['p']);
try {
$res = yield from $this->methodCallAsyncRead('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]]);
$res = yield from $this->methodCallAsyncRead('phone.acceptCall', ['peer' => ['id' => $call['id'], 'access_hash' => $call['access_hash'], '_' => 'inputPhoneCall'], 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'CALL_ALREADY_ACCEPTED') {
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['call_already_accepted'], $call['id']));
@ -322,6 +322,7 @@ trait AuthKeyHandler
{
\array_walk($this->calls, function ($controller, $id) {
if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
$this->logger("Discarding ended call...");
$controller->discard();
}
});

View File

@ -28,12 +28,12 @@ trait MessageHandler
if ($l <= 253) {
$concat .= \chr($l);
$concat .= $object;
$concat .= \pack('@'.$this->posmod(-$l - 1, 4));
$concat .= \pack('@'.Tools::posmod(-$l - 1, 4));
} else {
$concat .= \chr(254);
$concat .= \substr(Tools::packSignedInt($l), 0, 3);
$concat .= $object;
$concat .= \pack('@'.$this->posmod(-$l, 4));
$concat .= \pack('@'.Tools::posmod(-$l, 4));
}
return $concat;
@ -47,13 +47,13 @@ trait MessageHandler
if ($l === 254) {
$long_len = \unpack('V', \stream_get_contents($stream, 3).\chr(0))[1];
$x = \stream_get_contents($stream, $long_len);
$resto = $this->posmod(-$long_len, 4);
$resto = Tools::posmod(-$long_len, 4);
if ($resto > 0) {
\stream_get_contents($stream, $resto);
}
} else {
$x = \stream_get_contents($stream, $l);
$resto = $this->posmod(-($l + 1), 4);
$resto = Tools::posmod(-($l + 1), 4);
if ($resto > 0) {
\stream_get_contents($stream, $resto);
}
@ -78,8 +78,9 @@ trait MessageHandler
$message .= Tools::packUnsignedInt($flags);
$message .= \chr(\count($args['audio_streams']));
foreach ($args['audio_streams'] as $codec) {
$message .= \chr($codec);
$message .= $codec;
}
$message .= chr(0);
$message .= \chr(\count($args['video_streams']));
foreach ($args['video_streams'] as $codec) {
$message .= \chr($codec);
@ -95,7 +96,7 @@ trait MessageHandler
foreach ($args['all_streams'] as $stream) {
$message .= \chr($stream['id']);
$message .= \chr($stream['type']);
$message .= \chr($stream['codec']);
$message .= $stream['codec'];
$message .= \pack('v', $stream['frame_duration']);
$message .= \chr($stream['enabled']);
}
@ -184,11 +185,18 @@ trait MessageHandler
}
}
if (\in_array($this->voip_state, [\danog\MadelineProto\VoIP::STATE_WAIT_INIT, \danog\MadelineProto\VoIP::STATE_WAIT_INIT_ACK])) {
if ($this->peerVersion >= 8 || (!$this->peerVersion && true)) {
$payload = \chr($args['_']);
$payload .= Tools::packUnsignedInt($this->session_in_seq_no);
$payload .= Tools::packUnsignedInt($this->session_out_seq_no);
$payload .= Tools::packUnsignedInt($ack_mask);
$payload .= \chr(0);
$payload .= $message;
} elseif (\in_array($this->voip_state, [\danog\MadelineProto\VoIP::STATE_WAIT_INIT, \danog\MadelineProto\VoIP::STATE_WAIT_INIT_ACK])) {
$payload = $this->TLID_DECRYPTED_AUDIO_BLOCK;
$payload .= $this->random(8);
$payload .= Tools::random(8);
$payload .= \chr(7);
$payload .= $this->random(7);
$payload .= Tools::random(7);
$flags = 0;
$flags = $flags | 4; // call_id
$flags = $flags | 16; // seqno
@ -211,9 +219,9 @@ trait MessageHandler
}
} else {
$payload = $this->TLID_SIMPLE_AUDIO_BLOCK;
$payload .= $this->random(8);
$payload .= Tools::random(8);
$payload .= \chr(7);
$payload .= $this->random(7);
$payload .= Tools::random(7);
$message = \chr($args['_']).Tools::packUnsignedInt($this->session_in_seq_no).Tools::packUnsignedInt($this->session_out_seq_no).Tools::packUnsignedInt($ack_mask).$message;
$payload .= $this->pack_string($message);
@ -295,8 +303,29 @@ trait MessageHandler
$result['peer_port'] = Tools::unpackSignedInt(\stream_get_contents($payload, 4));
return $result;
default:
\danog\MadelineProto\Logger::log('Unknown packet received: '.\bin2hex($crc), \danog\MadelineProto\Logger::ERROR);
return false;
if ($this->peerVersion >= 8 || (!$this->peerVersion && true)) {
\fseek($payload, 0);
$result['_'] = \ord(\stream_get_contents($payload, 1));
$in_seq_no = \unpack('V', \stream_get_contents($payload, 4))[1];
$out_seq_no = \unpack('V', \stream_get_contents($payload, 4))[1];
$ack_mask = \unpack('V', \stream_get_contents($payload, 4))[1];
$flags = \ord(\stream_get_contents($payload, 1));
if ($flags & 1) {
$result['extra'] = [];
$count = \ord(\stream_get_contents($payload, 1));
for ($x = 0; $x < $count; $x++) {
$len = \ord(\stream_get_contents($payload, 1));
$result['extra'][]= \stream_get_contents($payload, $len);
}
}
$message = \fopen('php://memory', 'rw+b');
\fwrite($message, \stream_get_contents($payload));
\fseek($message, 0);
} else {
\danog\MadelineProto\Logger::log('Unknown packet received: '.\bin2hex($crc), \danog\MadelineProto\Logger::ERROR);
return false;
}
}
if (!$this->received_packet($in_seq_no, $out_seq_no, $ack_mask)) {
return false;
@ -306,19 +335,14 @@ trait MessageHandler
//
// packetInit#1 protocol:int min_protocol:int flags:# data_saving_enabled:flags.0?true audio_streams:byteVector<streamTypeSimple> video_streams:byteVector<streamTypeSimple> = Packet;
case \danog\MadelineProto\VoIP::PKT_INIT:
$result['protocol'] = Tools::unpackSignedInt(\stream_get_contents($message, 4));
$result['protocol'] = $this->peerVersion = Tools::unpackSignedInt(\stream_get_contents($message, 4));
$result['min_protocol'] = Tools::unpackSignedInt(\stream_get_contents($message, 4));
$flags = \unpack('V', \stream_get_contents($message, 4))[1];
$result['data_saving_enabled'] = (bool) ($flags & 1);
$result['audio_streams'] = [];
$length = \ord(\stream_get_contents($message, 1));
for ($x = 0; $x < $length; $x++) {
$result['audio_streams'][$x] = \ord(\stream_get_contents($message, 1));
}
$result['video_streams'] = [];
$length = \ord(\stream_get_contents($message, 1));
for ($x = 0; $x < $length; $x++) {
$result['video_streams'][$x] = \ord(\stream_get_contents($message, 1));
$result['audio_streams'][$x] = \stream_get_contents($message, 4);
}
break;
// streamType id:int8 type:int8 codec:int8 frame_duration:int16 enabled:int8 = StreamType;
@ -331,7 +355,7 @@ trait MessageHandler
$length = \ord(\stream_get_contents($message, 1));
for ($x = 0; $x < $length; $x++) {
$result['all_streams'][$x]['id'] = \ord(\stream_get_contents($message, 1));
$result['all_streams'][$x]['type'] = \ord(\stream_get_contents($message, 1));
$result['all_streams'][$x]['type'] = \stream_get_contents($message, 4);
$result['all_streams'][$x]['codec'] = \ord(\stream_get_contents($message, 1));
$result['all_streams'][$x]['frame_duration'] = \unpack('v', \stream_get_contents($message, 2))[1];
$result['all_streams'][$x]['enabled'] = \ord(\stream_get_contents($message, 1));

View File

@ -19,11 +19,13 @@
namespace danog\MadelineProto;
if (\class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal')) {
if (\class_exists(VoIPServerConfig::class)) {
return;
}
/**
* Manages storage of VoIP server config.
*/
class VoIPServerConfig extends VoIPServerConfigInternal
class VoIPServerConfig
{
/**
* The configuration.
@ -47,7 +49,6 @@ if (\class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal')) {
public static function update(array $config)
{
self::$_config = $config;
self::updateInternal(self::getFinal());
}
/**
* Get shared call settings.
@ -68,7 +69,6 @@ if (\class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal')) {
public static function updateDefault(array $configDefault)
{
self::$_configDefault = $configDefault;
self::updateInternal(self::getFinal());
}
/**
* Get default shared call settings.
@ -89,4 +89,3 @@ if (\class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal')) {
return \array_merge(self::$_configDefault, self::$_config);
}
}
}