-
Notifications
You must be signed in to change notification settings - Fork 0
/
icecastwebm.php
169 lines (153 loc) · 6.44 KB
/
icecastwebm.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<?php
class icecastwebm {
#Internal
var $this;
# Open socket connection to Icecast.
function initIcecastComm($serv='127.0.0.1', $port=8000, $mount="/setme", $pass="hackmake") {
global $iceGenre, $iceName, $iceDesc, $iceUrl, $icePublic, $icePrivate, $iceAudioInfoSamplerate, $iceAudioInfoBitrate, $iceAudioInfoChannels;
// We're going to create a tcp socket.
$sock = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
socket_set_block($sock) or die("Blocking error: ".socket_strerror(socket_last_error($sock))." \n");
if (!$sock) {
die("Unable to create socket! ".socket_strerror(socket_last_error($sock))." \n");
} else {
// Connect to the server.
echo "Connecting to $serv on port $port\n";
if(socket_connect($sock,$serv,$port)) {
echo "Connection established! Sending login....\n";
$this->sendData($sock,"SOURCE $mount HTTP/1.0\r\n");
$this->sendLogin($sock, $pass);
$this->sendData($sock, "User-Agent: icecastwebm-php\r\n");
$this->sendData($sock, "Content-Type: video/webm\r\n");
$this->sendData($sock, "ice-name: $iceName\r\n");
$this->sendData($sock, "ice-url: $iceUrl\r\n");
$this->sendData($sock, "ice-genre: $iceGenre\r\n");
$this->sendData($sock, "ice-public: $icePublic\r\n");
$this->sendData($sock, "ice-private: $icePrivate\r\n");
$this->sendData($sock, "ice-description: $iceDesc\r\n");
$this->sendData($sock, "ice-audio-info: ice-samplerate=$iceAudioInfoSamplerate;ice-bitrate=$iceAudioInfoBitrate;ice-channels=$iceAudioInfoChannels\r\n");
$this->sendData($sock, "\r\n");
// Go into loop mode.
$this->parseData($sock);
$this->initStream($sock); // Start the stream.
//die;
} else {
die("Unable to connect to server! ".socket_strerror(socket_last_error($sock))." \n");
}
}
}
# Send connection info.
function sendLogin($sock, $pass) {
// Authentication is basic, which requires the format to be 'user:pass' encoded in base64.
$b64enc = base64_encode("source:$pass");
$this->sendData($sock,"Authorization: Basic $b64enc\r\n");
}
function sendData($sock,$buf) {
global $debug;
if ($debug == 1) {
echo "-> $buf";
}
socket_write($sock, $buf, strlen($buf)) or die("Socket Write Error: ".socket_strerror(socket_last_error($sock))." \n");
}
function parseData($sock) {
global $debug;
$buf = '';
$buf = socket_read($sock, 1024, PHP_NORMAL_READ) or die("Socket Read Error: ".socket_strerror(socket_last_error($sock))." \n");
// Don't even bother if theres no information this go through.
if (strlen($buf) > 1) {
if ($debug == 1) {
echo "<- $buf strlen(".strlen($buf).")\n";
}
$parsebuf = explode(" ", $buf); // Needed to get the HTTP response code.
switch ($parsebuf[1]) {
case 200:
echo "Received OK from Icecast server!\n";
$this->initStream($sock);
break;
case 400:
echo "Recieved Bad Request!\n";
socket_close($sock);
die;
break;
case 403:
echo "Received Forbidden!\n";
socket_close($sock);
die;
break;
case 404:
echo "Received Not Found!\n";
socket_close($sock);
die;
break;
default:
echo "Unknown response received!\n";
die;
break;
}
}
}
# Core part of this script, it creates the processes that grab the stream, convert it, then send it off to Icecast.
function initStream($sock) {
global $rtmpurl, $appname, $swfVfy , $pageurl, $playpath, $rtmpdumpbin, $vcodec, $vcodecopts, $acodec, $acodecopts, $ffmpegbin, $coreopts;
# Build the command.
$cmd = $rtmpdumpbin." -r ".$rtmpurl." -a ".$appname." -W ".$swfVfy." -p ".$pageurl." -y ".$playpath." -q -o - | ".$ffmpegbin." -i - ".$coreopts." -c:v ".$vcodec." ".$vcodecopts." -f webm -c:a ".$acodec." ".$acodecopts." pipe:1";
//echo $cmd."\n\n";
//die;
// Sourced from http://stackoverflow.com/questions/16351302/reading-from-stdin-pipe-when-using-proc-open/16351484#16351484
// Modified to fit this script's purpose.
# Create our pipes.
$pipespec = array (
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a file to write to
);
$proc = proc_open($cmd, $pipespec, $pipes); // Create the process.
stream_set_blocking($pipes[1], 1); // Blocking
stream_set_blocking($pipes[2], 0); // Non-blocking
stream_set_blocking($pipes[0], 0); // Non-blocking
// check if opening has succeed
if($proc === FALSE){
throw new Exception('Cannot execute child process');
}
// get PID via get_status call
$status = proc_get_status($proc);
if($status === FALSE) {
throw new Exception (sprintf(
'Failed to obtain status information '
));
}
$pid = $status['pid'];
echo "RTMP to WEBM process started, streaming to Icecast server.\n";
// now, poll for childs termination
while(true) {
$this->parseData($sock);
// detect if the child has terminated - the php way
$status = proc_get_status($proc);
// check retval
if($status === FALSE) {
die("Failed to obtain status information for $pid");
}
if($status['running'] === FALSE) {
$exitcode = $status['exitcode'];
$pid = -1;
echo "child exited with code: $exitcode\n";
exit($exitcode);
}
// read from childs stdout
// check stdout for data
do {
$data = fread($pipes[1], 8092);
$this->sendData($sock, $data); // Write to Icecast.
} while (strlen($data) > 0);
}
echo "RTMP stream ended, cleaning up and exiting...\n";
// Clean up.
fclose($pipes[2]);
fclose($pipes[1]);
fclose($pipes[0]);
socket_close($sock);
proc_terminate($proc);
die;
}
}
?>