ساخت Video Transcoding Server با FFmpeg و Python؛ تبدیل H.264، H.265، AV1 و HLS روی VPS
اگر کاربران سایت یا اپلیکیشن شما ویدئو آپلود میکنند، نگهداری همان فایل خام معمولاً کافی نیست. یک ویدئوی 4K یا فایل ضبطشده با Bitrate بالا ممکن است برای پخش تحت وب بسیار بزرگ باشد و روی همه دستگاهها نیز Codec مناسبی نداشته باشد.
راه حرفهایتر ساخت یک Video Transcoding Server است. ویدئو به سرور ارسال میشود، یک Job در صف قرار میگیرد و Worker با FFmpeg نسخه H.264، H.265 یا AV1 تولید میکند. در صورت نیاز میتوان از همان سرور برای تولید HLS، چند Resolution و فایلهای مناسب Streaming استفاده کرد.
در این آموزش Python و FastAPI فقط API، Upload، Job Queue و وضعیت پردازش را مدیریت میکنند. تبدیل واقعی توسط FFmpeg انجام میشود؛ زیرا نوشتن Video Encoder در Python نه ساده است و نه منطقی.
📌 خروجی پروژه:
🔹 آپلود ویدئو از طریق API
🔹 Job Queue مبتنی بر SQLite
🔹 Worker مستقل از API
🔹 تبدیل ویدئو به H.264
🔹 تبدیل ویدئو به H.265/HEVC
🔹 تبدیل ویدئو به AV1
🔹 دریافت وضعیت Job
🔹 دانلود خروجی از Nginx
🔹 نمونه ساخت HLS برای Streaming
🔹 آشنایی با NVIDIA NVENC و Intel Quick Sync
FFmpeg چیست و چرا برای Transcoding مناسب است؟
FFmpeg مجموعهای از ابزارها و Libraryهای پردازش صوت و ویدئو است که Decode، Encode، Filter، Container Conversion، Streaming و دهها عملیات دیگر را پوشش میدهد.
نسخه جدید هنگام نگارش: FFmpeg 9.0.2 «Lei»
تاریخ انتشار: 18 سپتامبر 2026
Python در معماری ما FFmpeg را با آرگومانهای از پیش تعریفشده اجرا میکند. کاربر اجازه وارد کردن Command خام FFmpeg را ندارد؛ در نتیجه API به یک Remote Shell تبدیل نمیشود.
H.264، H.265 یا AV1؛ کدام را انتخاب کنیم؟
| Codec | مزیت اصلی | هزینه Encode | کاربرد |
|---|---|---|---|
| H.264/AVC | سازگاری بسیار گسترده | کمتر | پخش عمومی وب |
| H.265/HEVC | Compression بهتر نسبت به H.264 در بسیاری از محتواها | بیشتر | Archive، TV و دستگاههای سازگار |
| AV1 | Compression مدرن و Royalty-Free | CPU Encoding سنگینتر | Streaming جدید و فایلهای بهینه |
برای شروع سرویس عمومی، H.264 هنوز Profile مناسبی است. سپس میتوانید H.265 و AV1 را بهعنوان خروجیهای تکمیلی اضافه کنید.
چرا Transcoding را داخل Request اصلی FastAPI اجرا نمیکنیم؟
تبدیل یک ویدئوی ۳۰ دقیقهای ممکن است چند دقیقه یا حتی بیشتر زمان ببرد. اگر Endpoint منتظر پایان FFmpeg بماند، Worker وب برای مدت طولانی اشغال میشود.
معماری درستتر:
Client | | Upload video v FastAPI | | INSERT queued job v SQLite Queue | v Transcoding Worker | | FFmpeg v Output File | v Nginx Download
API بعد از Upload سریع Job ID برمیگرداند و Client میتواند وضعیت Job را جداگانه بررسی کند.
برای Video Transcoding چه سروری نیاز داریم؟
برخلاف Image Conversion، Video Transcoding میتواند CPU بسیار زیادی مصرف کند. تعداد Core، فرکانس CPU، Codec، Resolution، Preset و تعداد Jobهای همزمان روی سرعت تأثیر دارند.
| سناریو | شروع پیشنهادی |
|---|---|
| Lab و تست FFmpeg | 2 تا 4 vCPU |
| تعداد کم ویدئوی 720p/1080p | 4 تا 8 vCPU و RAM کافی |
| صف پردازش سنگین | CPU اختصاصی یا چند Worker Node |
| 4K و Transcoding زیاد | Dedicated CPU یا GPU Acceleration |
اجرای FFmpeg روی سرور مجازی
برای پروژههای سبک و Queue کوچک میتوان Transcoding Server را روی VPS لینوکس راهاندازی کرد. برای پردازش سنگین، تعداد زیاد Job یا 4K باید منابع CPU و Storage متناسب انتخاب شوند.
مرحله اول: نصب FFmpeg و Python
برای سادهترین نصب روی Ubuntu:
sudo apt update sudo apt install ffmpeg python3 python3-venv python3-pip sqlite3 nginx -y
نسخه نصبشده را بررسی کنید:
ffmpeg -version ffprobe -version
⚠️ توجه: نسخه FFmpeg موجود در Repository هر Ubuntu الزاماً همان آخرین نسخه Upstream یعنی 9.0.2 نیست. برای خود این پروژه شماره نسخه کمتر از داشتن Encoderهای مورد نیاز اهمیت دارد. اگر دقیقاً FFmpeg 9.0.2 میخواهید باید از Build معتبر یا Compile مناسب استفاده کنید.
بررسی Encoderهای نصبشده
ffmpeg -encoders | grep -E \ "libx264|libx265|libsvtav1|h264_nvenc|hevc_nvenc|h264_qsv|av1_qsv"
برای API پایه حداقل libx264 نیاز داریم. H.265 به Build دارای libx265 و Profile AV1 ما به libsvtav1 وابسته است.
مرحله دوم: ساخت Project Directory
sudo mkdir -p \ /opt/video-transcoder \ /srv/transcoder/uploads \ /srv/transcoder/outputs \ /srv/transcoder/logs sudo useradd --system --home /opt/video-transcoder --shell /usr/sbin/nologin transcoder sudo chown -R transcoder:transcoder /opt/video-transcoder /srv/transcoder
مرحله سوم: نصب FastAPI
sudo -u transcoder \ python3 -m venv \ /opt/video-transcoder/venv sudo -u transcoder /opt/video-transcoder/venv/bin/pip install fastapi uvicorn python-multipart
مرحله چهارم: ساخت API و Job Queue
sudo nano /opt/video-transcoder/app.py
import os
import secrets
import sqlite3
import time
import uuid
from pathlib import Path
from fastapi import (
FastAPI,
File,
Form,
Header,
HTTPException,
UploadFile,
)
from fastapi.responses import Response
app = FastAPI(
title="Iranica Video Transcoder"
)
ROOT = Path(
"/srv/transcoder"
)
UPLOAD_DIR = ROOT / "uploads"
OUTPUT_DIR = ROOT / "outputs"
DB_PATH = Path(
"/opt/video-transcoder/jobs.db"
)
API_KEY = os.getenv(
"TRANSCODER_API_KEY",
""
)
MAX_UPLOAD = (
20
* 1024
* 1024
* 1024
)
PROFILES = {
"h264-720p": "mp4",
"h264-1080p": "mp4",
"h265-1080p": "mp4",
"av1-1080p": "mp4",
}
def db():
conn = sqlite3.connect(
DB_PATH,
timeout=10
)
conn.row_factory = sqlite3.Row
conn.execute(
"PRAGMA journal_mode=WAL"
)
conn.execute(
"PRAGMA busy_timeout=10000"
)
return conn
def init_db():
conn = db()
conn.execute("""
CREATE TABLE IF NOT EXISTS jobs (
id TEXT PRIMARY KEY,
profile TEXT NOT NULL,
input_path TEXT NOT NULL,
output_path TEXT NOT NULL,
status TEXT NOT NULL,
original_name TEXT,
error TEXT,
created_at INTEGER NOT NULL,
started_at INTEGER,
finished_at INTEGER
)
""")
conn.commit()
conn.close()
def check_key(value):
if not API_KEY:
return
if not secrets.compare_digest(
value or "",
API_KEY
):
raise HTTPException(
status_code=401,
detail="Invalid API key"
)
init_db()
@app.post("/jobs")
async def create_job(
file: UploadFile = File(...),
profile: str = Form("h264-720p"),
x_api_key: str | None = Header(None),
):
check_key(x_api_key)
if profile not in PROFILES:
raise HTTPException(
status_code=400,
detail="Unknown profile"
)
job_id = uuid.uuid4().hex
suffix = Path(
file.filename or "video.bin"
).suffix.lower()[:12]
input_path = (
UPLOAD_DIR
/ f"{job_id}{suffix}"
)
extension = PROFILES[profile]
output_path = (
OUTPUT_DIR
/ f"{job_id}.{extension}"
)
total = 0
try:
with input_path.open("wb") as out:
while True:
chunk = await file.read(
1024 * 1024
)
if not chunk:
break
total += len(chunk)
if total > MAX_UPLOAD:
raise HTTPException(
status_code=413,
detail="Video too large"
)
out.write(chunk)
except Exception:
input_path.unlink(
missing_ok=True
)
raise
finally:
await file.close()
now = int(time.time())
conn = db()
conn.execute("""
INSERT INTO jobs (
id,
profile,
input_path,
output_path,
status,
original_name,
created_at
)
VALUES (?, ?, ?, ?, 'queued', ?, ?)
""", (
job_id,
profile,
str(input_path),
str(output_path),
Path(
file.filename or "video"
).name,
now
))
conn.commit()
conn.close()
return {
"job_id": job_id,
"status": "queued",
"profile": profile,
"size": total,
"status_url": f"/jobs/{job_id}"
}
@app.get("/jobs/{job_id}")
def job_status(
job_id: str,
x_api_key: str | None = Header(None),
):
check_key(x_api_key)
conn = db()
row = conn.execute("""
SELECT
id,
profile,
status,
error,
created_at,
started_at,
finished_at
FROM jobs
WHERE id = ?
""", (job_id,)).fetchone()
conn.close()
if row is None:
raise HTTPException(
status_code=404
)
result = dict(row)
if row["status"] == "done":
result["download_url"] = (
f"/jobs/{job_id}/download"
)
return result
@app.get("/jobs/{job_id}/download")
def download(
job_id: str,
x_api_key: str | None = Header(None),
):
check_key(x_api_key)
conn = db()
row = conn.execute("""
SELECT
status,
output_path
FROM jobs
WHERE id = ?
""", (job_id,)).fetchone()
conn.close()
if row is None:
raise HTTPException(
status_code=404
)
if row["status"] != "done":
raise HTTPException(
status_code=409,
detail="Job is not finished"
)
output = Path(
row["output_path"]
)
if not output.is_file():
raise HTTPException(
status_code=404,
detail="Output missing"
)
response = Response(
status_code=200
)
response.headers[
"X-Accel-Redirect"
] = (
"/_transcoded/"
+ output.name
)
response.headers[
"Content-Type"
] = "video/mp4"
response.headers[
"Content-Disposition"
] = (
f'attachment; '
f'filename="{output.name}"'
)
return response
مرحله پنجم: ساخت Worker پردازش FFmpeg
Worker تنها یک Job را از Queue برمیدارد، آن را به Processing تغییر میدهد و FFmpeg را اجرا میکند.
sudo nano /opt/video-transcoder/worker.py
import sqlite3
import subprocess
import time
from pathlib import Path
DB_PATH = Path(
"/opt/video-transcoder/jobs.db"
)
LOG_DIR = Path(
"/srv/transcoder/logs"
)
def db():
conn = sqlite3.connect(
DB_PATH,
timeout=10
)
conn.row_factory = sqlite3.Row
conn.execute(
"PRAGMA journal_mode=WAL"
)
conn.execute(
"PRAGMA busy_timeout=10000"
)
return conn
def claim_job():
now = int(time.time())
conn = db()
try:
conn.execute(
"BEGIN IMMEDIATE"
)
row = conn.execute("""
UPDATE jobs
SET
status = 'processing',
started_at = ?
WHERE id = (
SELECT id
FROM jobs
WHERE status = 'queued'
ORDER BY created_at
LIMIT 1
)
AND status = 'queued'
RETURNING *
""", (now,)).fetchone()
conn.commit()
return row
except Exception:
conn.rollback()
raise
finally:
conn.close()
def build_command(job):
source = job["input_path"]
output = job["output_path"]
profile = job["profile"]
common = [
"ffmpeg",
"-nostdin",
"-hide_banner",
"-y",
"-i",
source,
"-map",
"0:v:0",
"-map",
"0:a?",
]
if profile == "h264-720p":
codec = [
"-vf",
"scale=-2:720",
"-c:v",
"libx264",
"-preset",
"medium",
"-crf",
"23",
"-pix_fmt",
"yuv420p",
"-c:a",
"aac",
"-b:a",
"128k",
"-movflags",
"+faststart",
]
elif profile == "h264-1080p":
codec = [
"-vf",
"scale=-2:1080",
"-c:v",
"libx264",
"-preset",
"medium",
"-crf",
"22",
"-pix_fmt",
"yuv420p",
"-c:a",
"aac",
"-b:a",
"160k",
"-movflags",
"+faststart",
]
elif profile == "h265-1080p":
codec = [
"-vf",
"scale=-2:1080",
"-c:v",
"libx265",
"-preset",
"medium",
"-crf",
"28",
"-tag:v",
"hvc1",
"-pix_fmt",
"yuv420p",
"-c:a",
"aac",
"-b:a",
"160k",
"-movflags",
"+faststart",
]
elif profile == "av1-1080p":
codec = [
"-vf",
"scale=-2:1080",
"-c:v",
"libsvtav1",
"-preset",
"8",
"-crf",
"32",
"-pix_fmt",
"yuv420p",
"-c:a",
"aac",
"-b:a",
"160k",
"-movflags",
"+faststart",
]
else:
raise RuntimeError(
"Unknown profile"
)
return (
common
+ codec
+ [output]
)
def set_result(
job_id,
status,
error=None
):
conn = db()
conn.execute("""
UPDATE jobs
SET
status = ?,
error = ?,
finished_at = ?
WHERE id = ?
""", (
status,
error,
int(time.time()),
job_id
))
conn.commit()
conn.close()
def process(job):
command = build_command(job)
log_path = (
LOG_DIR
/ f"{job['id']}.log"
)
try:
with log_path.open(
"w",
encoding="utf-8"
) as log:
result = subprocess.run(
command,
stdout=subprocess.DEVNULL,
stderr=log,
check=False,
text=True
)
if result.returncode == 0:
set_result(
job["id"],
"done"
)
Path(
job["input_path"]
).unlink(
missing_ok=True
)
else:
set_result(
job["id"],
"failed",
f"FFmpeg exit code "
f"{result.returncode}"
)
except Exception as exc:
set_result(
job["id"],
"failed",
str(exc)[:500]
)
def main():
while True:
job = claim_job()
if job is None:
time.sleep(2)
continue
process(job)
if __name__ == "__main__":
main()
چرا shell=True استفاده نکردیم؟
یکی از خطاهای امنیتی رایج در APIهای FFmpeg این است که Command از String ورودی کاربر ساخته و با shell=True اجرا شود. در آن حالت ورودی دستکاریشده میتواند به Command Injection منجر شود.
در این پروژه Command به شکل List ساخته شده و Profileها از قبل داخل کد تعریف شدهاند. کاربر فقط نام Profile را انتخاب میکند.
مرحله ششم: اجرای API و Worker با systemd
API Service:
[Unit]
Description=Video Transcoder API
After=network.target
[Service]
User=transcoder
Group=transcoder
WorkingDirectory=/opt/video-transcoder
Environment="TRANSCODER_API_KEY=YOUR_SECRET_KEY"
ExecStart=/opt/video-transcoder/venv/bin/uvicorn
app:app
--host 127.0.0.1
--port 8200
--workers 2
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target
Worker Service:
[Unit]
Description=FFmpeg Transcoding Worker
After=network.target
[Service]
User=transcoder
Group=transcoder
WorkingDirectory=/opt/video-transcoder
ExecStart=/opt/video-transcoder/venv/bin/python
/opt/video-transcoder/worker.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
بعد از ساخت Unitها:
sudo systemctl daemon-reload sudo systemctl enable --now video-transcoder-api sudo systemctl enable --now video-transcoder-worker
در شروع کار فقط یک Worker Transcoding اجرا کنید. چند FFmpeg همزمان میتوانند تمام CPU و RAM سرور را اشغال کنند.
مرحله هفتم: تنظیم Nginx
server {
listen 80;
server_name video-api.example.com;
client_max_body_size 20g;
location / {
proxy_request_buffering off;
proxy_pass http://127.0.0.1:8200;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
location /_transcoded/ {
internal;
alias /srv/transcoder/outputs/;
add_header X-Content-Type-Options nosniff always;
}
}
ساخت اولین Job
curl -X POST \ https://video-api.example.com/jobs \ -H "X-API-Key: YOUR_SECRET_KEY" \ -F "profile=h264-720p" \ -F "file=@movie.mp4"
پاسخ:
{
"job_id": "5d33...",
"status": "queued",
"profile": "h264-720p",
"status_url": "/jobs/5d33..."
}
بررسی وضعیت Job
curl \ -H "X-API-Key: YOUR_SECRET_KEY" \ https://video-api.example.com/jobs/JOB_ID
Status میتواند queued، processing، done یا failed باشد.
CRF چیست؟
در Encoderهایی مانند x264 و x265، CRF یکی از روشهای کنترل کیفیت است. عدد کمتر معمولاً کیفیت بیشتر و فایل بزرگتر ایجاد میکند.
در نمونه این مقاله H.264 با CRF حدود ۲۲ تا ۲۳ و H.265 با CRF ۲۸ تنظیم شده است. این اعداد قانون ثابت نیستند و باید براساس محتوای واقعی آزمایش شوند.
Preset چه کاری انجام میدهد؟
Preset در x264/x265 تعادل بین زمان Encoding و Efficiency فشردهسازی را تغییر میدهد. Preset کندتر معمولاً زمان بیشتری صرف پیدا کردن روش فشردهسازی بهتر میکند.
برای سرویس On-Demand معمولاً medium یا fast نقطه شروع معقولتری از veryslow است.
ساخت HLS با FFmpeg
اگر بهجای فایل MP4 قرار است ویدئو Stream شود، میتوانید HLS تولید کنید.
نمونه H.264 HLS:
ffmpeg \ -i input.mp4 \ -map 0:v:0 \ -map 0:a? \ -c:v libx264 \ -preset medium \ -crf 22 \ -pix_fmt yuv420p \ -flags +cgop \ -force_key_frames "expr:gte(t,n_forced*4)" \ -c:a aac \ -b:a 128k \ -f hls \ -hls_time 4 \ -hls_playlist_type vod \ -hls_flags independent_segments+temp_file \ -hls_segment_filename \ "hls/segment_%05d.ts" \ hls/index.m3u8
خروجی شامل Playlist با پسوند m3u8 و Segmentهای ویدئویی خواهد بود.
Adaptive Bitrate Streaming
برای پلتفرم ویدئویی حرفهای بهتر است فقط یک Resolution تولید نکنید. میتوانید از فایل اصلی نسخههای مختلف ایجاد کنید:
360p 480p 720p 1080p 2160p
سپس Master Playlist براساس Bandwidth کاربر Stream مناسب را انتخاب میکند. این همان مسیری است که یک Transcoding Server ساده را به زیرساخت Video Streaming واقعی تبدیل میکند.
GPU Transcoding چیست؟
در CPU Encoding عملیات Codec روی CPU انجام میشود. اگر سرور GPU سازگار و Driver مناسب داشته باشد، میتوان برخی Codecها را با Hardware Encoder اجرا کرد.
ابتدا Encoderها را بررسی کنید:
ffmpeg -encoders | grep nvenc ffmpeg -encoders | grep qsv ffmpeg -encoders | grep vaapi
نمونه NVIDIA NVENC
ffmpeg \ -i input.mp4 \ -c:v h264_nvenc \ -preset p5 \ -cq 23 \ -c:a aac \ output.mp4
نمونه Intel Quick Sync
ffmpeg \ -i input.mp4 \ -c:v h264_qsv \ -global_quality 23 \ -c:a aac \ output.mp4
وجود نام Encoder در FFmpeg بهتنهایی کافی نیست؛ سختافزار، Driver و دسترسی Container/VM به Device نیز باید آماده باشد.
CPU بهتر است یا GPU؟
| ویژگی | CPU | GPU Hardware Encoding |
|---|---|---|
| راهاندازی | سادهتر | نیازمند Hardware و Driver |
| سرعت | وابسته به Coreها و Preset | معمولاً مناسبتر برای Throughput بالا |
| کنترل Encoder | بسیار منعطف | وابسته به Encoder سختافزاری |
امنیت Video Transcoding API
✅ FFmpeg را بهروز نگه دارید.
✅ API را با Authentication محافظت کنید.
✅ Command خام FFmpeg از کاربر نگیرید.
✅ shell=True استفاده نکنید.
✅ حجم Upload را محدود کنید.
✅ تعداد Jobهای همزمان را کنترل کنید.
✅ فایل خروجی را از مسیر internal Nginx تحویل دهید.
✅ فایلهای ورودی قدیمی و Jobهای Failed را Cleanup کنید.
✅ Disk Usage را مانیتور کنید.
چرا Disk خیلی مهم است؟
فرض کنید کاربر یک ویدئوی ۱۰ گیگابایتی Upload کند. در زمان Transcoding ممکن است هم فایل ورودی، هم خروجی و هم نسخههای HLS روی Disk قرار داشته باشند.
اگر چند Job همزمان داشته باشید، فضای مورد نیاز خیلی سریع افزایش پیدا میکند. قبل از پذیرش Job باید Free Space بررسی شود و فایلهای قدیمی نیز بهصورت دورهای حذف شوند.
چه زمانی از SQLite عبور کنیم؟
برای یک Transcoding Node و تعداد محدود Job، SQLite ساده و مناسب است. وقتی چند Worker Server دارید، بهتر است Queue را به Redis، RabbitMQ یا سیستم صف دیگری منتقل و Metadata را در PostgreSQL نگه دارید.
معماری بزرگتر میتواند چنین باشد:
API
|
v
Redis / RabbitMQ
|
+-----------+-----------+
| | |
Worker 1 Worker 2 Worker 3
| | |
+-----------+-----------+
|
v
Object Storage
|
v
CDN
خطاهای رایج FFmpeg Server
Unknown encoder ‘libx264’
Build نصبشده FFmpeg فاقد x264 است. فهرست Encoderها را بررسی کنید.
Unknown encoder ‘libsvtav1’
FFmpeg شما با SVT-AV1 ساخته نشده است. یا Build مناسب نصب کنید یا Profile AV1 را غیرفعال کنید.
No space left on device
Upload و Outputهای قبلی فضای Disk را پر کردهاند:
df -h du -sh /srv/transcoder/*
Transcoding بیش از حد کند است
CPU Usage، Preset، Codec، Resolution و تعداد Workerهای همزمان را بررسی کنید. AV1 و H.265 معمولاً از H.264 CPU بیشتری میخواهند.
فایل MP4 تا دانلود کامل پخش نمیشود
استفاده از:
-movflags +faststart
Metadata مورد نیاز پخش اولیه MP4 را به ابتدای فایل منتقل میکند و برای Progressive Download مفید است.
سؤالات متداول
Video Transcoding چیست؟
تبدیل ویدئو از Codec، Bitrate، Resolution یا Container فعلی به نسخه دیگری برای کاهش حجم، سازگاری یا Streaming است.
بهترین زبان برای ساخت Transcoding Server چیست؟
خود Encoding بهتر است توسط FFmpeg انجام شود. Python برای API، مدیریت Job، Queue و Integration انتخاب سادهای است.
آیا میتوان FFmpeg را روی VPS نصب کرد؟
بله. FFmpeg روی VPS لینوکس اجرا میشود، اما سرعت Transcoding مستقیماً به منابع CPU و Storage وابسته است.
H.264 بهتر است یا H.265؟
H.264 سازگاری وسیعتری دارد. H.265 میتواند Compression بهتری ایجاد کند اما Encode سنگینتر و پشتیبانی Client متفاوتی دارد.
AV1 برای Transcoding Server مناسب است؟
بله، مخصوصاً اگر کاهش Bitrate مهم باشد؛ ولی CPU Encoding AV1 میتواند سنگین باشد و استفاده از Hardware Encoder در مقیاس بالا ارزشمند است.
HLS چیست؟
HLS ویدئو را به Playlist و Segmentهای کوچک تقسیم میکند تا Player بتواند آن را بهصورت Streaming دریافت کند.
آیا چند FFmpeg را همزمان اجرا کنیم؟
فقط اگر CPU، RAM، Disk و Thermal/Resource Budget اجازه دهد. در بسیاری از VPSها اجرای چند Encode سنگین همزمان باعث کاهش شدید سرعت همه Jobها میشود.
جمعبندی
برای ساخت Video Transcoding Server لازم نیست Encoder را در Python پیادهسازی کنید. FFmpeg بخش پردازش ویدئو را بسیار بهتر انجام میدهد و Python میتواند لایه مدیریتی آن باشد.
معماری API + Queue + Worker باعث میشود Upload و درخواستهای وب از Jobهای طولانی FFmpeg جدا باشند. همین ساختار بعداً بهسادگی میتواند به Redis، چند Worker Node، Object Storage و CDN توسعه پیدا کند.
برای شروع، H.264/MP4 بهترین Profile عمومی است. بعد از راهاندازی پایدار سرویس میتوانید HEVC، AV1، HLS و در نهایت GPU Transcoding را اضافه کنید.






دیدگاهها (۰)