Skip to content

Commit e8dc1b2

Browse files
committed
Refactor RtspClient and add initial SdpData class
1 parent 04f7b15 commit e8dc1b2

File tree

4 files changed

+225
-128
lines changed

4 files changed

+225
-128
lines changed

RtspClient.php

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
3+
class RtspClient
4+
{
5+
protected $addr;
6+
protected $port;
7+
protected $user;
8+
protected $pass;
9+
10+
protected $cseq = 1;
11+
protected $auth = [];
12+
13+
protected $rawResponse;
14+
15+
protected $socket;
16+
17+
function __construct(string $addr, int $port, string $user = null, string $pass = null)
18+
{
19+
$this->addr = $addr;
20+
$this->port = $port;
21+
$this->user = $user;
22+
$this->pass = $pass;
23+
24+
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
25+
}
26+
27+
public function connect(): bool
28+
{
29+
return socket_connect($this->socket, $this->addr, $this->port);
30+
}
31+
32+
public function disconnect(): void
33+
{
34+
socket_shutdown($this->socket);
35+
socket_close($this->socket);
36+
}
37+
38+
public function getSocketError(): int
39+
{
40+
return socket_last_error($this->socket);
41+
}
42+
43+
public function getRawResponse(): ?string
44+
{
45+
return $this->rawResponse;
46+
}
47+
48+
protected function digest(string $user, string $realm, string $password, string $nonce, string $method, string $uri): string
49+
{
50+
$ha1 = hash("md5", $user.":".$realm.":".$password);
51+
$ha2 = hash("md5", $method.":".$uri);
52+
$result = hash("md5", $ha1.":".$nonce.":".$ha2);
53+
54+
return $result;
55+
}
56+
57+
public function request(string $method, string $uri, array $options = []): array
58+
{
59+
if (count($this->auth) > 0) {
60+
$options['Authorization'] = $this->auth['authtype'].' username="'.$this->user.'",realm="'.$this->auth['realm'].'",nonce="'.$this->auth['nonce'].'",uri="'.$this->auth['uri'].'",response="'.$this->digest($this->user, $this->auth['realm'], $this->pass, $this->auth['nonce'], $method, $this->auth['uri']).'"';
61+
}
62+
63+
$request = $method." ".$uri." RTSP/1.0\r\n";
64+
$request .= "CSeq: ".$this->cseq++."\r\n";
65+
foreach ($options as $k => $v) {
66+
$request .= $k.": ".$v."\r\n";
67+
}
68+
$request .= "\r\n";
69+
socket_write($this->socket, $request);
70+
71+
$this->rawResponse = '';
72+
while ($r = socket_read($this->socket, 4096)) {
73+
$this->rawResponse .= $r;
74+
if (strpos($this->rawResponse, "\r\n\r\n")) { break; }
75+
}
76+
77+
$headers = $this->interpretResponseHeaders($this->rawResponse);
78+
79+
$body = '';
80+
if (array_key_exists('Content-Length', $headers)) {
81+
$body = socket_read($this->socket, $headers['Content-Length']);
82+
$this->rawResponse .= $body;
83+
}
84+
85+
if (isset($headers['WWW-Authenticate'])) {
86+
// Did we already try to authenticate or do we not have credentials set?
87+
if (!$this->user || !$this->pass || isset($options['Authorization'])) {
88+
throw new RuntimeException('RTSP authorization failed');
89+
}
90+
91+
$wwwauth = preg_split('/(?<!,) /', $response['WWW-Authenticate']);
92+
if (count($wwwauth) != 2) {
93+
throw new RuntimeException("Invalid WWW-Authenticate string: {$response['WWW-Authenticate']}");
94+
}
95+
96+
$authtype = $wwwauth[0];
97+
if ($authtype != 'Digest') {
98+
throw new RuntimeException("Unsupported auth method: {$authtype}");
99+
}
100+
101+
$authdata = [];
102+
$x = preg_split('/, */', $wwwauth[1]);
103+
foreach ($x as $a) {
104+
$y = preg_split('/=/', $a);
105+
$y[1] = preg_replace('/"/', '', $y[1]);
106+
$authdata[$y[0]] = (isset($y[1])) ? $y[1] : '';
107+
}
108+
109+
$this->auth = array(
110+
'authtype'=>$authtype,
111+
'realm'=>$authdata['realm'],
112+
'nonce'=>$authdata['nonce'],
113+
'uri'=>$uri
114+
);
115+
116+
$options['Authorization'] = $this->auth['authtype'].' username="'.$this->user.'",realm="'.$this->auth['realm'].'",nonce="'.$this->auth['nonce'].'",uri="'.$this->auth['uri'].'",response="'.$this->digest($this->user, $this->auth['realm'], $this->pass, $this->auth['nonce'], $method, $this->auth['uri']).'"';
117+
118+
return $this->request($method, $uri, $options);
119+
}
120+
121+
return [
122+
'headers' => $headers,
123+
'body' => $body
124+
];
125+
}
126+
127+
protected function interpretResponseHeaders(string $response): array
128+
{
129+
$return = [];
130+
$lines = preg_split('/\r\n/', $response);
131+
foreach ($lines as $k => $v) {
132+
if ($k == 0) {
133+
$r = preg_split('/ /', $v, 3);
134+
$return['proto'] = (isset($r[0])) ? $r[0] : '';
135+
$return['code'] = (isset($r[1])) ? $r[1] : '';
136+
$return['msg'] = (isset($r[2])) ? $r[2] : '';
137+
} else {
138+
$r = preg_split('/: /', $v);
139+
if (isset($r[1])) {
140+
$return[$r[0]] = (isset($r[1])) ? $r[1] : '';
141+
}
142+
}
143+
}
144+
145+
return $return;
146+
}
147+
148+
}
149+

