IlmHamroh
JavaScript Full-stack/29-qism. Backend integratsiyalar: fayl, to'lov, bot, realtime8/69-dars16 daqiqa
Mundarija (27)

Video va audio: ffmpeg'ni Node'dan xavfsiz ishga tushirish

Qisqacha: Video bilan ishlashning amaliy yagona vositasi — ffmpeg, alohida dastur. Node uni child_process.spawn bilan ishga tushiradi: argumentlar massiv bo'lib beriladi (shell yo'q — fayl nomi orqali buyruq bajarib bo'lmaydi), -progress pipe:1 dan foiz o'qiladi, osilib qolsa timeout bilan o'ldiriladi. Kanon tajribasida 20 soniyalik video 4 soniyalik HLS bo'laklarga emas, 8.33 soniyalik bo'laklarga kesildi — sabab keyframe. Video qayta ishlash — og'ir ish: faqat navbat va alohida worker'da.

Bu darsda

  • ffmpeg'ni spawn bilan ishga tushirasiz va nega exec emasligini injection misolida ko'rasiz.
  • ffprobe bilan video haqida ma'lumot olasiz, thumbnail yasaysiz.
  • HLS segmentlash va keyframe bog'liqligini kanon chiqishi bilan tushunasiz.
  • Audioni MP3 va Opus'ga o'tkazasiz, progress va timeout'ni boshqarasiz.
  • Nega video ishlovi API jarayonida emas, worker'da bo'lishini bilasiz.

Oldin bilishingiz kerak: child_process: tashqi dasturlarni ishga tushirish, Rasmlarni qayta ishlash: sharp, Stream nima: to'rt turi va rejimlari, SSRF va tashqi URL'lar bilan ishlash.

Oldingi darsda rasmni sharp bilan qayta ishladik — kutubxona Node ichida ishladi. Video uchun bunday kutubxona yo'q: ishni tashqi dastur qiladi, Node uni boshqaradi.

1. Nega bu kerak?

Foydalanuvchi telefondan 2 daqiqalik video yukladi: 180 MB, HEVC kodek, 4K. Muammolar:

  1. Ko'rsatib bo'lmaydi. Ba'zi brauzerlar HEVC'ni o'ynatmaydi; 4K — telefon internetida to'xtab-to'xtab.
  2. Ro'yxatda nima ko'rsatiladi? Video uchun ham kichik rasm (thumbnail) kerak.
  3. Ovozli xabar (Telegram bot — shu qismning keyingi darslari) — .ogg kerak, foydalanuvchi .m4a yubordi.

Bularning hammasini ffmpeg qiladi: video va audioni o'qiydi, kodlaydi (transkodlash — transcoding), kesadi, bo'laklaydi. U C da yozilgan, juda tez, lekin juda og'ir: 1 daqiqalik videoni qayta kodlash protsessorni o'nlab soniya band qiladi.

Kanon mashinasidagi versiya:

text
ffmpeg version 9.0.2-full_build-www.gyan.dev Copyright (c) 2000-2026 the FFmpeg developers (winget, Gyan.dev full build)

Windows'da winget, Linux'da paket menejeri yoki Docker image orqali o'rnatiladi. npm'dagi ffmpeg-static paketi tayyor binar beradi — kanonga kerak bo'lmadi.

2. spawn va argumentlar massivi

2.1 Nega exec emas

child_process darsidan eslang: exec buyruqni shell orqali bajaradi, spawn/execFile esa dasturni to'g'ridan, argumentlar massivi bilan. Video uchun bu farq — xavfsizlik masalasi. Foydalanuvchi faylini x.mp4; echo BUZILDI > pwned.txt deb nomladi:

js
function thumbnailArgs(kirish, chiqish, soniya = 5, eni = 320) {
  return [
    "-hide_banner", "-nostdin", "-y",
    "-ss", String(soniya), "-i", kirish,
    "-frames:v", "1", "-vf", `scale=${eni}:-2`, chiqish,
  ];
}
const nom = "x.mp4; echo BUZILDI > pwned.txt";
const args = thumbnailArgs(nom, "t.jpg");
console.log(args.length, JSON.stringify(args[6]));
const satr = `ffmpeg -ss 5 -i ${nom} t.jpg`;
console.log(satr.split(";").map((s) => s.trim()));

