ساخت Adaptive Video Streaming با HLS و FFmpeg؛ پخش خودکار 360p تا 1080p روی VPS
اگر یک فایل ویدئویی 1080p را مستقیماً روی سایت قرار دهید، همه کاربران مجبور میشوند همان نسخه را دریافت کنند؛ حتی کسی که با اینترنت ضعیف یا صفحهنمایش کوچک وارد سایت شده است. نتیجه معمولاً توقف پخش، Buffering زیاد و مصرف بیهوده پهنای باند است.
Adaptive Video Streaming این مشکل را حل میکند. سرور از یک ویدئوی اصلی چند کیفیت مختلف مثل 360p، 720p و 1080p میسازد و Player هنگام پخش، براساس سرعت اینترنت کاربر بین آنها جابهجا میشود. در این آموزش با FFmpeg 9.0.2، HLS، Nginx و Python یک سیستم Adaptive Streaming واقعی روی VPS لینوکس میسازیم.
📌 خروجی نهایی پروژه:
🔹 تولید خودکار 360p، 720p و 1080p
🔹 ساخت Master Playlist با فرمت m3u8
🔹 ساخت Segmentهای HLS
🔹 تغییر خودکار کیفیت هنگام پخش
🔹 پخش در مرورگر با hls.js
🔹 سرو فایلها با Nginx
🔹 اسکریپت Python برای تبدیل خودکار فایلهای جدید
🔹 آمادهسازی معماری برای اتصال به CDN
Adaptive Streaming چیست؟
در Adaptive Streaming یک ویدئو فقط یک فایل نیست. از همان محتوای اصلی چند Rendition ساخته میشود؛ مثلاً:
360p 700 kbps 720p 2500 kbps 1080p 5000 kbps
Player همه کیفیتها را از طریق یک Master Playlist میشناسد. اگر سرعت اینترنت افت کند، کیفیت پایینتر انتخاب میشود و وقتی شرایط شبکه بهتر شود، Player میتواند دوباره به کیفیت بالاتر برگردد.
HLS چیست؟
HLS یا HTTP Live Streaming ویدئو را به فایلهای کوچک تقسیم میکند و فهرست آنها را داخل Playlistهایی با پسوند .m3u8 قرار میدهد.
ساختار ساده خروجی ما چیزی شبیه این خواهد شد:
movie/
├── master.m3u8
├── v360p/
│ ├── index.m3u8
│ ├── seg_000001.ts
│ └── seg_000002.ts
├── v720p/
│ ├── index.m3u8
│ └── ...
└── v1080p/
├── index.m3u8
└── ...
مرورگر ابتدا master.m3u8 را دریافت میکند و از آنجا کیفیتهای مختلف را میشناسد.
تفاوت Transcoding Server با Adaptive Streaming
در مقاله ساخت Video Transcoding Server با FFmpeg یک فایل ویدئویی را به Codec یا کیفیت دیگری تبدیل کردیم. اینجا یک مرحله جلوتر میرویم: چند نسخه از یک ویدئو میسازیم و آنها را داخل یک ساختار Streaming قرار میدهیم تا Player بتواند میان کیفیتها سوییچ کند.
برای HLS چه VPSای نیاز داریم؟
اگر فایلها از قبل Transcode شده باشند، سرو HLS توسط Nginx سبک است. بخش سنگین کار همان مرحله Encoding با FFmpeg است.
| سناریو | شروع پیشنهادی |
|---|---|
| تست و آموزش | ۲ تا ۴ vCPU و ۲GB RAM |
| تبدیل ویدئوی 1080p | ۴ تا ۸ vCPU |
| تعداد فایل زیاد | CPU بیشتر یا Worker جدا |
راهاندازی HLS روی سرور مجازی ایران
اگر کاربران ویدئوهای شما عمدتاً داخل ایران هستند، میتوانید Origin یا Streaming Server را روی سرور مجازی ایران قرار دهید. در سرو ویدئو علاوه بر CPU، میزان ترافیک ماهانه و ظرفیت پورت شبکه نیز اهمیت زیادی دارد.
مرحله اول: نصب FFmpeg و Nginx
sudo apt update sudo apt install ffmpeg nginx python3 -y
نسخه نصبشده را بررسی کنید:
ffmpeg -version
آخرین نسخه پایدار Upstream هنگام نگارش این مقاله FFmpeg 9.0.2 است، اما Repository اوبونتو ممکن است نسخه دیگری ارائه دهد. مهمتر از شماره نسخه، وجود Encoderهای مورد نیاز مثل libx264 است.
مرحله دوم: ساخت مسیر HLS
sudo mkdir -p /srv/hls/movie sudo chown -R $USER:www-data /srv/hls sudo chmod -R 755 /srv/hls
مرحله سوم: تولید سه کیفیت HLS با FFmpeg
فرض کنید فایل اصلی input.mp4 است. دستور زیر سه نسخه 360p، 720p و 1080p میسازد:
cd /srv/hls/movie ffmpeg -i /path/to/input.mp4 -filter_complex "[0:v]split=3[v360][v720][v1080]; [v360]scale=-2:360[v360out]; [v720]scale=-2:720[v720out]; [v1080]scale=-2:1080[v1080out]" -map "[v360out]" -map 0:a:0? -c:v:0 libx264 -preset medium -b:v:0 700k -maxrate:v:0 800k -bufsize:v:0 1400k -c:a:0 aac -b:a:0 96k -map "[v720out]" -map 0:a:0? -c:v:1 libx264 -preset medium -b:v:1 2500k -maxrate:v:1 2800k -bufsize:v:1 5000k -c:a:1 aac -b:a:1 128k -map "[v1080out]" -map 0:a:0? -c:v:2 libx264 -preset medium -b:v:2 5000k -maxrate:v:2 5500k -bufsize:v:2 10000k -c:a:2 aac -b:a:2 160k -flags +cgop -sc_threshold 0 -force_key_frames "expr:gte(t,n_forced*4)" -f hls -hls_time 4 -hls_playlist_type vod -hls_flags independent_segments -hls_segment_filename "v%v/seg_%06d.ts" -master_pl_name master.m3u8 -var_stream_map "v:0,a:0,name:360p v:1,a:1,name:720p v:2,a:2,name:1080p" "v%v/index.m3u8"
بعد از پایان Encoding باید master.m3u8 و سه مجموعه Segment داشته باشید.
چرا Keyframe باید با Segment هماهنگ باشد؟
HLS ترجیح میدهد Segmentها روی Keyframe شکسته شوند. اگر مرز Segment میان GOP قرار بگیرد، تغییر کیفیت میان Renditionها میتواند با تأخیر یا رفتار نامناسب همراه شود.
در دستور بالا هر چهار ثانیه یک Keyframe اجباری ایجاد کردهایم و hls_time نیز چهار ثانیه است.
Master Playlist چیست؟
فایل master.m3u8 به Player میگوید چه کیفیتهایی وجود دارند و پهنای باند تقریبی هرکدام چقدر است. Player براساس این اطلاعات مناسبترین Variant را انتخاب میکند.
مرحله چهارم: سرو HLS با Nginx
server {
listen 80;
server_name video.example.com;
location /hls/ {
alias /srv/hls/;
add_header Access-Control-Allow-Origin "*" always;
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
add_header Cache-Control \
"public, max-age=3600";
}
}
کانفیگ را فعال و Nginx را Reload کنید:
sudo nginx -t sudo systemctl reload nginx
Master Playlist اکنون باید از این URL در دسترس باشد:
https://video.example.com/hls/movie/master.m3u8
مرحله پنجم: پخش HLS در مرورگر با hls.js
<video
id="player"
controls
playsinline
style="width:100%;max-width:900px;"
></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<script>
const video = document.getElementById("player");
const src =
"https://video.example.com/hls/movie/master.m3u8";
if (video.canPlayType(
"application/vnd.apple.mpegurl"
)) {
video.src = src;
} else if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(video);
}
</script>
Safari میتواند HLS را بهصورت Native پخش کند و hls.js در مرورگرهایی که Media Source Extensions دارند، Playlist را مدیریت میکند.
ساخت اسکریپت Python برای تولید خودکار HLS
اگر قرار است فایلهای زیادی تبدیل شوند، بهتر است Command طولانی FFmpeg را دستی اجرا نکنید.
import subprocess
import sys
from pathlib import Path
source = Path(sys.argv[1]).resolve()
name = source.stem
output = Path("/srv/hls") / name
output.mkdir(
parents=True,
exist_ok=True
)
for folder in [
"v360p",
"v720p",
"v1080p",
]:
(output / folder).mkdir(
exist_ok=True
)
command = [
"ffmpeg",
"-y",
"-i", str(source),
"-filter_complex",
(
"[0:v]split=3[v360][v720][v1080];"
"[v360]scale=-2:360[v360out];"
"[v720]scale=-2:720[v720out];"
"[v1080]scale=-2:1080[v1080out]"
),
"-map", "[v360out]",
"-map", "0:a:0?",
"-c:v:0", "libx264",
"-b:v:0", "700k",
"-c:a:0", "aac",
"-b:a:0", "96k",
"-map", "[v720out]",
"-map", "0:a:0?",
"-c:v:1", "libx264",
"-b:v:1", "2500k",
"-c:a:1", "aac",
"-b:a:1", "128k",
"-map", "[v1080out]",
"-map", "0:a:0?",
"-c:v:2", "libx264",
"-b:v:2", "5000k",
"-c:a:2", "aac",
"-b:a:2", "160k",
"-flags", "+cgop",
"-sc_threshold", "0",
"-force_key_frames",
"expr:gte(t,n_forced*4)",
"-f", "hls",
"-hls_time", "4",
"-hls_playlist_type", "vod",
"-hls_flags", "independent_segments",
"-hls_segment_filename",
str(output / "v%v/seg_%06d.ts"),
"-master_pl_name",
"master.m3u8",
"-var_stream_map",
(
"v:0,a:0,name:360p "
"v:1,a:1,name:720p "
"v:2,a:2,name:1080p"
),
str(output / "v%v/index.m3u8"),
]
subprocess.run(
command,
check=True
)
اجرای نمونه:
python3 make_hls.py movie.mp4
Bitrate هر کیفیت را چطور انتخاب کنیم؟
Bitrate مناسب به Frame Rate، نوع محتوا و کیفیت مورد انتظار بستگی دارد. یک ویدئوی بازی یا مسابقه ورزشی نسبت به اسلاید آموزشی ساده Bitrate بیشتری میخواهد.
| کیفیت | بازه شروع تقریبی |
|---|---|
| 360p | ۵۰۰ تا ۹۰۰ kbps |
| 480p | ۹۰۰ تا ۱۵۰۰ kbps |
| 720p | ۱۸۰۰ تا ۳۵۰۰ kbps |
| 1080p | ۳۵۰۰ تا ۶۰۰۰ kbps |
این اعداد نسخه ثابت برای همه ویدئوها نیستند. بهتر است براساس محتوای واقعی خودتان چند Encode آزمایشی بگیرید.
HLS با TS یا fMP4؟
برای شروع از MPEG-TS استفاده کردیم چون ساده و شناختهشده است. FFmpeg همچنین میتواند HLS را با Fragmented MP4 یا fMP4 تولید کند.
fMP4 برای معماریهای جدیدتر و همگرایی HLS/DASH جذاب است، اما اگر هدف آموزش اولیه و سازگاری آسان است، TS انتخاب سادهتری است.
آیا HLS را به CDN متصل کنیم؟
بله. Playlist و Segmentهای VOD فایلهای HTTP معمولی هستند و میتوانند پشت CDN قرار بگیرند. در معماری واقعی بهتر است Origin فقط فایل را نگهداری کند و کاربران Segmentها را از Edge CDN دریافت کنند.
اگر قبلاً مقاله ساخت CDN شخصی با Nginx را اجرا کردهاید، همین HLS Origin را میتوان پشت Edge Cache قرار داد.
امنیت HLS خصوصی
اگر محتوای ویدئو عمومی نیست، مخفی کردن URL کافی نیست. بسته به پروژه میتوانید:
🔹 Master Playlist را با Token محافظت کنید.
🔹 URLهای Segment را امضا کنید.
🔹 از Cookie یا JWT برای Authentication استفاده کنید.
🔹 برای محتوای حساستر HLS Encryption یا DRM مناسب طراحی کنید.
HLS AES-128 با DRMهایی مثل Widevine یا FairPlay یکسان نیست؛ این دو سطح حفاظتی متفاوتاند.
خطاهای رایج
Player فقط 1080p را نشان میدهد
بررسی کنید Master Playlist واقعاً سه Variant دارد و دستور var_stream_map بدون خطا اجرا شده است.
بین کیفیتها تصویر مکث میکند
Keyframe و Segment Duration بین Renditionها باید هماهنگ باشند. GOP نامنظم یکی از دلایل رایج این مشکل است.
مرورگر خطای CORS میدهد
اگر Player روی Domain دیگری قرار دارد، Headerهای CORS مربوط به Playlist و Segment را بررسی کنید.
FFmpeg خیلی کند است
همزمان سه Encode انجام میشود؛ بنابراین مصرف CPU طبیعی است. Preset سریعتر، CPU بیشتر یا Hardware Encoding میتواند زمان پردازش را کاهش دهد.
سؤالات متداول
Adaptive Video Streaming چیست؟
روشی است که چند نسخه از ویدئو با Bitrate و Resolution مختلف تولید میشود و Player براساس شرایط شبکه بهترین نسخه را انتخاب میکند.
HLS چیست؟
پروتکل Streaming مبتنی بر HTTP است که محتوا را به Segmentهای کوچک و Playlistهای m3u8 تقسیم میکند.
آیا FFmpeg Master Playlist میسازد؟
بله. با var_stream_map و master_pl_name میتوان چند Variant و Master Playlist تولید کرد.
آیا HLS روی VPS اجرا میشود؟
بله. سرو فایلهای HLS سبک است، اما مرحله Transcoding به CPU قابل توجهی نیاز دارد.
آیا HLS برای Live هم قابل استفاده است؟
بله. HLS هم برای VOD و هم Live Streaming استفاده میشود. در مقاله بعدی Live Stream را از OBS دریافت میکنیم.
جمعبندی
با FFmpeg و HLS میتوان یک ویدئوی اصلی را به چند کیفیت تبدیل کرد و بدون وابستگی به سرویس خارجی Adaptive Streaming ساخت. Master Playlist کیفیتها را به Player معرفی میکند و Player براساس سرعت شبکه میان آنها جابهجا میشود.
برای پروژه واقعی بهتر است Transcoding، Storage و Delivery را از هم جدا کنید؛ یعنی Worker فایلها را Encode کند، Origin آنها را نگهداری کند و CDN Segmentها را به کاربر برساند.






دیدگاهها (۰)