BxStrmEngineOvenMediaEngine.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup Stream Stream
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. class BxStrmEngineOvenMediaEngine extends BxDol
  12. {
  13. public function getName()
  14. {
  15. return 'OvenMediaEngine';
  16. }
  17. public function isSreamFromBrowser()
  18. {
  19. return true;
  20. }
  21. public function getViewersNum($sStreamKey)
  22. {
  23. $a = $this->getStreamStats($sStreamKey);
  24. if (!$a)
  25. return false;
  26. if (isset($a['totalConnections'])) // old API
  27. return $a['totalConnections'];
  28. $i = 0;
  29. foreach ($a['connections'] as $k => $v) {
  30. $i += $v;
  31. }
  32. return $i;
  33. }
  34. public function getStreamStats($sStreamKey)
  35. {
  36. $sApp = getParam('bx_stream_app');
  37. if (!$sApp)
  38. return false;
  39. return $this->_apiRequest("/stats/current/vhosts/default/apps/{$sApp}/streams/{$sStreamKey}");
  40. }
  41. public function startRecording($iRecordingId, $sStreamKey)
  42. {
  43. $sApp = getParam('bx_stream_app');
  44. $sSourceName = getParam('bx_stream_server_ome_recordings_source');
  45. if (!$sApp)
  46. return false;
  47. if (!$sSourceName)
  48. $sSourceName = $sStreamKey;
  49. else
  50. $sSourceName = str_replace('{key}', $sStreamKey, $sSourceName);
  51. return $this->_apiRequest("/vhosts/default/apps/{$sApp}:startRecord", 'post-json', [
  52. "id" => "{$iRecordingId}",
  53. "stream" => [
  54. "name" => $sSourceName,
  55. ],
  56. ]);
  57. }
  58. public function stopRecording($iRecordingId)
  59. {
  60. $sApp = getParam('bx_stream_app');
  61. if (!$sApp)
  62. return false;
  63. return $this->_apiRequest("/vhosts/default/apps/{$sApp}:stopRecord", 'post-json', [
  64. "id" => "{$iRecordingId}",
  65. ]);
  66. }
  67. public function getRecordings($iRecordingId)
  68. {
  69. $sApp = getParam('bx_stream_app');
  70. if (!$sApp)
  71. return false;
  72. return $this->_apiRequest("/vhosts/default/apps/{$sApp}:records", 'post-json', [
  73. "id" => "{$iRecordingId}",
  74. ]);
  75. }
  76. public function processRecordings ($iRecordingId, $aContentInfo, $oModule, $iTries = 0)
  77. {
  78. $CNF = &$oModule->_oConfig->CNF;
  79. $bDeleteRecording = false;
  80. $oStorage = BxDolStorage::getObjectInstance('bx_stream_recordings');
  81. if ($oStorage) {
  82. $sFileUrl = getParam('bx_stream_recordings_url') . $aContentInfo['key'] . '_' . $iRecordingId . '.ts';
  83. $oModule->_oDb->updateRecording($iRecordingId, ['status' => 'processing']);
  84. $iFileId = $oStorage->storeFileFromUrl($sFileUrl, true, $aContentInfo[$CNF['FIELD_AUTHOR']], $aContentInfo['id']);
  85. if ($iFileId) {
  86. $bDeleteRecording = true;
  87. }
  88. else {
  89. bx_log('bx_stream', "Store recording from URL(" . $sFileUrl . ") failed for content id({$aContentInfo['id']}): " . $oStorage->getErrorString(), BX_LOG_ERR);
  90. }
  91. }
  92. else {
  93. bx_log('bx_stream', "Store recording failed because 'bx_stream_recordings' storage doesn't exist", BX_LOG_ERR);
  94. $bDeleteRecording = true;
  95. }
  96. if ($bDeleteRecording || ($iTries + 1) > 10) {
  97. $oModule->_oDb->deleteRecording($iRecordingId);
  98. }
  99. else {
  100. $oModule->_oDb->updateRecording($iRecordingId, ['tries' => $iTries + 1, 'status' => 'pending']);
  101. }
  102. }
  103. public function getRtmpSettings($sStreamKey)
  104. {
  105. $sHost = getParam('bx_stream_server_host');
  106. $sApp = getParam('bx_stream_app');
  107. $sPolicySecret = getParam('bx_stream_server_ome_policy_secret');
  108. if (!$sHost || !$sApp)
  109. return false;
  110. $sBaseUrl = "rtmp://$sHost:1935/$sApp";
  111. if (!$sPolicySecret)
  112. return ['server' => $sBaseUrl, 'key' => $sStreamKey];
  113. return ['server' => $sBaseUrl, 'key' => str_replace($sBaseUrl . '/', '', $this->_signUrl($sBaseUrl . '/' . $sStreamKey, $sPolicySecret))];
  114. }
  115. public function getRtmpIngestUrl($sStreamKey)
  116. {
  117. if (!($a = $this->getRtmpSettings($sStreamKey)))
  118. return false;
  119. list ($sBaseUrl, $sStreamKey) = $a;
  120. return $a['server'] . '/' . $a['key'];
  121. $sHost = getParam('bx_stream_server_host');
  122. $sApp = getParam('bx_stream_app');
  123. $sPolicySecret = getParam('bx_stream_server_ome_policy_secret');
  124. if (!$sHost || !$sApp)
  125. return false;
  126. $sBaseUrl = "rtmp://$sHost:1935/$sApp/$sStreamKey";
  127. if (!$sPolicySecret)
  128. return $sBaseUrl;
  129. return $this->_signUrl($sBaseUrl, $sPolicySecret);
  130. }
  131. public function getWebrtcIngestUrl($sStreamKey)
  132. {
  133. $sHost = getParam('bx_stream_server_host');
  134. $sApp = getParam('bx_stream_app');
  135. $sPolicySecret = getParam('bx_stream_server_ome_policy_secret');
  136. $sBaseUrl = "wss://$sHost:3334/$sApp/$sStreamKey?direction=send";
  137. if (!$sPolicySecret)
  138. return $sBaseUrl;
  139. return $this->_signUrl($sBaseUrl, $sPolicySecret);
  140. }
  141. protected function _signUrl($sBaseUrl, $sPolicySecret)
  142. {
  143. $sSignatureQueryKeyName = 'signature';
  144. $sPolicyQueryKeyName = 'policy';
  145. $sPolicy = json_encode(['url_expire' => 2556104400000]);//(time() + 50*365*24*60*60)*1000]);
  146. // 1. Perform base64url() for POLICY (RFC 4648 5.)
  147. $sPolicyBase64 = $this->_base64URLEncode($sPolicy);
  148. // 2. Generates an URL such as "ws://ome_host:3333/app/stream?policy=${POLICY_BASE64}"
  149. $sPolicyUrl = bx_append_url_params($sBaseUrl, [$sPolicyQueryKeyName => $sPolicyBase64]);
  150. // 3. Perform sha1(base64url()) for SIGNATURE (RFC 4648 5.)
  151. $sSha1 = hash_hmac('sha1', $sPolicyUrl, $sPolicySecret, true);
  152. $sSignature = $this->_base64URLEncode($sSha1);
  153. // 4. Create the whole URL
  154. $sWholeUrl = bx_append_url_params($sPolicyUrl, [$sSignatureQueryKeyName => $sSignature]);
  155. return $sWholeUrl;
  156. }
  157. protected function _base64URLEncode($s)
  158. {
  159. return trim(strtr(base64_encode($s), '+/', '-_'), '=');
  160. }
  161. protected function _apiRequest($sUri, $sMethod = 'get', $aBody = [])
  162. {
  163. $sApiProtocol = getParam('bx_stream_server_ome_api_protocol');
  164. $sHost = getParam('bx_stream_server_host');
  165. $iApiPort = (int)getParam('bx_stream_server_ome_api_port');
  166. $sApiKey = getParam('bx_stream_server_ome_api_key');
  167. if (!$sApiProtocol || !$sHost || !$iApiPort || !$sApiKey)
  168. return false;
  169. $sUrl = $sApiProtocol . "://{$sHost}:$iApiPort/v1{$sUri}";
  170. $s = bx_file_get_contents($sUrl, $aBody, $sMethod, ['Authorization: Basic ' . $sApiKey]);
  171. bx_log('bx_stream_ome_api', $sUrl . "\n" . $s, BX_LOG_DEBUG);
  172. if (!$s)
  173. return false;
  174. $a = @json_decode($s, true);
  175. if (null === $a)
  176. return false;
  177. if (!isset($a['response']))
  178. return false;
  179. return $a['response'];
  180. }
  181. }
  182. /** @} */