SdpData.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
class SdpData
4+
{
5+
protected $sdpData;
6+
7+
public function __construct(string $sdpData)
8+
{
9+
$this->sdpData = $sdpData;
10+
}
11+
12+
public function getMediaStreams(): array
13+
{
14+
$streams = [];
15+
$stream = null;
16+
$lines = preg_split('/\r\n/', $this->sdpData);
17+
foreach ($lines as $line) {
18+
if (isset($line[0])) {
19+
$paramValue = substr($line, 2);
20+
if ($line[0] === 'm') {
21+
$streams[$paramValue] = [];
22+
$stream = &$streams[$paramValue];
23+
} else if ($stream !== null && $line[0] === 'a') {
24+
$paramParts = explode(':', $paramValue, 2);
25+
$stream[$paramParts[0]] = $paramParts[1] ?? '';
26+
}
27+
}
28+
}
29+
30+
return $streams;
31+
}
32+
}
33+

rtsp-test.php

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,49 @@
11
<?php
2-
require "rtsp.php";
2+
require 'RtspClient.php';
3+
require 'SdpData.php';
34

4-
$url = "rtsp://10.10.9.110/media/video1";
5-
$test = new rtsp_client("10.10.9.110", 554, "admin", "123456");
5+
$url = "rtsp://127.0.0.1:8554/";
6+
$test = new RtspClient("127.0.0.1", 8554);
67

7-
$test->connect();
8+
echo "connect()\n";
9+
$test->connect();
810

9-
print_r($test->request("OPTIONS", $url));
10-
$desc = $test->request("DESCRIBE", $url);
11-
print_r($desc);
12-
$setup = $test->request("SETUP", $url."/video", array('Transport'=>'RTP/AVP;unicast;client_port=52614-52615', 'User-Agent'=>'PHPrtsp_client/0.0.1'));
13-
print_r($setup);
14-
print_r($test->request("PLAY", $url, array('Session'=>$setup['Session'], 'User-Agent'=>'PHPrtsp_client/0.0.1', 'Range'=>'npt=0.000-')));
15-
print_r($test->request("GET_PARAMETER", $url, array('Session'=>$setup['Session'])));
11+
echo "OPTIONS\n";
12+
print_r($test->request("OPTIONS", $url));
13+
echo "RAW: \n";
14+
echo $test->getRawResponse();
1615

17-
sleep(30);
16+
echo "DESCRIBE\n";
17+
$desc = $test->request("DESCRIBE", $url);
18+
print_r($desc);
19+
echo "RAW: \n";
20+
$response = $test->getRawResponse();
21+
echo $response;
22+
23+
$sdp = new SdpData($response);
24+
$streams = $sdp->getMediaStreams();
25+
echo "STREAMS: \n";
26+
print_r($streams);
27+
28+
echo "SETUP\n";
29+
$stream = $streams['video 0 RTP/AVP 96'];
30+
$setup = $test->request("SETUP", $stream['control'], array('Transport'=>'RTP/AVP;unicast;client_port=52614-52615', 'User-Agent'=>'PHPrtsp_client/0.0.1'));
31+
print_r($setup);
32+
echo "RAW: \n";
33+
echo $test->getRawResponse();
34+
35+
$session = explode(';', $setup['headers']['Session'])[0];
36+
37+
echo "PLAY\n";
38+
print_r($test->request("PLAY", $url, array('Session' => $session, 'User-Agent'=>'PHPrtsp_client/0.0.1')));
39+
echo "RAW: \n";
40+
echo $test->getRawResponse();
41+
42+
echo "GET_PARAMETER\n";
43+
print_r($test->request("GET_PARAMETER", $url, array('Session' => $session)));
44+
echo "RAW: \n";
45+
echo $test->getRawResponse();
46+
47+
echo "sleep(30)\n";
48+
sleep(30);
1849
?>

rtsp.php

Lines changed: 0 additions & 116 deletions
This file was deleted.

0 commit comments

Comments
 (0)