Konsolda:

text
12 "x.mp4; echo BUZILDI > pwned.txt"
[ 'ffmpeg -ss 5 -i x.mp4', 'echo BUZILDI > pwned.txt t.jpg' ]

Massivda nom — bitta element, ffmpeg uni fayl nomi deb oladi. Satrda esa shell ; ni buyruqlar ajratgichi deb ko'radi — ikkinchi buyruq bajariladi. Kanon massiv bilan haqiqatan sinadi:

text
yo'q fayl: ffmpeg kod=4294967294 signal=null: Error opening input files: No such file or directory
massiv argument: fayl nomi butunligicha ffmpeg'ga ketdi → ffmpeg kod=4294967274 signal=null: Error opening input files: Invalid argument
pwned.txt bor: false

pwned.txt yaratilmadi. ffmpeg nomni ochishga urindi va "Invalid argument" dedi (Windows'da ;, > fayl nomida taqiqlangan).

Kodlar g'alati ko'rinadi. Windows'da ffmpeg manfiy kod qaytaradi, Node esa uni 32 bitli ishorasiz son sifatida ko'rsatadi:

js
console.log(4294967294 | 0, 4294967274 | 0);

Konsolda:

text
-2 -22

-2 — "fayl yo'q" (ENOENT), -22 — "noto'g'ri argument" (EINVAL). Linux'da kod boshqacha bo'lishi mumkin (sinalmagan) — shuning uchun kodga emas, 0 yoki 0 emasligiga qarang, tafsilotni stderr'dan oling.

2.2 Kanonning ffmpeg yordamchisi

js
import { spawn } from "node:child_process";

/** argumentlar MASSIV (shell yo'q), stderr dan oxirgi xato,
 * stdout dan progress, timeout → SIGKILL */
function ffmpeg(args, { timeoutMs = 60_000, progress } = {}) {
  return new Promise((ok, yoq) => {
    const p = spawn("ffmpeg", ["-hide_banner", "-nostdin", "-y",
      "-progress", "pipe:1", "-nostats", ...args],
      { stdio: ["ignore", "pipe", "pipe"] });
    let xato = "";
    p.stderr.on("data", (b) => { xato = (xato + b).slice(-2000); });
    p.stdout.on("data", (b) => {
      for (const q of String(b).split("\n")) {
        if (q.startsWith("out_time_ms=") && progress) {
          progress(Number(q.slice(12)) / 1e6);
        }
      }
    });
    const t = setTimeout(() => { p.kill("SIGKILL"); }, timeoutMs);
    p.on("close", (kod, signal) => {
      clearTimeout(t);
      if (kod === 0) ok();
      else yoq(new Error(`ffmpeg kod=${kod} signal=${signal}: ` +
        xato.trim().split("\n").at(-1)));
    });
  });
}

Har bayroq nimaga:

Bayroq / sozlama Nega
-nostdin ffmpeg klaviaturadan buyruq kutmasin (fonda osilib qoladi)
-y chiqish fayli bo'lsa, so'ramasdan ustiga yozsin
-progress pipe:1 holatni kalit=qiymat qatorlari bilan stdout'ga
-nostats stderr'ni progress chiqindisi bilan to'ldirmasin
stderr oxirgi 2000 belgi ffmpeg ko'p yozadi — xotira o'smasin, xato oxirida
setTimeout + SIGKILL buzilgan fayl ffmpeg'ni osiltirib qo'yishi mumkin

Telefon uchun qatorlarni bo'ldik — mantiq aynan kanondagidek.

3. ffprobe: video haqida ma'lumot

Videoni qayta ishlashdan oldin — nima ekanini bilish. ffmpeg bilan birga keladigan ffprobe JSON beradi:

js
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const pexec = promisify(execFile);
const { stdout } = await pexec("ffprobe", ["-v", "error",
  "-print_format", "json", "-show_format", "-show_streams", video]);
const probe = JSON.parse(stdout);

Kanon 20 soniyalik sinov videosi (testsrc2 — ffmpeg'ning o'zi yasaydigan rangli sinov tasviri + 440 Hz tovush; 1.6 s da yasaldi, 6.85 MB) uchun:

text
{
  davomiylik: '20.000000',
  format: 'mov,mp4,m4a,3gp,3g2,mj2',
  bit_rate: '2874374',
  oqimlar: [ 'video:h264 1280x720', 'audio:aac' ]
}

Qiymatlar satr ('20.000000') — Number() bilan aylantiring. ffprobe natijasi bilan siz:

  • juda uzun videoni rad etasiz (limit — masalan 10 daqiqa);
  • video oqimi yo'q faylni (aslida audio) aniqlaysiz;
  • qayta kodlash kerakmi (h264 + aac — brauzerlar uchun tayyor) hal qilasiz.

Diqqat: Fayl kengaytmasi (.mp4) va Content-Type — mijoz so'zi (Fayl yuklash xavfsizligi). ffprobe ham faylni o'qiydi — u ham kirish ma'lumotini qayta ishlovchi parser. ffprobe'ni ham timeout bilan, ishonchsiz faylga alohida jarayonda yurgizing.

4. Thumbnail

5-soniyadagi kadr, eni 320:

text
-ss oldin  160 ms
-ss keyin  177 ms

-ss 5 -i video (kirishdan oldin) — ffmpeg 5-soniyaga tez "sakraydi" (yaqin keyframe'ga). -i video -ss 5 (keyin) — boshidan 5 soniyagacha hamma kadrni ochib boradi. 20 soniyalik videoda farq kichik (160 va 177 ms). 2 soatlik filmda 1-soatdagi kadr uchun "keyin" varianti butun birinchi soatni ochadi — farq soniyalar va daqiqalar.

scale=320:-2 — eni 320, bo'yi nisbat bo'yicha, juft son (-2): ko'p kodeklar toq o'lchamni qabul qilmaydi.

5. HLS: video bo'laklari

5.1 Nima uchun bo'laklash

Brauzer 200 MB videoni to'liq yuklab, keyin o'ynatishi shart emas. HLS (HTTP Live Streaming) videoni bir necha soniyalik bo'laklarga (segment, .ts) bo'ladi va ro'yxat fayli (.m3u8, playlist) yozadi. Pleyer ro'yxatni o'qiydi va bo'laklarni ketma-ket yuklaydi. Har bo'lak — oddiy fayl: S3 dan presigned yoki CDN orqali berish mumkin.

5.2 Kanon topilmasi: 4 emas, 8.33

Kanon -hls_time 4 (4 soniyalik bo'lak) so'radi:

text
986 ms; fayllar: index.m3u8 seg_000.ts seg_001.ts seg_002.ts
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:8.333333,
seg_000.ts
#EXTINF:8.333333,
seg_001.ts
#EXTINF:3.333333,
seg_002.ts
#EXT-X-ENDLIST

Ro'yxatni JS bilan tahlil qilamiz (kanon chiqishining o'zi kirish sifatida):

js
const m3u8 = `#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:8.333333,
seg_000.ts
#EXTINF:8.333333,
seg_001.ts
#EXTINF:3.333333,
seg_002.ts
#EXT-X-ENDLIST`;
const seg = [...m3u8.matchAll(/#EXTINF:([\d.]+),\n(\S+)/g)]
  .map((m) => ({ fayl: m[2], s: Number(m[1]) }));
console.log(seg);
const jami = seg.reduce((a, x) => a + x.s, 0);
console.log("jami:", jami.toFixed(2), "s");
console.log("GOP:", (250 / 30).toFixed(2), "s");

Konsolda:

text
[
  { fayl: 'seg_000.ts', s: 8.333333 },
  { fayl: 'seg_001.ts', s: 8.333333 },
  { fayl: 'seg_002.ts', s: 3.333333 }
]
jami: 20.00 s
GOP: 8.33 s

Sabab — keyframe (kalit kadr). Videoda har kadr to'liq saqlanmaydi: faqat kalit kadr to'liq, qolganlari — undan farq. Bo'lakni faqat kalit kadrda boshlash mumkin. Kodlovchi x264 sukutda har 250 kadrda kalit kadr qo'yadi (GOP — kadrlar guruhi). 30 fps da bu 8.33 soniya. ffmpeg 4 soniyada kesmoqchi bo'ldi, lekin u yerda kalit kadr yo'q edi — keyingisigacha kutdi.

Kalit kadrlarni majburlash:

text
#EXTINF:4.000000, #EXTINF:4.000000, #EXTINF:4.000000, #EXTINF:4.000000, #EXTINF:4.000000,

-force_key_frames "expr:gte(t,n_forced*4)" — "har 4 soniyada kalit kadr" — va bo'laklar aynan 4.000. Kalit kadr ko'paysa fayl biroz kattalashadi — bu kelishuv.

Tekshirib ko'ring: Video 25 fps, GOP 250 kadr, -hls_time 4. Bo'laklar taxminan qancha bo'ladi?

Javob

250 / 25 = 10 soniya. Kalit kadr har 10 soniyada — ffmpeg 4 soniyada kesa olmaydi, bo'laklar ~10 soniyadan chiqadi (oxirgisi qoldiq). Aniq 4 soniya uchun yana -force_key_frames yoki -g 100 (25 × 4).

5.3 Bir nechta sifat

Kanon bitta sifat (480p) yasadi. Haqiqiy pleyerlar internet tezligiga qarab sifatni almashtiradi (adaptive bitrate): har sifat — alohida ro'yxat, ularni master playlist birlashtiradi. Buni 3-mashqda yasaysiz. Har qo'shimcha sifat — yana bir to'liq transkodlash, ya'ni CPU vaqti.

flowchart LR
  V[Asl video] --> F1[ffmpeg 360p]
  V --> F2[ffmpeg 720p]
  F1 --> P1[360p/index.m3u8 + .ts]
  F2 --> P2[720p/index.m3u8 + .ts]
  P1 --> M[master.m3u8]
  P2 --> M
  M --> PL[Pleyer: tezlikka qarab tanlaydi]

6. Audio

text
mp3 313.6 KB
ogg 116.0 KB

20 soniyalik AAC ovoz: MP3 128 kbit/s — 313.6 KB, Opus 32 kbit/s (.ogg) — 116 KB. Opus — nutq uchun juda samarali kodek; Telegram Bot API hujjatiga ko'ra ovozli xabar (sendVoice) OPUS bilan kodlangan .ogg fayl bo'lishi kerak. Musiqa uchun 32 kbit/s kam — 96–128 tanlang.

Hisob: 128 000 bit/s × 20 s / 8 = 320 000 bayt ≈ 312.5 KB — kanon natijasiga juda yaqin (fayl sarlavhalari qo'shimcha).

7. Progress va timeout

7.1 Progress

-progress pipe:1 stdout'ga davriy ravishda (hujjatga ko'ra sukutda har 0.5 soniyada, -stats_period) va ish oxirida qatorlar yozadi: frame=…, out_time_ms=…, progress=continue, oxirida progress=end. Kanon 20 soniyalik videoni yasaganda 3 ta progress hodisasi oldi (oxirgisi 20.0 s). Foizni hisoblash (pastdagi kirish — ffmpeg formatidagi namuna, kanon chiqishi emas):

js
const namuna = [
  "frame=150", "fps=0.00", "out_time_us=5000000",
  "out_time_ms=5000000", "out_time=00:00:05.000000",
  "speed=10.1x", "progress=continue",
].join("\n");
function foiz(bolak, davomiylik) {
  for (const q of bolak.split("\n")) {
    if (q.startsWith("out_time_ms=")) {
      const s = Number(q.slice(12)) / 1e6;
      return Math.min(100, Math.round((s / davomiylik) * 100));
    }
  }
  return undefined;
}
console.log(foiz(namuna, 20), foiz("progress=end", 20));

Konsolda:

text
25 undefined

Nozik joy: out_time_ms nomiga qaramay mikrosoniyalarda — shuning uchun kanon 1e6 ga bo'ladi (out_time_us bilan bir xil qiymat). Davomiylik ffprobe'dan olinadi. Foizni bazaga yoki navbat vazifasining progress maydoniga yozasiz, frontend uni Server-Sent Events bilan ko'rsatadi.

7.2 Timeout

Kanon 10 daqiqalik 1080p videoni preset slow bilan kodlashni boshladi va 1 soniyada o'ldirdi:

text
ffmpeg kod=null signal=SIGKILL

kod=null — jarayon o'zi tugamadi, signal=SIGKILL bilan o'ldirildi. Windows'da kill("SIGKILL") jarayonni majburan tugatadi (Linux'da signal; Windows'da Node ekvivalentini qiladi). Yarim yozilgan chiqish faylini keyin o'chiring.

Timeout qanday tanlanadi: videoning davomiyligi × "tezlik koeffitsienti" × zaxira. Masalan 2 daqiqalik video veryfast bilan ~10x tezlikda — 12 s; timeout — 2–3 daqiqa.

8. fluent-ffmpeg va boshqa paketlar

Internetdagi ko'p misollar fluent-ffmpeg paketi bilan: zanjirli API (ffmpeg(kirish).size("320x?").save(chiqish)). Kanon npm view bilan tekshirdi — paket npm'da deprecated ("Package no longer supported") va repozitoriya arxivlangan. Yangi loyihada ishlatmang: u ham ichida spawn qiladi, foydasi — faqat qulay API. Kanon yo'li — o'zingizning 20 qatorlik yordamchingiz («Kanonning ffmpeg yordamchisi» bo'limi).

Variant Holat Izoh
O'z spawn yordamchingiz kanon to'liq nazorat, bog'liqliksiz
fluent-ffmpeg deprecated eski kodda uchraydi
ffmpeg-static tayyor binar serverga ffmpeg o'rnatib bo'lmasa
ffmpeg.wasm brauzerda sekin, kichik ishlar uchun

9. Navbat va worker

ffmpeg — CPU yeyuvchi. API jarayonida chaqirsangiz:

  • 3 ta parallel yuklash — 3 ta ffmpeg — barcha yadrolar band, API javob bermaydi;
  • foydalanuvchi so'rovi 30 soniya kutadi va timeout oladi;
  • server qayta yonsa — ish yo'qoladi.

To'g'ri tartib:

sequenceDiagram
  participant B as Brauzer
  participant A as API
  participant Q as Navbat
  participant W as Worker
  participant S as Ombor
  B->>A: Video yuklandi (kalit)
  A->>Q: transkod vazifasi
  A-->>B: 202 Qabul qilindi
  W->>Q: vazifani oladi
  W->>S: asl videoni yuklab oladi
  W->>W: ffprobe, ffmpeg (timeout)
  W->>S: HLS bo'laklari, thumbnail
  W->>A: holat: tayyor

API 202 Accepted qaytaradi (Bulk va uzoq davom etuvchi operatsiyalar). Worker — alohida jarayon (yoki alohida server), bir vaqtda 1–2 ta ffmpeg; navbat qayta urinish va ustuvorlikni boshqaradi (BullMQ asoslari).

10. Hujumchi nigohi

  1. Shell injection. exec(\ffmpeg -i ${nom}`)— fayl nomi buyruqqa aylanadi. Himoya:spawn/execFile` + massiv; kirish fayli — server yasagan vaqtinchalik nom, foydalanuvchi nomi emas.
  2. SSRF va fayl o'qish. ffmpeg faqat fayl emas, http://, file: va boshqa protokollarni ham ochadi. Foydalanuvchi yuklagan .m3u8 yoki concat ro'yxatida http://169.254.169.254/… yoki /etc/passwd bo'lsa — ffmpeg uni o'qiydi. Himoya (hujjat bo'yicha, kanonda sinalmagan): -protocol_whitelist file va playlist turidagi kirishni umuman qabul qilmaslik (SSRF va tashqi URL'lar bilan ishlash).
  3. Resurs tugashi. 10 soatlik yoki 8K video. Himoya: ffprobe bilan davomiylik/o'lcham limiti, timeout, worker parallelligi.
  4. Parser zaifliklari. ffmpeg katta C kod bazasi — vaqti-vaqti bilan zaifliklar topiladi. Himoya: yangilab turish, worker'ni cheklangan huquq (konteyner, alohida foydalanuvchi) bilan yurgizish.

11. Ko'p uchraydigan xatolar

Xato Sabab Davosi
ffmpeg fonda osilib qoldi stdin kutyapti -nostdin
spawn ffmpeg ENOENT ffmpeg o'rnatilmagan / PATH'da yo'q o'rnating yoki ffmpeg-static
HLS bo'laklari so'ralgandan uzun keyframe -force_key_frames
Thumbnail sekin -ss kirishdan keyin -ss ni -i dan oldin
height not divisible by 2 toq o'lcham scale=320:-2
Jarayon xotirasi o'sdi stderr to'liq yig'ildi oxirgi N belgi
Progress 100 dan oshdi davomiylik noto'g'ri Math.min(100, …)
API sekinlashdi ffmpeg so'rov ichida navbat + worker

12. Mashqlar

Mashqlar kurs/mashqlar/29/08-ffmpeg/ da.

1-mashq (oson): to'g'ri yoki noto'g'ri

  1. exec bilan fayl nomini satrga qo'yish xavfsiz, agar nom qo'shtirnoqda bo'lsa.
  2. HLS bo'lagi faqat kalit kadrda boshlanadi.
  3. -ss ni -i dan oldin qo'yish qidiruvni tezlashtiradi.
  4. fluent-ffmpeg — yangi loyiha uchun tavsiya etilgan paket.
Yechim
  1. Noto'g'ri — nomning o'zida qo'shtirnoq bo'lishi mumkin; yagona to'g'ri yo'l — massiv.
  2. To'g'ri — shuning uchun 4 emas, 8.33 soniya chiqdi.
  3. To'g'ri — yaqin kalit kadrga sakraydi.
  4. Noto'g'ri — npm'da deprecated.

2-mashq (o'rta): ffprobe xulosasi

xulosa(probe, maksSoniya = 600) funksiyasini yozing: ffprobe JSON'idan { soniya, olcham, kodek, kbps } qaytarsin. Video oqimi yo'q bo'lsa { xato: "video oqimi yo'q" }, davomiylik 0 yoki limitdan katta bo'lsa { xato: "davomiylik: N" }. Audio yo'q bo'lsa kodekda ovozsiz. Kanon chiqishidagi qiymatlar bilan, limit 10 bilan va faqat audio oqimi bilan sinang.

js
const probe = {
  format: { duration: "20.000000", bit_rate: "2874374" },
  streams: [
    { codec_type: "video", codec_name: "h264",
      width: 1280, height: 720 },
    { codec_type: "audio", codec_name: "aac" },
  ],
};
Yechim
js
const probe = {
  format: { duration: "20.000000", bit_rate: "2874374" },
  streams: [
    { codec_type: "video", codec_name: "h264",
      width: 1280, height: 720 },
    { codec_type: "audio", codec_name: "aac" },
  ],
};

function xulosa(p, maksSoniya = 600) {
  const s = Number(p.format.duration);
  const video = p.streams.find((x) => x.codec_type === "video");
  const audio = p.streams.find((x) => x.codec_type === "audio");
  if (!video) return { xato: "video oqimi yo'q" };
  if (!(s > 0) || s > maksSoniya) return { xato: `davomiylik: ${s}` };
  return {
    soniya: s,
    olcham: `${video.width}x${video.height}`,
    kodek: video.codec_name + "+" + (audio?.codec_name ?? "ovozsiz"),
    kbps: Math.round(Number(p.format.bit_rate) / 1000),
  };
}
console.log(xulosa(probe));
console.log(xulosa(probe, 10));
console.log(xulosa({ ...probe, streams: [probe.streams[1]] }));

Konsolda:

text
{ soniya: 20, olcham: '1280x720', kodek: 'h264+aac', kbps: 2874 }
{ xato: 'davomiylik: 20' }
{ xato: "video oqimi yo'q" }

!(s > 0) — NaN ni ham ushlaydi (NaN > 0 — false). Davomiylik satri buzilgan bo'lsa Number NaN beradi va video rad etiladi.

3-mashq (qiyin): master playlist

master(variantlar) funksiyasini yozing: { nom, eni, boyi, bitreyt } massividan HLS master ro'yxatini yasasin — birinchi qator #EXTM3U, har variant uchun #EXT-X-STREAM-INF:BANDWIDTH=<bitreyt>,RESOLUTION=<eni>x<boyi> va keyingi qatorda <nom>/index.m3u8. 360p (640×360, 800 000) va 720p (1280×720, 2 800 000) bilan sinang.

Yechim
js
const variantlar = [
  { nom: "360p", eni: 640, boyi: 360, bitreyt: 800_000 },
  { nom: "720p", eni: 1280, boyi: 720, bitreyt: 2_800_000 },
];
function master(v) {
  const q = ["#EXTM3U"];
  for (const x of v) {
    q.push(`#EXT-X-STREAM-INF:BANDWIDTH=${x.bitreyt},` +
      `RESOLUTION=${x.eni}x${x.boyi}`);
    q.push(`${x.nom}/index.m3u8`);
  }
  return q.join("\n");
}
console.log(master(variantlar));

Konsolda:

text
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p/index.m3u8

Pleyer master'ni o'qib, internet tezligiga mos variantni tanlaydi va keyin uning index.m3u8 sini yuklaydi. BANDWIDTH — bit/s, bayt emas. Format RFC 8216 da; kanon faqat bitta sifatli ro'yxat yasadi — master'ni haqiqiy pleyerda sinamadi.

13. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi

Bu darsda vazifalar-api ga kod qo'shilmaydi. Sabablari:

  • vazifalar ilovasida video yo'q: ilovalar ruxsat ro'yxati — rasm, PDF, matn (fayllar_mime_check).
  • ffmpeg — tashqi dastur: serverga o'rnatish, versiyasini kuzatish, worker kerak. Ehtiyojsiz murakkablik.

Kanon tajribasi — mustaqil skript: sinov videosini yasash, ffprobe, thumbnail, HLS (oddiy va majburiy kalit kadr bilan), MP3/Opus, timeout va injection sinovi. O'zingiz sinab ko'ring: ffmpeg o'rnating (winget install Gyan.FFmpeg yoki Linux paket menejeri), yuqoridagi ffmpeg yordamchisini faylga ko'chiring va testsrc2 bilan 20 soniyalik video yasang:

js
await ffmpeg(["-f", "lavfi", "-i",
  "testsrc2=size=1280x720:rate=30:duration=20",
  "-f", "lavfi", "-i", "sine=frequency=440:duration=20",
  "-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p",
  "-c:a", "aac", "-shortest", "kirish.mp4"],
  { progress: (s) => console.log(`${s.toFixed(1)} s`) });

Video kerak bo'lsa (masalan, 38-qism bozor loyihasida mahsulot videosi), naqsh tayyor: navbat vazifasi + shu yordamchi + ombor.

14. Real ishda

  • Katta video platformalar odatda har yuklangan videoni bir nechta sifatga transkodlaydi va bo'laklab (HLS yoki DASH bilan) beradi.
  • Bulut xizmatlari (AWS MediaConvert, Cloudflare Stream, Mux) — transkodlashni o'zlari qiladi; narxi daqiqaga. Kam video bo'lsa — o'z worker'ingiz arzon.
  • Ovozli xabarlar — Opus; nutqni matnga aylantirish (Nutq va rasm bilan ishlovchi AI) uchun ham ffmpeg avval formatni moslaydi.
  • Intervyuda: "Foydalanuvchi videosini qanday qayta ishlaysiz?", "exec va spawn farqi?", "HLS nima va nega bo'laklar?"

Xulosa

  • ffmpeg — tashqi dastur; Node'dan spawn + argumentlar massivi (shell yo'q → injection yo'q).
  • -nostdin, -y, -progress pipe:1, stderr oxiri, timeout → SIGKILL.
  • ffprobe — JSON ma'lumot; qiymatlar satr; limitlarni shu yerda tekshiring.
  • -ss kirishdan oldin — tez thumbnail; scale=320:-2 — juft o'lcham.
  • HLS bo'lagi faqat kalit kadrda: sukut GOP 250 kadr → 8.33 s; -force_key_frames → aynan 4 s.
  • fluent-ffmpeg deprecated; video ishlovi — navbat va worker'da.

Keyingi dars: PDF generatsiya — PDFKit, pdfmake va brauzerdan page.pdf(); o'zbek harflari va shrift.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!