﻿{"id":1553,"date":"2026-10-07T09:18:44","date_gmt":"2026-10-07T02:18:44","guid":{"rendered":"https:\/\/sangnguyen.drayddns.com\/?p=1553"},"modified":"2026-10-07T13:49:59","modified_gmt":"2026-10-07T06:49:59","slug":"huong-dan-tich-hop-he-thong-4-camera-imou-len-website","status":"publish","type":"post","link":"https:\/\/sangnguyen.drayddns.com\/index.php\/2026\/10\/07\/huong-dan-tich-hop-he-thong-4-camera-imou-len-website\/","title":{"rendered":"H\u01b0\u1edbng d\u1eabn T\u00edch h\u1ee3p h\u1ec7 th\u1ed1ng 4 Camera Imou l\u00ean Website"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>Ph\u1ea7n 1: Chu\u1ea9n b\u1ecb Camera (Thao t\u00e1c tr\u00ean App Imou Life)<\/strong>.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u0110\u1ec3 lu\u1ed3ng video c\u00f3 th\u1ec3 \u0111\u1ea9y l\u00ean Web m\u01b0\u1ee3t m\u00e0 v\u00e0 n\u00e9t nh\u1ea5t, ph\u1ea7n c\u1ee9ng camera ph\u1ea3i \u0111\u01b0\u1ee3c c\u1ea5u h\u00ecnh chu\u1ea9n ngay t\u1eeb \u0111\u1ea7u.<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Ki\u1ec3m tra quy\u1ec1n s\u1edf h\u1eefu g\u1ed1c:<\/strong> Camera ph\u1ea3i \u0111\u01b0\u1ee3c th\u00eam b\u1eb1ng ch\u00ednh t\u00e0i kho\u1ea3n Imou \u0111ang d\u00f9ng \u0111\u1ec3 t\u1ea1o API. N\u1ebfu l\u00e0 camera \u0111\u01b0\u1ee3c &#8220;chia s\u1ebb&#8221; (Share) t\u1eeb ng\u01b0\u1eddi kh\u00e1c, API s\u1ebd b\u00e1o l\u1ed7i OP1009 (No right).<\/li>\n\n\n\n<li><strong>\u0110\u1ed5i chu\u1ea9n n\u00e9n video (C\u1ef1c k\u1ef3 quan tr\u1ecdng):<\/strong> C\u00e1c tr\u00ecnh duy\u1ec7t web hi\u1ec7n t\u1ea1i kh\u00f4ng h\u1ed7 tr\u1ee3 gi\u1ea3i m\u00e3 H.265 qua HLS. N\u1ebfu \u0111\u1ec3 H.265, m\u00e1y ch\u1ee7 Imou s\u1ebd \u00e9p \u0111\u1ed9 ph\u00e2n gi\u1ea3i xu\u1ed1ng SD r\u1ea5t m\u1edd.\n<ul class=\"wp-block-list\">\n<li>M\u1edf app Imou Life &gt; C\u00e0i \u0111\u1eb7t Camera &gt; C\u00e0i \u0111\u1eb7t Video &gt; <strong>Chu\u1ea9n n\u00e9n video<\/strong>.<\/li>\n\n\n\n<li>\u0110\u1ed5i t\u1eeb <strong>H.265<\/strong> sang <strong>H.264<\/strong> \u0111\u1ec3 m\u1edf kh\u00f3a lu\u1ed3ng HD (streamId = 0) cho Web.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>L\u1ea5y m\u00e3 S\/N:<\/strong> Ghi l\u1ea1i m\u00e3 S\/N (Device ID) c\u1ee7a m\u1eb7t sau camera ho\u1eb7c trong ph\u1ea7n th\u00f4ng tin thi\u1ebft b\u1ecb tr\u00ean app.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Ph\u1ea7n 2: Kh\u1edfi t\u1ea1o API tr\u00ean Imou Cloud<\/strong>.<\/h2>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Truy c\u1eadp n\u1ec1n t\u1ea3ng m\u1edf c\u1ee7a Imou: <a href=\"https:\/\/open.imoulife.com\/\">Cloud Video And AIoT Services &#8211; Imou Open Platform<\/a>:\u0111\u0103ng nh\u1eadp b\u1eb1ng t\u00e0i kho\u1ea3n Imou g\u1ed1c.<\/li>\n\n\n\n<li>V\u00e0o ph\u1ea7n <strong>Control Board (B\u1ea3ng \u0111i\u1ec1u khi\u1ec3n)<\/strong> -&gt; <strong>My App<\/strong> -&gt; <strong>Create App<\/strong> \u0111\u1ec3 t\u1ea1o m\u1ed9t \u1ee9ng d\u1ee5ng m\u1edbi.<\/li>\n\n\n\n<li>L\u1ea5y th\u00f4ng tin x\u00e1c th\u1ef1c:\n<ul class=\"wp-block-list\">\n<li>Copy chu\u1ed7i <strong>AppId<\/strong>.<\/li>\n\n\n\n<li>Nh\u1ea5n n\u00fat <strong>Copy<\/strong> (bi\u1ec3u t\u01b0\u1ee3ng sao ch\u00e9p) b\u00ean c\u1ea1nh <strong>AppSecret<\/strong> \u0111\u1ec3 l\u1ea5y chu\u1ed7i 30 k\u00fd t\u1ef1. <em>(Tuy\u1ec7t \u0111\u1ed1i kh\u00f4ng d\u00f9ng chu\u1ed9t b\u00f4i \u0111en \u0111\u1ec3 tr\u00e1nh b\u1ecb l\u1ed7i, g\u00e2y l\u1ed7i sai ch\u1eef k\u00fd SN1001).<\/em><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Ph\u1ea7n 3: C\u1ea5u h\u00ecnh m\u00e3 ngu\u1ed3n PHP (Core System)<\/strong>.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">T\u1ea1o m\u1ed9t file t\u00ean l\u00e0 <strong><strong><em>XEMCAMERA.PHP<\/em><\/strong><\/strong> tr\u00ean m\u00e1y ch\u1ee7 web (n\u1ebfu ch\u1ea1y n\u1ed9i b\u1ed9 c\u00f3 th\u1ec3 \u0111\u1eb7t trong th\u01b0 m\u1ee5c root c\u1ee7a IIS tr\u00ean Windows Server, \u1edf \u0111\u00e2y m\u00ecnh s\u1ebd \u0111\u1eb7t l\u1ea1i th\u01b0 m\u1ee5c <strong>wwwroot<\/strong>). D\u00e1n to\u00e0n b\u1ed9 m\u00e3 ngu\u1ed3n chu\u1ea9n d\u01b0\u1edbi \u0111\u00e2y v\u00e0o file.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>\u0110o\u1ea1n code n\u00e0y \u0111\u00e3 bao g\u1ed3m: M\u00e0n h\u00ecnh kh\u00f3a m\u1eadt kh\u1ea9u (t\u1ef1 \u0111\u1ed9ng reset khi F5), l\u1ea5y Token 1 l\u1ea7n, l\u1ecdc lu\u1ed3ng HLS HTTPS chu\u1ea9n, v\u00e0 giao di\u1ec7n Grid 2&#215;2(4 camera).<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Sau khi coppy \u0111o\u1ea1n m\u00e3 b\u00ean d\u01b0\u1edbi v\u00e0 thay th\u1ebf th\u00f4ng tin c\u1ee7a b\u1ea1n \u0111\u1ea7y \u0111\u1ee7 v\u00e0 l\u01b0u l\u1ea1i file c\u00f3 \u0111u\u00f4i l\u00e0 PHP v\u00e0 d\u00e1n file v\u00e0o m\u1ee5c<\/em> <strong>wwwroot<\/strong> VD m\u00ecnh \u0111\u1eb7t t\u00ean l\u00e0 <strong><em>XEMCAMERA.PHP<\/em><\/strong> <em>th\u00ec s\u1ebd \u0111c nh\u01b0 h\u00ecnh:<\/em><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"652\" height=\"695\" src=\"https:\/\/sangnguyen.drayddns.com\/wp-content\/uploads\/2026\/10\/1.png\" alt=\"\" class=\"wp-image-1554\" style=\"aspect-ratio:0.9381509579529381;width:414px;height:auto\" srcset=\"https:\/\/sangnguyen.drayddns.com\/wp-content\/uploads\/2026\/10\/1.png 652w, https:\/\/sangnguyen.drayddns.com\/wp-content\/uploads\/2026\/10\/1-281x300.png 281w\" sizes=\"auto, (max-width: 652px) 100vw, 652px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">\u0110\u1ec3 truy c\u1eadp \u0111\u01b0\u1ee3c \u0111\u1ebfn Camera n\u00e0y ch\u1ec9 c\u1ea7n g\u00f5 nh\u01b0 Sau: <strong><em><a href=\"https:\/\/sangnguyen.drayddns.com\/XEMCAMERA.PHP\">https:\/\/sangnguyen.drayddns.com\/XEMCAMERA.PHP<\/a><\/em><\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u0110\u00e2y l\u00e0 to\u00e0n b\u1ed9 code (Code n\u00e0y d\u00e0nh cho giao di\u1ec7n 4 camera)<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\/\/ \u0110\u1eb6T M\u1eacT KH\u1ea8U C\u1ee6A B\u1ea0N \u1ede \u0110\u00c2Y\n$password = '\u0110\u1eb6T MK KH\u00d3A \u1ede \u0110\u00c2Y'; \n$da_dang_nhap = false;\n\n\/\/ Ki\u1ec3m tra m\u1eadt kh\u1ea9u \u0111\u01b0\u1ee3c g\u1eedi l\u00ean\nif (isset($_POST&#91;'mat_khau']) &amp;&amp; $_POST&#91;'mat_khau'] === $password) {\n    $da_dang_nhap = true;\n}\n\n\/\/ N\u1ebfu sai m\u1eadt kh\u1ea9u ho\u1eb7c load l\u1ea1i trang (kh\u00f4ng c\u00f3 POST) -> D\u1eebng v\u00e0 b\u1eaft nh\u1eadp l\u1ea1i\nif (!$da_dang_nhap) {\n    echo '\n    &lt;!DOCTYPE html>\n    &lt;html lang=\"vi\">\n    &lt;head>\n        &lt;meta charset=\"UTF-8\">\n        &lt;title>H\u1ec7 Th\u1ed1ng An Ninh&lt;\/title>\n        &lt;style>\n            body { font-family: Arial, sans-serif; background: #111; color: #fff; text-align: center; padding-top: 150px; }\n            .login-box { background: #222; padding: 30px; border-radius: 8px; border: 2px solid #444; display: inline-block; box-shadow: 0 4px 15px rgba(0,0,0,0.5); }\n            input { padding: 10px; width: 200px; border-radius: 4px; border: 1px solid #555; background: #333; color: #fff; outline: none; margin-bottom: 15px; }\n            button { padding: 10px 20px; background: #00bcd4; color: #fff; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; }\n            button:hover { background: #0097a7; }\n        &lt;\/style>\n    &lt;\/head>\n    &lt;body>\n        &lt;div class=\"login-box\">\n            &lt;h3 style=\"margin-top:0; color:#00bcd4;\">NH\u1eacP M\u1eacT KH\u1ea8U QU\u1ea2N TR\u1eca&lt;\/h3>\n            &lt;form method=\"POST\">\n                &lt;input type=\"password\" name=\"mat_khau\" placeholder=\"M\u1eadt kh\u1ea9u...\" required autofocus>&lt;br>\n                &lt;button type=\"submit\">Truy c\u1eadp Camera&lt;\/button>\n            &lt;\/form>\n        &lt;\/div>\n    &lt;\/body>\n    &lt;\/html>\n    ';\n    exit; \n}\n?>\n\n&lt;!-- \u0110o\u1ea1n Script n\u00e0y \u00e9p tr\u00ecnh duy\u1ec7t qu\u00ean m\u1eadt kh\u1ea9u ngay sau khi load xong. F5 l\u00e0 kh\u00f3a l\u1ea1i ngay. -->\n&lt;script>\n    if (window.history.replaceState) {\n        window.history.replaceState(null, null, window.location.href);\n    }\n&lt;\/script>\n\n\n&lt;?php\n\/\/ =======================================================================\n\/\/ PH\u1ea6N CODE C\u1ea4U H\u00ccNH APP ID, APP SECRET V\u00c0 4 CAMERA C\u1ee6A B\u1ea0N GI\u1eee NGUY\u00caN \u1ede D\u01af\u1edaI N\u00c0Y\n\/\/ =======================================================================\n\n$appId = 'NH\u1eacP M\u00c3 APPID C\u1ee6A B\u1ea0N V\u00c0O \u0110\u00c2Y';\n$appSecret = 'NH\u1eacP M\u00c3 APPSECRET V\u00c0O \u0110\u00c2Y 30 K\u00dd T\u1ef0';\n\n\/\/ \u0110I\u1ec0N M\u00c3 S\/N V\u00c0 T\u00caN 4 CAMERA C\u1ee6A B\u1ea0N V\u00c0O \u0110\u00c2Y\n$cameras = &#91;\n    &#91;\n        'id' => 'NH\u1eacP S\/N CAM 1', \n        'name' => 'CAM 1'\n    ],\n    &#91;\n        'id' => 'NH\u1eacP S\/N CAM 2', \/\/ \u0110i\u1ec1n m\u00e3 cam 2 v\u00e0o \u0111\u00e2y (L\u01b0u \u00fd: ph\u1ea3i l\u00e0 cam g\u1ed1c c\u1ee7a t\u00e0i kho\u1ea3n n\u00e0y)\n        'name' => 'CAM 2'\n    ],\n    &#91;\n        'id' => 'NH\u1eacP S\/N CAM 3', \/\/ \u0110i\u1ec1n m\u00e3 cam 3 v\u00e0o \u0111\u00e2y\n        'name' => 'CAM 3'\n    ],\n    &#91;\n        'id' => 'NH\u1eacP S\/N CAM 4', \/\/ \u0110i\u1ec1n m\u00e3 cam 4 v\u00e0o \u0111\u00e2y\n        'name' => 'CAM 4'\n    ]\n];\n\n\/\/ H\u00e0m t\u1ea1o Request ID ng\u1eabu nhi\u00ean (b\u1eaft bu\u1ed9c khi g\u1ecdi API Imou)\nfunction generateRequestId() {\n    return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));\n}\n\n\/\/ H\u00e0m giao ti\u1ebfp v\u1edbi m\u00e1y ch\u1ee7 Imou (g\u1eedi l\u1ec7nh v\u00e0 nh\u1eadn k\u1ebft qu\u1ea3)\nfunction callImouAPI($url, $appId, $appSecret, $params = null) {\n    $time = time(); \/\/ Th\u1eddi gian hi\u1ec7n t\u1ea1i\n    $nonce = bin2hex(random_bytes(16)); \/\/ Chu\u1ed7i ng\u1eabu nhi\u00ean ch\u1ed1ng t\u1ea5n c\u00f4ng l\u1eb7p l\u1ea1i\n    $sign = md5(\"time:$time,nonce:$nonce,appSecret:$appSecret\"); \/\/ M\u00e3 h\u00f3a ch\u1eef k\u00fd \u0111i\u1ec7n t\u1eed\n\n    \/\/ G\u00f3i d\u1eef li\u1ec7u g\u1eedi \u0111i chu\u1ea9n form c\u1ee7a Imou\n    $body = &#91;\n        'system' => &#91;'ver' => '1.0', 'appId' => $appId, 'sign' => $sign, 'time' => $time, 'nonce' => $nonce],\n        'id' => generateRequestId(),\n        'params' => $params === null ? new stdClass() : $params\n    ];\n\n    \/\/ M\u1edf lu\u1ed3ng CURL \u0111\u1ea9y d\u1eef li\u1ec7u l\u00ean m\u00e1y ch\u1ee7\n    $ch = curl_init($url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    curl_setopt($ch, CURLOPT_POST, true);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));\n    curl_setopt($ch, CURLOPT_HTTPHEADER, &#91;'Content-Type: application\/json; charset=utf-8']);\n    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);\n    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);\n    curl_setopt($ch, CURLOPT_TIMEOUT, 15);\n\n    $response = curl_exec($ch);\n    curl_close($ch);\n\n    return json_decode($response, true); \/\/ Tr\u1ea3 v\u1ec1 d\u1ea1ng M\u1ea3ng (Array) cho d\u1ec5 x\u1eed l\u00fd\n}\n\n\/\/ ==============================================================================\n\/\/ PH\u1ea6N 3: X\u1eec L\u00dd L\u1ea4Y LU\u1ed2NG VIDEO\n\/\/ ==============================================================================\n\n\/\/ B\u01af\u1edaC 3.1: Xin Token (M\u00e3 cho ph\u00e9p truy c\u1eadp) - Ch\u1ec9 g\u1ecdi 1 l\u1ea7n \u0111\u1ec3 d\u00f9ng chung\n$tokenResponse = callImouAPI('https:\/\/openapi.easy4ip.com\/openapi\/accessToken', $appId, $appSecret);\n$token = $tokenResponse&#91;'result']&#91;'data']&#91;'accessToken'] ?? \"\";\n$globalError = $token ? \"\" : \"L\u1ed7i x\u00e1c th\u1ef1c h\u1ec7 th\u1ed1ng: \" . json_encode($tokenResponse);\n\n$camData = &#91;]; \/\/ M\u1ea3ng ch\u1ee9a d\u1eef li\u1ec7u camera \u0111\u1ec3 xu\u1ea5t ra ngo\u00e0i web\n\n\/\/ B\u01af\u1edaC 3.2: Duy\u1ec7t qua t\u1eebng camera \u0111\u1ec3 xin link ph\u00e1t tr\u1ef1c ti\u1ebfp\nforeach ($cameras as $index => $cam) {\n    $hlsUrl = \"\"; $camError = \"\"; $devId = trim($cam&#91;'id']);\n\n    if (empty($devId)) {\n        $camError = \"Ch\u01b0a c\u1ea5u h\u00ecnh m\u00e3 Camera.\"; \/\/ B\u00e1o l\u1ed7i n\u1ebfu \u0111\u1ec3 tr\u1ed1ng m\u00e3 S\/N\n    } elseif ($token) {\n        \n        \/\/ G\u1ecdi l\u1ec7nh \u0111\u00e1nh th\u1ee9c camera v\u00e0 l\u1ea5y lu\u1ed3ng proxy\n        callImouAPI('https:\/\/openapi.easy4ip.com\/openapi\/bindDeviceLive', $appId, $appSecret, &#91;\n            'token' => $token, 'deviceId' => $devId, 'channelId' => '0', 'streamId' => 0, 'liveMode' => 'proxy'\n        ]);\n\n        \/\/ Y\u00eau c\u1ea7u m\u00e1y ch\u1ee7 Imou tr\u1ea3 v\u1ec1 \u0111\u01b0\u1eddng d\u1eabn Video (Link M3U8)\n        $streamInfo = callImouAPI('https:\/\/openapi.easy4ip.com\/openapi\/getLiveStreamInfo', $appId, $appSecret, &#91;\n            'token' => $token, 'deviceId' => $devId, 'channelId' => '0'\n        ]);\n\n        \/\/ L\u1ecdc l\u1ea5y \u0111\u00fang \u0111\u01b0\u1eddng d\u1eabn HLS (HTTPS) lu\u1ed3ng ch\u00ednh (streamId = 0)\n        if (isset($streamInfo&#91;'result']&#91;'data']&#91;'streams'])) {\n            foreach ($streamInfo&#91;'result']&#91;'data']&#91;'streams'] as $stream) {\n                if (isset($stream&#91;'streamId']) &amp;&amp; $stream&#91;'streamId'] == 0 &amp;&amp; !empty($stream&#91;'hls'])) {\n                    $hlsUrl = $stream&#91;'hls'];\n                    if (strpos($hlsUrl, 'https:\/\/') === 0) break; \/\/ Ch\u1ec9 l\u1ea5y link b\u1ea3o m\u1eadt https\n                }\n            }\n        }\n        if (empty($hlsUrl)) $camError = \"Kh\u00f4ng l\u1ea5y \u0111\u01b0\u1ee3c lu\u1ed3ng (OP1009).\";\n    }\n    \n    \/\/ G\u00f3i d\u1eef li\u1ec7u t\u1eebng cam \u0111\u1ec3 n\u00e9m ra cho HTML \u1edf b\u00ean d\u01b0\u1edbi\n    $camData&#91;] = &#91;'index' => $index, 'name' => $cam&#91;'name'], 'url' => $hlsUrl, 'error' => $camError];\n}\n?>\n\n&lt;!-- ============================================================================== -->\n&lt;!-- PH\u1ea6N 4: GIAO DI\u1ec6N HI\u1ec2N TH\u1eca WEB (FRONT-END HTML &amp; CSS)                          -->\n&lt;!-- ============================================================================== -->\n&lt;!DOCTYPE html>\n&lt;html lang=\"vi\">\n&lt;head>\n    &lt;meta charset=\"UTF-8\">\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    &lt;title>H\u1ec7 th\u1ed1ng Gi\u00e1m s\u00e1t \u0110a m\u00e0n h\u00ecnh&lt;\/title>\n    &lt;!-- Nh\u00fang th\u01b0 vi\u1ec7n HLS.js \u0111\u1ec3 ph\u00e1t lu\u1ed3ng camera tr\u00ean web -->\n    &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/hls.js@latest\">&lt;\/script>\n    &lt;style>\n        \/* C\u1ea5u h\u00ecnh chung cho to\u00e0n trang *\/\n        body { font-family: Arial, sans-serif; background: #111; color: #fff; margin: 0; padding: 10px; text-align: center; }\n        h2 { margin-top: 10px; margin-bottom: 15px; color: #00bcd4; text-transform: uppercase; letter-spacing: 1px; }\n        \n        \/* C\u1ea5u h\u00ecnh CSS cho Thanh c\u1ea3nh b\u00e1o HEVC *\/\n        .hevc-notice {\n            background: rgba(255, 193, 7, 0.1); border: 1px solid #ffc107; color: #ffca28;\n            padding: 12px 20px; border-radius: 6px; max-width: 80%; margin: 0 auto 15px auto;\n            font-size: 14px; display: flex; align-items: center; justify-content: space-between;\n            text-align: left; box-sizing: border-box;\n        }\n        .download-btn {\n            background: #ffc107; color: #111; text-decoration: none; padding: 8px 15px;\n            border-radius: 4px; font-weight: bold; white-space: nowrap; margin-left: 15px; transition: 0.2s;\n        }\n        .download-btn:hover { background: #e0a800; }\n        \n        \/* C\u1ea5u h\u00ecnh b\u1ed9 khung l\u01b0\u1edbi 4 Camera (2 c\u1ed9t) *\/\n        .grid-container { display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; max-width: 80%; margin: 0 auto; }\n        .cam-box { background: #222; border: 2px solid #444; border-radius: 8px; position: relative; aspect-ratio: 16\/9; overflow: hidden; box-shadow: 0 4px 10px rgba(0,0,0,0.5); }\n        .cam-name { position: absolute; top: 10px; left: 10px; background: rgba(0,0,0,0.7); color: #fff; padding: 5px 10px; font-size: 14px; font-weight: bold; border-radius: 4px; z-index: 10; }\n        video { width: 100%; height: 100%; object-fit: cover; background: #000; }\n        .error-msg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #ff4444; font-size: 14px; width: 80%; }\n        \n        \/* Responsive: T\u1ef1 \u0111\u1ed9ng gom th\u00e0nh 1 c\u1ed9t khi xem tr\u00ean \u0110i\u1ec7n tho\u1ea1i *\/\n        @media (max-width: 768px) {\n            .hevc-notice { flex-direction: column; text-align: center; gap: 10px; }\n            .download-btn { margin-left: 0; }\n            .grid-container { grid-template-columns: 1fr; max-width: 95%; }\n        }\n    &lt;\/style>\n&lt;\/head>\n&lt;body>\n    &lt;h2>XEM CAMERA NH\u00c0 \u00d4NG 6 D\u0168NG HIHI &lt;\/h2>\n\n    &lt;!-- KHUNG TH\u00d4NG B\u00c1O T\u1ea2I CODEC N\u1eb0M D\u01af\u1edaI TI\u00caU \u0110\u1ec0 -->\n    &lt;div class=\"hevc-notice\">\n        &lt;span>\u26a0\ufe0f &lt;strong>Tr\u01b0\u1eddng h\u1ee3p M\u00e0n h\u00ecnh \u0111en &amp; Xoay v\u00f2ng:&lt;\/strong> M\u00e1y t\u00ednh c\u1ee7a b\u1ea1n \u0111ang thi\u1ebfu b\u1ed9 gi\u1ea3i m\u00e3 video chu\u1ea9n n\u00e9n cao (H.265). Vui l\u00f2ng t\u1ea3i v\u00e0 c\u00e0i \u0111\u1eb7t Codec \u0111\u1ec3 xem h\u00ecnh \u1ea3nh s\u1eafc n\u00e9t nh\u1ea5t.&lt;\/span>\n        &lt;a href=\"https:\/\/sangnguyen.drayddns.com\/wp-content\/uploads\/tai-lieu-bi-kip\/LINH-TINH\/HEVCVideoExtension.rar\" class=\"download-btn\">\u2b07\ufe0f T\u1ea3i Codec Kh\u1eafc Ph\u1ee5c&lt;\/a>\n    &lt;\/div>\n    \n    &lt;!-- N\u1ebeU L\u1ed6I TOKEN T\u1ed4NG: In ra m\u00e0u \u0111\u1ecf, N\u1ebeU KH\u00d4NG: Hi\u1ec7n 4 m\u00e0n h\u00ecnh -->\n    &lt;?php if ($globalError): ?>\n        &lt;div style=\"color: red; padding: 20px;\">&lt;?php echo $globalError; ?>&lt;\/div>\n    &lt;?php else: ?>\n        &lt;div class=\"grid-container\">\n            &lt;?php foreach ($camData as $c): ?>\n                &lt;div class=\"cam-box\">\n                    &lt;div class=\"cam-name\">&lt;?php echo htmlspecialchars($c&#91;'name']); ?>&lt;\/div> &lt;!-- In t\u00ean Cam -->\n                    \n                    &lt;?php if ($c&#91;'url']): ?>\n                        &lt;!-- N\u1ebeU C\u00d3 LINK: Hi\u1ec7n th\u1ebb Video -->\n                        &lt;video id=\"video-&lt;?php echo $c&#91;'index']; ?>\" controls autoplay muted playsinline>&lt;\/video>\n                    &lt;?php else: ?>\n                        &lt;!-- N\u1ebeU L\u1ed6I (V\u00ed d\u1ee5: sai m\u00e3, m\u1ea5t m\u1ea1ng cam): B\u00e1o d\u00f2ng ch\u1eef \u0111\u1ecf -->\n                        &lt;div class=\"error-msg\">&lt;?php echo $c&#91;'error']; ?>&lt;\/div>\n                    &lt;?php endif; ?>\n                &lt;\/div>\n            &lt;?php endforeach; ?>\n        &lt;\/div>\n\n        &lt;!-- ============================================================================== -->\n        &lt;!-- PH\u1ea6N 5: TR\u00ccNH PH\u00c1T VIDEO JAVASCRIPT (\u00c9P CH\u1ea0Y V\u00c0 C\u1ee8U H\u1ed8)                        -->\n        &lt;!-- ============================================================================== -->\n        &lt;script>\n            \/\/ Nh\u1eadn d\u1eef li\u1ec7u link camera t\u1eeb kh\u1ed1i PHP \u1edf tr\u00ean n\u00e9m xu\u1ed1ng JS\n            const cameras = &lt;?php echo json_encode($camData); ?>;\n            \n            cameras.forEach(cam => {\n                if (cam.url) { \/\/ N\u1ebfu cam n\u00e0y c\u00f3 link m\u1edbi th\u1ef1c thi ti\u1ebfp\n                    const video = document.getElementById('video-' + cam.index);\n                    \n                    \/\/ \u00c9p c\u00e1c thu\u1ed9c t\u00ednh t\u1eaft ti\u1ebfng \u0111\u1ec3 l\u00e1ch quy \u0111\u1ecbnh c\u1ea5m Autoplay c\u1ee7a tr\u00ecnh duy\u1ec7t\n                    video.muted = true; video.defaultMuted = true; video.playsInline = true;\n                    \n                    if (Hls.isSupported()) {\n                        \/\/ Kh\u1edfi t\u1ea1o th\u01b0 vi\u1ec7n gi\u1ea3i m\u00e3 HLS (Live Stream)\n                        const hls = new Hls({ enableWorker: true, lowLatencyMode: true, liveSyncDurationCount: 3 });\n                        hls.loadSource(cam.url); \n                        hls.attachMedia(video);\n                        \n                        \/\/ Khi \u0111\u00e3 n\u1ea1p th\u00e0nh c\u00f4ng d\u1eef li\u1ec7u \u0111\u1ea7u ti\u00ean -> B\u1ea5m n\u00fat Play\n                        hls.on(Hls.Events.MANIFEST_PARSED, () => {\n                            let playPromise = video.play();\n                            if (playPromise !== undefined) {\n                                playPromise.catch(() => { \n                                    video.muted = true; video.play(); \/\/ Th\u1eed Play l\u1ea1i l\u1ea7n 2 n\u1ebfu b\u1ecb ch\u1eb7n\n                                });\n                            }\n                        });\n                        \n                        \/\/ L\u1eaeNG NGHE L\u1ed6I: T\u1ef1 \u0111\u1ed9ng c\u1ee9u h\u1ed9 (Kh\u00f4i ph\u1ee5c) n\u1ebfu video b\u1ecb gi\u00e1n \u0111o\u1ea1n m\u1ea1ng\n                        hls.on(Hls.Events.ERROR, function (event, data) {\n                            if (data.fatal) {\n                                switch (data.type) {\n                                    case Hls.ErrorTypes.NETWORK_ERROR: \n                                        hls.startLoad(); \/\/ C\u1ee9u r\u1edbt m\u1ea1ng -> T\u1ea3i l\u1ea1i\n                                        break;\n                                    case Hls.ErrorTypes.MEDIA_ERROR: \n                                        hls.recoverMediaError(); \/\/ C\u1ee9u l\u1ed7i h\u00ecnh -> X\u00f3a b\u1ed9 nh\u1edb \u0111\u1ec7m v\u00e0 kh\u00f4i ph\u1ee5c\n                                        break;\n                                    default: \n                                        hls.destroy(); \/\/ H\u1ecfng n\u1eb7ng -> H\u1ee7y phi\u00ean\n                                        break;\n                                }\n                            }\n                        });\n                    } else if (video.canPlayType('application\/vnd.apple.mpegurl')) {\n                        \/\/ H\u1ed7 tr\u1ee3 ri\u00eang cho ng\u01b0\u1eddi d\u00f9ng xem tr\u00ean m\u00e1y Mac \/ iPhone \/ Safari\n                        video.src = cam.url;\n                        video.addEventListener('loadedmetadata', () => { video.muted = true; video.play(); });\n                    }\n                }\n            });\n        &lt;\/script>\n    &lt;?php endif; ?>\n&lt;\/body>\n&lt;\/html><\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Ph\u1ea7n 1: Chu\u1ea9n b\u1ecb Camera (Thao t\u00e1c tr\u00ean App Imou Life). \u0110\u1ec3 lu\u1ed3ng video c\u00f3 th\u1ec3 \u0111\u1ea9y l\u00ean Web m\u01b0\u1ee3t m\u00e0 v\u00e0 n\u00e9t nh\u1ea5t, ph\u1ea7n c\u1ee9ng camera ph\u1ea3i \u0111\u01b0\u1ee3c c\u1ea5u h\u00ecnh chu\u1ea9n ngay t\u1eeb \u0111\u1ea7u. Ph\u1ea7n 2: Kh\u1edfi t\u1ea1o API tr\u00ean Imou Cloud. Ph\u1ea7n 3: C\u1ea5u h\u00ecnh m\u00e3 ngu\u1ed3n PHP (Core System). T\u1ea1o m\u1ed9t file [&#8230;]\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1553","post","type-post","status-publish","format-standard","hentry","category-chia-se-hoac-kien-thuc-cntt"],"_links":{"self":[{"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/posts\/1553","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/comments?post=1553"}],"version-history":[{"count":6,"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/posts\/1553\/revisions"}],"predecessor-version":[{"id":1561,"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/posts\/1553\/revisions\/1561"}],"wp:attachment":[{"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/media?parent=1553"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/categories?post=1553"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sangnguyen.drayddns.com\/index.php\/wp-json\/wp\/v2\/tags?post=1553"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}