From 606f4647b0f2df3b303ad18464b4630305aed19c Mon Sep 17 00:00:00 2001 From: sheisuka Date: Thu, 16 Oct 2025 00:37:55 +0700 Subject: [PATCH] translate README to russian --- README-ru.md | 979 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 980 insertions(+) create mode 100644 README-ru.md diff --git a/README-ru.md b/README-ru.md new file mode 100644 index 0000000..d59a654 --- /dev/null +++ b/README-ru.md @@ -0,0 +1,979 @@ +[πŸ‡¨πŸ‡³](/README-cn.md "Simplified Chinese") +[πŸ‡°πŸ‡·](/README-ko.md "Korean") +[πŸ‡ͺπŸ‡Έ](/README-es.md "Spanish") +[πŸ‡»πŸ‡³](/README-vn.md "Vietnamese") +[πŸ‡§πŸ‡·](/README-pt.md "Portuguese") + +[![license](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg) + +Π― искал Ρ‚ΡƒΡ‚ΠΎΡ€ΠΈΠ°Π»/ΠΊΠ½ΠΈΠ³Ρƒ, которая Π½Π°ΡƒΡ‡ΠΈΡ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ [FFmpeg](https://www.ffmpeg.org/) с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ libav, ΠΈ наткнулся Π½Π° руководство ["How to write a video player in less than 1k lines"](http://dranger.com/ffmpeg/). +К соТалСнию, ΠΎΠ½ΠΎ устарСло, поэтому я Ρ€Π΅ΡˆΠΈΠ» Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ этот Π³Π°ΠΉΠ΄. + +Π‘ΠΎΠ»ΡŒΡˆΠ°Ρ Ρ‡Π°ΡΡ‚ΡŒ ΠΊΠΎΠ΄Π° здСсь Π½Π° C, **Π½ΠΎ Π½Π΅ ΠΏΡƒΠ³Π°ΠΉΡ‚Π΅ΡΡŒ**: ΠΏΠΎΠ½ΡΡ‚ΡŒ ΠΈ пСрСнСсти ΠΈΠ΄Π΅ΠΈ Π½Π° Π»ΡŽΠ±ΠΈΠΌΡ‹ΠΉ язык нСслоТно. +Для FFmpeg/libav Π΅ΡΡ‚ΡŒ Π±ΠΈΠ½Π΄ΠΈΠ½Π³ΠΈ ΠΊΠΎ ΠΌΠ½ΠΎΠ³ΠΈΠΌ языкам β€” Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, [python](https://pyav.org/), [go](https://github.com/imkira/go-libav); Π΄Π°ΠΆΠ΅ Ссли для вашСго языка Π±ΠΈΠ½Π΄ΠΈΠ½Π³ΠΎΠ² Π½Π΅Ρ‚, ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΡΡ Ρ‡Π΅Ρ€Π΅Π· `ffi` (Π²ΠΎΡ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ с [Lua](https://github.com/daurnimator/ffmpeg-lua-ffi/blob/master/init.lua)). + +ΠœΡ‹ Π½Π°Ρ‡Π½Ρ‘ΠΌ с ΠΊΡ€Π°Ρ‚ΠΊΠΎΠ³ΠΎ Π»ΠΈΠΊΠ±Π΅Π·Π°: Ρ‡Ρ‚ΠΎ Ρ‚Π°ΠΊΠΎΠ΅ Π²ΠΈΠ΄Π΅ΠΎ, Π°ΡƒΠ΄ΠΈΠΎ, ΠΊΠΎΠ΄Π΅ΠΊ ΠΈ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€, Π·Π°Ρ‚Π΅ΠΌ Π²ΠΊΡ€Π°Ρ‚Ρ†Π΅ пройдСмся ΠΏΠΎ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строкС `FFmpeg`, ΠΈ, Π½Π°ΠΊΠΎΠ½Π΅Ρ†, ΠΏΠ΅Ρ€Π΅ΠΉΠ΄Ρ‘ΠΌ ΠΊ ΠΊΠΎΠ΄Ρƒ. Если Π²Π°ΠΌ это нСинтСрСсно, смСло пролистывайтС прямо ΠΊ Ρ€Π°Π·Π΄Π΅Π»Ρƒ [Learning FFmpeg libav the hard way](#learn-ffmpeg-libav-the-hard-way) (Π΄Π°, ссылка с ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠΎΠΉ посрСди Ρ„Ρ€Π°Π·Ρ‹ β€” это отсылка ΠΊ ΠΌΠ΅ΠΌΠ°ΠΌ, Π½Π΅ Π±Π°Π³). + +ΠšΡ‚ΠΎ-Ρ‚ΠΎ Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚, Ρ‡Ρ‚ΠΎ видСостриминг Π² Π˜Π½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Π΅ β€” Π±ΡƒΠ΄ΡƒΡ‰Π΅Π΅ Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½ΠΎΠ³ΠΎ Π’Π’. Π’ любом случаС, FFmpeg β€” Π²Π΅Ρ‰ΡŒ, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ стоит ΠΈΠ·ΡƒΡ‡ΠΈΡ‚ΡŒ. + +**ОглавлСниС** + +* [Π’Π²Π΅Π΄Π΅Π½ΠΈΠ΅](#intro) + + * [Π²ΠΈΠ΄Π΅ΠΎ β€” Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Ρ‚Ρ‹ видишь!](#video---what-you-see) + * [Π°ΡƒΠ΄ΠΈΠΎ β€” Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Ρ‚Ρ‹ ΡΠ»Ρ‹ΡˆΠΈΡˆΡŒ!](#audio---what-you-listen) + * [ΠΊΠΎΠ΄Π΅ΠΊ β€” сТатиС Π΄Π°Π½Π½Ρ‹Ρ…](#codec---shrinking-data) + * [ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ β€” Π΄ΠΎΠΌ для Π°ΡƒΠ΄ΠΈΠΎ ΠΈ Π²ΠΈΠ΄Π΅ΠΎ](#container---a-comfy-place-for-audio-and-video) +* [FFmpeg β€” командная строка](#ffmpeg---command-line) + + * [FFmpeg CLI 101](#ffmpeg-command-line-tool-101) +* [Π’ΠΈΠΏΠΎΠ²Ρ‹Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ с Π²ΠΈΠ΄Π΅ΠΎ](#common-video-operations) + + * [Transcoding (ΠΏΠ΅Ρ€Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅)](#transcoding) + * [Transmuxing (ΠΏΠ΅Ρ€Π΅ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΏΠ»Π΅ΠΊΡΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅)](#transmuxing) + * [Transrating (ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚Π°)](#transrating) + * [Transsizing (ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ)](#transsizing) + * [Бонус: Π°Π΄Π°ΠΏΡ‚ΠΈΠ²Π½Ρ‹ΠΉ стриминг](#bonus-round-adaptive-streaming) + * [Π”Π°Π»ΡŒΡˆΠ΅ β€” большС](#going-beyond) +* [Learning FFmpeg libav the hard way](#learn-ffmpeg-libav-the-hard-way) + + * [Π“Π»Π°Π²Π° 0 β€” ΠΏΠ΅Ρ‡Π°Π»ΡŒΠ½ΠΎ извСстный Β«hello worldΒ»](#chapter-0---the-infamous-hello-world) + + * [АрхитСктура FFmpeg libav](#ffmpeg-libav-architecture) + * [Π“Π»Π°Π²Π° 1 β€” Ρ‚Π°ΠΉΠΌΠΈΠ½Π³ΠΈ/синхронизация](#chapter-1---syncing-audio-and-video) + * [Π“Π»Π°Π²Π° 2 β€” remuxing](#chapter-2---remuxing) + * [Π“Π»Π°Π²Π° 3 β€” транскодированиС](#chapter-3---transcoding) + +# Π’Π²Π΅Π΄Π΅Π½ΠΈΠ΅ + +## Π²ΠΈΠ΄Π΅ΠΎ β€” Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Ρ‚Ρ‹ видишь + +Если ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ ΡΠ΅Ρ€ΠΈΡŽ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ с Π·Π°Π΄Π°Π½Π½ΠΎΠΉ частотой (скаТСм, [24 ΠΊΠ°Π΄Ρ€Π° Π² сСкунду](https://www.filmindependent.org/blog/hacking-film-24-frames-per-second/)), Π²ΠΎΠ·Π½ΠΈΠΊΠ½Π΅Ρ‚ [иллюзия двиТСния](https://en.wikipedia.org/wiki/Persistence_of_vision). +Π’ ΠΈΡ‚ΠΎΠ³Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π±Π°Π·ΠΎΠ²ΡƒΡŽ идСю, ΡΡ‚ΠΎΡΡŽΡ‰ΡƒΡŽ Π·Π° Π²ΠΈΠ΄Π΅ΠΎ: **ряд ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ/ΠΊΠ°Π΄Ρ€ΠΎΠ², ΠΈΠ΄ΡƒΡ‰ΠΈΡ… с Π·Π°Π΄Π°Π½Π½ΠΎΠΉ частотой**. + + + +ZeitgenΓΆssische Illustration (1886) + +## Π°ΡƒΠ΄ΠΈΠΎ β€” Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Ρ‚Ρ‹ ΡΠ»Ρ‹ΡˆΠΈΡˆΡŒ! + +ΠœΠΎΠ»Ρ‡Π°Π»ΠΈΠ²ΠΎΠ΅ Π²ΠΈΠ΄Π΅ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΌΠ½ΠΎΠ³ΠΎΠ΅ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ, Π½ΠΎ Π±Π΅Π· Π·Π²ΡƒΠΊΠ° это Π±ΡƒΠ΄Π΅Ρ‚ совсСм Π½Π΅ Ρ‚ΠΎ. + +Π—Π²ΡƒΠΊ β€” это вибрация, Ρ€Π°ΡΠΏΡ€ΠΎΡΡ‚Ρ€Π°Π½ΡΡŽΡ‰Π°ΡΡΡ ΠΊΠ°ΠΊ Π²ΠΎΠ»Π½Π° давлСния Ρ‡Π΅Ρ€Π΅Π· Π²ΠΎΠ·Π΄ΡƒΡ… ΠΈΠ»ΠΈ Π»ΡŽΠ±ΡƒΡŽ Π΄Ρ€ΡƒΠ³ΡƒΡŽ срСду β€” Π³Π°Π·, ΠΆΠΈΠ΄ΠΊΠΎΡΡ‚ΡŒ, Ρ‚Π²Ρ‘Ρ€Π΄ΠΎΠ΅ Ρ‚Π΅Π»ΠΎ. + +> Π’ Ρ†ΠΈΡ„Ρ€ΠΎΠ²ΠΎΠΉ аудиосистСмС ΠΌΠΈΠΊΡ€ΠΎΡ„ΠΎΠ½ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΡƒΠ΅Ρ‚ Π·Π²ΡƒΠΊ Π² Π°Π½Π°Π»ΠΎΠ³ΠΎΠ²Ρ‹ΠΉ элСктричСский сигнал, Π·Π°Ρ‚Π΅ΠΌ АЦП β€” ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ с использованиСм [PCM](https://en.wikipedia.org/wiki/Pulse-code_modulation) β€” ΠΏΡ€Π΅Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Π°Π½Π°Π»ΠΎΠ³ Π² Ρ†ΠΈΡ„Ρ€ΠΎΠ²ΠΎΠΉ. + +![audio analog to digital](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/CPT-Sound-ADC-DAC.svg/640px-CPT-Sound-ADC-DAC.svg.png "audio analog to digital") + +> [Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ](https://commons.wikimedia.org/wiki/File:CPT-Sound-ADC-DAC.svg) + +## ΠΊΠΎΠ΄Π΅ΠΊ β€” сТатиС Π΄Π°Π½Π½Ρ‹Ρ… + +> CODEC β€” это элСктронная схСма ΠΈΠ»ΠΈ ПО, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ **сТимаСт/распаковываСт Ρ†ΠΈΡ„Ρ€ΠΎΠ²ΠΎΠ΅ Π°ΡƒΠ΄ΠΈΠΎ/Π²ΠΈΠ΄Π΅ΠΎ.** Оно ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ΠΈΡ‚ сырыС (нСсТатыС) Π΄Π°Π½Π½Ρ‹Π΅ Π² сТатый Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ ΠΈ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎ. +> [https://en.wikipedia.org/wiki/Video_codec](https://en.wikipedia.org/wiki/Video_codec) + +Если ΠΆΠ΅ просто ΡΠ»ΠΎΠΆΠΈΡ‚ΡŒ ΠΌΠΈΠ»Π»ΠΈΠΎΠ½Ρ‹ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ Π² ΠΎΠ΄ΠΈΠ½ Ρ„Π°ΠΉΠ» ΠΈ Π½Π°Π·Π²Π°Ρ‚ΡŒ это Ρ„ΠΈΠ»ΡŒΠΌΠΎΠΌ, Ρ€Π°Π·ΠΌΠ΅Ρ€ получится Ρ‡ΡƒΠ΄ΠΎΠ²ΠΈΡ‰Π½Ρ‹ΠΌ. ΠŸΠΎΡΡ‡ΠΈΡ‚Π°Π΅ΠΌ: + +ΠŸΡƒΡΡ‚ΡŒ Π΅ΡΡ‚ΡŒ Π²ΠΈΠ΄Π΅ΠΎ с Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ΠΌ `1080 x 1920` (высота Γ— ΡˆΠΈΡ€ΠΈΠ½Π°), Π½Π° ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ пиксСль Ρ‚Ρ€Π°Ρ‚ΠΈΠΌ `3 Π±Π°ΠΉΡ‚Π°` (Ρ†Π²Π΅Ρ‚ Π² [24 Π±ΠΈΡ‚Π°Ρ…](https://en.wikipedia.org/wiki/Color_depth#True_color_.2824-bit.29), Ρ‚.Π΅. 16 777 216 Ρ†Π²Π΅Ρ‚ΠΎΠ²), частота `24 ΠΊΠ°Π΄Ρ€Π°/с`, Π΄Π»ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ `30 ΠΌΠΈΠ½ΡƒΡ‚`. + +```c +toppf = 1080 * 1920 // total_of_pixels_per_frame β€” всСго пиксСлСй Π½Π° ΠΊΠ°Π΄Ρ€ +cpp = 3 // cost_per_pixel β€” Π±Π°ΠΉΡ‚ Π½Π° пиксСль +tis = 30 * 60 // time_in_seconds β€” врСмя Π² сСкундах +fps = 24 // frames_per_second β€” ΠΊΠ°Π΄Ρ€Ρ‹ Π² сСкунду + +required_storage = tis * fps * toppf * cpp +``` + +Π’Π°ΠΊΠΎΠ΅ Π²ΠΈΠ΄Π΅ΠΎ Π·Π°ΠΉΠΌΡ‘Ρ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ `250.28GB` ΠΈΠ»ΠΈ ΠΏΠΎΡ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ `1.19 Gbps` пропускной способности! ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ Π½Π°ΠΌ ΠΈ Π½ΡƒΠΆΠ΅Π½ [CODEC](https://github.com/leandromoreira/digital_video_introduction#how-does-a-video-codec-work). + +## ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ β€” Π΄ΠΎΠΌ для Π°ΡƒΠ΄ΠΈΠΎ ΠΈ Π²ΠΈΠ΄Π΅ΠΎ + +> ΠšΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ (wrapper format) β€” это ΠΌΠ΅Ρ‚Π°Ρ„Π°ΠΉΠ»-Ρ„ΠΎΡ€ΠΌΠ°Ρ‚, спСцификация ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ описываСт, ΠΊΠ°ΠΊ Π² ΠΎΠ΄Π½ΠΎΠΌ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π½ΠΎΠΌ Ρ„Π°ΠΉΠ»Π΅ ΡΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‚ Ρ€Π°Π·Π½Ρ‹Π΅ элСмСнты Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ ΠΌΠ΅Ρ‚Π°Π΄Π°Π½Π½Ρ‹Ρ…. +> [https://en.wikipedia.org/wiki/Digital_container_format](https://en.wikipedia.org/wiki/Digital_container_format) + +**Один Ρ„Π°ΠΉΠ», содСрТащий всС ΠΏΠΎΡ‚ΠΎΠΊΠΈ** (ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ Π°ΡƒΠ΄ΠΈΠΎ ΠΈ Π²ΠΈΠ΄Π΅ΠΎ) ΠΈ ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ **ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·Π°Ρ†ΠΈΡŽ ΠΈ ΠΎΠ±Ρ‰ΠΈΠ΅ ΠΌΠ΅Ρ‚Π°Π΄Π°Π½Π½Ρ‹Π΅** β€” Π½Π°Π·Π²Π°Π½ΠΈΠ΅, Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ ΠΈ Ρ‚.ΠΏ. + +Часто Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ Ρ„Π°ΠΉΠ»Π° ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ½ΡΡ‚ΡŒ ΠΏΠΎ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡŽ: Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, `video.webm` β€” это, скорСС всСго, Π²ΠΈΠ΄Π΅ΠΎ Π² ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π΅ [`webm`](https://www.webmproject.org/). + +![container](/img/container.png) + +# FFmpeg β€” командная строка + +> ΠŸΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½ΠΎΠ΅ кроссплатформСнноС Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ для записи, ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ ΠΈ стриминга Π°ΡƒΠ΄ΠΈΠΎ ΠΈ Π²ΠΈΠ΄Π΅ΠΎ. + +Для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΌΠ΅Π΄ΠΈΠ° Π΅ΡΡ‚ΡŒ Π²Π΅Π»ΠΈΠΊΠΎΠ»Π΅ΠΏΠ½Ρ‹ΠΉ инструмСнт/Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° [FFmpeg](https://www.ffmpeg.org/). Π‘ΠΊΠΎΡ€Π΅Π΅ всСго, Ρ‚Ρ‹ ΡƒΠΆΠ΅ Π·Π½Π°ΠΊΠΎΠΌ с Π½ΠΈΠΌ Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ ΠΈΠ»ΠΈ косвСнно (ΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΡˆΡŒΡΡ [Chrome?](https://www.chromium.org/developers/design-documents/video)). + +Π•ΡΡ‚ΡŒ CLI-ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° `ffmpeg` β€” простая, Π½ΠΎ мощная. +НапримСр, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ `mp4` Π² `avi`, достаточно: + +```bash +$ ffmpeg -i input.mp4 output.avi +``` + +ΠœΡ‹ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‡Ρ‚ΠΎ сдСлали **рСмаксим** (remuxing) β€” ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π»ΠΈ ΠΎΠ΄ΠΈΠ½ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ Π² Π΄Ρ€ΡƒΠ³ΠΎΠΉ. +ВСхничСски FFmpeg ΠΌΠΎΠ³ ΠΈ ΠΏΠ΅Ρ€Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΎΡ‚ΠΎΠΊΠΈ, Π½ΠΎ ΠΎΠ± этом ΠΏΠΎΠ·ΠΆΠ΅. + +## FFmpeg CLI 101 + +Π£ FFmpeg Π΅ΡΡ‚ΡŒ [докумСнтация](https://www.ffmpeg.org/ffmpeg.html), ΠΎΡ‚Π»ΠΈΡ‡Π½ΠΎ ΠΎΠ±ΡŠΡΡΠ½ΡΡŽΡ‰Π°Ρ, ΠΊΠ°ΠΊ ΠΎΠ½ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚. + +```bash +# Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΡŽ ΠΌΠΎΠΆΠ½ΠΎ ΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ ΠΈ ΠΈΠ· ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строки + +ffmpeg -h full | grep -A 10 -B 10 avoid_negative_ts +``` + +Если ΠΊΡ€Π°Ρ‚ΠΊΠΎ, `ffmpeg` ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅Ρ‚ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ `ffmpeg {1} {2} -i {3} {4} {5}`, Π³Π΄Π΅: + +1. Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹Π΅ ΠΎΠΏΡ†ΠΈΠΈ +2. ΠΎΠΏΡ†ΠΈΠΈ Π²Ρ…ΠΎΠ΄Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Π° +3. Π²Ρ…ΠΎΠ΄Π½ΠΎΠΉ URL/ΠΏΡƒΡ‚ΡŒ +4. ΠΎΠΏΡ†ΠΈΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Π° +5. Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ URL/ΠΏΡƒΡ‚ΡŒ + +Π‘Π»ΠΎΠΊΠΈ 2–5 ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΡΡ‚ΡŒ сколько ΡƒΠ³ΠΎΠ΄Π½ΠΎ. +ΠŸΡ€ΠΎΡ‰Π΅ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅: + +```bash +# Π’ΠΠ˜ΠœΠΠΠ˜Π•: Ρ„Π°ΠΉΠ» ΠΎΠΊΠΎΠ»ΠΎ 300 ΠœΠ‘ +$ wget -O bunny_1080p_60fps.mp4 http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_60fps_normal.mp4 + +$ ffmpeg \ +-y \ # Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹Π΅ ΠΎΠΏΡ†ΠΈΠΈ +-c:a libfdk_aac \ # ΠΎΠΏΡ†ΠΈΠΈ Π²Ρ…ΠΎΠ΄Π° +-i bunny_1080p_60fps.mp4 \ # Π²Ρ…ΠΎΠ΄Π½ΠΎΠΉ url +-c:v libvpx-vp9 -c:a libvorbis \ # ΠΎΠΏΡ†ΠΈΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π° +bunny_1080p_60fps_vp9.webm # Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ url +``` + +Π­Ρ‚Π° ΠΊΠΎΠΌΠ°Π½Π΄Π° Π±Π΅Ρ€Ρ‘Ρ‚ `mp4` с двумя ΠΏΠΎΡ‚ΠΎΠΊΠ°ΠΌΠΈ (Π°ΡƒΠ΄ΠΈΠΎ Π² `aac` ΠΈ Π²ΠΈΠ΄Π΅ΠΎ Π² `h264`) ΠΈ Π΄Π΅Π»Π°Π΅Ρ‚ `webm`, измСняя ΠΈ Π°ΡƒΠ΄ΠΈΠΎ-, ΠΈ Π²ΠΈΠ΄Π΅ΠΎ-ΠΊΠΎΠ΄Π΅ΠΊΠΈ. + +ΠšΠΎΠΌΠ°Π½Π΄Ρƒ ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠΏΡ€ΠΎΡΡ‚ΠΈΡ‚ΡŒ, Π½ΠΎ Ρ‚ΠΎΠ³Π΄Π° FFmpeg подставит/ΡƒΠ³Π°Π΄Π°Π΅Ρ‚ значСния ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ. +НапримСр, Ссли Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ `ffmpeg -i input.avi output.mp4`, Ρ‚ΠΎ ΠΊΠ°ΠΊΠΎΠΉ Π°ΡƒΠ΄ΠΈΠΎ/Π²ΠΈΠ΄Π΅ΠΎ ΠΊΠΎΠ΄Π΅ΠΊ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π±Ρ€Π°Π½ для `output.mp4`? + +Π£ Π’Π΅Ρ€Π½Π΅Ρ€Π° Π ΠΎΠ±ΠΈΡ‚Ρ†Ρ‹ Π΅ΡΡ‚ΡŒ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΊ ΠΏΡ€ΠΎΡ‡Ρ‚Π΅Π½ΠΈΡŽ/Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΡŽ [курс ΠΏΠΎ ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ ΠΈ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ Π² FFmpeg](http://slhck.info/ffmpeg-encoding-course/#/). + +# Π’ΠΈΠΏΠΎΠ²Ρ‹Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ с Π²ΠΈΠ΄Π΅ΠΎ + +Работая с Π°ΡƒΠ΄ΠΈΠΎ/Π²ΠΈΠ΄Π΅ΠΎ, ΠΌΡ‹ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ Ρ€Π΅ΡˆΠ°Π΅ΠΌ ряд Ρ‚ΠΈΠΏΠΎΠ²Ρ‹Ρ… Π·Π°Π΄Π°Ρ‡. + +## Transcoding (ΠΏΠ΅Ρ€Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅) + +![transcoding](/img/transcoding.png) + +**Π§Ρ‚ΠΎ это?** ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈΠ· ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² (Π°ΡƒΠ΄ΠΈΠΎ ΠΈΠ»ΠΈ Π²ΠΈΠ΄Π΅ΠΎ) ΠΈΠ· ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π΅ΠΊΠ° Π² Π΄Ρ€ΡƒΠ³ΠΎΠΉ. + +**Π—Π°Ρ‡Π΅ΠΌ?** ΠΈΠ½ΠΎΠ³Π΄Π° устройство (TV, смартфон, консоль ΠΈ Ρ‚.Π΄.) Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ ΠΊΠΎΠ΄Π΅ΠΊ X, Π½ΠΎ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ ΠΊΠΎΠ΄Π΅ΠΊ Y; Ρ‚Π°ΠΊΠΆΠ΅ Π½ΠΎΠ²Ρ‹Π΅ ΠΊΠΎΠ΄Π΅ΠΊΠΈ Π½Π΅Ρ€Π΅Π΄ΠΊΠΎ Π΄Π°ΡŽΡ‚ Π»ΡƒΡ‡ΡˆΡƒΡŽ ΡΡ‚Π΅ΠΏΠ΅Π½ΡŒ сТатия. + +**Как?** ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ Π²ΠΈΠ΄Π΅ΠΎ `H264` (AVC) Π² `H265` (HEVC). + +```bash +$ ffmpeg \ +-i bunny_1080p_60fps.mp4 \ +-c:v libx265 \ +bunny_1080p_60fps_h265.mp4 +``` + +## Transmuxing (ΠΏΠ΅Ρ€Π΅ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΏΠ»Π΅ΠΊΡΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅) + +![transmuxing](/img/transmuxing.png) + +**Π§Ρ‚ΠΎ это?** конвСртация ΠΈΠ· ΠΎΠ΄Π½ΠΎΠ³ΠΎ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° (ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π°) Π² Π΄Ρ€ΡƒΠ³ΠΎΠΉ. + +**Π—Π°Ρ‡Π΅ΠΌ?** Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ устройства Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‚ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ X, Π½ΠΎ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‚ Y; Π½ΠΎΠ²Ρ‹Π΅ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Ρ‹ ΠΈΠ½ΠΎΠ³Π΄Π° Π΄Π°ΡŽΡ‚ соврСмСнныС Ρ„ΠΈΡ‡ΠΈ. + +**Как?** ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ `mp4` Π² `ts`. + +```bash +$ ffmpeg \ +-i bunny_1080p_60fps.mp4 \ +-c copy \ # просим ffmpeg ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΠ΅Ρ€Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ +bunny_1080p_60fps.ts +``` + +## Transrating (ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚Π°) + +![transrating](/img/transrating.png) + +**Π§Ρ‚ΠΎ это?** ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚Π° ΠΈΠ»ΠΈ созданиС Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹Ρ… Β«Ρ€Π΅Π΄ΠΈΡˆΠ½ΠΎΠ²Β» (вСрсий). + +**Π—Π°Ρ‡Π΅ΠΌ?** ΠΊΡ‚ΠΎ-Ρ‚ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ‡Π΅Ρ€Π΅Π· `2G` Π½Π° слабом смартфонС, Π° ΠΊΡ‚ΠΎ-Ρ‚ΠΎ β€” Ρ‡Π΅Ρ€Π΅Π· `fiber` Π½Π° 4K-TV; стоит ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠΈΡ‚ΡŒ нСсколько вСрсий ΠΎΠ΄Π½ΠΎΠ³ΠΎ Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎΠ΄ возмоТности зритСля. + +**Как?** Π΄Π΅Π»Π°Π΅ΠΌ Π²Π΅Ρ€ΡΠΈΡŽ с Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚ΠΎΠΌ ΠΌΠ΅ΠΆΠ΄Ρƒ 964K ΠΈ 3856K. + +```bash +$ ffmpeg \ +-i bunny_1080p_60fps.mp4 \ +-minrate 964K -maxrate 3856K -bufsize 2000K \ +bunny_1080p_60fps_transrating_964_3856.mp4 +``` + +ΠžΠ±Ρ‹Ρ‡Π½ΠΎ transrating ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ вмСстС с transsizing. Π£ Π’Π΅Ρ€Π½Π΅Ρ€Π° Π ΠΎΠ±ΠΈΡ‚Ρ†Ρ‹ Π΅ΡΡ‚ΡŒ Π΅Ρ‰Ρ‘ ΠΎΠ΄Π½Π° отличная [сСрия постов ΠΎ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π΅ Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚Π° Π² FFmpeg](http://slhck.info/posts/). + +## Transsizing (ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ) + +![transsizing](/img/transsizing.png) + +**Π§Ρ‚ΠΎ это?** ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ. Как ΡƒΠΆΠ΅ сказано, часто ΠΈΠ΄Ρ‘Ρ‚ Π² ΠΏΠ°Ρ€Π΅ с transrating. + +**Π—Π°Ρ‡Π΅ΠΌ?** ΠΏΡ€ΠΈΡ‡ΠΈΠ½Ρ‹ Ρ‚Π΅ ΠΆΠ΅, Ρ‡Ρ‚ΠΎ ΠΈ для transrating. + +**Как?** ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ `1080p` Π² `480p`. + +```bash +$ ffmpeg \ +-i bunny_1080p_60fps.mp4 \ +-vf scale=480:-1 \ +bunny_1080p_60fps_transsizing_480.mp4 +``` + +## Bonus Round: Adaptive Streaming + +![adaptive streaming](/img/adaptive-streaming.png) + +**Π§Ρ‚ΠΎ это?** ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΠΌ нСсколько Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠΉ (Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚ΠΎΠ²), Ρ€Π΅ΠΆΠ΅ΠΌ ΠΌΠ΅Π΄ΠΈΠ° Π½Π° Ρ‡Π°Π½ΠΊΠΈ ΠΈ ΠΎΡ‚Π΄Π°Ρ‘ΠΌ ΠΏΠΎ HTTP. + +**Π—Π°Ρ‡Π΅ΠΌ?** Π³ΠΈΠ±ΠΊΠΎΡΡ‚ΡŒ β€” ΠΌΠΎΠΆΠ½ΠΎ ΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ ΠΈ Π½Π° слабом смартфонС, ΠΈ Π½Π° 4K-TV; Π»Π΅Π³ΠΊΠΎ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈ Π΄Π΅ΠΏΠ»ΠΎΠΈΡ‚ΡŒ, Π½ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π·Π°Π΄Π΅Ρ€ΠΆΠΊΡƒ. + +**Как?** создаём Π°Π΄Π°ΠΏΡ‚ΠΈΠ²Π½ΠΎΠ΅ WebM Ρ‡Π΅Ρ€Π΅Π· DASH. + +```bash +# Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡ‚ΠΎΠΊΠΈ +$ ffmpeg -i bunny_1080p_60fps.mp4 -c:v libvpx-vp9 -s 160x90 -b:v 250k -keyint_min 150 -g 150 -an -f webm -dash 1 video_160x90_250k.webm + +$ ffmpeg -i bunny_1080p_60fps.mp4 -c:v libvpx-vp9 -s 320x180 -b:v 500k -keyint_min 150 -g 150 -an -f webm -dash 1 video_320x180_500k.webm + +$ ffmpeg -i bunny_1080p_60fps.mp4 -c:v libvpx-vp9 -s 640x360 -b:v 750k -keyint_min 150 -g 150 -an -f webm -dash 1 video_640x360_750k.webm + +$ ffmpeg -i bunny_1080p_60fps.mp4 -c:v libvpx-vp9 -s 640x360 -b:v 1000k -keyint_min 150 -g 150 -an -f webm -dash 1 video_640x360_1000k.webm + +$ ffmpeg -i bunny_1080p_60fps.mp4 -c:v libvpx-vp9 -s 1280x720 -b:v 1500k -keyint_min 150 -g 150 -an -f webm -dash 1 video_1280x720_1500k.webm + +# Π°ΡƒΠ΄ΠΈΠΎΠΏΠΎΡ‚ΠΎΠΊΠΈ +$ ffmpeg -i bunny_1080p_60fps.mp4 -c:a libvorbis -b:a 128k -vn -f webm -dash 1 audio_128k.webm + +# DASH-манифСст +$ ffmpeg \ + -f webm_dash_manifest -i video_160x90_250k.webm \ + -f webm_dash_manifest -i video_320x180_500k.webm \ + -f webm_dash_manifest -i video_640x360_750k.webm \ + -f webm_dash_manifest -i video_640x360_1000k.webm \ + -f webm_dash_manifest -i video_1280x720_500k.webm \ + -f webm_dash_manifest -i audio_128k.webm \ + -c copy -map 0 -map 1 -map 2 -map 3 -map 4 -map 5 \ + -f webm_dash_manifest \ + -adaptation_sets "id=0,streams=0,1,2,3,4 id=1,streams=5" \ + manifest.mpd +``` + +PS: Π― позаимствовал ΠΏΡ€ΠΈΠΌΠ΅Ρ€ ΠΈΠ· [Instructions to playback Adaptive WebM using DASH](http://wiki.webmproject.org/adaptive-streaming/instructions-to-playback-adaptive-webm-using-dash) + +## Π”Π°Π»ΡŒΡˆΠ΅ β€” большС + +[ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠΉ FFmpeg β€” Ρ‚ΡŒΠΌΠ°](https://github.com/leandromoreira/digital_video_introduction/blob/master/encoding_pratical_examples.md#split-and-merge-smoothly). +Π― ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽ Π΅Π³ΠΎ вмСстС с *iMovie* для создания/ΠΌΠΎΠ½Ρ‚Π°ΠΆΠ° Ρ€ΠΎΠ»ΠΈΠΊΠΎΠ² для YouTube β€” ΠΈ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡ‚ΡŒ Π΅Π³ΠΎ ΠΏΡ€ΠΎΡ„Π΅ΡΡΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ. + +# Learn FFmpeg libav the Hard Way + +> Don't you wonder sometimes 'bout sound and vision? +> **David Robert Jones** + +Π Π°Π· ΡƒΠΆ [FFmpeg](#ffmpeg---command-line) Π½Π°ΡΡ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠΎΠ»Π΅Π·Π΅Π½ Π² CLI для Π±Π°Π·ΠΎΠ²Ρ‹Ρ… ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ с ΠΌΠ΅Π΄ΠΈΠ°Ρ„Π°ΠΉΠ»Π°ΠΌΠΈ, ΠΊΠ°ΠΊ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π³ΠΎ Π² своих ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°Ρ…? + +FFmpeg β€” это [Π½Π°Π±ΠΎΡ€ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ](https://www.ffmpeg.org/doxygen/trunk/index.html), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π² свои прилоТСния. +ΠžΠ±Ρ‹Ρ‡Π½ΠΎ ΠΏΡ€ΠΈ установкС FFmpeg ставятся ΠΈ эти Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ. Π― Π±ΡƒΠ΄Ρƒ Π½Π°Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΈΡ… совокупно **FFmpeg libav**. + +> Π­Ρ‚ΠΎ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ β€” дань уваТСния сСрии Zed Shaw [Learn X the Hard Way](https://learncodethehardway.org/), Π² частности Π΅Π³ΠΎ ΠΊΠ½ΠΈΠ³Π΅ Learn C the Hard Way. + +## Π“Π»Π°Π²Π° 0 β€” ΠΏΠ΅Ρ‡Π°Π»ΡŒΠ½ΠΎ извСстный Β«hello worldΒ» + +Π­Ρ‚ΠΎΡ‚ hello world Π½Π΅ Π²Ρ‹Π²Π΅Π΄Π΅Ρ‚ `"hello world"` Π² Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π» :tongue: +ВмСсто этого ΠΌΡ‹ **распСчатаСм ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ Π²ΠΈΠ΄Π΅ΠΎ** β€” Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ (ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€), Π΄Π»ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ, Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, ΠΊΠ°Π½Π°Π»Ρ‹ Π°ΡƒΠ΄ΠΈΠΎ; Π° Π·Π°Ρ‚Π΅ΠΌ **Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅ΠΌ нСсколько ΠΊΠ°Π΄Ρ€ΠΎΠ² ΠΈ сохраним ΠΈΡ… ΠΊΠ°ΠΊ изобраТСния**. + +### АрхитСктура FFmpeg libav + +ΠŸΡ€Π΅ΠΆΠ΄Π΅ Ρ‡Π΅ΠΌ ΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄, разбСрёмся, **ΠΊΠ°ΠΊ устроСна FFmpeg libav** ΠΈ ΠΊΠ°ΠΊ Ρ€Π°Π·Π½Ρ‹Π΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡ‚Π²ΡƒΡŽΡ‚ ΠΌΠ΅ΠΆΠ΄Ρƒ собой. + +Π‘Ρ…Π΅ΠΌΠ° процСсса дСкодирования Π²ΠΈΠ΄Π΅ΠΎ: + +![ffmpeg libav architecture - decoding process](/img/decoding.png) + +Π‘Π½Π°Ρ‡Π°Π»Π° Π½ΡƒΠΆΠ½ΠΎ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΠΌΠ΅Π΄ΠΈΠ°Ρ„Π°ΠΉΠ» Π² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ [`AVFormatContext`](https://ffmpeg.org/doxygen/trunk/structAVFormatContext.html) (ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ Ρ‚Π°ΠΊΠΆΠ΅ Π½Π°Π·Ρ‹Π²Π°ΡŽΡ‚ Β«formatΒ»). +На самом Π΄Π΅Π»Π΅ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ Ρ„Π°ΠΉΠ» Π½Π΅ читаСтся: Π·Π°Ρ‡Π°ΡΡ‚ΡƒΡŽ парсится Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ. + +ПослС чтСния минимального **Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ° ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π°** ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ доступ ΠΊ Π΅Π³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ°ΠΌ (ΠΏΠΎ сути β€” сырой Π°ΡƒΠ΄ΠΈΠΎ/Π²ΠΈΠ΄Π΅ΠΎ). +ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ доступСн Ρ‡Π΅Ρ€Π΅Π· [`AVStream`](https://ffmpeg.org/doxygen/trunk/structAVStream.html). + +> Stream β€” красивоС слово для Β«Π½Π΅ΠΏΡ€Π΅Ρ€Ρ‹Π²Π½ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ° Π΄Π°Π½Π½Ρ‹Ρ…Β». + +Допустим, Ρƒ нашСго Π²ΠΈΠ΄Π΅ΠΎ Π΄Π²Π° ΠΏΠΎΡ‚ΠΎΠΊΠ°: Π°ΡƒΠ΄ΠΈΠΎ Π² [AAC](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) ΠΈ Π²ΠΈΠ΄Π΅ΠΎ Π² [H264 (AVC)](https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC). Из ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ° ΠΌΡ‹ ΠΈΠ·Π²Π»Π΅ΠΊΠ°Π΅ΠΌ **кусочки Π΄Π°Π½Π½Ρ‹Ρ…** β€” ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹, [`AVPacket`](https://ffmpeg.org/doxygen/trunk/structAVPacket.html). + +**Π”Π°Π½Π½Ρ‹Π΅ Π² ΠΏΠ°ΠΊΠ΅Ρ‚Π°Ρ… всё Π΅Ρ‰Ρ‘ ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹** (сТаты), ΠΈ Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΡ… Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ, ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘ΠΌ ΠΈΡ… ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰Π΅ΠΌΡƒ [`AVCodec`](https://ffmpeg.org/doxygen/trunk/structAVCodec.html). + +`AVCodec` Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΈΡ… Π² [`AVFrame`](https://ffmpeg.org/doxygen/trunk/structAVFrame.html), ΠΈ, Π½Π°ΠΊΠΎΠ½Π΅Ρ†, ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ **нСсТатый ΠΊΠ°Π΄Ρ€**. ΠžΠ±Ρ€Π°Ρ‚ΠΈ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: тСрминология/процСсс ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²Ρ‹ ΠΈ для Π°ΡƒΠ΄ΠΈΠΎ, ΠΈ для Π²ΠΈΠ΄Π΅ΠΎ. + +### ВрСбования + +ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΡΡ‚Π°Π»ΠΊΠΈΠ²Π°Π»ΠΈΡΡŒ с [ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°ΠΌΠΈ ΠΏΡ€ΠΈ сборкС/запускС ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ²](https://github.com/leandromoreira/ffmpeg-libav-tutorial/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+compiling), **ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ [`Docker`](https://docs.docker.com/install/) ΠΊΠ°ΠΊ ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ/запуска**. Π’Π°ΠΊΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Ρ€ΠΎΠ»ΠΈΠΊ big buck bunny; Ссли Π΅Π³ΠΎ Π½Π΅Ρ‚ локально, Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚Π΅ `make fetch_small_bunny_video`. + +### Π“Π»Π°Π²Π° 0 - пройдСмся ΠΏΠΎ ΠΊΠΎΠ΄Ρƒ + +> #### TLDR; ΠΏΠΎΠΊΠ°ΠΆΠΈ [ΠΊΠΎΠ΄](/0_hello_world.c) ΠΈ ΠΊΠ°ΠΊ Π΅Π³ΠΎ Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ. +> +> ```bash +> $ make run_hello +> ``` + +ΠžΠΏΡƒΡΡ‚ΠΈΠΌ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π΄Π΅Ρ‚Π°Π»ΠΈ, Π½ΠΎ Π½Π΅ ΠΏΠ΅Ρ€Π΅ΠΆΠΈΠ²Π°ΠΉΡ‚Π΅: [исходники Π½Π° GitHub](/0_hello_world.c). + +Π’Ρ‹Π΄Π΅Π»ΠΈΠΌ ΠΏΠ°ΠΌΡΡ‚ΡŒ ΠΏΠΎΠ΄ [`AVFormatContext`](http://ffmpeg.org/doxygen/trunk/structAVFormatContext.html), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΎ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ (ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π΅). + +```c +AVFormatContext *pFormatContext = avformat_alloc_context(); +``` + +Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΎΡ‚ΠΊΡ€ΠΎΠ΅ΠΌ Ρ„Π°ΠΉΠ», ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π΅ΠΌ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ ΠΈ Π·Π°ΠΏΠΎΠ»Π½ΠΈΠΌ `AVFormatContext` минимальной ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠ΅ΠΉ ΠΎ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ (Π·Π°ΠΌΠ΅Ρ‚ΡŒ, ΠΊΠΎΠ΄Π΅ΠΊΠΈ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ Π½Π΅ ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°ΡŽΡ‚ΡΡ). +Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ [`avformat_open_input`](http://ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#ga31d601155e9035d5b0e7efedc894ee49). На Π²Ρ…ΠΎΠ΄ β€” `AVFormatContext`, `filename` ΠΈ Π΄Π²Π° Π½Π΅ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π°: [`AVInputFormat`](https://ffmpeg.org/doxygen/trunk/structAVInputFormat.html) (Ссли `NULL`, FFmpeg ΡƒΠ³Π°Π΄Π°Π΅Ρ‚ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚) ΠΈ [`AVDictionary`](https://ffmpeg.org/doxygen/trunk/structAVDictionary.html) (ΠΎΠΏΡ†ΠΈΠΈ Π΄Π΅ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΏΠ»Π΅ΠΊΡΠΎΡ€Π°). + +```c +avformat_open_input(&pFormatContext, filename, NULL, NULL); +``` + +МоТно вывСсти Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° ΠΈ Π΄Π»ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ: + +```c +printf("Format %s, duration %lld us", pFormatContext->iformat->long_name, pFormatContext->duration); +``` + +Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ `streams`, Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· ΠΌΠ΅Π΄ΠΈΠ°. Ѐункция [`avformat_find_stream_info`](https://ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#gad42172e27cddafb81096939783b157bb) Π΄Π΅Π»Π°Π΅Ρ‚ это. +Π’Π΅ΠΏΠ΅Ρ€ΡŒ `pFormatContext->nb_streams` β€” число ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ², Π° `pFormatContext->streams[i]` β€” сам `i`-ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ (`AVStream`). + +```c +avformat_find_stream_info(pFormatContext, NULL); +``` + +ΠŸΡ€ΠΎΠΉΠ΄Ρ‘ΠΌΡΡ ΠΏΠΎ всСм ΠΏΠΎΡ‚ΠΎΠΊΠ°ΠΌ: + +```c +for (int i = 0; i < pFormatContext->nb_streams; i++) +{ + // +} +``` + +Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ° сохраним [`AVCodecParameters`](https://ffmpeg.org/doxygen/trunk/structAVCodecParameters.html) β€” свойства ΠΊΠΎΠ΄Π΅ΠΊΠ°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ Π·Π°ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ ΠΏΠΎΡ‚ΠΎΠΊ `i`. + +```c +AVCodecParameters *pLocalCodecParameters = pFormatContext->streams[i]->codecpar; +``` + +Зная свойства ΠΊΠΎΠ΄Π΅ΠΊΠ°, ΠΈΡ‰Π΅ΠΌ подходящий Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€ Ρ‡Π΅Ρ€Π΅Π· [`avcodec_find_decoder`](https://ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga19a0ca553277f019dd5b0fec6e1f9dca) β€” ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ зарСгистрированный Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€ ΠΏΠΎ `codec_id`, Ρ‚.Π΅. [`AVCodec`](http://ffmpeg.org/doxygen/trunk/structAVCodec.html) β€” ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΡƒΠΌΠ΅Π΅Ρ‚ **enCO**de/**DEC**ode ΠΏΠΎΡ‚ΠΎΠΊ. + +```c +AVCodec *pLocalCodec = avcodec_find_decoder(pLocalCodecParameters->codec_id); +``` + +Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠΆΠ½ΠΎ вывСсти ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ ΠΊΠΎΠ΄Π΅ΠΊΠ°Ρ…. + +```c +// Π²ΠΈΠ΄Π΅ΠΎ ΠΈ Π°ΡƒΠ΄ΠΈΠΎ +if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO) { + printf("Video Codec: resolution %d x %d", pLocalCodecParameters->width, pLocalCodecParameters->height); +} else if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_AUDIO) { + printf("Audio Codec: %d channels, sample rate %d", pLocalCodecParameters->channels, pLocalCodecParameters->sample_rate); +} +// ΠΎΠ±Ρ‰Π΅Π΅ +printf("\tCodec %s ID %d bit_rate %lld", pLocalCodec->long_name, pLocalCodec->id, pLocalCodecParameters->bit_rate); +``` + +Π‘ ΠΊΠΎΠ΄Π΅ΠΊΠΎΠΌ ΠΌΠΎΠΆΠ½ΠΎ Π²Ρ‹Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΏΠ°ΠΌΡΡ‚ΡŒ ΠΏΠΎΠ΄ [`AVCodecContext`](https://ffmpeg.org/doxygen/trunk/structAVCodecContext.html) β€” контСкст для процСссов кодирования/дСкодирования, Π·Π°Ρ‚Π΅ΠΌ Π·Π°ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Π΅Π³ΠΎ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ ΠΊΠΎΠ΄Π΅ΠΊΠ° Ρ‡Π΅Ρ€Π΅Π· [`avcodec_parameters_to_context`](https://ffmpeg.org/doxygen/trunk/group__lavc__core.html#gac7b282f51540ca7a99416a3ba6ee0d16). + +ПослС заполнСния контСкста β€” ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ ΠΊΠΎΠ΄Π΅ΠΊ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ [`avcodec_open2`](https://ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d). + +```c +AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec); +avcodec_parameters_to_context(pCodecContext, pCodecParameters); +avcodec_open2(pCodecContext, pCodec, NULL); +``` + +Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ ΠΈΠ· ΠΏΠΎΡ‚ΠΎΠΊΠ° ΠΈ Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈΡ… Π² ΠΊΠ°Π΄Ρ€Ρ‹, Π½ΠΎ спСрва Π²Ρ‹Π΄Π΅Π»ΠΈΠΌ ΠΏΠ°ΠΌΡΡ‚ΡŒ для [`AVPacket`](https://ffmpeg.org/doxygen/trunk/structAVPacket.html) ΠΈ [`AVFrame`](https://ffmpeg.org/doxygen/trunk/structAVFrame.html). + +```c +AVPacket *pPacket = av_packet_alloc(); +AVFrame *pFrame = av_frame_alloc(); +``` + +Π‘Ρ‡ΠΈΡ‚Ρ‹Π²Π°Π΅ΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ ΠΈΠ· ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² Ρ‡Π΅Ρ€Π΅Π· [`av_read_frame`](https://ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#ga4fdb3084415a82e3810de6ee60e46a61), ΠΏΠΎΠΊΠ° ΠΎΠ½ΠΈ Π΅ΡΡ‚ΡŒ. + +```c +while (av_read_frame(pFormatContext, pPacket) >= 0) { + //... +} +``` + +**ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ сТатый ΠΏΠ°ΠΊΠ΅Ρ‚** (compressed frame) Π² Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€ Ρ‡Π΅Ρ€Π΅Π· контСкст ΠΊΠΎΠ΄Π΅ΠΊΠ° β€” [`avcodec_send_packet`](https://ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga58bc4bf1e0ac59e27362597e467efff3). + +```c +avcodec_send_packet(pCodecContext, pPacket); +``` + +И **ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ нСсТатый ΠΊΠ°Π΄Ρ€** ΠΈΠ· Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€Π° Ρ‡Π΅Ρ€Π΅Π· Ρ‚ΠΎΡ‚ ΠΆΠ΅ контСкст β€” [`avcodec_receive_frame`](https://ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c). + +```c +avcodec_receive_frame(pCodecContext, pFrame); +``` + +МоТно вывСсти Π½ΠΎΠΌΠ΅Ρ€ ΠΊΠ°Π΄Ρ€Π°, [PTS](https://en.wikipedia.org/wiki/Presentation_timestamp), DTS, [Ρ‚ΠΈΠΏ ΠΊΠ°Π΄Ρ€Π°](https://en.wikipedia.org/wiki/Video_compression_picture_types) ΠΈ Ρ‚.Π΄. + +```c +printf( + "Frame %c (%d) pts %d dts %d key_frame %d [coded_picture_number %d, display_picture_number %d]", + av_get_picture_type_char(pFrame->pict_type), + pCodecContext->frame_number, + pFrame->pts, + pFrame->pkt_dts, + pFrame->key_frame, + pFrame->coded_picture_number, + pFrame->display_picture_number +); +``` + +И Π½Π°ΠΊΠΎΠ½Π΅Ρ† ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ ΠΊΠ°Π΄Ρ€ ΠΊΠ°ΠΊ [простоС «сСроС» ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅](https://en.wikipedia.org/wiki/Netpbm_format#PGM_example). Всё просто: Π±Π΅Ρ€Ρ‘ΠΌ `pFrame->data`, Π³Π΄Π΅ индСксы ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‚ [плоскостям Y, Cb ΠΈ Cr](https://en.wikipedia.org/wiki/YCbCr), ΠΈ Π±Π΅Ρ€Ρ‘ΠΌ `0` (Y), Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ Π³Ρ€Π°Π΄Π°Ρ†ΠΈΠΈ сСрого. + +```c +save_gray_frame(pFrame->data[0], pFrame->linesize[0], pFrame->width, pFrame->height, frame_filename); + +static void save_gray_frame(unsigned char *buf, int wrap, int xsize, int ysize, char *filename) +{ + FILE *f; + int i; + f = fopen(filename,"w"); + // пишСм ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ для Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° pgm + // portable graymap format -> https://en.wikipedia.org/wiki/Netpbm_format#PGM_example + fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255); + + // пишСм построчно + for (i = 0; i < ysize; i++) + fwrite(buf + i * wrap, 1, xsize, f); + fclose(f); +} +``` + +VoilΓ ! Π£ нас Π΅ΡΡ‚ΡŒ ΠΎΡ‚Ρ‚Π΅Π½ΠΊΠΈ сСрого ~2 ΠœΠ‘: + +![saved frame](/img/generated_frame.png) + +## Π“Π»Π°Π²Π° 1 β€” Ρ‚Π°ΠΉΠΌΠΈΠ½Π³ΠΈ/синхронизация + +> **Π‘ΡƒΠ΄ΡŒ ΠΏΠ»Π΅Π΅Ρ€ΠΎΠΌ** β€” ΠΌΠΎΠ»ΠΎΠ΄ΠΎΠΉ JS-Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΏΠΈΡˆΠ΅Ρ‚ Π½ΠΎΠ²Ρ‹ΠΉ MSE-Π²ΠΈΠ΄Π΅ΠΎΠΏΠ»Π΅Π΅Ρ€. + +ΠŸΡ€Π΅ΠΆΠ΄Π΅ Ρ‡Π΅ΠΌ [ΠΏΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ ΠΊ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρƒ транскодирования](#chapter-2---transcoding), ΠΏΠΎΠ³ΠΎΠ²ΠΎΡ€ΠΈΠΌ ΠΏΡ€ΠΎ **Ρ‚Π°ΠΉΠΌΠΈΠ½Π³** β€” ΠΊΠ°ΠΊ ΠΏΠ»Π΅Π΅Ρ€ ΠΏΠΎΠ½ΠΈΠΌΠ°Π΅Ρ‚, ΠΊΠΎΠ³Π΄Π° ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΊΠ°Π΄Ρ€. + +Π’ ΠΏΡ€ΠΎΡˆΠ»ΠΎΠΌ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ΠΌΡ‹ сохранили нСсколько ΠΊΠ°Π΄Ρ€ΠΎΠ²: + +![frame 0](/img/hello_world_frames/frame0.png) +![frame 1](/img/hello_world_frames/frame1.png) +![frame 2](/img/hello_world_frames/frame2.png) +![frame 3](/img/hello_world_frames/frame3.png) +![frame 4](/img/hello_world_frames/frame4.png) +![frame 5](/img/hello_world_frames/frame5.png) + +ΠŸΡ€ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ Π²ΠΈΠ΄Π΅ΠΎΠΏΠ»Π΅Π΅Ρ€Π° Π½ΡƒΠΆΠ½ΠΎ **ΠΏΡ€ΠΎΠΈΠ³Ρ€Ρ‹Π²Π°Ρ‚ΡŒ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΊΠ°Π΄Ρ€ Π² Π½ΡƒΠΆΠ½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚** β€” ΠΈΠ½Π°Ρ‡Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π»ΠΈΠ±ΠΎ слишком быстро, Π»ΠΈΠ±ΠΎ слишком ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎ. + +Для ΠΏΠ»Π°Π²Π½ΠΎΠ³ΠΎ воспроизвСдСния Ρƒ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΊΠ°Π΄Ρ€Π° Π΅ΡΡ‚ΡŒ **PTS (presentation timestamp)** β€” Π²ΠΎΠ·Ρ€Π°ΡΡ‚Π°ΡŽΡ‰Π΅Π΅ число Π² **timebase** (Ρ€Π°Ρ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΠ΅ число, Π³Π΄Π΅ Π·Π½Π°ΠΌΠ΅Π½Π°Ρ‚Π΅Π»ΡŒ β€” **timescale**), ΠΊΡ€Π°Ρ‚Π½ΠΎΠ΅ **fps**. + +ΠŸΡ€ΠΎΡ‰Π΅ Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°Ρ…. + +Для `fps=60/1` ΠΈ `timebase=1/60000` PTS увСличиваСтся Π½Π° `timescale / fps = 1000`, Π·Π½Π°Ρ‡ΠΈΡ‚ **Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠ΅ врСмя PTS** ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΊΠ°Π΄Ρ€Π° (Ссли старт с 0): + +* `frame=0, PTS = 0, PTS_TIME = 0` +* `frame=1, PTS = 1000, PTS_TIME = PTS * timebase = 0.016` +* `frame=2, PTS = 2000, PTS_TIME = PTS * timebase = 0.033` + +ΠŸΠΎΡ‡Ρ‚ΠΈ Ρ‚ΠΎΡ‚ ΠΆΠ΅ сцСнарий с `timebase=1/60`: + +* `frame=0, PTS = 0, PTS_TIME = 0` +* `frame=1, PTS = 1, PTS_TIME = 0.016` +* `frame=2, PTS = 2, PTS_TIME = 0.033` +* `frame=3, PTS = 3, PTS_TIME = 0.050` + +Для `fps=25/1` ΠΈ `timebase=1/75` PTS растёт Π½Π° `3`, Π²Ρ€Π΅ΠΌΠ΅Π½Π°: + +* `frame=0, PTS = 0, PTS_TIME = 0` +* `frame=1, PTS = 3, PTS_TIME = 0.04` +* `frame=2, PTS = 6, PTS_TIME = 0.08` +* `frame=3, PTS = 9, PTS_TIME = 0.12` +* ... +* `frame=24, PTS = 72, PTS_TIME = 0.96` +* ... +* `frame=4064, PTS = 12192, PTS_TIME = 162.56` + +Зная `pts_time`, ΠΌΠΎΠΆΠ½ΠΎ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΡ‚ΡŒ, синхронизируя с Π°ΡƒΠ΄ΠΈΠΎ `pts_time` ΠΈΠ»ΠΈ систСмными часами. FFmpeg libav ΠΎΡ‚Π΄Π°Ρ‘Ρ‚ эти ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Ρ‡Π΅Ρ€Π΅Π· API: + +* fps = [`AVStream->avg_frame_rate`](https://ffmpeg.org/doxygen/trunk/structAVStream.html#a946e1e9b89eeeae4cab8a833b482c1ad) +* tbr = [`AVStream->r_frame_rate`](https://ffmpeg.org/doxygen/trunk/structAVStream.html#ad63fb11cc1415e278e09ddc676e8a1ad) +* tbn = [`AVStream->time_base`](https://ffmpeg.org/doxygen/trunk/structAVStream.html#a9db755451f14e2bf590d4b85d82b32e6) + +К слову: ΠΊΠ°Π΄Ρ€Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ сохранили, ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΠ»ΠΈ Π² порядкС DTS (1,6,4,2,3,5), Π½ΠΎ Π²ΠΎΡΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΠ»ΠΈΡΡŒ ΠΏΠΎ PTS (1,2,3,4,5). И ΠΎΠ±Ρ€Π°Ρ‚ΠΈ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, насколько «дСшСвлС» B-ΠΊΠ°Π΄Ρ€Ρ‹ ΠΏΠΎ ΡΡ€Π°Π²Π½Π΅Π½ΠΈΡŽ с P ΠΈ I. + +``` +LOG: AVStream->r_frame_rate 60/1 +LOG: AVStream->time_base 1/60000 +... +LOG: Frame 1 (type=I, size=153797 bytes) pts 6000 key_frame 1 [DTS 0] +LOG: Frame 2 (type=B, size=8117 bytes) pts 7000 key_frame 0 [DTS 3] +LOG: Frame 3 (type=B, size=8226 bytes) pts 8000 key_frame 0 [DTS 4] +LOG: Frame 4 (type=B, size=17699 bytes) pts 9000 key_frame 0 [DTS 2] +LOG: Frame 5 (type=B, size=6253 bytes) pts 10000 key_frame 0 [DTS 5] +LOG: Frame 6 (type=P, size=34992 bytes) pts 11000 key_frame 0 [DTS 1] +``` + +## Π“Π»Π°Π²Π° 2 β€” remuxing + +Remuxing β€” смСна Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° (ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π°). НапримСр, ΠΌΠΎΠΆΠ½ΠΎ Π±Π΅Π· Π±ΠΎΠ»ΠΈ ΠΏΠΎΠΌΠ΅Π½ΡΡ‚ΡŒ [MPEG-4](https://en.wikipedia.org/wiki/MPEG-4_Part_14) Π½Π° [MPEG-TS](https://en.wikipedia.org/wiki/MPEG_transport_stream) с FFmpeg: + +```bash +ffmpeg input.mp4 -c copy output.ts +``` + +FFmpeg Π΄Π΅ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΏΠ»Π΅ΠΊΡΠΈΡ€ΡƒΠ΅Ρ‚ mp4, Π½ΠΎ **Π½Π΅** Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅Ρ‚/ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅Ρ‚ (`-c copy`), Π° Π² ΠΊΠΎΠ½Ρ†Π΅ ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΏΠ»Π΅ΠΊΡΠΈΡ€ΡƒΠ΅Ρ‚ Π² `mpegts`. Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ Ρ‡Π΅Ρ€Π΅Π· `-f`, FFmpeg попытаСтся ΡƒΠ³Π°Π΄Π°Ρ‚ΡŒ ΠΏΠΎ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡŽ. + +ΠžΠ±Ρ‰Π°Ρ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π° использования FFmpeg/libav Ρ‚Π°ΠΊΠΎΠ²Π°: + +* **[protocol layer](https://ffmpeg.org/doxygen/trunk/protocols_8c.html)** β€” ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ `input` (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, `file`, Π½ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ `rtmp`/`HTTP`) +* **[format layer](https://ffmpeg.org/doxygen/trunk/group__libavf.html)** β€” `demuxes` (Π΄Π΅ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΏΠ»Π΅ΠΊΡΠΈΡ€ΡƒΠ΅Ρ‚) содСрТимоС, ΠΎΡ‚Π΄Π°Ρ‘Ρ‚ ΠΌΠ΅Ρ‚Π°Π΄Π°Π½Π½Ρ‹Π΅ ΠΈ ΠΏΠΎΡ‚ΠΎΠΊΠΈ +* **[codec layer](https://ffmpeg.org/doxygen/trunk/group__libavc.html)** β€” `decodes` (Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅Ρ‚) сТатыС Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² *Π½Π΅ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ* +* **[pixel layer](https://ffmpeg.org/doxygen/trunk/group__lavfi.html)** β€” примСняСт `filters` ΠΊ сырым ΠΊΠ°Π΄Ρ€Π°ΠΌ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅) *Π½Π΅ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ* +* ΠΏΠΎΡ‚ΠΎΠΌ ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹ΠΉ ΠΏΡƒΡ‚ΡŒ: +* **[codec layer](https://ffmpeg.org/doxygen/trunk/group__libavc.html)** β€” `encodes`/`re-encodes`/`transcodes` сырыС ΠΊΠ°Π΄Ρ€Ρ‹ *Π½Π΅ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ* +* **[format layer](https://ffmpeg.org/doxygen/trunk/group__libavf.html)** β€” `muxes`/`remuxes` сТатыС Π΄Π°Π½Π½Ρ‹Π΅ +* **[protocol layer](https://ffmpeg.org/doxygen/trunk/protocols_8c.html)** β€” Π²Ρ‹Π΄Π°Ρ‘Ρ‚ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π½Π° `output` (Ρ„Π°ΠΉΠ» ΠΈΠ»ΠΈ ΡΠ΅Ρ‚ΡŒ) + +![ffmpeg libav workflow](/img/ffmpeg_libav_workflow.jpeg) + +> Π“Ρ€Π°Ρ„ΠΈΠΊ Π²Π΄ΠΎΡ…Π½ΠΎΠ²Π»Ρ‘Π½ Ρ€Π°Π±ΠΎΡ‚Π°ΠΌΠΈ [Leixiaohua](http://leixiaohua1020.github.io/#ffmpeg-development-examples) ΠΈ [Slhck](https://slhck.info/ffmpeg-encoding-course/#/9). + +Π’Π΅ΠΏΠ΅Ρ€ΡŒ напишСм ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π½Π° libav, эквивалСнтный `ffmpeg input.mp4 -c copy output.ts`. + +ΠœΡ‹ Ρ‡ΠΈΡ‚Π°Π΅ΠΌ ΠΈΠ· Π²Ρ…ΠΎΠ΄Π° (`input_format_context`) ΠΈ пишСм Π² Π²Ρ‹Ρ…ΠΎΠ΄ (`output_format_context`). + +```c +AVFormatContext *input_format_context = NULL; +AVFormatContext *output_format_context = NULL; +``` + +Π‘Π½Π°Ρ‡Π°Π»Π° β€” ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹Π΅ шаги: Π²Ρ‹Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΏΠ°ΠΌΡΡ‚ΡŒ ΠΈ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Π²Ρ…ΠΎΠ΄. Π’ этом кСйсС β€” ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Π²Ρ…ΠΎΠ΄Π½ΠΎΠΉ Ρ„Π°ΠΉΠ» ΠΈ Π²Ρ‹Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΏΠ°ΠΌΡΡ‚ΡŒ ΠΏΠΎΠ΄ Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ. + +```c +if ((ret = avformat_open_input(&input_format_context, in_filename, NULL, NULL)) < 0) { + fprintf(stderr, "Could not open input file '%s'", in_filename); + goto end; +} +if ((ret = avformat_find_stream_info(input_format_context, NULL)) < 0) { + fprintf(stderr, "Failed to retrieve input stream information"); + goto end; +} + +avformat_alloc_output_context2(&output_format_context, NULL, NULL, out_filename); +if (!output_format_context) { + fprintf(stderr, "Could not create output context\n"); + ret = AVERROR_UNKNOWN; + goto end; +} +``` + +Π Π΅ΠΌΡƒΠΊΡΠΈΡ‚ΡŒ Π±ΡƒΠ΄Π΅ΠΌ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π²ΠΈΠ΄Π΅ΠΎ, Π°ΡƒΠ΄ΠΈΠΎ ΠΈ субтитры, поэтому Π΄Π΅Ρ€ΠΆΠΈΠΌ индСксы Π½ΡƒΠΆΠ½Ρ‹Ρ… ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² Π² массивС. + +``` +number_of_streams = input_format_context->nb_streams; +streams_list = av_mallocz_array(number_of_streams, sizeof(*streams_list)); +``` + +ПослС выдСлСния памяти ΠΏΡ€ΠΎΡ…ΠΎΠ΄ΠΈΠΌ ΠΏΠΎ всСм ΠΏΠΎΡ‚ΠΎΠΊΠ°ΠΌ; для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ создаём Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ ΠΏΠΎΡ‚ΠΎΠΊ Π² `output_format_context` Ρ‡Π΅Ρ€Π΅Π· [avformat_new_stream](https://ffmpeg.org/doxygen/trunk/group__lavf__core.html#gadcb0fd3e507d9b58fe78f61f8ad39827). ΠŸΠΎΡ‚ΠΎΠΊΠΈ Π½Π΅ Ρ‚ΠΈΠΏΠ° Π²ΠΈΠ΄Π΅ΠΎ/Π°ΡƒΠ΄ΠΈΠΎ/субтитры ΠΏΠΎΠΌΠ΅Ρ‡Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΡ… ΠΏΠΎΡ‚ΠΎΠΌ ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ. + +```c +for (i = 0; i < input_format_context->nb_streams; i++) { + AVStream *out_stream; + AVStream *in_stream = input_format_context->streams[i]; + AVCodecParameters *in_codecpar = in_stream->codecpar; + if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO && + in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO && + in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { + streams_list[i] = -1; + continue; + } + streams_list[i] = stream_index++; + out_stream = avformat_new_stream(output_format_context, NULL); + if (!out_stream) { + fprintf(stderr, "Failed allocating output stream\n"); + ret = AVERROR_UNKNOWN; + goto end; + } + ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar); + if (ret < 0) { + fprintf(stderr, "Failed to copy codec parameters\n"); + goto end; + } +} +``` + +Π’Π΅ΠΏΠ΅Ρ€ΡŒ создаём Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ Ρ„Π°ΠΉΠ». + +```c +if (!(output_format_context->oformat->flags & AVFMT_NOFILE)) { + ret = avio_open(&output_format_context->pb, out_filename, AVIO_FLAG_WRITE); + if (ret < 0) { + fprintf(stderr, "Could not open output file '%s'", out_filename); + goto end; + } +} + +ret = avformat_write_header(output_format_context, NULL); +if (ret < 0) { + fprintf(stderr, "Error occurred when opening output file\n"); + goto end; +} +``` + +ПослС этого ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠΎΡ‚ΠΎΠΊΠΈ ΠΏΠ°ΠΊΠ΅Ρ‚ Π·Π° ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ ΠΈΠ· Π²Ρ…ΠΎΠ΄Π° Π² Π²Ρ‹Ρ…ΠΎΠ΄. Π’ Ρ†ΠΈΠΊΠ»Π΅, ΠΏΠΎΠΊΠ° Π΅ΡΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ (`av_read_frame`): пСрСсчитываСм PTS/DTS ΠΈ пишСм (`av_interleaved_write_frame`) Π² Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ контСкст. + +```c +while (1) { + AVStream *in_stream, *out_stream; + ret = av_read_frame(input_format_context, &packet); + if (ret < 0) + break; + in_stream = input_format_context->streams[packet.stream_index]; + if (packet.stream_index >= number_of_streams || streams_list[packet.stream_index] < 0) { + av_packet_unref(&packet); + continue; + } + packet.stream_index = streams_list[packet.stream_index]; + out_stream = output_format_context->streams[packet.stream_index]; + /* ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚ */ + packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); + packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); + packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base); + // https://ffmpeg.org/doxygen/trunk/structAVPacket.html#ab5793d8195cf4789dfb3913b7a693903 + packet.pos = -1; + + // https://ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga37352ed2c63493c38219d935e71db6c1 + ret = av_interleaved_write_frame(output_format_context, &packet); + if (ret < 0) { + fprintf(stderr, "Error muxing packet\n"); + break; + } + av_packet_unref(&packet); +} +``` + +Π§Ρ‚ΠΎΠ±Ρ‹ Π·Π°Π²Π΅Ρ€ΡˆΠΈΡ‚ΡŒ, записываСм «хвост» ΠΏΠΎΡ‚ΠΎΠΊΠ° Π² Ρ„Π°ΠΉΠ» Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ [av_write_trailer](https://ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga7f14007e7dc8f481f054b21614dfec13). + +```c +av_write_trailer(output_format_context); +``` + +Π’Π΅ΠΏΠ΅Ρ€ΡŒ протСстируСм β€” ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ тСст: смСна ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π° ΠΈΠ· MP4 Π² MPEG-TS. По сути, повторяСм `ffmpeg input.mp4 -c copy output.ts` Π½Π° libav. + +```bash +make run_remuxing_ts +``` + +Π Π°Π±ΠΎΡ‚Π°Π΅Ρ‚!!! НС Π²Π΅Ρ€ΠΈΡ‚Π΅? И ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ β€” ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ `ffprobe`: + +```bash +ffprobe -i remuxed_small_bunny_1080p_60fps.ts + +Input #0, mpegts, from 'remuxed_small_bunny_1080p_60fps.ts': + Duration: 00:00:10.03, start: 0.000000, bitrate: 2751 kb/s + Program 1 + Metadata: + service_name : Service01 + service_provider: FFmpeg + Stream #0:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 60 fps, 60 tbr, 90k tbn, 120 tbc + Stream #0:1[0x101]: Audio: ac3 ([129][0][0][0] / 0x0081), 48000 Hz, 5.1(side), fltp, 320 kb/s +``` + +Π˜Ρ‚ΠΎΠ³ Π½Π° ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ΅: возвращаСмся ΠΊ [ΠΈΠ΄Π΅Π΅ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹ libav](https://github.com/leandromoreira/ffmpeg-libav-tutorial#ffmpeg-libav-architecture), Π½ΠΎ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠ΄Π΅ΠΊ-слой ΠΌΡ‹ пропустили. + +![remuxing libav components](/img/remuxing_libav_components.png) + +ΠŸΠ΅Ρ€Π΅Π΄ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ΠΌ Π³Π»Π°Π²Ρ‹ β€” Π²Π°ΠΆΠ½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚: **ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Ρ‚ΡŒ ΠΎΠΏΡ†ΠΈΠΈ ΠΌΡƒΠ»ΡŒΡ‚ΠΈΠΏΠ»Π΅ΠΊΡΠΎΡ€Ρƒ**. Допустим, Ρ…ΠΎΡ‚ΠΈΠΌ Π²Ρ‹Π΄Π°Π²Π°Ρ‚ΡŒ [MPEG-DASH](https://developer.mozilla.org/en-US/docs/Web/Apps/Fundamentals/Audio_and_video_delivery/Setting_up_adaptive_streaming_media_sources#MPEG-DASH_Encoding); для этого Π½ΡƒΠΆΠ΅Π½ [fragmented mp4](https://stackoverflow.com/a/35180327) (`fmp4`) вмСсто MPEG-TS ΠΈΠ»ΠΈ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠ³ΠΎ MP4. + +Π§Π΅Ρ€Π΅Π· CLI это просто: + +``` +ffmpeg -i non_fragmented.mp4 -movflags frag_keyframe+empty_moov+default_base_moof fragmented.mp4 +``` + +ΠŸΠΎΡ‡Ρ‚ΠΈ ΡΡ‚ΠΎΠ»ΡŒ ΠΆΠ΅ просто β€” Π½Π° libav: ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘ΠΌ ΠΎΠΏΡ†ΠΈΠΈ ΠΏΡ€ΠΈ записи Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ° (Π΄ΠΎ копирования ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ²). + +```c +AVDictionary* opts = NULL; +av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov+default_base_moof", 0); +ret = avformat_write_header(output_format_context, &opts); +``` + +Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ fragmented mp4: + +```bash +make run_remuxing_fragmented_mp4 +``` + +Π§Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ±Π΅Π΄ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ я Π½Π΅ Π²Ρ€Ρƒ, ΠΌΠΎΠΆΠ½ΠΎ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΡˆΠΈΠΊΠ°Ρ€Π½Ρ‹ΠΌΠΈ Ρ‚ΡƒΠ»Π·Π°ΠΌΠΈ [gpac/mp4box.js](http://download.tsi.telecom-paristech.fr/gpac/mp4box.js/filereader.html) ΠΈΠ»ΠΈ [http://mp4parser.com/](http://mp4parser.com/). Π‘Π½Π°Ρ‡Π°Π»Π° Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚Π΅ Β«ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΉΒ» mp4. + +![mp4 boxes](/img/boxes_normal_mp4.png) + +Π’ΠΈΠ΄ΠΈΠΌ ΠΎΠ΄ΠΈΠ½ `mdat` Π°Ρ‚ΠΎΠΌ/бокс β€” **здСсь Π»Π΅ΠΆΠ°Ρ‚ Π²ΠΈΠ΄Π΅ΠΎ/Π°ΡƒΠ΄ΠΈΠΎ ΠΊΠ°Π΄Ρ€Ρ‹**. Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚Π΅ fragmented mp4 ΠΈ ΡƒΠ²ΠΈΠ΄ΠΈΡ‚Π΅, Ρ‡Ρ‚ΠΎ `mdat` Ρ€Π°Π·Π±ΠΈΡ‚ Π½Π° части. + +![fragmented mp4 boxes](/img/boxes_fragmente_mp4.png) + +## Π“Π»Π°Π²Π° 3 β€” транскодированиС + +> #### TLDR; ΠΏΠΎΠΊΠ°ΠΆΠΈ [ΠΊΠΎΠ΄](/3_transcoding.c) ΠΈ ΠΊΠ°ΠΊ Π΅Π³ΠΎ Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ. +> +> ```bash +> $ make run_transcoding +> ``` +> +> Π”Π΅Ρ‚Π°Π»ΠΈ частично опустим β€” [исходники Π½Π° GitHub](/3_transcoding.c). + +Π’ этой Π³Π»Π°Π²Π΅ создадим минималистичный транскодСр Π½Π° C, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ Π²ΠΈΠ΄Π΅ΠΎ ΠΈΠ· H264 Π² H265, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ **FFmpeg/libav**: [libavcodec](https://ffmpeg.org/libavcodec.html), libavformat ΠΈ libavutil. + +![media transcoding flow](/img/transcoding_flow.png) + +> *ΠšΠΎΡ€ΠΎΡ‚ΠΊΠΈΠΉ ΠΏΠΎΠ²Ρ‚ΠΎΡ€:* [**AVFormatContext**](https://www.ffmpeg.org/doxygen/trunk/structAVFormatContext.html) β€” абстракция Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° (ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π°: MKV, MP4, WebM, TS). [**AVStream**](https://www.ffmpeg.org/doxygen/trunk/structAVStream.html) β€” Ρ‚ΠΈΠΏ Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ (audio, video, subtitle, metadata). [**AVPacket**](https://www.ffmpeg.org/doxygen/trunk/structAVPacket.html) β€” Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ сТатых Π΄Π°Π½Π½Ρ‹Ρ… ΠΈΠ· `AVStream`, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ дСкодируСтся [**AVCodec**](https://www.ffmpeg.org/doxygen/trunk/structAVCodec.html) (av1, h264, vp9, hevc) Π² сырыС [**AVFrame**](https://www.ffmpeg.org/doxygen/trunk/structAVFrame.html). + +### Transmuxing + +Начнём с простого рСмукса, Π·Π°Ρ‚Π΅ΠΌ просто Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΠΌ Π΅Π³ΠΎ. ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ шаг β€” **Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π²Ρ…ΠΎΠ΄Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Π°**. + +```c +// ВыдСляСм AVFormatContext +avfc = avformat_alloc_context(); +// ΠžΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ Π²Ρ…ΠΎΠ΄ ΠΈ Ρ‡ΠΈΡ‚Π°Π΅ΠΌ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ. +avformat_open_input(avfc, in_filename, NULL, NULL); +// Π§ΠΈΡ‚Π°Π΅ΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ°Ρ…. +avformat_find_stream_info(avfc, NULL); +``` + +Π”Π°Π»Π΅Π΅ Π³ΠΎΡ‚ΠΎΠ²ΠΈΠΌ Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€: `AVFormatContext` Π΄Π°Ρ‘Ρ‚ доступ ΠΊΠΎ всСм `AVStream`. Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π½Π°Ρ…ΠΎΠ΄ΠΈΠΌ `AVCodec`, создаём `AVCodecContext` ΠΈ ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ ΠΊΠΎΠ΄Π΅ΠΊ β€” послС этого ΠΌΠΎΠΆΠ½ΠΎ Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ. + +> [**AVCodecContext**](https://www.ffmpeg.org/doxygen/trunk/structAVCodecContext.html) Ρ…Ρ€Π°Π½ΠΈΡ‚ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ ΠΌΠ΅Π΄ΠΈΠ°: Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚, fps, sample rate, channels, высоту/ΡˆΠΈΡ€ΠΈΠ½Ρƒ ΠΈ ΠΌΠ½ΠΎΠ³ΠΎΠ΅ Π΄Ρ€ΡƒΠ³ΠΎΠ΅. + +```c +for (int i = 0; i < avfc->nb_streams; i++) +{ + AVStream *avs = avfc->streams[i]; + AVCodec *avc = avcodec_find_decoder(avs->codecpar->codec_id); + AVCodecContext *avcc = avcodec_alloc_context3(*avc); + avcodec_parameters_to_context(*avcc, avs->codecpar); + avcodec_open2(*avcc, *avc, NULL); +} +``` + +НуТно ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΈΡ‚ΡŒ ΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ ΠΌΠ΅Π΄ΠΈΠ°Ρ„Π°ΠΉΠ» для рСмакса: **выдСляСм ΠΏΠ°ΠΌΡΡ‚ΡŒ** ΠΏΠΎΠ΄ `AVFormatContext` для Π²Ρ‹Ρ…ΠΎΠ΄Π°. Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ **ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ** Π² Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΌ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅. Π§Ρ‚ΠΎΠ±Ρ‹ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ ΡƒΠΏΠ°ΠΊΠΎΠ²Π°Π»ΠΎΡΡŒ, **ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ ΠΊΠΎΠ΄Π΅ΠΊΠ°** ΠΈΠ· Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€Π°. + +Π‘Ρ‚Π°Π²ΠΈΠΌ Ρ„Π»Π°Π³ `AV_CODEC_FLAG_GLOBAL_HEADER`, говоря энкодСру ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹Π΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ, Π·Π°Ρ‚Π΅ΠΌ ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ **Ρ„Π°ΠΉΠ» Π½Π° запись** ΠΈ сохраняСм Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ. + +```c +avformat_alloc_output_context2(&encoder_avfc, NULL, NULL, out_filename); + +AVStream *avs = avformat_new_stream(encoder_avfc, NULL); +avcodec_parameters_copy(avs->codecpar, decoder_avs->codecpar); + +if (encoder_avfc->oformat->flags & AVFMT_GLOBALHEADER) + encoder_avfc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + +avio_open(&encoder_avfc->pb, encoder->filename, AVIO_FLAG_WRITE); +avformat_write_header(encoder->avfc, &muxer_opts); +``` + +ΠœΡ‹ Π±Π΅Ρ€Ρ‘ΠΌ `AVPacket` ΠΈΠ· Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€Π°, ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚ΠΈΡ€ΡƒΠ΅ΠΌ таймстампы ΠΈ записываСм Π΅Π³ΠΎ Π² Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ Ρ„Π°ΠΉΠ». НСсмотря Π½Π° Π½Π°Π·Π²Π°Π½ΠΈΠ΅ `av_interleaved_write_frame`, записываСтся ΠΏΠ°ΠΊΠ΅Ρ‚. Π—Π°Π²Π΅Ρ€ΡˆΠ°Π΅ΠΌ рСмакс записью Ρ‚Ρ€Π΅ΠΉΠ»Π΅Ρ€Π°. + +```c +AVFrame *input_frame = av_frame_alloc(); +AVPacket *input_packet = av_packet_alloc(); + +while (av_read_frame(decoder_avfc, input_packet) >= 0) +{ + av_packet_rescale_ts(input_packet, decoder_video_avs->time_base, encoder_video_avs->time_base); + av_interleaved_write_frame(*avfc, input_packet) < 0)); +} + +av_write_trailer(encoder_avfc); +``` + +### Transcoding + +Π’ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ Ρ€Π°Π·Π΄Π΅Π»Π΅ Π±Ρ‹Π» простой рСмуксСр. Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ β€” Π½Π°ΡƒΡ‡ΠΈΠΌ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ Ρ‚Ρ€Π°Π½ΡΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π²ΠΈΠ΄Π΅ΠΎ `h264` β†’ `h265`. + +ПослС ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠΈ Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€Π°, Π½ΠΎ Π΄ΠΎ настройки Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΌΠ΅Π΄ΠΈΠ°Ρ„Π°ΠΉΠ»Π° β€” настраиваСм энкодСр. + +* Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ Π²ΠΈΠ΄Π΅ΠΎ-`AVStream` Π² энкодСрС, [`avformat_new_stream`](https://www.ffmpeg.org/doxygen/trunk/group__lavf__core.html#gadcb0fd3e507d9b58fe78f61f8ad39827) +* Π‘Π΅Ρ€Ρ‘ΠΌ `AVCodec` ΠΏΠΎ ΠΈΠΌΠ΅Π½ΠΈ `libx265`, [`avcodec_find_encoder_by_name`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__encoding.html#gaa614ffc38511c104bdff4a3afa086d37) +* Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ `AVCodecContext` для этого ΠΊΠΎΠ΄Π΅ΠΊΠ°, [`avcodec_alloc_context3`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#gae80afec6f26df6607eaacf39b561c315) +* НастраиваСм Π±Π°Π·ΠΎΠ²Ρ‹Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ сСссии +* ΠžΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ ΠΊΠΎΠ΄Π΅ΠΊ ΠΈ ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ ΠΈΠ· контСкста Π² ΠΏΠΎΡ‚ΠΎΠΊ: [`avcodec_open2`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d), [`avcodec_parameters_from_context`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga0c7058f764778615e7978a1821ab3cfe) + +```c +AVRational input_framerate = av_guess_frame_rate(decoder_avfc, decoder_video_avs, NULL); +AVStream *video_avs = avformat_new_stream(encoder_avfc, NULL); + +char *codec_name = "libx265"; +char *codec_priv_key = "x265-params"; +// ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½ΠΈΠ΅ ΠΎΠΏΡ†ΠΈΠΈ x265: +// ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ Π΄Π΅Ρ‚Π΅ΠΊΡ†ΠΈΡŽ смСны сцСны ΠΈ фиксируСм +// GOP Π½Π° 60 ΠΊΠ°Π΄Ρ€ΠΎΠ². +char *codec_priv_value = "keyint=60:min-keyint=60:scenecut=0"; + +AVCodec *video_avc = avcodec_find_encoder_by_name(codec_name); +AVCodecContext *video_avcc = avcodec_alloc_context3(video_avc); +// ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ энкодСра +av_opt_set(sc->video_avcc->priv_data, codec_priv_key, codec_priv_value, 0); +video_avcc->height = decoder_ctx->height; +video_avcc->width = decoder_ctx->width; +video_avcc->pix_fmt = video_avc->pix_fmts[0]; +// ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒ Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚Π° +video_avcc->bit_rate = 2 * 1000 * 1000; +video_avcc->rc_buffer_size = 4 * 1000 * 1000; +video_avcc->rc_max_rate = 2 * 1000 * 1000; +video_avcc->rc_min_rate = 2.5 * 1000 * 1000; +// time base +video_avcc->time_base = av_inv_q(input_framerate); +video_avs->time_base = sc->video_avcc->time_base; + +avcodec_open2(sc->video_avcc, sc->video_avc, NULL); +avcodec_parameters_from_context(sc->video_avs->codecpar, sc->video_avcc); +``` + +Π Π°ΡΡˆΠΈΡ€ΠΈΠΌ Ρ†ΠΈΠΊΠ» дСкодирования для видСотранскодирования: + +* ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ Π²Ρ…ΠΎΠ΄Π½ΠΎΠΉ `AVPacket` Π² Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€ β€” [`avcodec_send_packet`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga58bc4bf1e0ac59e27362597e467efff3) +* ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ сырой `AVFrame` β€” [`avcodec_receive_frame`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c) +* НачинаСм ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ этот сырой ΠΊΠ°Π΄Ρ€, +* ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ ΠΊΠ°Π΄Ρ€ β€” [`avcodec_send_frame`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga9395cb802a5febf1f00df31497779169) +* ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ сТатый `AVPacket` β€” [`avcodec_receive_packet`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga5b8eff59cf259747cf0b31563e38ded6) +* ВыставляСм таймстампы β€” [`av_packet_rescale_ts`](https://www.ffmpeg.org/doxygen/trunk/group__lavc__packet.html#gae5c86e4d93f6e7aa62ef2c60763ea67e) +* ПишСм Π² Π²Ρ‹Ρ…ΠΎΠ΄Π½ΠΎΠΉ Ρ„Π°ΠΉΠ» β€” [`av_interleaved_write_frame`](https://www.ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga37352ed2c63493c38219d935e71db6c1) + +```c +AVFrame *input_frame = av_frame_alloc(); +AVPacket *input_packet = av_packet_alloc(); + +while (av_read_frame(decoder_avfc, input_packet) >= 0) +{ + int response = avcodec_send_packet(decoder_video_avcc, input_packet); + while (response >= 0) { + response = avcodec_receive_frame(decoder_video_avcc, input_frame); + if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) { + break; + } else if (response < 0) { + return response; + } + if (response >= 0) { + encode(encoder_avfc, decoder_video_avs, encoder_video_avs, decoder_video_avcc, input_packet->stream_index); + } + av_frame_unref(input_frame); + } + av_packet_unref(input_packet); +} +av_write_trailer(encoder_avfc); + +// ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠ°Ρ функция +int encode(AVFormatContext *avfc, AVStream *dec_video_avs, AVStream *enc_video_avs, AVCodecContext video_avcc int index) { + AVPacket *output_packet = av_packet_alloc(); + int response = avcodec_send_frame(video_avcc, input_frame); + + while (response >= 0) { + response = avcodec_receive_packet(video_avcc, output_packet); + if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) { + break; + } else if (response < 0) { + return -1; + } + + output_packet->stream_index = index; + output_packet->duration = enc_video_avs->time_base.den / enc_video_avs->time_base.num / dec_video_avs->avg_frame_rate.num * dec_video_avs->avg_frame_rate.den; + + av_packet_rescale_ts(output_packet, dec_video_avs->time_base, enc_video_avs->time_base); + response = av_interleaved_write_frame(avfc, output_packet); + } + av_packet_unref(output_packet); + av_packet_free(&output_packet); + return 0; +} +``` + +ΠœΡ‹ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π»ΠΈ Π²ΠΈΠ΄Π΅ΠΎΠΏΠΎΡ‚ΠΎΠΊ `h264` β†’ `h265`. Как ΠΈ оТидалось, `h265`-вСрсия мСньшС `h264`. ΠŸΡ€ΠΈ этом [написанная ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°](/3_transcoding.c) ΡƒΠΌΠ΅Π΅Ρ‚ ΠΏΡ€ΠΎΠ²ΠΎΠ΄ΠΈΡ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ: + +```c + + /* + * H264 -> H265 + * Audio -> remuxed (Π±Π΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ) + * MP4 - MP4 + */ + StreamingParams sp = {0}; + sp.copy_audio = 1; + sp.copy_video = 0; + sp.video_codec = "libx265"; + sp.codec_priv_key = "x265-params"; + sp.codec_priv_value = "keyint=60:min-keyint=60:scenecut=0"; + + /* + * H264 -> H264 (фиксированный GOP) + * Audio -> remuxed (Π±Π΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ) + * MP4 - MP4 + */ + StreamingParams sp = {0}; + sp.copy_audio = 1; + sp.copy_video = 0; + sp.video_codec = "libx264"; + sp.codec_priv_key = "x264-params"; + sp.codec_priv_value = "keyint=60:min-keyint=60:scenecut=0:force-cfr=1"; + + /* + * H264 -> H264 (фиксированный GOP) + * Audio -> remuxed (Π±Π΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ) + * MP4 - fragmented MP4 + */ + StreamingParams sp = {0}; + sp.copy_audio = 1; + sp.copy_video = 0; + sp.video_codec = "libx264"; + sp.codec_priv_key = "x264-params"; + sp.codec_priv_value = "keyint=60:min-keyint=60:scenecut=0:force-cfr=1"; + sp.muxer_opt_key = "movflags"; + sp.muxer_opt_value = "frag_keyframe+empty_moov+delay_moov+default_base_moof"; + + /* + * H264 -> H264 (фиксированный GOP) + * Audio -> AAC + * MP4 - MPEG-TS + */ + StreamingParams sp = {0}; + sp.copy_audio = 0; + sp.copy_video = 0; + sp.video_codec = "libx264"; + sp.codec_priv_key = "x264-params"; + sp.codec_priv_value = "keyint=60:min-keyint=60:scenecut=0:force-cfr=1"; + sp.audio_codec = "aac"; + sp.output_extension = ".ts"; + + /* Π’ Ρ€Π°Π±ΠΎΡ‚Π΅ :P -> Π½Π΅ ΠΈΠ³Ρ€Π°Π΅Ρ‚ Π²ΠΎ VLC, ΠΈΡ‚ΠΎΠ³ΠΎΠ²Ρ‹ΠΉ Π±ΠΈΡ‚Ρ€Π΅ΠΉΡ‚ ΠΎΠ³Ρ€ΠΎΠΌΠ΅Π½ + * H264 -> VP9 + * Audio -> Vorbis + * MP4 - WebM + */ + //StreamingParams sp = {0}; + //sp.copy_audio = 0; + //sp.copy_video = 0; + //sp.video_codec = "libvpx-vp9"; + //sp.audio_codec = "libvorbis"; + //sp.output_extension = ".webm"; + +``` + +> ЧСстно говоря, это оказалось [слоТнСС, Ρ‡Π΅ΠΌ я Π΄ΡƒΠΌΠ°Π»](https://github.com/leandromoreira/ffmpeg-libav-tutorial/pull/54): ΠΏΡ€ΠΈΡˆΠ»ΠΎΡΡŒ ΠΊΠΎΠΏΠ°Ρ‚ΡŒ [исходники FFmpeg CLI](https://github.com/leandromoreira/ffmpeg-libav-tutorial/pull/54#issuecomment-570746749) ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ Ρ‚Π΅ΡΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ. ΠŸΠΎΡ…ΠΎΠΆΠ΅, я Π΅Ρ‰Ρ‘ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ ΡƒΠΏΡƒΡΠΊΠ°ΡŽ: ΠΏΡ€ΠΈΡˆΠ»ΠΎΡΡŒ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ `force-cfr`, Ρ‡Ρ‚ΠΎΠ±Ρ‹ `h264` Π·Π°Ρ€Π°Π±ΠΎΡ‚Π°Π», ΠΈ я всё Π΅Ρ‰Ρ‘ Π²ΠΈΠΆΡƒ прСдупрСТдСния Π²Ρ€ΠΎΠ΄Π΅ `warning messages (forced frame type (5) at 80 was changed to frame type (3))`. diff --git a/README.md b/README.md index 8b88cb5..7885879 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [πŸ‡ͺπŸ‡Έ](/README-es.md "Spanish") [πŸ‡»πŸ‡³](/README-vn.md "Vietnamese") [πŸ‡§πŸ‡·](/README-pt.md "Portuguese") +[πŸ‡·πŸ‡Ί](/README-ru.md "Russian") [![license](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)