CameraServices.php 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: qiuzijian
  5. * Date: 2021-04-20
  6. * Time: 15:33
  7. */
  8. namespace Modules\Camera\Services;
  9. use App\Enum\ApiEnum;
  10. use Illuminate\Support\Facades\Cache;
  11. use Illuminate\Support\Facades\Config;
  12. use Illuminate\Support\Facades\Input;
  13. use Illuminate\Support\Facades\Log;
  14. use Modules\Admin\Enum\AdminEnum;
  15. use Modules\Camera\Entities\CameraFieldList;
  16. use Modules\Camera\Entities\CameraList;
  17. use Modules\Camera\Enum\CameraEnum;
  18. use Modules\Camera\Http\Controllers\Api\HaiKangController;
  19. use Modules\Mine\Entities\MineList;
  20. use Modules\Mine\Entities\MineListExt;
  21. use Modules\Mine\Entities\WorkingSurfaceCamera;
  22. use Modules\Mine\Enum\MineEnum;
  23. use Modules\Mine\Services\MineServices;
  24. use OSS\OssClient;
  25. class CameraServices
  26. {
  27. //添加摄像头链接
  28. public static function addCameraList($params)
  29. {
  30. $result['status'] = true;
  31. $result['msg'] = AdminEnum::RETURN_SUCCESS;
  32. //通过ip+端口+通道号作为唯一标识判断数据是否存在
  33. if ($params['ip'] && $params['port'] && $params['com_number']) {
  34. $query = CameraList::where(
  35. [
  36. 'ip' => $params['ip'],
  37. 'port' => $params['port'],
  38. 'com_number' => $params['com_number'],
  39. ]
  40. )->first();
  41. if ($query) {
  42. $result['status'] = false;
  43. $result['msg'] = AdminEnum::RECORD_ALREADY_EXIST;
  44. return $result;
  45. }
  46. }
  47. if ($params['camera_source'] == CameraEnum::CAMERA_SOURCE_3) {
  48. $camera_url = $params['camera_url'];
  49. } else {
  50. $camera_url = self::makeCameraUrl($params);
  51. }
  52. //入库动作
  53. $res = CameraList::insert(
  54. [
  55. 'mine_id' => $params['mine_id'],
  56. 'ip' => $params['ip'],
  57. 'port' => $params['port'],
  58. 'user_name' => $params['user_name'],
  59. 'password' => $params['password'],
  60. 'com_number' => $params['com_number'],
  61. 'camera_name' => $params['camera_name'],
  62. 'revert_id' => CameraEnum::CAMERA_DEFAULT_REVERT_ID,
  63. // 'status' => $params['status'],
  64. 'camera_url' => $camera_url,
  65. 'sort' => $params['sort'],
  66. 'camera_type' => $params['camera_type'],
  67. 'camera_source' => $params['camera_source'],
  68. 'code_stream' => $params['code_stream'],
  69. 'cover_picture' => $params['cover_picture'],
  70. 'video_recorder' => $params['video_recorder'],
  71. 'created_at' => date('Y-m-d H:i:s'),
  72. 'updated_at' => date('Y-m-d H:i:s'),
  73. ]
  74. );
  75. if (!$res) {
  76. $result['status'] = false;
  77. $result['msg'] = AdminEnum::DATABASE_ERROR;
  78. }
  79. return $result;
  80. }
  81. //生成摄像头请求链接
  82. public static function makeCameraUrl($params)
  83. {
  84. if ($params['video_recorder'] == CameraEnum::VIDEO_RECORDER_HK) {
  85. if (isset($params['code_stream'])) {
  86. $code_stream = CameraEnum::CODE_STREAM_TRANS[$params['code_stream']];
  87. } else {
  88. $code_stream = 'sub';
  89. }
  90. if ($params['user_name'] && $params['password'] && $params['ip'] && $params['port'] && $params['com_number']) {
  91. $url = 'rtsp://' . $params['user_name'] . ':' . $params['password'] . '@';
  92. $url .= $params['ip'] . ':' . $params['port'] . '/' . 'h264' . '/' . 'ch' . $params['com_number'] . '/';
  93. $url .= $code_stream . '/av_stream'; //sub 子码流 main 主码流
  94. } else {
  95. $url = '';
  96. }
  97. } elseif($params['video_recorder'] == CameraEnum::VIDEO_RECORDER_DH){
  98. if (isset($params['code_stream'])) {
  99. $code_stream = CameraEnum::CODE_STREAM_DH[$params['code_stream']];
  100. } else {
  101. $code_stream = 'sub';
  102. }
  103. if ($params['user_name'] && $params['password'] && $params['ip'] && $params['port'] && $params['com_number']) {
  104. $url = 'rtsp://' . $params['user_name'] . ':' . $params['password'] . '@';
  105. $url .= $params['ip'] . ':' . $params['port'] . '/cam/realmonitor?channel=' . $params['com_number'] . '&';
  106. $url .= 'subtype=' . $code_stream;
  107. } else {
  108. $url = '';
  109. }
  110. }elseif($params['video_recorder'] == CameraEnum::VIDEO_RECORDER_YS){
  111. if (isset($params['code_stream'])) {
  112. $code_stream = CameraEnum::CODE_STREAM_DH[$params['code_stream']];
  113. } else {
  114. $code_stream = 'sub';
  115. }
  116. if ($params['user_name'] && $params['password'] && $params['ip'] && $params['port'] && $params['com_number']) {
  117. $url = 'rtsp://' . $params['user_name'] . ':' . $params['password'] . '@';
  118. $url .= $params['ip'] . ':' . $params['port'] . '/unicast/c' . $params['com_number'] . '/s';
  119. $url .= $code_stream.'/live';
  120. } else {
  121. $url = '';
  122. }
  123. }
  124. return $url;
  125. }
  126. //编辑摄像头链接
  127. public static function editCameraList($params)
  128. {
  129. $result['status'] = true;
  130. $result['msg'] = AdminEnum::RETURN_SUCCESS;
  131. if ($params['camera_source'] == CameraEnum::CAMERA_SOURCE_3) {
  132. $camera_url = $params['camera_url'];
  133. } else {
  134. $camera_url = self::makeCameraUrl($params);
  135. }
  136. //判断记录是否存在
  137. $query = CameraList::find($params['id']);
  138. if (!$query) {
  139. $result['status'] = false;
  140. $result['msg'] = AdminEnum::RECORD_NOT_EXIST;
  141. return $result;
  142. }
  143. //通过ip+端口+通道号作为唯一标识判断数据是否存在
  144. if ($params['ip'] && $params['port'] && $params['com_number']) {
  145. $query = CameraList::where(
  146. [
  147. 'ip' => $params['ip'],
  148. 'port' => $params['port'],
  149. 'com_number' => $params['com_number'],
  150. ]
  151. )->where('id', '!=', $params['id'])->first();
  152. if ($query && !$params['cover_picture']) {
  153. $result['status'] = false;
  154. $result['msg'] = AdminEnum::RECORD_ALREADY_EXIST;
  155. return $result;
  156. }
  157. }
  158. //更新动作
  159. $res = CameraList::where('id', $params['id'])->update(
  160. [
  161. 'mine_id' => $params['mine_id'],
  162. 'ip' => $params['ip'],
  163. 'port' => $params['port'],
  164. 'user_name' => $params['user_name'],
  165. 'password' => $params['password'],
  166. 'com_number' => $params['com_number'],
  167. 'camera_name' => $params['camera_name'],
  168. // 'status' => $params['status'],
  169. 'camera_url' => $camera_url,
  170. 'sort' => $params['sort'],
  171. 'camera_type' => $params['camera_type'],
  172. 'camera_source' => $params['camera_source'],
  173. 'code_stream' => $params['code_stream'],
  174. 'cover_picture' => $params['cover_picture'],
  175. 'video_recorder' => $params['video_recorder'],
  176. 'updated_at' => date('Y-m-d H:i:s'),
  177. ]
  178. );
  179. if (!$res) {
  180. $result['status'] = false;
  181. $result['msg'] = AdminEnum::DATABASE_ERROR;
  182. }
  183. return $result;
  184. }
  185. //删除摄像头列表记录
  186. public static function delCameraList($params)
  187. {
  188. $result['status'] = true;
  189. $result['msg'] = AdminEnum::RETURN_SUCCESS;
  190. //判断字段是否存在
  191. $query = CameraList::find($params['id']);
  192. if (!$query) {
  193. $result['status'] = false;
  194. $result['msg'] = AdminEnum::RECORD_NOT_EXIST;
  195. return $result;
  196. }
  197. $res = CameraList::where('id', $params['id'])->delete();
  198. if (!$res) {
  199. $result['status'] = false;
  200. $result['msg'] = AdminEnum::DATABASE_ERROR;
  201. }
  202. return $result;
  203. }
  204. //添加摄像头链接生成字段
  205. public static function addCameraFieldList($params)
  206. {
  207. $result['status'] = true;
  208. $result['msg'] = AdminEnum::RETURN_SUCCESS;
  209. //判断字段是否已存在
  210. $query = CameraFieldList::where('field_name', $params['field_name'])->first();
  211. if ($query) {
  212. $result['status'] = false;
  213. $result['msg'] = AdminEnum::RECORD_ALREADY_EXIST;
  214. return $result;
  215. }
  216. //入库动作
  217. $res = CameraFieldList::insert(
  218. [
  219. 'field_name' => $params['field_name'],
  220. 'created_at' => date('Y-m-d H:i:s'),
  221. 'updated_at' => date('Y-m-d H:i:s'),
  222. ]
  223. );
  224. if (!$res) {
  225. $result['status'] = false;
  226. $result['msg'] = AdminEnum::DATABASE_ERROR;
  227. }
  228. return $result;
  229. }
  230. //编辑摄像头链接生成字段
  231. public static function editCameraFieldList($params)
  232. {
  233. $result['status'] = true;
  234. $result['msg'] = AdminEnum::RETURN_SUCCESS;
  235. //判断字段是否存在
  236. $query = CameraFieldList::find($params['id']);
  237. if (!$query) {
  238. $result['status'] = false;
  239. $result['msg'] = AdminEnum::RECORD_NOT_EXIST;
  240. return $result;
  241. }
  242. $res = CameraFieldList::where('id', $params['id'])->update(
  243. [
  244. 'field_name' => $params['field_name'],
  245. 'updated_at' => date('Y-m-d H:i:s'),
  246. ]
  247. );
  248. if (!$res) {
  249. $result['status'] = false;
  250. $result['msg'] = AdminEnum::DATABASE_ERROR;
  251. }
  252. return $result;
  253. }
  254. //删除摄像头链接生成字段
  255. public static function delCameraFieldList($params)
  256. {
  257. $result['status'] = true;
  258. $result['msg'] = AdminEnum::RETURN_SUCCESS;
  259. //判断字段是否存在
  260. $query = CameraFieldList::find($params['id']);
  261. if (!$query) {
  262. $result['status'] = false;
  263. $result['msg'] = AdminEnum::RECORD_NOT_EXIST;
  264. return $result;
  265. }
  266. $res = CameraFieldList::where('id', $params['id'])->delete();
  267. if (!$res) {
  268. $result['status'] = false;
  269. $result['msg'] = AdminEnum::DATABASE_ERROR;
  270. }
  271. return $result;
  272. }
  273. //根据矿区id获取摄像头列表
  274. public static function getCameraListByMineId($mine_id, $camera_type = CameraEnum::CAMERA_TYPE_ALL, $parent_id, $surface_id = '')
  275. {
  276. $result['status'] = true;
  277. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  278. $query = CameraList::where('is_show', CameraEnum::IS_SHOW_YES)->orderBy('sort', 'desc');
  279. if ($camera_type && $camera_type != CameraEnum::CAMERA_TYPE_ALL) {
  280. $query = $query->where('camera_type', $camera_type);
  281. }
  282. $query = $query->select(
  283. [
  284. 'id as camera_id',
  285. 'mine_id',
  286. 'camera_name',
  287. 'cover_picture',
  288. 'camera_status',
  289. ]
  290. )->get()->toArray();
  291. $surface_camera = [];
  292. if ($surface_id) {
  293. $surface_camera = WorkingSurfaceCamera::where('surface_id', $surface_id)->pluck('camera_id')->all();
  294. }
  295. $arr = [];
  296. foreach ($query as $key => $val) {
  297. if ($surface_id && !in_array($val['camera_id'], $surface_camera)) {
  298. continue;
  299. }
  300. $val['parent_id'] = $parent_id;
  301. if ($camera_type && !in_array($camera_type, [CameraEnum::CAMERA_TYPE_ALL, CameraEnum::CAMERA_TYPE_NORMAL])) {
  302. $degree = MineList::where('id', $val['mine_id'])->value('degree');
  303. if ($mine_id == explode('|', $degree)[0]) {
  304. $arr[] = $val;
  305. }
  306. } else {
  307. if ($mine_id == $val['mine_id']) {
  308. $arr[] = $val;
  309. }
  310. }
  311. }
  312. $result['data'] = $arr;
  313. return $result;
  314. }
  315. //根据摄像头id获取摄像头播放链接
  316. public static function getCameraUrlByCameraId($camera_id)
  317. {
  318. $result['status'] = true;
  319. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  320. $query = CameraList::where('id', $camera_id)->first();
  321. if (!$query) {
  322. $result['status'] = false;
  323. $result['msg'] = ApiEnum::NO_CAMERA_URL;
  324. return $result;
  325. }
  326. $degree = MineList::where('id', $query->mine_id)->value('degree');
  327. $mine_list = MineList::whereIn('id', explode('|', $degree))->select(['title'])->get()->toArray();
  328. $path = '';
  329. foreach ($mine_list as $key => $val) {
  330. $path .= '/' . $val['title'];
  331. }
  332. $path .= '/' . $query->camera_name . $camera_id;
  333. //标记摄像头为在线状态
  334. CameraList::where('id', $camera_id)->update(['camera_status' => CameraEnum::CAMERA_STATUS_ONLINE]);
  335. //已有拉流id,直接返回拉流链接
  336. $has_file = file_exists(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/' . CameraEnum::M3U8_FILE_NAME);
  337. if ($has_file) {
  338. $result['data'] = [
  339. 'camera_id' => $camera_id,
  340. 'url' => env('VIDEO_SYSTEM_URL') . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/' . CameraEnum::M3U8_FILE_NAME . '?' . time(),
  341. ];
  342. return $result;
  343. }
  344. $data = [
  345. 'url' => $query->camera_url,
  346. 'customPath' => $path,
  347. ];
  348. Log::info('请求流媒体服务器参数------------');
  349. Log::info($data);
  350. $curl = env('EASY_DARWIN_URL') . CameraEnum::API_STREAM_START . '?' . http_build_query($data);
  351. //请求流媒体服务器拉流
  352. $curl_res = curl_request($curl);
  353. Log::info('请求流媒体服务器result------------');
  354. Log::info($curl_res);
  355. if (strpos($curl_res,' ')) {
  356. //标记摄像头为异常状态
  357. CameraList::where('id', $camera_id)->update(['camera_status' => CameraEnum::CAMERA_STATUS_ERROR]);
  358. $result['status'] = false;
  359. $result['msg'] = ApiEnum::EASY_DAWIN_REQUEST_FAIL;
  360. return $result;
  361. }
  362. $result['data'] = [
  363. 'camera_id' => $camera_id,
  364. 'url' => env('VIDEO_SYSTEM_URL') . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/' . CameraEnum::M3U8_FILE_NAME . '?' . time(),
  365. ];
  366. //sleep10秒生成文件
  367. $i = 1;
  368. while ($i <= 10) {
  369. //判断m3u8文件是否存在
  370. $file_exists = file_exists(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/' . CameraEnum::M3U8_FILE_NAME);
  371. //判断ts文件是否存在
  372. $ts_exists = glob(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/' . CameraEnum::TS_FILE_SUFFIX);
  373. //查询是否存在封面图
  374. $cover_picture = CameraList::where('id', $camera_id)->value('cover_picture');
  375. if ($file_exists && $ts_exists) {
  376. //查询是否存在封面图
  377. if (!$cover_picture) {
  378. //获取第一帧作为封面图
  379. $file_name = time() . CameraEnum::COVER_PICTURE_NAME;
  380. $cover_exec = 'ffmpeg -i ' . public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/' . CameraEnum::M3U8_FILE_NAME . ' -vf "select=between(mod(n\, 25)\, 0\, 0), setpts=N/24/TB" ' . public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/' . $file_name;
  381. exec($cover_exec);
  382. //判断图片是否存在
  383. $picture_exists = file_exists(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/' . $file_name);
  384. if ($picture_exists) {
  385. //上传图片到oss并更新数据库
  386. $picture_path = self::uploadOssFile($file_name, public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path . '/' . date('Ymd', time()) . '/');
  387. CameraList::where('id', $camera_id)->update(['cover_picture' => $picture_path]);
  388. }
  389. }
  390. //更新拉流id
  391. CameraList::where('id', $camera_id)->update(
  392. [
  393. 'revert_id' => trim($curl_res, '"'),
  394. 'updated_at' => date('Y-m-d H:i:s'),
  395. ]
  396. );
  397. return $result;
  398. } else {
  399. sleep(1);
  400. }
  401. $i++;
  402. }
  403. //标记摄像头为异常状态
  404. CameraList::where('id', $camera_id)->update(['camera_status' => CameraEnum::CAMERA_STATUS_ERROR]);
  405. $result['status'] = false;
  406. $result['msg'] = ApiEnum::EASY_DAWIN_REQUEST_FAIL;
  407. return $result;
  408. }
  409. //停止流媒体推流
  410. public static function stopCameraStream()
  411. {
  412. $result['status'] = true;
  413. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  414. //先查询流媒体推流列表
  415. $data = [
  416. // 'start' => 0,
  417. // 'limit' => 100,
  418. ];
  419. $curl = env('EASY_DARWIN_URL') . CameraEnum::API_PUSHERS . '?' . http_build_query($data);
  420. //请求流媒体服务器获取推流列表
  421. $curl_res = curl_request($curl);
  422. $curl_res = json_decode($curl_res, true);
  423. $success = 0;
  424. if ($curl_res['total'] > 0) {
  425. //根据返回id调停止推流接口
  426. foreach ($curl_res['rows'] as $key => $val) {
  427. $data = [
  428. 'id' => $val['id']
  429. ];
  430. $curl = env('EASY_DARWIN_URL') . CameraEnum::API_STREAM_STOP . '?' . http_build_query($data);
  431. //请求流媒体服务器停止推流
  432. $stop_res = curl_request($curl);
  433. $stop_res = json_decode($stop_res, true);
  434. if ($stop_res == 'OK') {
  435. //更新拉流id为默认id
  436. CameraList::where('revert_id', $val['id'])->update(
  437. [
  438. 'revert_id' => CameraEnum::CAMERA_DEFAULT_REVERT_ID,
  439. 'updated_at' => date('Y-m-d H:i:s'),
  440. ]
  441. );
  442. $success++;
  443. } else {
  444. Log::info('流媒体服务器停止推流失败id:' . $val['id']);
  445. Log::info('流媒体服务器停止推流失败返回结果');
  446. Log::info($stop_res);
  447. }
  448. }
  449. }
  450. if ($success == $curl_res['total']) {
  451. CameraList::where('revert_id', '!=', CameraEnum::CAMERA_DEFAULT_REVERT_ID)->update(
  452. [
  453. 'revert_id' => CameraEnum::CAMERA_DEFAULT_REVERT_ID,
  454. 'updated_at' => date('Y-m-d H:i:s'),
  455. ]
  456. );
  457. }
  458. //清空海康视频服务器已存在id
  459. CameraList::where('revert_id', CameraEnum::CAMERA_FILE_EXIST)->update(['revert_id' => CameraEnum::CAMERA_DEFAULT_REVERT_ID]);
  460. //递归删除目录
  461. $dir = public_path() . '/files/video/m3u8/';
  462. if (is_dir($dir)) {
  463. deldir($dir);
  464. }
  465. return $result;
  466. }
  467. //查询海康矿井摄像头列表
  468. public static function getHaiKangCamera($parent_id, $mine_id, $camera_type)
  469. {
  470. $result['status'] = true;
  471. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  472. $result['data'] = [];
  473. if ($camera_type != CameraEnum::CAMERA_TYPE_ALL) {
  474. return $result;
  475. }
  476. $query = MineListExt::where('mine_id', $parent_id)->first();
  477. $list = [];
  478. Input::replace(
  479. [
  480. 'url' => trim($query->ip, '/') . ':' . $query->port,
  481. 'key' => $query->key,
  482. 'secret' => $query->secret,
  483. 'indexCode' => $mine_id,
  484. ]
  485. );
  486. $haikang = new HaiKangController();
  487. $result_hk = $haikang->getCamerasList();
  488. if ($result_hk['data']['list']) {
  489. foreach ($result_hk['data']['list'] as $key => $val) {
  490. $list[$key]['mine_id'] = $mine_id;
  491. $list[$key]['camera_id'] = $val['cameraIndexCode'];
  492. $list[$key]['camera_name'] = $val['cameraName'];
  493. $list[$key]['trans_code'] = $val['encodeDevIndexCode'];
  494. $list[$key]['com_number'] = $val['channelNo'];
  495. }
  496. }
  497. $result['data'] = $list;
  498. return $result;
  499. }
  500. //查询海康摄像头编码设备列表
  501. public static function getHaiKangTransList($parent_id)
  502. {
  503. $result['status'] = true;
  504. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  505. $result['data'] = [];
  506. $query = MineListExt::where('mine_id', $parent_id)->first();
  507. //先获取编码设备列表
  508. Input::replace(
  509. [
  510. 'url' => trim($query->ip, '/') . ':' . $query->port,
  511. 'key' => $query->key,
  512. 'secret' => $query->secret,
  513. ]
  514. );
  515. $haikang = new HaiKangController();
  516. $result_hk = $haikang->getTranscodeList();
  517. if ($result_hk['data']['list']) {
  518. $result['data'] = $result_hk['data']['list'];
  519. }
  520. return $result;
  521. }
  522. //查询海康矿井摄像头播放链接
  523. public static function getHaiKangCameraUrl($parent_id, $camera_id)
  524. {
  525. $result['status'] = true;
  526. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  527. $result['data'] = [];
  528. $query = MineListExt::where('mine_id', $parent_id)->first();
  529. $slug = MineList::where('id', $parent_id)->value('slug');
  530. $count = strpos($slug, 'MeiKuang');
  531. if ($count) {
  532. $slug = substr_replace($slug, '', $count, 8);
  533. }
  534. $slug = strtolower($slug);
  535. Input::replace(
  536. [
  537. 'url' => trim($query->ip, '/') . ':' . $query->port,
  538. 'key' => $query->key,
  539. 'secret' => $query->secret,
  540. 'cameraIndexCode' => $camera_id,
  541. ]
  542. );
  543. $haikang = new HaiKangController();
  544. $result_hk = $haikang->getCamerasUrl();
  545. if ($result_hk['data']['url']) {
  546. $url = CameraEnum::VIDEO_AGENCY_URL . $slug . '/video/' . strstr($result_hk['data']['url'], 'openUrl');
  547. $result['data']['camera_id'] = $camera_id;
  548. $result['data']['url'] = $url;
  549. }
  550. return $result;
  551. }
  552. //查询海康矿井摄像头列表
  553. public static function getHaiKangTransCode($parent_id)
  554. {
  555. $result['status'] = true;
  556. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  557. $result['data'] = [];
  558. $query = MineListExt::where('mine_id', $parent_id)->first();
  559. Input::replace(
  560. [
  561. 'url' => trim($query->ip, '/') . ':' . $query->port,
  562. 'key' => $query->key,
  563. 'secret' => $query->secret,
  564. ]
  565. );
  566. $haikang = new HaiKangController();
  567. $result_hk = $haikang->getTranscodeInfo();
  568. if ($result_hk) {
  569. $result['data'] = json_decode($result_hk, true);
  570. }
  571. return $result;
  572. }
  573. //获取大南湖监控列表
  574. public static function getLeChangeCamera()
  575. {
  576. $result['status'] = true;
  577. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  578. $token = self::getLeChangeToken();
  579. if (!$token) {
  580. $result['status'] = false;
  581. $result['msg'] = ApiEnum::LECHANGE_REQUEST_FAIL;
  582. return $result;
  583. }
  584. $url = CameraEnum::LIVE_LIST_URL;
  585. $params = [
  586. 'token' => $token,
  587. 'queryRange' => '1-10',
  588. ];
  589. $data = self::getLeChangeSign($params);
  590. $ch = curl_init();
  591. $res = curl_setopt($ch, CURLOPT_URL, $url);
  592. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  593. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  594. curl_setopt($ch, CURLOPT_HEADER, 0);
  595. curl_setopt($ch, CURLOPT_POST, 1);
  596. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  597. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  598. $curl_res = curl_exec($ch);
  599. curl_close($ch);
  600. $curl_res = json_decode(iconv('utf-8', 'UTF-8', $curl_res), true);
  601. if ($curl_res == null || $curl_res['result']['msg'] != CameraEnum::LECHANGE_API_SUCCESS) {
  602. $result['status'] = false;
  603. $result['msg'] = ApiEnum::LECHANGE_REQUEST_FAIL;
  604. return $result;
  605. }
  606. foreach ($curl_res['result']['data']['lives'] as $key => $val) {
  607. $result['data'][$key] = [
  608. 'camera_name' => $val['deviceId'],
  609. 'camera_url' => $val['streams'][3]['hls']
  610. ];
  611. }
  612. return $result;
  613. }
  614. //乐橙获取access_token
  615. public static function getLeChangeToken()
  616. {
  617. if (Cache::has(CameraEnum::ACCESS_TOKEN_CACHE_KEY)) {
  618. return Cache::get(CameraEnum::ACCESS_TOKEN_CACHE_KEY);
  619. }
  620. $url = CameraEnum::ACCESS_TOKEN_URL;
  621. $data = self::getLeChangeSign();
  622. $ch = curl_init();
  623. $res = curl_setopt($ch, CURLOPT_URL, $url);
  624. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  625. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  626. curl_setopt($ch, CURLOPT_HEADER, 0);
  627. curl_setopt($ch, CURLOPT_POST, 1);
  628. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  629. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  630. $result = curl_exec($ch);
  631. curl_close($ch);
  632. $result = json_decode(nl2br(iconv('utf-8', 'UTF-8', $result)), true);
  633. if ($result == null || $result['result']['msg'] != CameraEnum::LECHANGE_API_SUCCESS) {
  634. return false;
  635. }
  636. $token = $result['result']['data']['accessToken'];
  637. $time = substr($result['result']['data']['expireTime'], 0, 2) . '0000';
  638. Cache::put(CameraEnum::ACCESS_TOKEN_CACHE_KEY, $token, $time);
  639. return $token;
  640. }
  641. //乐橙数据接口验签
  642. private static function getLeChangeSign($params = [])
  643. {
  644. $time = strtotime(date('Y-m-d H:i:s', time()));
  645. $nonce = md5(time());
  646. $appId = CameraEnum::LECHANGE_APPID;
  647. $appSecret = CameraEnum::LECHANGE_APPSECRET;
  648. $signStr = "time:$time,nonce:$nonce,appSecret:$appSecret";
  649. $sign = md5($signStr);
  650. $data = json_encode(
  651. [
  652. 'system' =>
  653. [
  654. 'ver' => '1.0',
  655. 'sign' => $sign,
  656. 'appId' => $appId,
  657. 'time' => $time,
  658. 'nonce' => $nonce
  659. ],
  660. 'params' => empty($params) ? new \stdClass() : $params,
  661. 'id' => '88'
  662. ]
  663. );
  664. return $data;
  665. }
  666. //通过rtmp同步m3u8文件并返回url
  667. public static function downloadCameraFiles($parent_id, $camera_id)
  668. {
  669. $result['status'] = true;
  670. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  671. $query = MineListExt::where('mine_id', $parent_id)->first();
  672. $camera = CameraList::find($camera_id);
  673. $degree = MineList::where('id', $camera->mine_id)->value('degree');
  674. $degree = explode('|', $degree);
  675. $path = '';
  676. foreach ($degree as $key => $val) {
  677. $path .= MineList::where('id', $val)->value('title') . '/';
  678. }
  679. $path .= $camera->camera_name . '/' . date('Ymd');
  680. //标记摄像头为在线状态
  681. CameraList::where('id', $camera_id)->update(['camera_status' => CameraEnum::CAMERA_STATUS_ONLINE]);
  682. //如果m3u8文件已拉取,直接返回播放链接
  683. if ($camera->revert_id == CameraEnum::CAMERA_FILE_EXIST) {
  684. $result['data'] = [
  685. 'camera_id' => $camera_id,
  686. 'url' => env('VIDEO_SYSTEM_URL') . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . CameraEnum::M3U8_FILE_NAME . '?' . time(),
  687. ];
  688. return $result;
  689. }
  690. Input::replace(
  691. [
  692. 'url' => trim($query->ip, '/') . ':' . $query->port,
  693. 'key' => $query->key,
  694. 'secret' => $query->secret,
  695. 'cameraIndexCode' => $camera->index_code,
  696. 'streamType' => CameraEnum::CODE_STREAM_HAK[$camera->code_stream],
  697. 'protocol' => 'rtmp'
  698. ]
  699. );
  700. $haikang = new HaiKangController();
  701. $result_hk = $haikang->getCamerasUrl();
  702. if ($result_hk['data']['url']) {
  703. //先取当前码流
  704. $res = self::getVideoStream($result_hk, $path, $camera_id);
  705. if (!$res) {
  706. $code_stream = self::exchangCodeStream($camera->code_stream);
  707. Input::replace(
  708. [
  709. 'url' => trim($query->ip, '/') . ':' . $query->port,
  710. 'key' => $query->key,
  711. 'secret' => $query->secret,
  712. 'cameraIndexCode' => $camera->index_code,
  713. 'streamType' => CameraEnum::CODE_STREAM_HAK[$code_stream],
  714. 'protocol' => 'rtmp',
  715. ]
  716. );
  717. $haikang = new HaiKangController();
  718. $result_hk_2 = $haikang->getCamerasUrl();
  719. if (!$result_hk_2['data']['url']) {
  720. //标记摄像头为异常状态
  721. CameraList::where('id', $camera_id)->update(['camera_status' => CameraEnum::CAMERA_STATUS_ERROR]);
  722. $result['status'] = false;
  723. $result['msg'] = ApiEnum::HK_REQUEST_FAIL;
  724. return $result;
  725. }
  726. $res = self::getVideoStream($result_hk_2, $path, $camera_id);
  727. if (!$res) {
  728. //标记摄像头为异常状态
  729. CameraList::where('id', $camera_id)->update(['camera_status' => CameraEnum::CAMERA_STATUS_ERROR]);
  730. $result['status'] = false;
  731. $result['msg'] = ApiEnum::HK_REQUEST_FAIL;
  732. return $result;
  733. } else {
  734. //更新视频码流
  735. CameraList::where('id', $camera_id)->update(['code_stream' => $code_stream]);
  736. $result['data'] = $res['data'];
  737. }
  738. } else {
  739. $result['data'] = $res['data'];
  740. }
  741. } else { //直接获取rtmp失败时切换码流重新获取
  742. $code_stream = self::exchangCodeStream($camera->code_stream);
  743. Input::replace(
  744. [
  745. 'url' => trim($query->ip, '/') . ':' . $query->port,
  746. 'key' => $query->key,
  747. 'secret' => $query->secret,
  748. 'cameraIndexCode' => $camera->index_code,
  749. 'streamType' => CameraEnum::CODE_STREAM_HAK[$code_stream],
  750. 'protocol' => 'rtmp',
  751. ]
  752. );
  753. $haikang = new HaiKangController();
  754. $result_hk_2 = $haikang->getCamerasUrl();
  755. if (!$result_hk_2['data']['url']) {
  756. //标记摄像头为异常状态
  757. CameraList::where('id', $camera_id)->update(['camera_status' => CameraEnum::CAMERA_STATUS_ERROR]);
  758. $result['status'] = false;
  759. $result['msg'] = ApiEnum::HK_REQUEST_FAIL;
  760. return $result;
  761. }
  762. $res = self::getVideoStream($result_hk_2, $path, $camera_id);
  763. if (!$res) {
  764. //标记摄像头为异常状态
  765. CameraList::where('id', $camera_id)->update(['camera_status' => CameraEnum::CAMERA_STATUS_ERROR]);
  766. $result['status'] = false;
  767. $result['msg'] = ApiEnum::HK_REQUEST_FAIL;
  768. return $result;
  769. } else {
  770. //更新视频码流
  771. CameraList::where('id', $camera_id)->update(['code_stream' => $code_stream]);
  772. $result['data'] = $res['data'];
  773. }
  774. }
  775. return $result;
  776. }
  777. //获取视频流
  778. public static function getVideoStream($result_hk, $path, $camera_id)
  779. {
  780. $result = [];
  781. if (!is_dir('/www/wwwroot/video.nxjiewei.com/public/' . CameraEnum::M3U8_FILE_PATH . '/' . $path)) {
  782. mkdir('/www/wwwroot/video.nxjiewei.com/public/' . CameraEnum::M3U8_FILE_PATH . '/' . $path, 0777, true);
  783. }
  784. //取流进程
  785. $exec = 'nohup /usr/bin/ffmpeg -i ' . $result_hk['data']['url'] . ' -vcodec copy -acodec copy -vbsf h264_mp4toannexb -f hls -hls_flags delete_segments -segment_list_size 10 -hls_list_size 2 /www/wwwroot/video.nxjiewei.com/public/' . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . CameraEnum::M3U8_FILE_NAME . ' >/dev/null &';
  786. shell_exec($exec);
  787. //sleep10秒生成文件
  788. $i = 1;
  789. while ($i <= 10) {
  790. //判断m3u8文件是否存在
  791. $file_exists = file_exists(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . CameraEnum::M3U8_FILE_NAME);
  792. //判断ts文件是否存在
  793. $ts_exists = glob(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . CameraEnum::TS_FILE_SUFFIX);
  794. //查询是否存在封面图
  795. $cover_picture = CameraList::where('id', $camera_id)->value('cover_picture');
  796. if ($file_exists && $ts_exists) {
  797. //查询是否存在封面图
  798. if (!$cover_picture) {
  799. //获取第一帧作为封面图
  800. $file_name = time() . CameraEnum::COVER_PICTURE_NAME;
  801. $cover_exec = 'ffmpeg -i ' . public_path() . '/' . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . CameraEnum::M3U8_FILE_NAME . ' -vf "select=between(mod(n\, 25)\, 0\, 0), setpts=N/24/TB" ' . public_path() . '/' . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . $file_name;
  802. exec($cover_exec);
  803. //判断图片是否存在
  804. $picture_exists = file_exists(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . $file_name);
  805. if ($picture_exists) {
  806. //上传图片到oss并更新数据库
  807. $picture_path = self::uploadOssFile($file_name, public_path() . '/' . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/');
  808. CameraList::where('id', $camera_id)->update(['cover_picture' => $picture_path]);
  809. }
  810. }
  811. $result['data'] = [
  812. 'camera_id' => $camera_id,
  813. 'url' => env('VIDEO_SYSTEM_URL') . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . CameraEnum::M3U8_FILE_NAME . '?' . time(),
  814. ];
  815. CameraList::where('id', $camera_id)->update(['revert_id' => CameraEnum::CAMERA_FILE_EXIST]);
  816. return $result;
  817. } else {
  818. sleep(1);
  819. }
  820. $i++;
  821. }
  822. return false;
  823. }
  824. //切换视频码流
  825. public static function exchangCodeStream($code_stream)
  826. {
  827. $return_code = CameraEnum::CODE_STREAM_MAIN;
  828. //子码流切主码流
  829. if ($code_stream == CameraEnum::CODE_STREAM_SUB) {
  830. $return_code = CameraEnum::CODE_STREAM_MAIN;
  831. }
  832. //主码流切子码流
  833. if ($code_stream == CameraEnum::CODE_STREAM_MAIN) {
  834. $return_code = CameraEnum::CODE_STREAM_SUB;
  835. }
  836. return $return_code;
  837. }
  838. //获取内网服务器摄像头播放地址
  839. public static function getIntranetCameraUrl($camera_id)
  840. {
  841. $result['status'] = true;
  842. $result['msg'] = ApiEnum::RETURN_SUCCESS;
  843. $camera_url = CameraList::where('id', $camera_id)->value('camera_url');
  844. if (!$camera_url) {
  845. $result['status'] = false;
  846. $result['msg'] = ApiEnum::NO_CAMERA_URL;
  847. return $result;
  848. }
  849. $result['data'] = [
  850. 'camera_id' => $camera_id,
  851. 'url' => rtrim($camera_url, '/') . '/' . date('Ymd', time()) . '/' . CameraEnum::M3U8_FILE_NAME
  852. ];
  853. return $result;
  854. }
  855. //上传文件到本地
  856. public static function uploadFile($file)
  857. {
  858. $destinationPath = public_path() . '/filedownload/';
  859. $extension = $file->getClientOriginalExtension();
  860. $fileName = time() . '.' . $extension;
  861. $file->move($destinationPath, $fileName);
  862. return $fileName;
  863. }
  864. //oss上传文件
  865. public static function uploadOssFile($file_name, $file_path)
  866. {
  867. try{
  868. $oss = new OssClient(
  869. Config::get('filesystems.disks.oss_view.access_key'),
  870. Config::get('filesystems.disks.oss_view.secret_key'),
  871. Config::get('filesystems.disks.oss_view.endpoint')
  872. );
  873. $res = $oss->uploadFile(Config::get('filesystems.disks.oss_view.bucket'), '摄像头封面/oss-' . $file_name, $file_path . $file_name);
  874. } catch(OssException $e) {
  875. printf(__FUNCTION__ . ": FAILED\n");
  876. printf($e->getMessage() . "\n");
  877. return;
  878. }
  879. if (isset($res['info']['url']) && $res['info']['url']) {
  880. return urldecode($res['info']['url']);
  881. } else {
  882. return false;
  883. }
  884. }
  885. //删除临时上传文件
  886. public static function delUploadFile($delDir = FALSE)
  887. {
  888. $path = public_path() . '/filedownload/';
  889. $handle = opendir($path);
  890. if ($handle) {
  891. while (false !== ( $item = readdir($handle) )) {
  892. if ($item != "." && $item != "..")
  893. is_dir("$path/$item") ? delDirAndFile("$path/$item", $delDir) : unlink("$path/$item");
  894. }
  895. closedir($handle);
  896. if ($delDir)
  897. return rmdir($path);
  898. }else {
  899. if (file_exists($path)) {
  900. return unlink($path);
  901. } else {
  902. return FALSE;
  903. }
  904. }
  905. }
  906. }