For path 1, I want to try basic thing to streaming video on internet. Let begin with using ffmpeg + video.js
Backend service
main.go
func main() {
fileServer := http.FileServer(http.Dir("./videos"))
http.Handle("/upload", http.HandlerFunc(uploadHandler))
http.Handle("/stream/", http.StripPrefix("/stream/", fileServer))
err := http.ListenAndServe(":8080", func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}(http.DefaultServeMux))
if err != nil {
log.Fatal(err)
}
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
r.Body = http.MaxBytesReader(w, r.Body, 2<<30)
file, header, err := r.FormFile("video")
if err != nil {
http.Error(w, fmt.Sprintf("Error retrieving file: %v", err), http.StatusBadRequest)
return
}
defer file.Close()
log.Printf("📥 กำลังรับไฟล์: %s (ขนาด: %d bytes)", header.Filename, header.Size)
uploadDir := "./raw_videos"
if err := os.MkdirAll(uploadDir, os.ModePerm); err != nil {
http.Error(w, "Unable to create directory", http.StatusInternalServerError)
return
}
destPath := filepath.Join(uploadDir, header.Filename)
destFile, err := os.Create(destPath)
if err != nil {
http.Error(w, "Unable to create file on server", http.StatusInternalServerError)
return
}
defer destFile.Close()
_, err = io.Copy(destFile, file)
if err != nil {
http.Error(w, fmt.Sprintf("Error saving file: %v", err), http.StatusInternalServerError)
return
}
log.Printf("✅ อัปโหลดสำเร็จ: %s ถูกเซฟไว้ที่ %s", header.Filename, destPath)
seq.Add(1)
id := seq.Load()
// ** --------------------- Focus here --------------------- **
go func(id int64, path string) {
if e := tsToHLS(destPath, "./videos"); e != nil {
log.Panicln(e)
}
}(id, destPath)
w.WriteHeader(http.StatusOK)
w.Write(fmt.Appendf(nil, "Upload successful: %s", header.Filename))
}
func tsToHLS(i, o string) error {
plPath := filepath.Join(o, "playlist.m3u8")
sPath := filepath.Join(o, "video_%03d.ts")
cmd := exec.Command("ffmpeg",
"-i", i,
"-vf", "scale=854:-2",
"-crf", "26",
"-b:v", "1000k",
"-maxrate", "1000k",
"-bufsize", "2000k",
"-codec:v", "libx264",
"-codec:a", "aac",
"-f", "hls",
"-hls_time", "10",
"-hls_list_size", "0",
"-hls_segment_filename", sPath,
plPath,
)
log.Printf("⏳ กำลังเริ่มแปลงไฟล์: %s ...", i)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("FFmpeg error: %v, output: %s", err, string(output))
}
log.Println("✅ แปลงไฟล์เป็น HLS สำเร็จแล้ว!")
return nil
}
ตารางสรุปพารามิเตอร์ FFmpeg สำหรับระบบ Streaming
| พารามิเตอร์ (Args) | หน้าที่และความหมาย | ตัวอย่างการใช้งาน | ประโยชน์ที่มีต่อระบบ Local Netflix |
|---|---|---|---|
-i |
Input: ระบุพาธหรือชื่อไฟล์วิดีโอต้นฉบับที่ต้องการนำมาประมวลผล | -i input.mp4 |
บอกให้ FFmpeg รู้ว่าจะเอาหนังเรื่องไหนมาจัดการ |
-vf |
Video Filter: ใช้จัดการตัวเนื้อภาพ ในที่นี้เราใช้ฟิลเตอร์ scale เพื่อย่อ/ขยายขนาดภาพ |
-vf "scale=1280:-2" |
ลดความละเอียดภาพ (เช่น เหลือ 720p) ทำให้ไฟล์เล็กลงอย่างเห็นได้ชัด |
-crf |
Constant Rate Factor: คุมคุณภาพของภาพภาพรวม (ค่าเลขยิ่งเยอะ ไฟล์ยิ่งเล็ก แต่ภาพจะแตก) | -crf 26 |
ช่วยบีบอัดไฟล์ให้เล็กลงโดยรักษาความชัดให้อยู่ในเกณฑ์ที่สายตาคนมองว่าสวย |
-b:v |
Video Bitrate: กำหนดอัตราการส่งข้อมูลของภาพวิดีโอต่อวินาที | -b:v 2000k |
ควบคุมไม่ให้บิตเรตพุ่งสูงเกินไป ป้องกันการกระตุกของ WiFi ในบ้าน |
-maxrate |
Maximum Bitrate: กำหนดเพดานบิตเรตสูงสุดไม่ให้เกินค่านี้เด็ดขาดในฉากที่เคลื่อนไหวเร็ว | -maxrate 2000k |
ป้องกันไม่ให้ฉากแอคชั่นกินแบนด์วิดท์เน็ตเวิร์กสูงเกินไป |
-bufsize |
Buffer Size: กำหนดขนาดของถังพักข้อมูลที่ FFmpeg จะใช้วิเคราะห์เพื่อคุมบิตเรต | -bufsize 4000k |
ช่วยให้การคำนวณบิตเรตไหลลื่นและคงที่นิ่งขึ้น |
-codec:v (หรือ -c:v)
|
Video Codec: เลือกตัวถอดรหัส/เข้ารหัสภาพวิดีโอ | -codec:v libx264 |
แปลงให้เป็นฟอร์แมต H.264 ซึ่งเป็นมาตรฐานที่ Browser ทุกตัวเปิดดูได้ชัวร์ |
-codec:a (หรือ -c:a)
|
Audio Codec: เลือกตัวถอดรหัส/เข้ารหัสระบบเสียง | -codec:a aac |
แปลงระบบเสียงเป็น AAC ซึ่งเป็นที่นิยมและรองรับบนหน้าเว็บอย่างสมบูรณ์ |
-b:a |
Audio Bitrate: กำหนดอัตราการส่งข้อมูลของเสียงต่อวินาที | -b:a 128k |
บีบอัดไฟล์เสียงให้เล็กลง แต่ยังคงความชัดเจนของเสียงพูดและเพลง |
-f |
Format: ระบุรูปแบบปลายทางของไฟล์ผลลัพธ์ | -f hls |
สั่งให้ส่งเอาต์พุตออกมาเป็นระบบ HLS (HTTP Live Streaming) |
-hls_time |
HLS Segment Duration: กำหนดความยาว (วินาที) ของไฟล์วิดีโอย่อยแต่ละชิ้น | -hls_time 10 |
สั่งให้หั่นวิดีโอซอยออกมาเป็นชิ้นละ 10 วินาที (ไฟล์ตระกูล .ts) |
-hls_list_size |
Playlist Window Size: กำหนดจำนวนไฟล์ย่อยที่จะแสดงในสารบัญ (0 หมายถึงให้ใส่ทั้งหมด) | -hls_list_size 0 |
บังคับให้ไฟล์ .m3u8 เก็บรายชื่อวิดีโอตั้งแต่ต้นจนจบเรื่อง (ถ้าตั้งเป็น 5 จะดูได้แค่ 5 ชิ้นล่าสุดเหมือนไลฟ์สด) |
-hls_segment_filename |
Segment Naming: กำหนดรูปแบบการตั้งชื่อและพาธของไฟล์วิดีโอย่อยที่ถูกหั่น | -hls_segment_filename "video_%03d.ts" |
ได้ไฟล์ชื่อเรียงตามลำดับ เช่น video_001.ts, video_002.ts เพื่อให้ไล่ลำดับได้ถูกต้อง |
Top comments (0)