สร้างแอปสตรีมวิดีโอด้วย Nuxt.js, Node และ Express
เผยแพร่แล้ว: 2022-03-10วิดีโอทำงานร่วมกับสตรีม ซึ่งหมายความว่าแทนที่จะส่งวิดีโอทั้งหมดในคราวเดียว วิดีโอจะถูกส่งเป็นชุดของชิ้นเล็กๆ ที่ประกอบเป็นวิดีโอเต็ม สิ่งนี้อธิบายว่าทำไมวิดีโอถึงบัฟเฟอร์เมื่อดูวิดีโอบนบรอดแบนด์ที่ช้าเพราะเล่นเฉพาะส่วนที่ได้รับและพยายามโหลดมากขึ้น
บทความนี้มีไว้สำหรับนักพัฒนาที่ต้องการเรียนรู้เทคโนโลยีใหม่โดยการสร้างโครงการจริง: แอปสตรีมวิดีโอที่มี Node.js เป็นแบ็กเอนด์ และ Nuxt.js เป็นไคลเอ็นต์
- Node.js เป็นรันไทม์ที่ใช้สำหรับสร้างแอปพลิเคชันที่รวดเร็วและปรับขนาดได้ เราจะใช้มันเพื่อจัดการกับการดึงและสตรีมวิดีโอ สร้างภาพขนาดย่อสำหรับวิดีโอ และให้บริการคำอธิบายภาพและคำบรรยายสำหรับวิดีโอ
- Nuxt.js เป็นเฟรมเวิร์ก Vue.js ที่ช่วยให้เราสร้างแอปพลิเคชัน Vue.js ที่แสดงผลบนเซิร์ฟเวอร์ได้อย่างง่ายดาย เราจะใช้ API ของเราสำหรับวิดีโอและแอปพลิเคชันนี้จะมีมุมมองสองแบบ: รายการของวิดีโอที่มีอยู่และการดูของผู้เล่นสำหรับแต่ละวิดีโอ
ข้อกำหนดเบื้องต้น
- ความเข้าใจเกี่ยวกับ HTML, CSS, JavaScript, Node/Express และ Vue
- โปรแกรมแก้ไขข้อความ (เช่น VS Code)
- เว็บเบราว์เซอร์ (เช่น Chrome, Firefox)
- ติดตั้ง FFmpeg บนเวิร์กสเตชันของคุณแล้ว
- โหนด.js เอ็นวีเอ็ม
- คุณสามารถรับซอร์สโค้ดบน GitHub
การตั้งค่าแอปพลิเคชันของเรา
ในแอปพลิเคชันนี้ เราจะสร้างเส้นทางเพื่อส่งคำขอจากส่วนหน้า:
- เส้นทาง
videos
เพื่อรับรายการวิดีโอและข้อมูล - เส้นทางในการดึงวิดีโอเพียงรายการเดียวจากรายการวิดีโอของเรา
- เส้นทางการ
streaming
มเพื่อสตรีมวิดีโอ - เส้นทาง
captions
เพื่อเพิ่มคำอธิบายภาพให้กับวิดีโอที่เรากำลังสตรีม
หลังจากที่สร้างเส้นทางของเราแล้ว เราจะสร้างส่วนหน้า Nuxt
ซึ่งเราจะสร้าง Home
และหน้า player
แบบไดนามิก จากนั้นเราขอเส้นทาง videos
ของเราเพื่อเติมข้อมูลวิดีโอในหน้าแรก คำขออื่นในการสตรีมวิดีโอบนหน้าโปรแกรม player
ของเรา และสุดท้ายคือคำขอให้บริการไฟล์คำอธิบายภาพที่วิดีโอใช้
ในการตั้งค่าแอปพลิเคชันของเรา เราสร้างไดเร็กทอรีโครงการของเรา
mkdir streaming-app
การตั้งค่าเซิร์ฟเวอร์ของเรา
ในไดเร็กทอรี streaming-app
เราสร้างโฟลเดอร์ชื่อ backend
cd streaming-app mkdir backend
ในโฟลเดอร์แบ็กเอนด์ เราเริ่มต้นไฟล์ package.json
เพื่อเก็บข้อมูลเกี่ยวกับโครงการเซิร์ฟเวอร์ของเรา
cd backend npm init -y
เราจำเป็นต้องติดตั้งแพ็คเกจต่อไปนี้เพื่อสร้างแอพของเรา
-
nodemon
จะรีสตาร์ทเซิร์ฟเวอร์ของเราโดยอัตโนมัติเมื่อเราทำการเปลี่ยนแปลง -
express
ให้อินเทอร์เฟซที่ดีแก่เราในการจัดการเส้นทาง -
cors
จะช่วยให้เราสามารถส่งคำขอข้ามต้นทางได้ เนื่องจากไคลเอนต์และเซิร์ฟเวอร์ของเราจะทำงานบนพอร์ตต่างๆ
ในไดเร็กทอรีแบ็กเอนด์ของเรา เราสร้าง assets
โฟลเดอร์เพื่อเก็บวิดีโอของเราสำหรับการสตรีม
mkdir assets
คัดลอกไฟล์ .mp4
ลงในโฟลเดอร์เนื้อหา แล้วตั้งชื่อเป็น video1
คุณสามารถใช้วิดีโอตัวอย่างสั้น .mp4
ที่สามารถพบได้ใน Github Repo
สร้างไฟล์ app.js
และเพิ่มแพ็คเกจที่จำเป็นสำหรับแอพของเรา
const express = require('express'); const fs = require('fs'); const cors = require('cors'); const path = require('path'); const app = express(); app.use(cors())
โมดูล fs
ใช้เพื่ออ่านและเขียนลงในไฟล์อย่างง่ายดายบนเซิร์ฟเวอร์ของเรา ในขณะที่โมดูล path
ให้วิธีการทำงานกับไดเร็กทอรีและพาธของไฟล์
ตอนนี้เราสร้างเส้นทาง ./ ./video
เมื่อได้รับการร้องขอก็จะส่งไฟล์วิดีโอกลับไปให้ลูกค้า
// add after 'const app = express();' app.get('/video', (req, res) => { res.sendFile('assets/video1.mp4', { root: __dirname }); });
เส้นทางนี้ให้บริการไฟล์วิดีโอ video1.mp4
เมื่อมีการร้องขอ จากนั้นเราฟังเซิร์ฟเวอร์ของเราที่พอร์ต 3000
// add to end of app.js file app.listen(5000, () => { console.log('Listening on port 5000!') });
มีการเพิ่มสคริปต์ในไฟล์ package.json
เพื่อเริ่มต้นเซิร์ฟเวอร์ของเราโดยใช้ nodemon
"scripts": { "start": "nodemon app.js" },
จากนั้นรันเทอร์มินัลของคุณ:
npm run start
หากคุณเห็นข้อความ Listening on port 3000!
ในเทอร์มินัล แสดงว่าเซิร์ฟเวอร์ทำงานอย่างถูกต้อง ไปที่ https://localhost:5000/video ในเบราว์เซอร์ของคุณและคุณจะเห็นวิดีโอที่เล่นอยู่
คำขอให้จัดการโดย Frontend
ด้านล่างนี้คือคำขอที่เราจะทำกับแบ็กเอนด์จากฟรอนท์เอนด์ของเราซึ่งเราต้องการให้เซิร์ฟเวอร์จัดการ
-
/videos
ส่งกลับอาร์เรย์ของข้อมูลจำลองวิดีโอที่จะใช้เพื่อเติมรายการวิดีโอบนHome
ในส่วนหน้าของเรา -
/video/:id/data
ส่งคืนข้อมูลเมตาสำหรับวิดีโอเดียว ใช้โดยหน้าPlayer
ในส่วนหน้าของเรา -
/video/:id
สตรีมวิดีโอด้วย ID ที่กำหนด ใช้โดยหน้าPlayer
มาสร้างเส้นทางกันเถอะ
ส่งคืนข้อมูลจำลองสำหรับรายการวิดีโอ
สำหรับแอปพลิเคชันสาธิตนี้ เราจะสร้าง อาร์เรย์ของออบเจ็ กต์ที่จะเก็บข้อมูลเมตาและส่งไปที่ฟรอนต์เอนด์เมื่อมีการร้องขอ ในแอปพลิเคชันจริง คุณอาจจะอ่านข้อมูลจากฐานข้อมูล ซึ่งจากนั้นก็จะถูกใช้เพื่อสร้างอาร์เรย์แบบนี้ เพื่อความเรียบง่าย เราจะไม่ทำอย่างนั้นในบทช่วยสอนนี้
ในโฟลเดอร์แบ็กเอนด์ของเรา ให้สร้างไฟล์ mockdata.js
และเติมด้วยข้อมูลเมตาสำหรับรายการวิดีโอของเรา
const allVideos = [ { id: "tom and jerry", poster: 'https://image.tmdb.org/t/p/w500/fev8UFNFFYsD5q7AcYS8LyTzqwl.jpg', duration: '3 mins', name: 'Tom & Jerry' }, { id: "soul", poster: 'https://image.tmdb.org/t/p/w500/kf456ZqeC45XTvo6W9pW5clYKfQ.jpg', duration: '4 mins', name: 'Soul' }, { id: "outside the wire", poster: 'https://image.tmdb.org/t/p/w500/lOSdUkGQmbAl5JQ3QoHqBZUbZhC.jpg', duration: '2 mins', name: 'Outside the wire' }, ]; module.exports = allVideos
เราสามารถเห็นได้จากด้านบน วัตถุแต่ละชิ้นมีข้อมูลเกี่ยวกับวิดีโอ สังเกตแอตทริบิวต์ poster
ซึ่งมีลิงก์ไปยังภาพโปสเตอร์ของวิดีโอ
มาสร้างเส้นทางของ videos
กันเถอะ เนื่องจากคำขอทั้งหมดของเราที่จะทำโดยส่วนหน้านั้นต่อท้ายด้วย /videos
ในการดำเนินการนี้ ให้สร้างโฟลเดอร์ routes
และเพิ่มไฟล์ Video.js
สำหรับเส้นทาง /videos
ของเรา ในไฟล์นี้ เราต้องใช้ express
และใช้ express router เพื่อสร้างเส้นทางของเรา
const express = require('express') const router = express.Router()
เมื่อเราไปที่เส้นทาง /videos
เราต้องการรับรายการวิดีโอของเรา ดังนั้นให้กำหนดให้ไฟล์ mockData.js
อยู่ในไฟล์ Video.js
ของเราและทำการร้องขอ
const express = require('express') const router = express.Router() const videos = require('../mockData') // get list of videos router.get('/', (req,res)=>{ res.json(videos) }) module.exports = router;
เส้นทาง /videos
ได้รับการประกาศแล้ว บันทึกไฟล์และควรรีสตาร์ทเซิร์ฟเวอร์โดยอัตโนมัติ เมื่อเริ่มต้นแล้ว ให้ไปที่ https://localhost:3000/videos และอาร์เรย์ของเราจะถูกส่งคืนในรูปแบบ JSON
ส่งคืนข้อมูลสำหรับวิดีโอเดียว
เราต้องการที่จะสามารถขอวิดีโอรายการใดรายการหนึ่งในรายการวิดีโอของเรา เราสามารถดึงข้อมูลวิดีโอเฉพาะในอาร์เรย์ของเราโดยใช้ id
ที่เราให้มา มาขอกันเถอะ ยังอยู่ในไฟล์ Video.js
ของเรา
// make request for a particular video router.get('/:id/data', (req,res)=> { const id = parseInt(req.params.id, 10) res.json(videos[id]) })
โค้ดด้านบนรับ id
จากพารามิเตอร์เส้นทางและแปลงเป็นจำนวนเต็ม จากนั้นเราจะส่งอ็อบเจ็กต์ที่ตรงกับ id
จากอาร์เรย์ videos
กลับไปยังไคลเอ็นต์
สตรีมมิ่งวิดีโอ
ในไฟล์ app.js
เราได้สร้างเส้นทาง /video
ที่ให้บริการวิดีโอแก่ลูกค้า เราต้องการให้ปลายทางนี้ส่งวิดีโอชิ้นเล็กๆ แทนที่จะให้บริการไฟล์วิดีโอทั้งหมดตามคำขอ
เราต้องการที่จะให้บริการหนึ่งในสามวิดีโอ แบบไดนามิก ที่อยู่ในอาร์เรย์ allVideos
และสตรีมวิดีโอเป็นชิ้น ๆ ดังนั้น:
ลบเส้นทาง /video
จาก app.js
เราต้องการวิดีโอสามรายการ ดังนั้นให้คัดลอกวิดีโอตัวอย่างจากซอร์สโค้ดของบทช่วยสอนไปยังไดเร็กทอรี asset assets/
ของโปรเจ็กต์ server
ของคุณ ตรวจสอบให้แน่ใจว่าชื่อไฟล์สำหรับวิดีโอนั้นสอดคล้องกับ id
ในอาร์เรย์ videos
:
กลับไปที่ไฟล์ Video.js
ของเรา สร้างเส้นทางสำหรับการสตรีมวิดีโอ
router.get('/video/:id', (req, res) => { const videoPath = `assets/${req.params.id}.mp4`; const videoStat = fs.statSync(videoPath); const fileSize = videoStat.size; const videoRange = req.headers.range; if (videoRange) { const parts = videoRange.replace(/bytes=/, "").split("-"); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : fileSize-1; const chunksize = (end-start) + 1; const file = fs.createReadStream(videoPath, {start, end}); const head = { 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/mp4', }; res.writeHead(206, head); file.pipe(res); } else { const head = { 'Content-Length': fileSize, 'Content-Type': 'video/mp4', }; res.writeHead(200, head); fs.createReadStream(videoPath).pipe(res); } });
หากเราไปที่ https://localhost:5000/videos/video/outside-the-wire ในเบราว์เซอร์ เราจะเห็นการสตรีมวิดีโอ
เส้นทางการสตรีมวิดีโอทำงานอย่างไร
มีโค้ดบางส่วนที่เขียนในเส้นทางสตรีมวิดีโอของเรา ดังนั้นเรามาดูทีละบรรทัดกัน
const videoPath = `assets/${req.params.id}.mp4`; const videoStat = fs.statSync(videoPath); const fileSize = videoStat.size; const videoRange = req.headers.range;
อันดับแรก จากคำขอของเรา เราได้รับ id
จากเส้นทางโดยใช้ req.params.id
และใช้เพื่อสร้าง videoPath
ไปยังวิดีโอ จากนั้นเราอ่าน fileSize
โดยใช้ระบบ fs
ที่เรานำเข้า สำหรับวิดีโอ เบราว์เซอร์ของผู้ใช้จะส่งพารามิเตอร์ range
ในคำขอ ซึ่งจะทำให้เซิร์ฟเวอร์ทราบว่าจะส่งวิดีโอส่วนใดกลับไปยังไคลเอ็นต์
เบราว์เซอร์บางตัวส่ง ช่วง ในคำขอเริ่มต้น แต่บางเบราว์เซอร์ไม่ส่ง สำหรับผู้ที่ไม่ส่ง หรือหากเบราว์เซอร์ไม่ส่งช่วงด้วยเหตุผลอื่น เราจะจัดการกับส่วนนั้นในบล็อก else
รหัสนี้รับขนาดไฟล์และส่งวิดีโอสองสามส่วนแรก:
else { const head = { 'Content-Length': fileSize, 'Content-Type': 'video/mp4', }; res.writeHead(200, head); fs.createReadStream(path).pipe(res); }
เราจะจัดการกับคำขอที่ตามมารวมถึงช่วงในบล็อก if
if (videoRange) { const parts = videoRange.replace(/bytes=/, "").split("-"); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : fileSize-1; const chunksize = (end-start) + 1; const file = fs.createReadStream(videoPath, {start, end}); const head = { 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/mp4', }; res.writeHead(206, head); file.pipe(res); }
รหัสด้านบนนี้สร้างสตรีมการอ่านโดยใช้ค่า start
และ end
ของช่วง ตั้งค่า Content-Length
ของส่วนหัวการตอบกลับเป็นขนาดกลุ่มที่คำนวณจากค่า start
และ end
เรายังใช้รหัส HTTP 206 ซึ่งหมายความว่าการตอบสนองมีเนื้อหาบางส่วน ซึ่งหมายความว่าเบราว์เซอร์จะทำการขอต่อไปจนกว่าจะดึงชิ้นส่วนของวิดีโอทั้งหมด
จะเกิดอะไรขึ้นกับการเชื่อมต่อที่ไม่เสถียร
หากผู้ใช้ใช้การเชื่อมต่อที่ช้า สตรีมเครือข่ายจะส่งสัญญาณโดยขอให้แหล่ง I/O หยุดชั่วคราวจนกว่าไคลเอ็นต์จะพร้อมสำหรับข้อมูลเพิ่มเติม นี้เรียกว่า แรงดันย้อนกลับ เราสามารถยกตัวอย่างนี้ไปอีกขั้นหนึ่งและดูว่าการขยายสตรีมนั้นง่ายเพียงใด เพิ่มการบีบอัดได้อย่างง่ายดายด้วย!
const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : fileSize-1; const chunksize = (end-start) + 1; const file = fs.createReadStream(videoPath, {start, end});
เราสามารถเห็นข้างต้นว่า ReadStream
ถูกสร้างขึ้นและให้บริการวิดีโอทีละอัน
const head = { 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/mp4', }; res.writeHead(206, head); file.pipe(res);
ส่วนหัวของคำขอมีช่วง Content-Range
ซึ่งเป็นจุดเริ่มต้นและจุดสิ้นสุดของการเปลี่ยนแปลงเพื่อให้กลุ่มวิดีโอถัดไปสตรีมไปยังส่วนหน้า ส่วน content-length
คือส่วนของวิดีโอที่ส่ง นอกจากนี้เรายังระบุประเภทของเนื้อหาที่เรากำลังสตรีมซึ่งก็คือ mp4
หัวเขียน 206 ถูกตั้งค่าให้ตอบสนองเฉพาะสตรีมที่สร้างขึ้นใหม่เท่านั้น
การสร้างไฟล์คำบรรยายสำหรับวิดีโอของเรา
นี่คือลักษณะของไฟล์คำอธิบายภาพ .vtt
WEBVTT 00:00:00.200 --> 00:00:01.000 Creating a tutorial can be very 00:00:01.500 --> 00:00:04.300 fun to do.
ไฟล์คำอธิบายภาพมีข้อความสำหรับสิ่งที่พูดในวิดีโอ นอกจากนี้ยังมีรหัสเวลาที่จะแสดงข้อความแต่ละบรรทัด เราต้องการให้วิดีโอของเรามีคำบรรยาย และเราจะไม่สร้างไฟล์คำอธิบายภาพของเราเองสำหรับบทช่วยสอนนี้ ดังนั้นคุณสามารถตรงไปที่โฟลเดอร์คำอธิบายภาพในไดเรกทอรี assets
ใน repo และดาวน์โหลดคำอธิบายภาพ
มาสร้างเส้นทางใหม่ที่จะจัดการกับคำขอคำบรรยาย:
router.get('/video/:id/caption', (req, res) => res.sendFile(`assets/captions/${req.params.id}.vtt`, { root: __dirname }));
การสร้าง Frontend ของเรา
ในการเริ่มต้นในส่วนที่มองเห็นได้ของระบบของเรา เราจะต้องสร้างโครงนั่งร้านส่วนหน้าของเรา
หมายเหตุ : คุณต้องใช้ vue-cli เพื่อสร้างแอปของเรา หากคุณไม่ได้ติดตั้งไว้บนคอมพิวเตอร์ คุณสามารถเรียกใช้ npm install -g @vue/cli
เพื่อติดตั้งได้
การติดตั้ง
ที่รูทของโปรเจ็กต์ มาสร้างโฟลเดอร์ front-end กัน:
mkdir frontend cd frontend
และในนั้น เราเริ่มต้นไฟล์ package.json
ของเรา คัดลอกและวางสิ่งต่อไปนี้ในนั้น:
{ "name": "my-app", "scripts": { "dev": "nuxt", "build": "nuxt build", "generate": "nuxt generate", "start": "nuxt start" } }
จากนั้นติดตั้ง nuxt
:
npm add nuxt
และรันคำสั่งต่อไปนี้เพื่อเรียกใช้แอป Nuxt.js:
npm run dev
โครงสร้างไฟล์ Nuxt ของเรา
ตอนนี้เราได้ติดตั้ง Nuxt แล้ว เราสามารถเริ่มจัดวางส่วนหน้าของเราได้
ขั้นแรก เราต้องสร้างโฟลเดอร์ layouts
ย์เอาต์ที่รูทของแอพของเรา โฟลเดอร์นี้กำหนดเลย์เอาต์ของแอพ ไม่ว่าเราจะนำทางไปยังหน้าใดก็ตาม สิ่งต่างๆ เช่น แถบนำทางและส่วนท้ายของเรามีอยู่ที่นี่ ในโฟลเดอร์ฟรอนท์เอนด์ เราสร้าง default.vue
สำหรับเลย์เอาต์เริ่มต้นเมื่อเราเริ่มแอปฟรอนต์เอนด์
mkdir layouts cd layouts touch default.vue
จากนั้นโฟลเดอร์ components
เพื่อสร้างส่วนประกอบทั้งหมดของเรา เราต้องการเพียงสององค์ประกอบคือ NavBar
และส่วนประกอบ video
ดังนั้นในโฟลเดอร์รูทของฟรอนท์เอนด์เรา:
mkdir components cd components touch NavBar.vue touch Video.vue
สุดท้าย โฟลเดอร์เพจที่เพจของเราทั้งหมดเช่น home
และ about
สามารถสร้างขึ้นได้ สองหน้าที่เราต้องการในแอพนี้ คือหน้า home
แสดงวิดีโอและข้อมูลวิดีโอทั้งหมดของเรา และหน้าเครื่องเล่นแบบไดนามิกที่นำทางไปยังวิดีโอที่เราคลิก
mkdir pages cd pages touch index.vue mkdir player cd player touch _name.vue
ไดเร็กทอรีส่วนหน้าของเราตอนนี้มีลักษณะดังนี้:
|-frontend |-components |-NavBar.vue |-Video.vue |-layouts |-default.vue |-pages |-index.vue |-player |-_name.vue |-package.json |-yarn.lock
ส่วนประกอบแถบนำทาง
NavBar.vue
ของเรามีลักษณะดังนี้:
<template> <div class="navbar"> <h1>Streaming App</h1> </div> </template> <style scoped> .navbar { display: flex; background-color: #161616; justify-content: center; align-items: center; } h1{ color:#a33327; } </style>
NavBar
มีแท็ก h1
ที่แสดง Streaming App โดยมีรูปแบบเล็กน้อย
มานำเข้า NavBar
เข้าในโครงร่าง default.vue
ของเรา
// default.vue <template> <div> <NavBar /> <nuxt /> </div> </template> <script> import NavBar from "@/components/NavBar.vue" export default { components: { NavBar, } } </script>
เลย์เอาต์ default.vue
ตอนนี้มีองค์ประกอบ NavBar
และแท็ก <nuxt />
หลังจากที่มันบ่งบอกว่าหน้าใด ๆ ที่เราสร้างจะแสดงขึ้น
ใน index.vue
ของเรา (ซึ่งเป็นหน้าแรกของเรา) ให้ส่งคำขอไปที่ https://localhost:5000/videos
เพื่อรับวิดีโอทั้งหมดจากเซิร์ฟเวอร์ของเรา การส่งข้อมูลเป็นอุปกรณ์ประกอบฉากไปยังองค์ประกอบ video.vue
ของเราเราจะสร้างขึ้นในภายหลัง แต่สำหรับตอนนี้เราได้นำเข้าไปแล้ว
<template> <div> <Video :videoList="videos"/> </div> </template> <script> import Video from "@/components/Video.vue" export default { components: { Video }, head: { title: "Home" }, data() { return { videos: [] } }, async fetch() { this.videos = await fetch( 'https://localhost:5000/videos' ).then(res => res.json()) } } </script>
ส่วนประกอบวิดีโอ
ด้านล่าง เราประกาศพร็อพของเราก่อน เนื่องจากขณะนี้ข้อมูลวิดีโอพร้อมใช้งานในคอมโพเนนต์แล้ว เราจึงแสดงข้อมูลดังกล่าวโดยใช้ Vue's v-for
ซ้ำกับข้อมูลทั้งหมดที่ได้รับและสำหรับแต่ละข้อมูล เราสามารถใช้คำสั่ง v-for
เพื่อวนซ้ำข้อมูลและแสดงเป็นรายการ มีการเพิ่มสไตล์พื้นฐานบางอย่างเข้ามาด้วย
<template> <div> <div class="container"> <div v-for="(video, id) in videoList" :key="id" class="vid-con" > <NuxtLink :to="`/player/${video.id}`"> <div : class="vid" ></div> <div class="movie-info"> <div class="details"> <h2>{{video.name}}</h2> <p>{{video.duration}}</p> </div> </div> </NuxtLink> </div> </div> </div> </template> <script> export default { props:['videoList'], } </script> <style scoped> .container { display: flex; justify-content: center; align-items: center; margin-top: 2rem; } .vid-con { display: flex; flex-direction: column; flex-shrink: 0; justify-content: center; width: 50%; max-width: 16rem; margin: auto 2em; } .vid { height: 15rem; width: 100%; background-position: center; background-size: cover; } .movie-info { background: black; color: white; width: 100%; } .details { padding: 16px 20px; } </style>
เรายังสังเกตเห็นว่า NuxtLink
มีเส้นทางแบบไดนามิก ซึ่งกำลังกำหนดเส้นทางไปยัง /player/video.id
ฟังก์ชันที่เราต้องการคือเมื่อผู้ใช้คลิกที่วิดีโอใด ๆ วิดีโอก็จะเริ่มสตรีม เพื่อให้บรรลุสิ่งนี้ เราใช้ลักษณะไดนามิกของเส้นทาง _name.vue
ในนั้น เราสร้างเครื่องเล่นวิดีโอและตั้งค่าแหล่งที่มาเป็นปลายทางของเราสำหรับการสตรีมวิดีโอ แต่เราต่อท้ายวิดีโอที่จะเล่นไปยังปลายทางของเราแบบไดนามิกด้วยความช่วยเหลือของ this.$route.params.name
ที่รวบรวมพารามิเตอร์ที่ลิงก์ได้รับ .
<template> <div class="player"> <video controls muted autoPlay> <source :src="`https://localhost:5000/videos/video/${vidName}`" type="video/mp4"> </video> </div> </template> <script> export default { data() { return { vidName: '' } }, mounted(){ this.vidName = this.$route.params.name } } </script> <style scoped> .player { display: flex; justify-content: center; align-items: center; margin-top: 2em; } </style>
เมื่อเราคลิกที่วิดีโอใด ๆ เราจะได้รับ:
การเพิ่มไฟล์คำบรรยายของเรา
ในการเพิ่มไฟล์แทร็ก เราต้องแน่ใจว่าไฟล์ . .vtt
ทั้งหมดในโฟลเดอร์ คำอธิบายภาพ มีชื่อเดียวกันกับ id
ของเรา อัปเดตองค์ประกอบวิดีโอของเราด้วยแทร็ก โดยส่งคำขอคำบรรยาย
<template> <div class="player"> <video controls muted autoPlay crossOrigin="anonymous"> <source :src="`https://localhost:5000/videos/video/${vidName}`" type="video/mp4"> <track label="English" kind="captions" srcLang="en" :src="`https://localhost:5000/videos/video/${vidName}/caption`" default> </video> </div> </template>
เราได้เพิ่ม crossOrigin="anonymous"
ให้กับองค์ประกอบวิดีโอ มิฉะนั้น การขอคำอธิบายภาพจะล้มเหลว รีเฟรชแล้วคุณจะเห็นคำบรรยายถูกเพิ่มเรียบร้อยแล้ว
สิ่งที่ควรคำนึงถึงในการสร้างการสตรีมวิดีโอที่ยืดหยุ่น
เมื่อสร้างแอปพลิเคชันการสตรีมเช่น Twitch, Hulu หรือ Netflix มีหลายสิ่งที่ต้องคำนึงถึง:
- ไปป์ไลน์การประมวลผลข้อมูลวิดีโอ
นี่อาจเป็นความท้าทายทางเทคนิคเนื่องจากจำเป็นต้องใช้เซิร์ฟเวอร์ที่มีประสิทธิภาพสูงเพื่อให้บริการวิดีโอนับล้านแก่ผู้ใช้ ควรหลีกเลี่ยงเวลาแฝงสูงหรือการหยุดทำงานในทุกกรณี - เก็บเอาไว้
กลไกการแคชควรใช้เมื่อสร้างแอปพลิเคชันประเภทนี้ เช่น Cassandra, Amazon S3, AWS SimpleDB - ภูมิศาสตร์ของผู้ใช้
ควรพิจารณาถึงสภาพทางภูมิศาสตร์ของผู้ใช้ของคุณสำหรับการแจกจ่าย
บทสรุป
ในบทช่วยสอนนี้ เราได้เห็นวิธีสร้างเซิร์ฟเวอร์ใน Node.js ที่สตรีมวิดีโอ สร้างคำบรรยายสำหรับวิดีโอเหล่านั้น และให้บริการข้อมูลเมตาของวิดีโอ เรายังได้เห็นวิธีการใช้ Nuxt.js ในส่วนหน้าเพื่อใช้ปลายทางและข้อมูลที่สร้างโดยเซิร์ฟเวอร์
ต่างจากเฟรมเวิร์กอื่นๆ การสร้างแอปพลิเคชันด้วย Nuxt.js และ Express.js นั้นค่อนข้างง่ายและรวดเร็ว ส่วนที่ยอดเยี่ยมเกี่ยวกับ Nuxt.js คือวิธีที่มันจัดการเส้นทางของคุณและทำให้คุณจัดโครงสร้างแอปของคุณได้ดีขึ้น
- คุณสามารถรับข้อมูลเพิ่มเติมเกี่ยวกับ Nuxt.js ได้ที่นี่
- คุณสามารถรับซอร์สโค้ดบน Github
ทรัพยากร
- “การเพิ่มคำบรรยายในวิดีโอ HTML5” MDN Web Docs
- “การทำความเข้าใจคำบรรยายและคำบรรยาย” Screenfont.ca