├── README.md ├── app-call.php ├── app-functions.php ├── clean_older.php ├── images ├── avatar.png ├── chat.png └── no-picture.png ├── index.php ├── info.php ├── settings.php ├── snapshots ├── h5a-2w-c.png └── h5a-playback.jpg ├── sounds ├── buzz.mp3 ├── call.mp3 ├── error.mp3 ├── hello.mp3 ├── leave.mp3 ├── message.mp3 └── warning.mp3 ├── static ├── css │ ├── 2.4f53f970.chunk.css │ └── main.c308b1b0.chunk.css ├── js │ ├── 2.18e7dc77.chunk.js │ ├── main.75b83baf.chunk.js │ └── runtime-main.5e6501f7.js └── media │ ├── brand-icons.70150a2b.ttf │ ├── brand-icons.83494ca2.svg │ ├── brand-icons.85917bf2.eot │ ├── brand-icons.cac133c0.woff │ ├── brand-icons.dd746785.woff2 │ ├── flags.99f63ae7.png │ ├── icons.2f6dbd9f.eot │ ├── icons.9b4d14a5.ttf │ ├── icons.acc6b6bf.woff2 │ ├── icons.c8a5f741.svg │ ├── icons.e4efd599.woff │ ├── outline-icons.02428635.svg │ ├── outline-icons.6810be1d.eot │ ├── outline-icons.8a7914c9.woff │ ├── outline-icons.a3b4cd30.ttf │ └── outline-icons.a3f7358b.woff2 ├── tips ├── coins1.mp3 ├── coins2.mp3 ├── gift1.png ├── gift2.png ├── gift3.png ├── gift4.png ├── gift5.png └── register.mp3 ├── uploads └── integration.txt └── videos └── hamsterad.mp4 /README.md: -------------------------------------------------------------------------------- 1 | ## PHP-HTML5-Videochat / Live Streaming - Standalone PHP 2 | 3 | ### Live Demos for PHP Live Streaming / HTML5 Videochat : Broadcast & Playback Live Video 4 | 5 | [HTML5 Live Video Streaming using WowzaSE relay](https://demo.videowhisper.com/html5-videochat-php/) 6 | [HTML5 Live Video Streaming using P2P WebRTC](https://demo.videowhisper.com/vws-html5-livestreaming/) 7 | 8 |  9 | 10 | Before installing, test the simple setup in the live demos above. 11 | 12 | This edition showcases streaming from 1 broadcaster to multiple viewers and chat. 13 | This plain php edition includes code and minimal scripts to embed a HTML5 Videochat app and test/showcase some features. This edition is for integrating/using application with own scripts/framework. 14 | For a complete implementation of advanced capabilities, see [Turnkey HTML5 Videochat Site](https://paidvideochat.com/html5-videochat/) edition, available as WordPress plugin with full php source. The turnkey site edition implements pay per minute videochat (group and private 2 way video calls) with membership, billing, advanced tools. 15 | 16 | ### Simple PHP Edition Features: Live Streaming: Broadcast & Playback 17 | 18 | - [x] Automatically create a room as broadcaster on access and show link to invite participants that will access as viewers 19 | - [x] Embed app to broadcast and playback live video using HTML5 WebRTC 20 | - [x] Simple implementation of signaling broadcast (to connect automatically) and text chat, using plain files 21 | 22 | ### Key Features for HTML5 Videochat / Live Streaming: Broadcast & Playback 23 | 24 | - [x] WebRTC 1 way to many live video streaming, in public lobby 25 | - [x] WebRTC relayed streaming (reliable and scalable to many clients from Wowza SE streaming server, independent of broadcaster upload connection) / P2P using VideoWhisper WebRTC 26 | - [x] select camera, microphone, resolution, bitrate 27 | - [x] screen sharing toggle, with microphone track mixed 28 | - [x] video/audio recorder, emoticons, mentions in text chat 29 | - [x] fullscreen for videochat interface or playback video 30 | - [x] adaptive target video bitrate (depending on cam resolution) and configuration in resolution change 31 | - [x] broadcasting/playback stats (open controls and stats should show in few seconds) 32 | - [x] dark mode / lights on: each user can toggle interface mode live at runtime, SFX (sound effects) 33 | - [x] translation and text change support 34 | - [ ] request private 2 way calls / shows from group chat 35 | - [ ] random videochat with Next button to move to different performer room 36 | - [ ] live wallet balance display (updates from tips and other transfers) 37 | - [ ] tips with multiple customizable options, gift images 38 | 39 | Warning: some of these features are not active/implemented in this simplified edition, but can be enabled as in turnkey site edition. 40 | 41 | ## Installation Instructions 42 | 43 | Before installing, make sure your hosting environment meets all [requirements](https://videowhisper.com/?p=Requirements) including the Wowza SE as HTML5 WebRTC streaming relay and/or the [VideoWhisper WebRTC signaling server](https://github.com/videowhisper/videowhisper-webrtc/). Production implementations should also involve Session Control for security and website integration (like list of live channels). 44 | For testing, get a free plan from [WebRTC Host: P2P](https://webrtchost.com/hosting-plans/#WebRTC-Only). 45 | 46 | 1. If you don't use a [turnkey webrtc relay streaming host](https://webrtchost.com/hosting-plans/), configure WebRTC + SSL with Wowza SE or the VideoWhisper WebRTC + STUN/TURN server. 47 | 2. Deploy files to your web installation location. (Example: yoursite.domain/html5-videochat/) 48 | 3. Fill your streaming settings in settings.php file 49 | 4. If you don't have SuPHP, enable write permissions (0777) for folder "uploads", required to save session and chat info. 50 | 51 | ## Plain PHP Edition Limitations 52 | 53 | - The plain php edition refers to minimal scripts for configuring and accessing videochat room, so developers can integrate with own scripts. 54 | - Plain php edition does not involve database and systems to manage members, rooms, billing. These depend on framework you want to integrate, plugins, database, member system. 55 | - Applications reads parameters, wallet balance and other data with ajax calls from framework/integration scripts (that need to be implemented depending on framework, database, user scripts). 56 | - A complete implementation of features is available for WordPress framework. See [Turnkey HTML5 Videochat Site](https://paidvideochat.com/html5-videochat/) edition, available as WordPress plugin with full php source. Includes user role management (performers/clients), pay per minute, integrates billing wallets. 57 | - Plain edition implements 1 way streaming and chat with broadcast / playback screens for broadcaster and other participants. Application supports but this edition does not implement signaling for requesting 2 way video calls or parameters and content for conference/collaborations. 58 | - Setup starts in demo mode, to prevent high resource usage by visitors. To enable and confirm full mode you need to fill application version in modeVersion parameter. [Consult VideoWhisper](https://consult.videowhisper.com) for assistance or a turnkey site setup. 59 | 60 | ## Main Integration Scripts 61 | 62 | - index.php embeds the html5 application: accessed directly creates a room and shows room link to invite others 63 | - app-call.php is called by application to retrieve parameters, interact with web server, update status and chat (ajax calls) 64 | - app-functions.php functions implementing features for app-call.php , including translated texts, app settings 65 | - settings.php settings and options, including streaming settings and url for calls (when integrating with own framework) 66 | 67 | Scripts also contain comments for clarifications/suggestions. 68 | 69 | This is a simple setup showcasing easy app deployment and integration with other PHP scripts. 70 | For a quick setup, see [VideoWhisper Turnkey Stream Hosting Plans](https://webrtchost.com/hosting-plans/) that include requirements for all features and free installation. 71 | 72 | ### VideoWhisper HTML5 Project Demos 73 | 74 | - [Video Call PHP / HTML5 Videochat on Wowza SE](https://demo.videowhisper.com/videocall-html5-videochat-php/) 75 | - [Video Call PHP / HTML5 Videochat on VideoWhisper WebRTC](https://demo.videowhisper.com/p2p-html5-videocall/) 76 | - [Live Streaming PHP / HTML5 Videochat on Wowza SE](https://demo.videowhisper.com/html5-videochat-php/) 77 | - [Live Streaming PHP / HTML5 Videochat on VideoWhisper WebRTC](https://demo.videowhisper.com/vws-html5-livestreaming/) 78 | - [Cam/Mic Recorder HTML5 - Standalone](https://demo.videowhisper.com/cam-recorder-html5-video-audio/) 79 | - [PaidVideochat Turnkey Site](https://paidvideochat.com/demo/) 80 | 81 | ### VideoWhisper HTML5 Project Downloads 82 | 83 | - [Video Call - HTML5 Videochat - GitHub](https://github.com/videowhisper/VideoCall-HTML5-Videochat-PHP) 84 | - [Live Streaming - HTML5 Videochat - GitHub](https://github.com/videowhisper/HTML5-Videochat-PHP) 85 | - [Cam/Mic Recorder HTML5 - GitHub](https://github.com/videowhisper/Cam-Recorder-HTML5-Video-Audio) 86 | - [PaidVideochat Turnkey Site - WordPress](https://wordpress.org/plugins/ppv-live-webcams/) 87 | - [Video Calls & Random Chat Turnkey Site - WordPress](https://wordpress.org/plugins/webcam-2way-videochat/) 88 | - [WebRTC Signaling Server](https://github.com/videowhisper/videowhisper-webrtc/) 89 | 90 | [Consult VideoWhisper](https://consult.videowhisper.com) for commercial services like turnkey site platforms, compatible hosting, custom development services. 91 | -------------------------------------------------------------------------------- /app-call.php: -------------------------------------------------------------------------------- 1 | intval($userID), 40 | 'name'=> (($userID>10000)?'Performer':'User') . $userID, 41 | 'sessionID'=> intval($sessionID), 42 | 'loggedIn' => true, 43 | 'balance' => 100, 44 | 'avatar' => VW_H5V_URL .'images/avatar.png', 45 | ]; 46 | 47 | 48 | //on login check if any private request was active to restore 49 | //return private room/session if active, depending on integration 50 | $response['room'] = appPublicRoom($roomID, $userID, $options, 'Login success!'); 51 | 52 | //config params, const 53 | $response['config'] = [ 54 | 'serverType' => $options['serverType'], 55 | 'vwsSocket' => $options['vwsSocket'], 56 | 'vwsToken' => $options['vwsToken'], 57 | 58 | 'wss' => $options['wsURLWebRTC'], 59 | 'application' => $options['applicationWebRTC'], 60 | 61 | 'videoCodec' => $options['webrtcVideoCodec'], 62 | 'videoBitrate' => $options['webrtcVideoBitrate'], 63 | 'audioBitrate' => $options['webrtcAudioBitrate'], 64 | 'audioCodec' => $options['webrtcAudioCodec'], 65 | 66 | 'snapshotInterval' => 180, 67 | 'snapshotDisable' => true, 68 | 69 | // 'autoBroadcast' => false, 70 | 'actionFullscreen' => true, 71 | 'actionFullpage' => false, 72 | 73 | 'serverURL' => VW_H5V_CALL, 74 | 'modeVersion' => '', 75 | 76 | ]; 77 | 78 | $response['config']['text'] = appText(); //translations 79 | $response['config']['sfx'] = appSfx(); 80 | 81 | $response['config']['exitURL'] = VW_H5V_URL . 'info.php?i=exit'; 82 | $response['config']['balanceURL'] = VW_H5V_URL . 'info.php?i=wallet' ; 83 | 84 | //pass app setup config parameters 85 | if (is_array($options['appSetup'])) 86 | if (array_key_exists('Config', $options['appSetup'])) 87 | if (is_array($options['appSetup']['Config'])) 88 | foreach ($options['appSetup']['Config'] as $key => $value) 89 | $response['config'][$key] = $value; 90 | 91 | 92 | if (VW_DEVMODE) 93 | { 94 | // $response['config']['cameraAutoBroadcast'] = '0'; 95 | // $response['config']['videoAutoPlay '] = '0'; 96 | 97 | } 98 | 99 | //if (!$isPerformer) $response['config']['cameraAutoBroadcast'] = '0'; 100 | 101 | $response['config']['loaded'] = true; 102 | 103 | } 104 | //end: task==login 105 | 106 | 107 | //room 108 | $roomName = 'Room' . $roomID; 109 | $userName = (($userID>10000)?'Performer':'User') . $userID; 110 | $ztime = time(); 111 | 112 | //create/update session in room session list 113 | 114 | $sessions = arrayLoad($roomID . '_sessions'); 115 | if (array_key_exists($sessionID, $sessions)) $session = $sessions[$sessionID]; 116 | else $session = array( 117 | 'id' => $sessionID, 118 | 'uid' => $userID, 119 | 'sdate' => time(), 120 | 'madeBy' => 'access', 121 | ); 122 | 123 | $session['edate'] = time(); 124 | 125 | $sessions[$sessionID] = $session; 126 | 127 | varSave($roomID . '_sessions', $sessions); 128 | 129 | 130 | //update private session if in private mode 131 | 132 | $needUpdate = array(); 133 | 134 | //process app task (other than login) 135 | switch ($task) 136 | { 137 | case 'snapshot': 138 | break; 139 | 140 | case 'login': 141 | break; 142 | 143 | case 'tick': 144 | break; 145 | 146 | case 'options': 147 | break; 148 | 149 | case 'update': 150 | //something changed - let everybody know : update room 151 | $update = filter_var($_POST['update'], FILTER_SANITIZE_STRING); 152 | 153 | 154 | $room = arrayLoad($roomID . '_room'); 155 | $room['updated_' . $update] = time(); 156 | 157 | varSave($roomID . '_room', $room); 158 | $needUpdate[$update] = 1; 159 | break; 160 | 161 | case 'media': 162 | //notify user media (streaming) updates 163 | 164 | $connected = ($_POST['connected'] == 'true'?true:false); 165 | 166 | if ($session['meta']) 167 | if (!is_array($userMeta = unserialize($session['meta']))) $userMeta = array(); 168 | 169 | $userMeta['connected'] = $connected; 170 | $userMeta['connectedUpdate'] = time(); 171 | 172 | $userMetaS = serialize($userMeta); 173 | 174 | $sessions = arrayLoad($roomID . '_sessions'); 175 | if (!array_key_exists($sessionID, $sessions)) $sessions[$sessionID] = array('id' =>$sessionID, 'sdate' => time(), 176 | 'madeBy'=>'media'); 177 | $sessions[$sessionID]['meta'] = $userMetaS; 178 | 179 | varSave($roomID . '_sessions', $sessions); 180 | break; 181 | 182 | 183 | case 'message': 184 | 185 | $message = $_POST['message']; //array 186 | 187 | if ($message) 188 | { 189 | $response['message'] = $message; 190 | } 191 | 192 | $messageText = filter_var( $message['text'], FILTER_SANITIZE_STRING); 193 | $messageUser = filter_var( $message['userName'], FILTER_SANITIZE_STRING); 194 | $messageUserAvatar = filter_var( $message['userAvatar'], FILTER_SANITIZE_URL); 195 | 196 | $meta = array( 197 | 'notification'=> filter_var($message['notification'],FILTER_SANITIZE_STRING), 198 | 'userAvatar' => $messageUserAvatar, 199 | 'mentionMessage' => intval($message['mentionMessage']), 200 | 'mentionUser'=> filter_var($message['mentionUser'],FILTER_SANITIZE_STRING), 201 | ); 202 | $metaS = serialize($meta); 203 | 204 | if (!$privateUID) $privateUID = 0; //public room 205 | 206 | $messages = arrayLoad($roomID . '_messages'); 207 | $ix = count($messages)+1; 208 | 209 | 210 | $messageNew = array( 211 | 'id' => $ix, 212 | 'username' => $messageUser, 213 | 'room' => $roomName, 214 | 'room_id' => $roomID, 215 | 'message' => $messageText, 216 | 'mdate' => $ztime, 217 | 'type' => 2, 218 | 'user_id' => $userID, 219 | 'meta' => $metaS, 220 | 'private_uid' => $privateUID 221 | ); 222 | 223 | 224 | $messages[$ix] = $messageNew; 225 | $response['messageNew'] = $messageNew; 226 | 227 | varSave($roomID . '_messages', $messages); 228 | 229 | break; 230 | 231 | 232 | case 'recorder_upload': 233 | 234 | 235 | if (!$roomName) appFail('No room for recording.'); 236 | if (strstr($filename, ".php")) appFail('Bad uploader!'); 237 | 238 | 239 | $mode = $_POST['mode']; // video/audio 240 | $scenario = $_POST['scenario']; // chat/standalone 241 | 242 | if (!$privateUID) $privateUID = 0; //public room 243 | 244 | //generate same private room folder for both users 245 | if ($privateUID) 246 | { 247 | if ($isPerformer) $proom = $userID . "_" . $privateUID; //performer id first 248 | else $proom = $privateUID ."_". $userID; 249 | } 250 | 251 | $destination = 'uploads'; 252 | if (!file_exists($destination)) mkdir($destination); 253 | 254 | $destination.="/$roomName"; 255 | if (!file_exists($destination)) mkdir($destination); 256 | 257 | if ($proom) 258 | { 259 | $destination.="/$proom"; 260 | if (!file_exists($destination)) mkdir($destination); 261 | } 262 | 263 | $response['_FILES'] = $_FILES; 264 | 265 | 266 | $allowed = array('mp3', 'ogg', 'opus', 'mp4', 'webm', 'mkv'); 267 | 268 | $uploads = 0; 269 | $filename = ''; 270 | 271 | if ($_FILES) if (is_array($_FILES)) 272 | foreach ($_FILES as $ix => $file) 273 | { 274 | 275 | $filename = filter_var( $file['name'], FILTER_SANITIZE_STRING); 276 | 277 | $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); 278 | $response['uploadRecLastExt'] = $ext; 279 | $response['uploadRecLastF'] = $filename; 280 | 281 | $filepath = $destination . '/' . $filename; 282 | 283 | if (in_array($ext, $allowed)) 284 | if (file_exists($file['tmp_name'])) 285 | { 286 | move_uploaded_file($file['tmp_name'], $filepath); 287 | $response['uploadRecLast'] = $destination . $filename; 288 | $uploads++; 289 | } 290 | } 291 | 292 | $response['uploadCount'] = $uploads; 293 | 294 | 295 | if (!file_exists($filepath)) 296 | { 297 | $response['warning'] = 'Recording upload failed!'; 298 | } 299 | 300 | 301 | 302 | if ( !$response['warning'] && $scenario == 'chat' ) 303 | { 304 | $url = path2url($filepath); 305 | 306 | $response['recordingUploadSize'] = filesize($filepath); 307 | $response['recordingUploadURL'] = $url; 308 | 309 | $messageText = ''; 310 | $messageUser = $userName; 311 | $userAvatar = VW_H5V_URL .'images/avatar.png'; 312 | $messageUserAvatar = $userAvatar; 313 | 314 | $meta = array( 315 | 'userAvatar' => $messageUserAvatar, 316 | ); 317 | 318 | if ($mode == 'video') $meta['video']= $url; 319 | else $meta['audio']= $url; 320 | 321 | $metaS = serialize($meta); 322 | 323 | 324 | $messages = arrayLoad($roomID . '_messages'); 325 | $ix = count($messages)+1; 326 | 327 | $messageNew = array( 328 | 'id' => $ix, 329 | 'username' => $messageUser, 330 | 'room' => $roomName, 331 | 'room_id' => $roomID, 332 | 'message' => $messageText, 333 | 'mdate' => $ztime, 334 | 'type' => 2, 335 | 'user_id' => $userID, 336 | 'meta' => $metaS, 337 | 'private_uid' => $privateUID 338 | ); 339 | $messages[$ix] = $messageNew; 340 | 341 | varSave($roomID . '_messages', $messages); 342 | 343 | $response['messageNew'] = $messageNew; 344 | 345 | //also update chat log file 346 | /* 347 | if ($roomName) 348 | { 349 | $messageText = strip_tags($messageText, '
');
350 | $messageText = date("F j, Y, g:i a", $ztime) . " $userName: $messageText ";
351 | $day=date("y-M-j", time());
352 | $dfile = fopen($destination . "/Log$day.html", "a");
353 | fputs($dfile, $messageText."
");
354 | fclose($dfile);
355 | }
356 | */
357 | }
358 | break;
359 |
360 | default:
361 | $response['warning'] = 'Not implemented in this integration: ' . $task;
362 |
363 | }
364 |
365 |
366 | //update time
367 | $lastMessage = intval($_POST['lastMessage'] ?? 0);
368 | $lastMessageID = intval($_POST['lastMessageID'] ?? 0);
369 |
370 | //retrieve only messages since user came online / updated
371 | $sdate = 0;
372 | if ($session) $sdate = $session['sdate'];
373 | $startTime = max($sdate, $lastMessage);
374 |
375 | $response['startTime'] = $startTime;
376 |
377 |
378 | //!messages
379 |
380 | $messages = arrayLoad($roomID . '_messages');
381 |
382 | //clean old chat logs
383 | $closeTime = time() - 900; //only keep for 15min
384 | foreach ($messages as $key => $message) if ($message['mdate'] < $closeTime) unset($messages[$key]);
385 | varSave($roomID . '_messages', $messages);
386 |
387 |
388 |
389 | //sort by key (time)
390 | ksort($messages);
391 |
392 |
393 | $items = array();
394 | $idMax = 0;
395 |
396 |
397 | foreach ($messages as $key => $message)
398 | {
399 | $show = 1;
400 |
401 | //chat message or own notification (type 3)
402 | if ($message['type'] > 3) $show = 0;
403 | if ($message['type'] == 3 && ($message['user_id'] != $userID && $message['username'] != $userName) ) $show = 0;
404 |
405 |
406 | if (!$privateUID) if ($message['private_uid'] != 0) $show = 0; //private messages only in private
407 |
408 | if ($privateUID)
409 | {
410 | if ($message['private_uid'] != $privateUID) $show = 0; //not in this private
411 | if ($message['user_id'] != $userID && $message['user_id'] != $privateUID) $show = 0; //not for user or other
412 | }
413 |
414 |
415 | //enable this to show only messages after entering $startTime:
416 | //if ($message['mdate'] < $startTime || $message['mdate'] > $ztime) $show = 0;
417 |
418 | if ($message['id'] <= $lastMessageID) $show = 0;
419 |
420 | $idMax = 0;
421 | if ($show)
422 | {
423 | $item = [];
424 |
425 | $item['ID'] = intval($message['id']);
426 | if ($item['ID']>$idMax) $idMax = $item['ID'];
427 |
428 | $item['userName'] = $message['username'];
429 | $item['userID'] = intval($message['user_id']);
430 |
431 | $item['text'] = $message['message'];
432 | $item['time'] = intval($message['mdate'] * 1000); //time in ms for js
433 |
434 | $item['userAvatar'] = VW_H5V_URL .'images/avatar.png';
435 |
436 | //meta
437 | if ($message['meta'])
438 | {
439 | $meta = unserialize($message['meta']);
440 | foreach ($meta as $key=>$value) $item[$key] = $value;
441 |
442 | $item['notification'] = (isset($meta['notification']) && $meta['notification'] == 'true' ? true : false );
443 | }
444 |
445 | if ($message['type'] == 3) $item['notification'] = true;
446 |
447 | $items[] = $item;
448 | }
449 |
450 | }
451 |
452 |
453 | $response['messages'] = $items; //messages list
454 |
455 | $response['timestamp'] = $ztime; //update time
456 | ///update message
457 |
458 | $response['lastMessageID'] = $idMax;
459 |
460 | $response['roomUpdate']['users'] = appRoomUsers($roomID, $options);
461 |
462 |
463 | //send response to app
464 | echo json_encode($response);
465 |
--------------------------------------------------------------------------------
/app-functions.php:
--------------------------------------------------------------------------------
1 | $base . 'message.mp3',
70 | 'hello' => $base . 'hello.mp3',
71 | 'leave' => $base . 'leave.mp3',
72 | 'call' => $base . 'call.mp3',
73 | 'warning' => $base . 'warning.mp3',
74 | 'error' => $base . 'error.mp3',
75 | 'buzz' => $base . 'buzz.mp3',
76 | );
77 | }
78 |
79 | function appText()
80 | {
81 | //implement translations
82 |
83 | //returns texts
84 | return array(
85 | 'Send' => __('Send', 'ppv-live-webcams'),
86 | 'Type your message' => __('Type your message', 'ppv-live-webcams'),
87 |
88 | 'Chat' => __('Chat', 'ppv-live-webcams'),
89 | 'Camera' => __('Camera', 'ppv-live-webcams'),
90 | 'Users' => __('Users', 'ppv-live-webcams'),
91 | 'Options' => __('Options', 'ppv-live-webcams'),
92 | 'Files' => __('Files', 'ppv-live-webcams'),
93 | 'Presentation' => __('Presentation', 'ppv-live-webcams'),
94 |
95 | 'Tap for Sound' => __('Tap for Sound', 'ppv-live-webcams'),
96 | 'Enable Audio' => __('Enable Audio', 'ppv-live-webcams'),
97 | 'Mute' => __('Mute', 'ppv-live-webcams'),
98 | 'Reload' => __('Reload', 'ppv-live-webcams'),
99 |
100 | 'Broadcast' => __('Broadcast', 'ppv-live-webcams'),
101 | 'Stop Broadcast' => __('Stop Broadcast', 'ppv-live-webcams'),
102 | 'Make a selection to start!' => __('Make a selection to start!', 'ppv-live-webcams'),
103 |
104 | 'Lights On' => __('Lights On', 'ppv-live-webcams'),
105 | 'Dark Mode' => __('Dark Mode', 'ppv-live-webcams'),
106 | 'Enter Fullscreen' => __('Enter Fullscreen', 'ppv-live-webcams'),
107 | 'Exit Fullscreen' => __('Exit Fullscreen', 'ppv-live-webcams'),
108 |
109 | 'Site Menu' => __('Site Menu', 'ppv-live-webcams'),
110 |
111 | 'Request Private' => __('Request Private', 'ppv-live-webcams'),
112 | 'Request Private 2 Way Videochat Show' => __('Request Private 2 Way Videochat Show', 'ppv-live-webcams'),
113 | 'Performer Disabled Private Requests' => __('Performer Disabled Private Requests', 'ppv-live-webcams'),
114 | 'Performer is Busy in Private' => __('Performer is Busy in Private', 'ppv-live-webcams'),
115 | 'Performer is Not Online' => __('Performer is Not Online', 'ppv-live-webcams'),
116 | 'Nevermind' => __('Nevermind', 'ppv-live-webcams'),
117 | 'Accept' => __('Accept', 'ppv-live-webcams'),
118 | 'Decline' => __('Decline', 'ppv-live-webcams'),
119 | 'Close Private' => __('Close Private', 'ppv-live-webcams'),
120 |
121 | 'Next' => __('Next', 'ppv-live-webcams'),
122 | 'Next: Random Videochat Room' => __('Next: Random Videochat Room', 'ppv-live-webcams'),
123 |
124 | 'Name' => __('Name', 'ppv-live-webcams'),
125 | 'Size' => __('Size', 'ppv-live-webcams'),
126 | 'Age' => __('Age', 'ppv-live-webcams'),
127 | 'Upload: Drag and drop files here, or click to select files' => __('Upload: Drag and drop files here, or click to select files', 'ppv-live-webcams'),
128 | 'Uploading. Please wait...' => __('Uploading. Please wait...', 'ppv-live-webcams'),
129 | 'Open' => __('Open', 'ppv-live-webcams'),
130 | 'Delete' => __('Delete', 'ppv-live-webcams'),
131 |
132 | 'Media Displayed' => __('Media Displayed', 'ppv-live-webcams'),
133 | 'Remove' => __('Remove', 'ppv-live-webcams'),
134 | 'Default' => __('Default', 'ppv-live-webcams'),
135 | 'Empty' => __('Empty', 'ppv-live-webcams'),
136 |
137 | 'Profile' => __('Profile', 'ppv-live-webcams'),
138 | 'Show' => __('Show', 'ppv-live-webcams'),
139 |
140 | 'Private Call' => __('Private Call', 'ppv-live-webcams'),
141 | 'Exit' => __('Exit', 'ppv-live-webcams'),
142 |
143 | 'External Broadcast' => __('External Broadcast', 'ppv-live-webcams'),
144 | 'Broadcast with external apps for advanced compositions, scenes, effects or reliability compared to web based interface and protocols. External broadcasts have higher latency and improved capacity, reliability specific to HLS delivery method. New broadcasts show in about 10 seconds and unavailability updates after 1 minute.' => __('Broadcast with external apps for advanced compositions, scenes, effects or reliability compared to web based interface and protocols. External broadcasts have higher latency and improved capacity, reliability specific to HLS delivery method. New broadcasts show in about 10 seconds and unavailability updates after 1 minute.', 'ppv-live-webcams'),
145 | );
146 | }
147 |
148 |
149 | function appRoomUsers($roomID, $options)
150 | {
151 |
152 | $sessions = arrayLoad($roomID . '_sessions');
153 |
154 | foreach ($sessions as $key => $session)
155 | {
156 | if (!isset($session['meta']) || !is_array( $userMeta = unserialize($session['meta']) ) ) $userMeta = array();
157 |
158 | $item = [];
159 | $item['userID'] = intval($session['uid']);
160 | $item['userName'] = $session['username'] ?? '';
161 | if (!$item['userName']) $item['userName'] = '#' . $session['uid'];
162 |
163 | $item['sdate'] = intval($session['sdate']);
164 | $item['meta'] = $userMeta;
165 | $item['updated'] = intval($session['edate']);
166 | $item['avatar'] = VW_H5V_URL .'images/avatar.png';
167 | $item['url'] = 'https://videowhisper.com/tickets_submit.php';
168 |
169 | $items[intval($session['uid'])] = $item;
170 | }
171 |
172 | return $items;
173 | }
174 |
175 |
176 | function appTipOptions($options)
177 | {
178 |
179 | $tipOptions = stripslashes($options['tipOptions']);
180 | if ($tipOptions)
181 | {
182 | $p = xml_parser_create();
183 | xml_parse_into_struct($p, trim($tipOptions), $vals, $index);
184 | $error = xml_get_error_code($p);
185 | xml_parser_free($p);
186 |
187 | if (is_array($vals)) return $vals;
188 | }
189 |
190 | return array();
191 |
192 | }
193 |
194 |
195 | function appStream($userID, $roomID, $options)
196 | {
197 | $key = $options['webKey'] . $roomID; //a secret key to implement stream verification
198 |
199 | return 'stream' . $userID . '?channel_id=' . $roomID . '&userID=' . urlencode($userID) . '&key=' . urlencode($key) . '&ip=' . urlencode($_SERVER['REMOTE_ADDR']) . '&transcoding=0&room=Room' . $roomID. '&privateUID=0';
200 |
201 | }
202 |
203 | function appPublicRoom($roomID, $userID, $options, $welcome ='')
204 | {
205 | //public room parameters, specific for this user
206 | //depends on integration
207 |
208 | $room = array();
209 |
210 | $room['ID'] = $roomID;
211 | $room['name'] = 'Room' . $roomID;
212 |
213 | $room['performer'] = 'Performer' . (10000+$roomID);
214 | $room['performerID'] = (10000+$roomID);
215 |
216 | $collaboration = $options['collaboration'];
217 | if (VW_DEVMODE && VW_DEVMODE_COLLABORATION) $collaboration = true;
218 |
219 | $isPerformer = ($userID == (10000+$roomID));
220 |
221 | //screen
222 | if ($isPerformer) $roomScreen = 'BroadcastScreen';
223 | else $roomScreen = 'PlaybackScreen';
224 | if ($collaboration) $roomScreen = 'CollaborationScreen';
225 | $room['screen'] = $roomScreen;
226 |
227 | $room['streamBroadcast'] = appStream($userID, $roomID, $options);
228 |
229 | $room['streamUID'] = intval($room['performerID']);
230 | $room['streamPlayback'] = appStream($room['performerID'], $roomID, $options);
231 |
232 | //$room['actionPrivate'] = !$isPerformer;
233 | $room['actionPrivateClose'] = false;
234 | $room['privateUID'] = 0;
235 |
236 | $room['actionID'] = 0;
237 |
238 | $room['welcome'] = ' 💬 ' . sprintf('Welcome to public room "%s", user #%s!', $room['name'] , $userID);
239 | $room['welcomeImage'] = VW_H5V_URL . 'images/chat.png';
240 |
241 |
242 |
243 | if ($isPerformer) //member: performer
244 | {
245 | $room['welcome'] .= "\n 📡 ". 'You are broadcaster (room owner/performer). Use best network available if you have the option: 5GHz on WiFi instead of 2.4 GHz, LTE/4G on mobile instead of 3G, wired instead of wireless. ';
246 | }
247 | else //member: client
248 | {
249 | $room['welcome'] .= "\n 👤 ".'You are invited participant.';
250 | }
251 |
252 |
253 |
254 |
255 | //if ($options['videochatNext']) if (!$isPerformer) $room['next'] = true;
256 |
257 | if ($welcome) $room['welcome'] .= "\n" . $welcome;
258 |
259 | //configure tipping options for clients
260 | $room['tips'] = false;
261 | if ($options['tips'])
262 | if (!$isPerformer)
263 | {
264 | $tipOptions = appTipOptions($options);
265 | if (count($tipOptions))
266 | {
267 | $room['tipOptions'] = $tipOptions;
268 | $room['tips'] = true;
269 | $room['tipsURL'] = VW_H5V_URL . 'tips/';
270 | }
271 | }
272 |
273 | //demo goal
274 | $room['welcome'] .= "\n 🎁 " . 'Current gifts goal' .': '. 'Demo Goal';
275 | $room['welcome'] .= "\n - " . 'Goal description' .': ' . 'Chat can display goals that can be achieved with gifts/donations.';
276 | $room['welcomeProgressValue'] = 8;
277 | $room['welcomeProgressTotal'] = 10;
278 | $room['welcomeProgressDetails'] = 'Demo Goal';
279 |
280 | //offline snapshot (poster) and video
281 | $room['snapshot'] = VW_H5V_URL . 'images/no-picture.png';
282 | $room['videoOffline'] = VW_H5V_URL . 'videos/hamsterad.mp4';;
283 |
284 | return $room;
285 | }
286 |
287 |
--------------------------------------------------------------------------------
/clean_older.php:
--------------------------------------------------------------------------------
1 | $old) return @rmdir($path);
28 | else return -1;
29 | } else {
30 | if ($delete_this) if (time()-filemtime($path)> $old) return @unlink($path);
31 | else return -1;
32 | }
33 | return -1;
34 | }
35 |
36 | function cleanUp($dir)
37 | {
38 | global $old;
39 |
40 | echo "Room URL
Participants can access room playback screen at this address:
' . $roomURL;
57 | $bodyCode .= '
Watch ';
58 | $bodyCode .= 'HTML5 Videochat - Plain PHP / Live Streaming: Broadcast & Playback
This setup implements the room lobby with simple live video streaming (from performer to participants) and text chat. This is a simple embedding preview edition, with simple scripts to embed app and showcase few features.
66 |
67 |
+ Official Live Demo for Live Streaming / HTML5 Videochat Standalone PHP: Live Streaming on WowzaSE | Live Streaming on VideoWhisper WebRTC
68 |
+ Download from GitHub: Live Streaming / HTML5 Videochat PHP
69 |
+ All Plain PHP Demos: P2P Video Call | Video Call on Wowza SE | Live Streaming on Wowza SE | Live Streaming on VideoWhisper WebRTC | Cam/Mic Recorder
70 |
+ Server GitHub: VideoWhisper WebRTC signaling server (NodeJS, supports using STUN/TURN serverlike CoTURN)
71 |
+ For testing, get a Free plan from WebRTC Host: P2P.
72 |
+ By default application starts in demo mode, for free testing with low resources by site visitors.
73 |
+ Technical support & turnkey site plans: Consult VideoWhisper
74 |
+ Turnkey Cam Site Solution as WP plugins: Turnkey HTML5 Videochat Site.
75 | Integration Info
This functionality depends on site framework, features, database, user and billing systems.