Skip to content
ํ”„๋กœ์ ํŠธ

leandromoreira/ffmpeg-libav-tutorial has 11,066 GitHub stars and 1,010 forks. GitHot measured -1 stars in 24 hours. Last checked 2026-11-08 12:53 UTC. Last push 2026-06-18.

์ด ์ˆ˜์น˜๋ฅผ ์ธก์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•

FFmpeg libav tutorial - learn how media works from basic to transmuxing, transcoding and more. Translations: ๐Ÿ‡บ๐Ÿ‡ธ ๐Ÿ‡จ๐Ÿ‡ณ ๐Ÿ‡ฐ๐Ÿ‡ท ๐Ÿ‡ช๐Ÿ‡ธ ๐Ÿ‡ป๐Ÿ‡ณ ๐Ÿ‡ง๐Ÿ‡ท ๐Ÿ‡ท๐Ÿ‡บ

์ธก์ •๋œ ์Šคํƒ€ ์ฆ๊ฐ€

in 24h
-1
in 7 days
๊ธฐ๋ก ์ˆ˜์ง‘ ์ค‘
in 15 days
๊ธฐ๋ก ์ˆ˜์ง‘ ์ค‘
in 1 month
๊ธฐ๋ก ์ˆ˜์ง‘ ์ค‘

์„œ๋กœ ๊ฒน์น˜๋Š” ๊ธฐ๊ฐ„์˜ ์ˆœ์ฆ๊ฐ€๋Ÿ‰์ด๋ฉฐ ์ผ๋ณ„ ํ•ฉ๊ณ„๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ธก์ •๊ฐ’์ด ์—†์œผ๋ฉด ๋น„์›Œ ๋‘ก๋‹ˆ๋‹ค. ์Šคํƒ€๋Š” ๊ด€์‹ฌ๋„๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ ํ’ˆ์งˆ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

README-ko.md

Original

license

FFmpeg์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ(a.k.a. libav) ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ์‹œ์ž‘ํ•ด์•ผํ• ์ง€ ์•Œ๋ ค์ค„๋งŒํ•  ํŠœํ† ๋ฆฌ์–ผ/์ฑ…์„ ์ฐพ์•„๋ดค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ ๋Š” "How to write a video player in less than 1k lines" ๋ผ๋Š” ํŠœํ† ๋ฆฌ์–ผ์„ ์ฐพ์•˜์ฃ . ํ•˜์ง€๋งŒ ์•ˆํƒ€๊น๊ฒŒ๋„ ๊ทธ๊ฑด ๋”์ด์ƒ ๊ด€๋ฆฌ๊ฐ€ ์•ˆ๋˜๊ณ  ์žˆ์–ด์„œ ์ด ๊ธ€์„ ์“ฐ๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์‚ฌ์šฉ๋œ ๋Œ€๋ถ€๋ถ„์˜ ์ฝ”๋“œ๋Š” C๋กœ ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฑฑ์ •ํ•˜์ง€ ๋งˆ์„ธ์š”: ๋‹น์‹ ๋„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ๊ฒƒ์ด๊ณ  ์„ ํ˜ธํ•˜๋Š” ์–ธ์–ด์—๋„ ์ ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค. FFmpeg libav๋Š” python, go์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์–ธ์–ด๋กœ ๋œ ๋งŽ์€ bindings์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์‚ฌ์šฉํ•˜๋ ค๋Š” ์–ธ์–ด์— ๊ทธ๊ฒƒ์ด ์—†๋‹ค๋ฉด ffi๋ฅผ ํ†ตํ•ด์„œ๋„ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (Lua ์˜ˆ์‹œ)

์šฐ๋ฆฌ๋Š” ๋น„๋””์˜ค์™€ ์˜ค๋””์˜ค, ์ฝ”๋ฑ, ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•ด ๋น ๋ฅด๊ฒŒ ํ•™์Šตํ•œ ํ›„์— FFmpeg ๋ช…๋ น์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๋Œ€ํ•ด์„œ ํŒŒํ—ค์ณ๋ณด๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ์ฝ”๋“œ๋„ ์ž‘์„ฑํ•ด๋ณผ ๊ฒƒ์ž…๋‹ˆ๋‹ค, ์‚ฝ์งˆํ•˜๋ฉด์„œ FFmpeg libav ๋ฐฐ์šฐ๊ธฐ ์„น์…˜์œผ๋กœ ๋ฐ”๋กœ ๋„˜์–ด๊ฐ€์…”๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

ํ˜น์ž๋Š” ์ธํ„ฐ๋„ท ๋น„๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ์ด ์ „ํ†ต์ ์ธ TV์˜ ๋ฏธ๋ž˜๋ผ๊ณ  ์ด์•ผ๊ธฐํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ ๋˜๋“  FFmpeg์€ ๊ณต๋ถ€ํ•ด๋‘˜๋งŒํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ชฉ์ฐจ

์†Œ๊ฐœ

๋น„๋””์˜ค - ๋‹น์‹ ์ด ๋ฌด์—‡์„ ๋ณด๋Š”์ง€!

๋งŒ์•ฝ ๋‹น์‹ ์ด ์—ฌ๋Ÿฌ ์—ฐ์†๋œ ์ด๋ฏธ์ง€๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  ์ด๊ฒƒ๋“ค์„ ์ฃผ์–ด์ง„ ์ฃผํŒŒ์ˆ˜์— ๋งž๊ฒŒ ๋ณ€ํ™”์‹œํ‚จ๋‹ค๋ฉด (์ด๋ฅผํ…Œ๋ฉด ์ดˆ๋‹น 24์žฅ์˜ ์ด๋ฏธ์ง€), ์›€์ง์ž„์˜ ์ž”์ƒ์„ ๋งŒ๋“ค๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์š”์•ฝํ•˜๋ฉด ์ด๊ฒŒ ๋น„๋””์˜ค๋ผ๋Š” ๊ฒƒ์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์•„์ด๋””์–ด์ž…๋‹ˆ๋‹ค: ์ •ํ•ด์ง„ ์†๋„์— ๋งž๊ฒŒ ๋Œ์•„๊ฐ€๋Š” ์—ฐ์†๋œ ์‚ฌ์ง„๋“ค / ํ”„๋ ˆ์ž„๋“ค.

Zeitgenรถssische Illustration (1886)

์˜ค๋””์˜ค - ๋‹น์‹ ์ด ๋ฌด์—‡์„ ๋“ฃ๋Š”์ง€!

์Œ์†Œ๊ฑฐ๋œ ๋น„๋””์˜ค๋งŒ์œผ๋กœ๋„ ๋‹ค์–‘ํ•œ ๊ฐ์ •๋“ค์„ ํ‘œํ˜„ํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ์—ฌ๊ธฐ์— ์†Œ๋ฆฌ๋ฅผ ๋”ํ•ด์ค€๋‹ค๋ฉด ํ›จ์”ฌ ๋” ์ฆ๊ฑฐ์šด ๊ฒฝํ—˜์„ ๊ฐ€์ ธ๋‹ค ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์†Œ๋ฆฌ๋Š” ๊ณต๊ธฐ ํ˜น์€ ๊ฐ€์Šค, ์•ก์ฒด, ๊ณ ์ฒด์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ๋งค์ฒด๋“ค์„ ํ†ตํ•ด ์••๋ ฅ์˜ ํŒŒ๋™ ํ˜•ํƒœ๋กœ ์ „ํŒŒ๋˜๋Š” ์ง„๋™์ž…๋‹ˆ๋‹ค.

๋””์ง€ํ„ธ ์˜ค๋””์˜ค ์‹œ์Šคํ…œ์—์„œ๋Š” ๋งˆ์ดํฌ๊ฐ€ ์†Œ๋ฆฌ๋ฅผ ์•„๋‚ ๋กœ๊ทธ ์ „๊ธฐ ์‹ ํ˜ธ๋กœ ์ „ํ™˜ํ•˜๊ณ , ์•„๋‚ ๋กœ๊ทธ-๋””์ง€ํ„ธ ๋ณ€ํ™˜๊ธฐ (ADC) - ๋ณดํ†ต ํŽ„์Šค-๋ถ€ํ˜ธ ๋ณ€์กฐ (PCM)๋ฅผ ์ด์šฉํ•˜์—ฌ - ์•„๋‚ ๋กœ๊ทธ ์‹ ํ˜ธ๋ฅผ ๋””์ง€ํƒˆ ์‹ ํ˜ธ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

audio analog to digital

์›๋ฌธ

์ฝ”๋ฑ - ๋ฐ์ดํ„ฐ๋ฅผ ์ค„์ด๊ธฐ

CODEC์€ ๋””์ง€ํ„ธ ์˜ค๋””์˜ค/๋น„๋””์˜ค๋ฅผ ์••์ถ•ํ•˜๊ฑฐ๋‚˜ ์••์ถ•ํ•ด์ œํ•˜๋Š” ์ „์žํšŒ๋กœ๋‚˜ ์†Œํ”„ํŠธ์›จ์–ด์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ raw (์••์ถ•์ด์•ˆ๋œ) ๋””์ง€ํ„ธ ์˜ค๋””์˜ค/๋น„๋””์˜ค๋ฅผ ์••์ถ•๋œ ํ˜•ํƒœ๋กœ ํ˜น์€ ๊ทธ ๋ฐ˜๋Œ€๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. https://en.wikipedia.org/wiki/Video_codec

๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ์ˆ˜๋งŽ์€ ์ด๋ฏธ์ง€๋“ค์„ ์ฐจ๊ณก์ฐจ๊ณก ์ฑ„์›Œ์„œ ์˜ํ™”๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ํ•˜๋‚˜์˜ ํŒŒ์ผ๋กœ ๋งŒ๋“ ๋‹ค๋ฉด, ๊ฒฐ๊ณผ์ ์œผ๋กœ ์—„์ฒญ๋‚˜๊ฒŒ ํฐ ํ•˜๋‚˜์˜ ํŒŒ์ผ์„ ์ ‘ํ•˜๊ฒŒ ๋  ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ํ•œ๋ฒˆ ๊ณ„์‚ฐํ•ด๋ด…์‹œ๋‹ค:

ํ•œ๋ฒˆ ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. ํ•ด์ƒ๋„๊ฐ€ 1080 x 1920 (๋†’์ด x ๋„ˆ๋น„)์ธ ๋น„๋””์˜ค๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค๊ฑด๋ฐ ์ƒ‰์„ ์ธ์ฝ”๋”ฉํ•˜๋Š”๋ฐ ํ”ฝ์…€๋‹น 3 bytes (ํ™”๋ฉด์˜ ์ตœ์†Œ ํ™”์†Œ)๋ฅผ ์“ธ ๊ฒƒ์ž…๋‹ˆ๋‹ค. (ํ˜น์€ 24๋น„ํŠธ ์ปฌ๋Ÿฌ, 16,777,216๊ฐœ์˜ ๋‹ค๋ฅธ ์ƒ‰์ƒ์„ ์ œ๊ณต) ๊ทธ๋ฆฌ๊ณ  ์ด ๋น„๋””์˜ค๋Š” ์ดˆ๋‹น 24ํ”„๋ ˆ์ž„์œผ๋กœ ์žฌ์ƒ๋˜๊ณ  30๋ถ„ ์ •๋„ ๊ธธ์ด์ž…๋‹ˆ๋‹ค.

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.19Gbps์˜ ๋Œ€์—ญํญ์ด ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค! ์ด๊ฒƒ์ด ๋ฐ”๋กœ ์šฐ๋ฆฌ๊ฐ€ CODEC์„ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

์ปจํ…Œ์ด๋„ˆ - ์˜ค๋””์˜ค์™€ ๋น„๋””์˜ค์˜ ์•ˆ์‹์ฒ˜

์ปจํ…Œ์ด๋„ˆ ํ˜น์€ ๋ž˜ํผ(wrapper) ํฌ๋งท์€ ๋ฐ์ดํ„ฐ์™€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ ๋‹ค์–‘ํ•œ ์š”์†Œ๋“ค์ด ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์˜ ์ปดํ“จํ„ฐ ํŒŒ์ผ์— ๊ตฌ์„ฑ๋˜์–ด์žˆ๋Š”์ง€๋ฅผ ๊ธฐ์ˆ ํ•˜๋Š” ์ŠคํŽ™์„ ๋‹ด์€ ๋ฉ”ํƒ€ํŒŒ์ผ ํฌ๋งท์ž…๋‹ˆ๋‹ค. https://en.wikipedia.org/wiki/Digital_container_format

ํ•˜๋‚˜์˜ ํŒŒ์ผ์ด ๋ชจ๋“  ์ŠคํŠธ๋ฆผ์„ ๋‹ด๊ณ  ์žˆ๊ณ  (์ฃผ๋กœ ์˜ค๋””์˜ค์™€ ๋น„๋””์˜ค) ์ด๊ฒƒ์€ ๋˜ ๋™๊ธฐํ™”์™€ ์ œ๋ชฉ, ํ•ด์ƒ๋„ ๋“ฑ๊ณผ ๊ฐ™์€ ์ผ๋ฐ˜์ ์ธ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋ณดํ†ต ์šฐ๋ฆฌ๋Š” ํŒŒ์ผ์˜ ํ™•์žฅ์ž๋ฅผ ๋ณด๊ณ  ํฌ๋งท์„ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: ์˜ˆ๋ฅผ๋“ค๋ฉด video.webm์€ ์•„๋งˆ๋„ webm๋ฅผ ์ปจํ…Œ์ด๋„ˆ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋น„๋””์˜ค๊ฒ ์ฃ .

container

FFmpeg - ๋ช…๋ น์ค„ ๋„๊ตฌ

์˜ค๋””์˜ค์™€ ๋น„๋””์˜ค๋ฅผ ๋…นํ™”ํ•˜๊ณ  ๋ณ€ํ™˜ํ•˜๊ณ  ์ŠคํŠธ๋ฆฌ๋ฐํ•  ์ˆ˜ ์žˆ๋Š” ์™„์ „ํ•œ ํฌ๋กœ์Šค-ํ”Œ๋žซํผ ์†”๋ฃจ์…˜.

๋ฉ€ํ‹ฐ๋ฏธ๋””์–ด ์ž‘์—…์„ ํ•œ๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” FFmpeg์ด๋ผ๊ณ  ํ•˜๋Š” ์ •๋ง ์ฉŒ๋Š” ํˆด/๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์—ฌ๋Ÿฌ๋ถ„๋„ ์ด๊ฒƒ์„ ์ง๊ฐ„์ ‘์ ์œผ๋กœ ์•Œ๊ณ ์žˆ๊ฑฐ๋‚˜/์‚ฌ์šฉํ–ˆ๋˜ ๊ธฐํšŒ๊ฐ€ ์žˆ์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. (Chrome ์‚ฌ์šฉ์‹œ์ฃ ?).

์ด๊ฒƒ์€ ffmpeg์ด๋ผ๊ณ ํ•˜๋Š” ์•„์ฃผ ๋‹จ์ˆœํ•˜์ง€๋งŒ ํŒŒ์›Œํ’€ํ•œ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•ํƒœ์˜ ๋ช…๋ ค์ค„ ํ”„๋กœ๊ทธ๋žจ๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด, ์•„๋ž˜ ๋ช…๋ น์„ ์น˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ์ปจํ…Œ์ด๋„ˆ๋ฅผ mp4์—์„œ avi๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

$ ffmpeg -i input.mp4 output.avi

์šฐ๋ฆฌ๋Š” ๋ฐฉ๊ธˆ ์–ด๋–ค ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋‹ค๋ฅธ ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ธ remuxing์„ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ธฐ์ˆ ์ ์œผ๋กœ FFmpeg์€ ํŠธ๋žœ์Šค์ฝ”๋”ฉ(transcoding)๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค๋งŒ ์ด๊ฒƒ๋“ค์— ๋Œ€ํ•ด์„œ๋Š” ๋’ค์—์„œ ๋‹ค์‹œ ์ด์•ผ๊ธฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

FFmpeg ๋ช…๋ น์ค„ ๋„๊ตฌ 101

FFmpeg์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ์•„์ฃผ ์ž˜ ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋Š” ๋ฌธ์„œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•˜๋ฉด, FFmpeg ๋ช…๋ น์ค„ ํ”„๋กœ๊ทธ๋žจ์€ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์˜ ์ธ์ž๋ฅผ ๊ฐ–์ถฐ์•ผํ•ฉ๋‹ˆ๋‹ค ffmpeg {1} {2} -i {3} {4} {5}, ์—ฌ๊ธฐ์„œ:

  1. ์ „์—ญ ์˜ต์…˜
  2. ์ž…๋ ฅ ํŒŒ์ผ ์˜ต์…˜
  3. ์ž…๋ ฅ url
  4. ์ถœ๋ ฅ ํŒŒ์ผ ์˜ต์…˜
  5. ์ถœ๋ ฅ url

2, 3, 4, 5 ๋ถ€๋ถ„์€ ํ•„์š”ํ•œ๋งŒํผ ๋งŽ์•„์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ˆ˜ํ–‰ํ•ด๋ณด๋ฉด ์ด ์ธ์ž ํ˜•์‹์„ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

# WARNING: this file is around 300MB
$ 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

์ด ๋ช…๋ น์€ ๋‘๊ฐœ์˜ ์ŠคํŠธ๋ฆผ(aac ์ฝ”๋ฑ์œผ๋กœ ์ธ์ฝ”๋”ฉ๋œ ์˜ค๋””์˜ค์™€ h264 ์ฝ”๋ฑ์œผ๋กœ ์ธ์ฝ”๋”ฉ๋œ ๋น„๋””์˜ค)์„ ํฌํ•จํ•˜๋Š” mp4๋ฅผ ์ž…๋ ฅ ํŒŒ์ผ๋กœ ๋ฐ›๊ณ  ์ด๋ฅผ webm์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค, ๋ฌผ๋ก  ๊ทธ ์•ˆ์˜ ์˜ค๋””์˜ค์™€ ๋น„๋””์˜ค ์ฝ”๋ฑ๋“ค๋„ ๋ณ€ํ™˜ํ•˜๊ณ  ์žˆ์ฃ .

์œ„์˜ ๋ช…๋ น์„ ๋” ๋‹จ์ˆœํ™”ํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ ๊ทธ๋Ÿฌ๋ฉด FFmpeg์ด ๊ธฐ๋ณธ๊ฐ’๋“ค์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ถ”์ธกํ•˜๊ฒŒ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด ffmpeg -i input.avi output.mp4 ์ด๋ ‡๊ฒŒ๋งŒ ์นœ๋‹ค๋ฉด ์–ด๋–ค ์˜ค๋””์˜ค/๋น„๋””์˜ค ์ฝ”๋ฑ์ด output.mp4๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ ๊นŒ์š”?

Werner Robitza๊ฐ€ ์ž‘์„ฑํ•œ ๊ผญ ์ฝ๊ณ /์‹คํ–‰ํ•ด๋ณผ๋งŒํ•œ FFmpeg์œผ๋กœ ์ธ์ฝ”๋”ฉํ•˜๊ณ  ํŽธ์ง‘ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ํŠœํ† ๋ฆฌ์–ผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณตํ†ต ๋น„๋””์˜ค ์—ฐ์‚ฐ

์˜ค๋””์˜ค/๋น„๋””์˜ค ์ž‘์—… ์ค‘ ๋ณดํ†ต ๋ฏธ๋””์–ด์— ๋Œ€ํ•ด ์ผ๋ จ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํŠธ๋žœ์Šค์ฝ”๋”ฉ (Transcoding)

transcoding

๋ฌด์—‡์ธ๊ฐ€? ์ŠคํŠธ๋ฆผ (์˜ค๋””์˜ค ๋˜๋Š” ๋น„๋””์˜ค) ์ค‘์— ํ•˜๋‚˜๋ฅผ ๊ธฐ์กด ์ฝ”๋ฑ์—์„œ ๋‹ค๋ฅธ ์ฝ”๋ฑ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—….

์™œ? ๊ฐ€๋” ์–ด๋–ค ์žฅ์น˜๋“ค์€ (ํ…”๋ ˆ๋น„์ „, ์Šค๋งˆํŠธํฐ, ์ฝ˜์†” ๋“ฑ) X๋Š” ์ง€์›ํ•˜์ง€ ์•Š์ง€๋งŒ Y๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋” ์ƒˆ๋กœ์šด ์ฝ”๋ฑ๋“ค์€ ๋” ๋‚˜์€ ์••์ถ•๋ฅ ์„ ์ œ๊ณตํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ? H264 (AVC) ๋น„๋””์˜ค๋ฅผ H265 (HEVC)๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ.

$ ffmpeg \
-i bunny_1080p_60fps.mp4 \
-c:v libx265 \
bunny_1080p_60fps_h265.mp4

ํŠธ๋žœ์Šค๋จน์‹ฑ (Transmuxing)

transmuxing

๋ฌด์—‡์ธ๊ฐ€? ํ•˜๋‚˜์˜ ํฌ๋งท์„ (์ปจํ…Œ์ด๋„ˆ) ๋‹ค๋ฅธ ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—….

์™œ? ๊ฐ€๋” ์–ด๋–ค ์žฅ์น˜๋“ค์€ (ํ…”๋ ˆ๋น„์ „, ์Šค๋งˆํŠธํฐ, ์ฝ˜์†” ๋“ฑ) X๋Š” ์ง€์›ํ•˜์ง€ ์•Š์ง€๋งŒ Y๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋•Œ๋•Œ๋กœ ๋” ์ƒˆ๋กœ์šด ์ปจํ…Œ์ดํ„ฐ๋“ค์€ ์ตœ์‹ ์œผ๋กœ ์š”๊ตฌ๋˜๋Š” ํ”ผ์ฒ˜๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ? mp4์—์„œ webm์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ.

$ ffmpeg \
-i bunny_1080p_60fps.mp4 \
-c copy \ # just saying to ffmpeg to skip encoding
bunny_1080p_60fps.webm

ํŠธ๋žœ์Šค๋ ˆ์ดํŒ… (Transrating)

transrating

๋ฌด์—‡์ธ๊ฐ€? ๋น„ํŠธ๋ ˆ์ดํŠธ๋ฅผ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ๋ณ€ํ™˜๋ณธ(renditions)์„ ๋งŒ๋“œ๋Š” ์ž‘์—….

์™œ? ์‚ฌ๋žŒ๋“ค์€ 2G (edge)๊ฐ€ ์—ฐ๊ฒฐ๋œ ์ €์‚ฌ์–‘์˜ ์Šค๋งˆํŠธํฐ์—์„œ๋“  ๊ด‘ํ†ต์‹  ์ธํ„ฐ๋„ท์ด ์—ฐ๊ฒฐ๋œ 4K ํ…”๋ ˆ๋น„์ „์—๋“  ๋‹น์‹ ์˜ ๋น„๋””์˜ค ๋ณผ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ™์€ ๋น„๋””์˜ค๋ผ๋„ ์—ฌ๋Ÿฌ ๋น„ํŠธ๋ ˆ์ดํŠธ๋ฅผ ๊ฐ€์ง„ ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ณ€ํ™˜๋ณธ์„ ์ œ๊ณตํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ? 3856K์™€ 2000K ์‚ฌ์ด์˜ ๋น„ํŠธ๋ ˆ์ดํŠธ๋ฅผ ๊ฐ€์ง„ ๋ณ€ํ™˜๋ณธ์„ ์ƒ์„ฑํ•˜๊ธฐ.

$ ffmpeg \
-i bunny_1080p_60fps.mp4 \
-minrate 964K -maxrate 3856K -bufsize 2000K \
bunny_1080p_60fps_transrating_964_3856.mp4

๋ณดํ†ต ํŠธ๋žœ์Šค๋ ˆ์ดํŒ…(transrating)์€ ํŠธ๋žœ์Šค์‚ฌ์ด์ง•(transsizing)๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Werner Robitza๊ฐ€ ์ž‘์„ฑํ•œ ๋˜ ๋‹ค๋ฅธ ํ•„๋…/์‹คํ–‰๋ฌผ FFmpeg rate ์ œ์–ด์— ๋Œ€ํ•œ ์—ฐ์žฌ ํฌ์ŠคํŒ…๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŠธ๋žœ์Šค์‚ฌ์ด์ง• (Transsizing)

transsizing

๋ฌด์—‡์ธ๊ฐ€? ํ•˜๋‚˜์˜ ํ•ด์ƒ๋„์—์„œ ๋‹ค๋ฅธ ๊ฒƒ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—…. ์ด์ „์— ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ํŠธ๋žœ์Šค์‚ฌ์ด์ง•(transsizing)์€ ์ฃผ๋กœ ํŠธ๋žœ์Šค๋ ˆ์ดํŒ…(transrating)๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์™œ? ํŠธ๋žœ์Šค๋ ˆ์ดํŒ…(transrating)์—์„œ์˜ ์ด์œ ์™€ ๋™์ผํ•จ.

์–ด๋–ป๊ฒŒ? 1080p์˜ ํ•ด์ƒ๋„๋ฅผ 480p๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ.

$ ffmpeg \
-i bunny_1080p_60fps.mp4 \
-vf scale=480:-1 \
bunny_1080p_60fps_transsizing_480.mp4

๋ณด๋„ˆ์Šค: ์ ์‘ํ˜• ์ŠคํŠธ๋ฆฌ๋ฐ (Adaptive Streaming)

adaptive streaming

๋ฌด์—‡์ธ๊ฐ€? ๋‹ค์–‘ํ•œ (๋น„ํŠธ๋ ˆ์ดํŠธ์˜) ํ•ด์ƒ๋„๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ฏธ๋””์–ด๋“ค์„ ์—ฌ๋Ÿฌ ์ฒญํฌ๋กœ ๋‚˜๋ˆ ์„œ http๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šคํ•˜๋Š” ์ž‘์—….

์™œ? ์ €์‚ฌ์–‘ ์Šค๋งˆํŠธํฐ ํ˜น์€ 4K TV์—์„œ ์‹œ์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์—ฐํ•œ ๋ฏธ๋””์–ด๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด. ๋˜ํ•œ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ™•์žฅ์ด๋‚˜ ๋ฐฐํฌํ•˜๊ธฐ๊ฐ€ ์‰ฝ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ง€์—ฐ์‹œ๊ฐ„์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ? DASH๋ฅผ ์ด์šฉํ•˜์—ฌ ์ ์‘ํ˜• WebM์„ ์ƒ์„ฑํ•˜๊ธฐ.

# video streams
$ 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

# audio streams
$ ffmpeg -i bunny_1080p_60fps.mp4 -c:a libvorbis -b:a 128k -vn -f webm -dash 1 audio_128k.webm

# the DASH manifest
$ 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: ์ €๋Š” ์ด ์˜ˆ์ œ๋ฅผ DASH๋ฅผ ์ด์šฉํ•œ Adaptive WebM ์žฌ์ƒ์— ๋Œ€ํ•œ ์ง€์นจ์—์„œ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

๋” ๋“ค์–ด๊ฐ€๊ธฐ

FFmpeg์— ๋Œ€ํ•œ ์•„์ฃผ ์ˆ˜๋งŽ์€ ๋‹ค๋ฅธ ์‚ฌ์šฉ๋ฐฉ๋ฒ•๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์ด๊ฑธ YouTube ์šฉ ๋™์˜์ƒ๋“ค์„ ๋งŒ๋“ค๊ณ /ํŽธ์ง‘ํ•˜๋Š”๋ฐ iMovie์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์—ฌ๋Ÿฌ๋ถ„๋„ ํ”„๋กœํŽ˜์…”๋„์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฝ์งˆํ•˜๋ฉด์„œ FFmpeg libav ๋ฐฐ์šฐ๊ธฐ

๊ฐ€๋” '์†Œ๋ฆฌ๋‚˜๋Š” ๊ฒƒ๊ณผ ๋ณด์ด๋Š” ๊ฒƒ์ด' ๊ถ๊ธˆํ•˜์ง€ ์•Š์œผ์„ธ์š”? David Robert Jones

FFmpeg๋Š” ๋ฏธ๋””์–ด ํŒŒ์ผ๋“ค์— ๋Œ€ํ•œ ํ•„์ˆ˜ ์ž‘์—…๋“ค์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ช…๋ น์ค„ ๋„๊ตฌ๋กœ์จ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ ์šฐ๋ฆฌ์˜ ํ”„๋กœ๊ทธ๋žจ์— ์ด์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

FFmpeg๋Š” ์šฐ๋ฆฌ์˜ ํ”„๋กœ๊ทธ๋žจ์— ํ†ตํ•ฉ๋  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ๋ณดํ†ต, FFmpeg์„ ์„ค์น˜ํ• ๋•Œ ์ด ๋ชจ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋„ ์ž๋™์œผ๋กœ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชจ์Œ๋“ค์„ FFmpeg libav๋ผ๊ณ  ํ•ด๋ณด์ฃ .

์ด ์ œ๋ชฉ์€ Zed Shaw์˜ Learn X the Hard Way ์‹œ๋ฆฌ์ฆˆ, ํŠนํžˆ ๊ทธ์˜ ์ฑ… Learn C the Hard Way์— ๋Œ€ํ•œ ์˜ค๋งˆ์ฃผ์ž…๋‹ˆ๋‹ค.

์ฑ•ํ„ฐ 0 - ์•…๋ช… ๋†’์€ hello world

์ด hello world๋Š” ์‹ค์ œ๋กœ "hello world" ๋ฉ”์‹œ์ง€๋ฅผ ํ„ฐ๋ฏธ๋„์— ๋ณด์—ฌ์ฃผ์ง„ ์•Š์Šต๋‹ˆ๋‹ค. :tongue: ๋Œ€์‹  ์šฐ๋ฆฌ๋Š” ๋น„๋””์˜ค์˜ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋น„๋””์˜ค์˜ ํฌ๋งท (์ปจํ…Œ์ด๋„ˆ), ๊ธธ์ด, ํ•ด์ƒ๋„, ์˜ค๋””์˜ค ์ฑ„๋„๋“ค ๊ฐ™์€ ๊ฒƒ๋“ค์„ ๋ง์ด์ฃ . ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ช‡๋ช‡ ํ”„๋ ˆ์ž„๋“ค์„ ๋””์ฝ”๋”ฉํ•˜๊ณ  ์ด๋ฏธ์ง€ ํŒŒ์ผ๋กœ ์ €์žฅํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

FFmpeg libav ์•„ํ‚คํ…์ฒ˜

ํ•˜์ง€๋งŒ ์ฝ”๋”ฉ์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์—, FFmpeg libav ์•„ํ‚คํ…์ฒ˜๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์ด๊ฒƒ๋“ค์˜ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์„œ๋กœ ์–ด๋–ป๊ฒŒ ํ†ต์‹ ํ•˜๋Š”์ง€๋ฅผ ๋ฐฐ์›Œ๋ด…์‹œ๋‹ค.

์—ฌ๊ธฐ ๋น„๋””์˜ค๊ฐ€ ๋””์ฝ”๋”ฉ๋˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๋‹ด์€ ๋‹ค์ด์–ด๊ทธ๋žจ์ด ํ•˜๋‚˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ffmpeg libav architecture - decoding process

์šฐ์„  ์—ฌ๋Ÿฌ๋ถ„์˜ ๋ฏธ๋””์–ด ํŒŒ์ผ์„ AVFormatContext (๋น„๋””์˜ค ์ปจํ…Œ์ด๋„ˆ๋Š” ํฌ๋งท์ด๋ผ๊ณ ๋„ ํ•ฉ๋‹ˆ๋‹ค)๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถˆ๋Ÿฌ์˜ฌ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฑด ์‚ฌ์‹ค ํŒŒ์ผ ์ „์ฒด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๊ฑด ์•„๋‹™๋‹ˆ๋‹ค: ์ข…์ข… ํ—ค๋”๋งŒ์„ ์ฝ์ฃ .

์ผ๋‹จ ์ตœ์†Œํ•œ์˜ ์ปจํ…Œ์ด๋„ˆ ํ—ค๋”๋ฅผ ๋ถˆ๋Ÿฌ์™”๋‹ค๋ฉด, ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์˜ ์ŠคํŠธ๋ฆผ (๊ธฐ๋ณธ์ ์ด๊ณ  ํ•„์ˆ˜์ ์ธ ์˜ค๋””์˜ค์™€ ๋น„๋””์˜ค ๋ฐ์ดํ„ฐ๋ผ๊ณ  ๊ฐ„์ฃผํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์ŠคํŠธ๋ฆผ์€ AVStream๋ผ๊ณ  ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ŠคํŠธ๋ฆผ์€ ๋ฐ์ดํ„ฐ์˜ ์—ฐ์†์ ์ธ ํ๋ฆ„์„ ์˜๋ฏธํ•˜๋Š” fancyํ•œ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.

๋น„๋””์˜ค๊ฐ€ ๋‘๊ฐœ์˜ ์ŠคํŠธ๋ฆผ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค: ์˜ค๋””์˜ค๋Š” AAC CODEC๋กœ ์ธ์ฝ”๋”ฉ๋˜์–ด์žˆ๊ณ  ๋น„๋””์˜ค๋Š” H264 (AVC) CODEC๋กœ ์ธ์ฝ”๋”ฉ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์ŠคํŠธ๋ฆผ์œผ๋กœ๋ถ€ํ„ฐ AVPacket ์ปดํฌ๋„ŒํŠธ๋กœ ๋กœ๋“œ๋  ํŒจํ‚ท์ด๋ผ ์นญํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์กฐ๊ฐ๋“ค์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒจํ‚ท์•ˆ์˜ ๋ฐ์ดํ„ฐ๋Š” ์—ฌ์ „ํžˆ ์ธ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค (์••์ถ•๋œ์ƒํƒœ). ์ด ํŒจํ‚ท์„ ๋””์ฝ”๋”ฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ๋“ค์„ ํŠน์ •ํ•œ AVCodec์— ๋„˜๊ฒจ์•ผํ•ฉ๋‹ˆ๋‹ค.

AVCodec์€ ๊ทธ๊ฒƒ๋“ค์„ AVFrame์œผ๋กœ ๋””์ฝ”๋”ฉํ•˜๋ฉฐ ์ตœ์ข…์ ์œผ๋กœ ์šฐ๋ฆฌ์—๊ฒŒ ์••์ถ• ํ•ด์ œ๋œ ํ”„๋ ˆ์ž„์„ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค. ์˜ค๋””์˜ค ๋ฐ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์—์„œ ๋™์ผํ•œ ์šฉ์–ด/ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค๋Š” ์ ์„ ์œ ์˜ํ•˜์‹ญ์‹œ์˜ค.

์š”๊ตฌ ์‚ฌํ•ญ

๊ฐ„ํ˜น ์˜ˆ์ œ๋ฅผ ์ปดํŒŒ์ผํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š”๋ฐ ์ด์Šˆ๋“ค์„ ๊ฒช๋Š” ๋ถ„๋“ค์ด ๊ณ„์…”์„œ ์šฐ๋ฆฌ์˜ ๊ฐœ๋ฐœ/์‹คํ–‰ ํ™˜๊ฒฝ์œผ๋กœ Docker๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค, ์šฐ๋ฆฌ๋Š” ๋˜ํ•œ big buck bunny ๋น„๋””์˜ค๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ธ๋ฐ ๋”ฐ๋กœ ๋กœ์ปฌ์— ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋ฉด make fetch_small_bunny_video ๋ช…๋ น๋งŒ ์‹คํ–‰ํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ฑ•ํ„ฐ 0 - ๋ชธํ’€๊ธฐ ์ฝ”๋“œ

TLDR; ์ฝ”๋“œ๋ž‘ ์‹คํ–‰ํ•˜๋Š”๊ฑฐ๋‚˜ ๋ณด์—ฌ์ฃผ์„ธ์š”.

$ make run_hello

์ข€ ์ƒ์„ธํ•œ ๋ถ€๋ถ„์€ ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฑฑ์ •ํ•˜์ง„ ๋งˆ์„ธ์š”: ์†Œ์Šค ์ฝ”๋“œ๋Š” github์— ์žˆ์Šต๋‹ˆ๋‹ค.

ํฌ๋งท (์ปจํ…Œ์ด๋„ˆ)์— ๊ด€ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” AVFormatContext ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

AVFormatContext *pFormatContext = avformat_alloc_context();

์ด์ œ ์šฐ๋ฆฌ๋Š” ํŒŒ์ผ์„ ์—ด๊ณ  ํ—ค๋”๋ฅผ ์ฝ์–ด์„œ AVFormatContext์— ํฌ๋งท์— ๊ด€ํ•œ ๊ธฐ๋ณธ์ ์ธ ์ •๋ณด๋ฅผ ์ฑ„์›Œ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค (๋ณดํ†ต ์ฝ”๋ฑ์€ ์—ด๋ฆฌ์ง€ ์•Š์Œ). ์ด๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•  ํ•จ์ˆ˜๋Š” avformat_open_input์ž…๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” AVFormatContext์™€ filename ๋‘๊ฐœ์˜ ์˜ต์…”๋„ ์ธ์ž๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค: AVInputFormat (NULL์„ ๋„˜๊ธฐ๋ฉด FFmpeg์ด ํฌ๋งท์„ ์ถ”์ธก)๊ณผ AVDictionary (demuxer์— ๋Œ€ํ•œ ์˜ต์…˜)

avformat_open_input(&pFormatContext, filename, NULL, NULL);

ํฌ๋งท ์ด๋ฆ„๊ณผ ๋ฏธ๋””์–ด ๊ธธ์ด๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

printf("Format %s, duration %lld us", pFormatContext->iformat->long_name, pFormatContext->duration);

streams์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, ๋ฏธ๋””์–ด๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. avformat_find_stream_info ํ•จ์ˆ˜๊ฐ€ ๊ทธ ์ผ์„ ํ•˜์ฃ . pFormatContext->nb_streams๊ฐ€ ์ŠคํŠธ๋ฆผ์˜ ๊ฐœ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  pFormatContext->streams[i]๋Š” i๋ฒˆ์งธ ์ŠคํŠธ๋ฆผ (AVStream)์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

avformat_find_stream_info(pFormatContext,  NULL);

์ด์ œ ๋ชจ๋“  ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•ด ๋ฃจํ”„๋ฅผ ๋Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

for (int i = 0; i < pFormatContext->nb_streams; i++)
{
  //
}

๊ฐ ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•ด์„œ, i๋ฒˆ์งธ ์ŠคํŠธ๋ฆผ์— ์‚ฌ์šฉ๋œ ์ฝ”๋ฑ ์†์„ฑ๋“ค์„ ๋‹ด๊ณ ์žˆ๋Š” AVCodecParameters๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค.

AVCodecParameters *pLocalCodecParameters = pFormatContext->streams[i]->codecpar;

์ด ์ฝ”๋ฑ ์†์„ฑ์„ ์ด์šฉํ•˜์—ฌ avcodec_find_decoder ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ ์ ˆํ•œ ์ฝ”๋ฑ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋ฑ id์— ๋งž๋Š” ๋“ฑ๋ก๋œ ๋””์ฝ”๋”๋ฅผ ์ฐพ๊ณ  ์ŠคํŠธ๋ฆผ์„ ์–ด๋–ป๊ฒŒ enCOde์™€ DECodeํ• ์ง€๋ฅผ ์•Œ๊ณ  ์žˆ๋Š” AVCodec ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐ˜ํ™˜๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

AVCodec *pLocalCodec = avcodec_find_decoder(pLocalCodecParameters->codec_id);

์ด์ œ ์ฝ”๋ฑ์— ๊ด€ํ•œ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// specific for video and audio
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);
}
// general
printf("\tCodec %s ID %d bit_rate %lld", pLocalCodec->long_name, pLocalCodec->id, pLocalCodecParameters->bit_rate);

์ด ์ฝ”๋ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋””์ฝ”๋”ฉ/์ธ์ฝ”๋”ฉ ํ”„๋กœ์„ธ์Šค์— ๋Œ€ํ•œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋‹ด๊ณ ์žˆ๋Š” AVCodecContext์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ ์ฝ”๋ฑ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฝ”๋ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ฑ„์›Œ์ค๋‹ˆ๋‹ค; avcodec_parameters_to_context๋กœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ผ๋‹จ ์ฝ”๋ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ฑ„์› ๋‹ค๋ฉด ์ด์ œ ์ฝ”๋ฑ์„ ์—ด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. avcodec_open2๋กœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
avcodec_parameters_to_context(pCodecContext, pCodecParameters);
avcodec_open2(pCodecContext, pCodec, NULL);

์ด์ œ ์ŠคํŠธ๋ฆผ์œผ๋กœ๋ถ€ํ„ฐ ํŒจํ‚ท์„ ์ฝ๊ณ  ๋””์ฝ”๋”ฉํ•˜์—ฌ ํ”„๋ ˆ์ž„์œผ๋กœ ๋งŒ๋“ค์–ด๋ณผ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์šฐ์„ , AVPacket์™€ AVFrame ๋‘ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

AVPacket *pPacket = av_packet_alloc();
AVFrame *pFrame = av_frame_alloc();

ํŒจํ‚ท์ด ์กด์žฌํ•˜๋Š” ๋™์•ˆ ๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ av_read_frame ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ŠคํŠธ๋ฆผ์œผ๋กœ๋ถ€ํ„ฐ ํŒจํ‚ท์„ ๋ฐ›์•„์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค.

while (av_read_frame(pFormatContext, pPacket) >= 0) {
  //...
}

์ฝ”๋ฑ ์ปจํ…์ŠคํŠธ๋ฅผ avcodec_send_packet ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋””์ฝ”๋”์— raw ๋ฐ์ดํ„ฐ ํŒจํ‚ท (์••์ถ•๋œ ํ”„๋ ˆ์ž„)์„ ๋ณด๋‚ด๋ด…์‹œ๋‹ค.

avcodec_send_packet(pCodecContext, pPacket);

๊ทธ๋ฆฌ๊ณ  ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ฝ”๋ฑ ์ปจํ…์ŠคํŠธ๋ฅผ avcodec_receive_frame ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋””์ฝ”๋”๋กœ๋ถ€ํ„ฐ raw ๋ฐ์ดํ„ฐ ํ”„๋ ˆ์ž„ (์••์ถ• ํ•ด์ œ๋œ ํ”„๋ ˆ์ž„)๋ฅผ ๋ฐ›์•„๋ด…์‹œ๋‹ค.

avcodec_receive_frame(pCodecContext, pFrame);

ํ”„๋ ˆ์ž„ ๋ฒˆํ˜ธ, PTS, DTS, ํ”„๋ ˆ์ž„ ํƒ€์ž… ๋“ฑ์„ ์ถœ๋ ฅํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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
);

๋งˆ์ง€๋ง‰์œผ๋กœ ๋””์ฝ”๋”ฉ๋œ ํ”„๋ ˆ์ž„์„ ์‹ฌํ”Œ ํ‘๋ฐฑ ์ด๋ฏธ์ง€๋กœ ์ €์žฅํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์€ ๋งค์šฐ ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค, ์ธ๋ฑ์Šค๊ฐ€ planes Y, Cb, Cr๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š” pFrame->data๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ํ‘๋ฐฑ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด 0 (Y) ์ธ๋ฑ์Šค๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

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");
    // writing the minimal required header for a pgm file format
    // portable graymap format -> https://en.wikipedia.org/wiki/Netpbm_format#PGM_example
    fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);

    // writing line by line
    for (i = 0; i < ysize; i++)
        fwrite(buf + i * wrap, 1, xsize, f);
    fclose(f);
}

voilร ! ์ด์ œ ์šฐ๋ฆฌ๋Š” 2MB์งœ๋ฆฌ ํ‘๋ฐฑ ์ด๋ฏธ์ง€๋ฅผ ์–ป์–ด๋ƒˆ์Šต๋‹ˆ๋‹ค:

saved frame

์ฑ•ํ„ฐ 1 - ์˜ค๋””์˜ค์™€ ๋น„๋””์˜ค ๋™๊ธฐํ™”

ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋˜์„ธ์š” - ์‹ ๊ทœ MSE ๋น„๋””์˜ค ํ”Œ๋ ˆ์ด์–ด๋ฅผ ์ž‘์„ฑ ์ค‘์ธ ์ Š์€ JS ๊ฐœ๋ฐœ์ž

ํŠธ๋žœ์Šค์ฝ”๋”ฉ ์˜ˆ์ œ ์ฝ”๋“œ๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ „์— ํƒ€์ด๋ฐ ํ˜น์€ ์–ด๋–ป๊ฒŒ ๋น„๋””์˜ค ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ํ•˜๋‚˜์˜ ํ”„๋ ˆ์ž„์„ ์ œ์‹œ๊ฐ„์— ์žฌ์ƒํ•ด์•ผํ•˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ ์ด์•ผ๊ธฐํ•ด๋ด…์‹œ๋‹ค.

์ง€๋‚œ ์˜ˆ์ œ์—์„œ, ์šฐ๋ฆฌ๋Š” ์ด๋ ‡๊ฒŒ ๋ณด์ด๋Š” ํ”„๋ ˆ์ž„๋“ค์„ ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

frame 0 frame 1 frame 2 frame 3 frame 4 frame 5

๋น„๋””์˜ค ํ”Œ๋ ˆ์ด์–ด๋ฅผ ๋””์ž์ธ ํ• ๋•Œ ๊ฐ ํ”„๋ ˆ์ž„์„ ์ฃผ์–ด์ง„ ์†๋„์— ์žฌ์ƒํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค, ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋„ˆ๋ฌด ๋น ๋ฅด๊ฑฐ๋‚˜ ๋„ˆ๋ฌด ๋А๋ฆฌ๊ฒŒ ์žฌ์ƒ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„๋””์˜ค๋ฅผ ์ œ๋Œ€๋กœ ์ฆ๊ธฐ๊ธฐ ์–ด๋ ค์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋ญ”๊ฐ€ ํ”„๋ ˆ์ž„์„ ์›ํ™œํ•˜๊ฒŒ ์žฌ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋กœ์ง์„ ์†Œ๊ฐœํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ด์Šˆ๋ฅผ ์œ„ํ•ด, ๊ฐ ํ”„๋ ˆ์ž„์€ ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ํƒ€์ž„์Šคํƒฌํ”„ (PTS)๋ฅผ ๊ฐ–๊ฒŒ ๋˜๋Š”๋ฐ ์ด๊ฒƒ์€ ํ”„๋ ˆ์ž„์†๋„(fps) ๋กœ ๋‚˜๋ˆ„์–ด์ง€๋Š” ํƒ€์ž„๋ฒ ์ด์Šค(timebase) ๋ผ๊ณ  ํ•˜๋Š” ์œ ๋ฆฌ์ˆ˜(๋ถ„๋ชจ๊ฐ€ ํƒ€์ž„์Šค์ผ€์ผ(timescale) ๋กœ ์•Œ๋ ค์ง„)๋กœ ๊ตฌ์„ฑ๋œ(factored) ์ฆ๊ฐ€ํ•˜๋Š” ์ˆซ์ž์ž…๋‹ˆ๋‹ค.

์˜ˆ์ œ๋ฅผ ์ข€ ๋ณธ๋‹ค๋ฉด ์ดํ•ด๊ฐ€ ๋” ์‰ฌ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค, ๋ช‡๊ฐœ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•ด์ฃ .

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

๋™์ผํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์ง€๋งŒ ํƒ€์ž„๋ฒ ์ด์Šค๊ฐ€ 1/60์ด๋ผ๋ฉด.

  • frame=0, PTS = 0, PTS_TIME = 0
  • frame=1, PTS = 1, PTS_TIME = PTS * timebase = 0.016
  • frame=2, PTS = 2, PTS_TIME = PTS * timebase = 0.033
  • frame=3, PTS = 3, PTS_TIME = PTS * timebase = 0.050

fps=25/1์™€ timebase=1/75์— ๋Œ€ํ•ด์„œ๋Š” ๊ฐ PTS๋Š” timescale / fps = 3๋งŒํผ ์ฆ๊ฐ€ํ•  ๊ฒƒ์ด๊ณ  PTS ์‹œ๊ฐ„์€ ์ด๋ ‡๊ฒŒ ๋  ๊ฒƒ ์ž…๋‹ˆ๋‹ค:

  • frame=0, PTS = 0, PTS_TIME = 0
  • frame=1, PTS = 3, PTS_TIME = PTS * timebase = 0.04
  • frame=2, PTS = 6, PTS_TIME = PTS * timebase = 0.08
  • frame=3, PTS = 9, PTS_TIME = PTS * timebase = 0.12
  • ...
  • frame=24, PTS = 72, PTS_TIME = PTS * timebase = 0.96
  • ...
  • frame=4064, PTS = 12192, PTS_TIME = PTS * timebase = 162.56

์ด์ œ ์ด pts_time์œผ๋กœ ์˜ค๋””์˜ค์˜ pts_time ํ˜น์€ ์‹œ์Šคํ…œ ์‹œ๊ฐ„๊ณผ ๋™๊ธฐํ™”ํ•ด์„œ ์žฌ์ƒํ•  ๋ฐฉ๋ฒ•์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. FFmpeg libav๋Š” ๊ทธ ์ •๋ณด๋“ค์„ ์•„๋ž˜ API๋ฅผ ํ†ตํ•ด ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํ˜ธ๊ธฐ์‹ฌ์— ๋ณด์ž๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ์ €์žฅํ–ˆ๋˜ ํ”„๋ ˆ์ž„๋“ค์„ DTS ์ˆœ์œผ๋กœ (frames: 1,6,4,2,3,5) ๋ณด๋‚ด์กŒ์ง€๋งŒ ์žฌ์ƒ์€ PTS ์ˆœ (frames: 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์€ ํ•˜๋‚˜์˜ ํฌ๋งท (์ปจํ…Œ์ด๋„ˆ)์—์„œ ๋‹ค๋ฅธ ๊ฒƒ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ์ฒ˜๋Ÿผ FFmpeg์„ ์“ฐ๋ฉด ๋ณ„๋กœ ์–ด๋ ต์ง€ ์•Š๊ฒŒ MPEG-4 ๋น„๋””์˜ค๋ฅผ MPEG-TS๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

ffmpeg input.mp4 -c copy output.ts

์ด๊ฒƒ์€ mp4๋ฅผ demuxํ•˜์ง€๋งŒ ๋””์ฝ”๋”ฉ์ด๋‚˜ ์ธ์ฝ”๋”ฉ์€ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. (-c copy) ์ตœ์ข…์ ์œผ๋กœ mpegts ํŒŒ์ผ๋กœ muxํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํฌ๋งท์„ ์˜๋ฏธํ•˜๋Š” -f๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์œผ๋ฉด ffmpeg์€ ํŒŒ์ผ ํ™•์žฅ์ž๋กœ ํฌ๋งท์„ ์ถ”์ธกํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

FFmpeg ํ˜น์€ libav์˜ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ๋ฒ•์€ ์•„๋ž˜ ํŒจํ„ด/์•„ํ‚คํ…์ฒ˜ ๋˜๋Š” ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค:

ffmpeg libav workflow

์ด ๊ทธ๋ž˜ํ”„๋Š” Leixiaohua's์™€ Slhck's์˜ ์ž‘์—…์œผ๋กœ๋ถ€ํ„ฐ ํฐ ์˜๊ฐ์„ ๋ฐ›์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ž ์ด์ œ ffmpeg input.mp4 -c copy output.ts์™€ ๋™์ผํ•œ ํšจ๊ณผ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋„๋ก libav ๋ฅผ ์ด์šฉํ•œ ์˜ˆ์ œ๋ฅผ ํ•˜๋‚˜ ๊ตฌํ˜„ํ•ด๋ด…์‹œ๋‹ค.

์ž…๋ ฅ (input_format_context)์œผ๋กœ๋ถ€ํ„ฐ ์ฝ์€ ๊ฒƒ์„ ๋‹ค๋ฅธ ์ถœ๋ ฅ (output_format_context)์œผ๋กœ ๋ณ€ํ™˜ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

AVFormatContext *input_format_context = NULL;
AVFormatContext *output_format_context = NULL;

์ผ๋ฐ˜์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์„ ์‹œ์ž‘ํ•˜๊ณ  ์ž…๋ ฅ ํฌ๋งท์„ ์—ฝ๋‹ˆ๋‹ค. ์ด๋ฒˆ๊ฐ™์€ ํŠน์ •ํ•œ ๊ฒฝ์šฐ์—๋Š”, ์ž…๋ ฅ ํŒŒ์ผ์„ ์—ด๊ณ ๋‚˜์„œ ์ถœ๋ ฅ ํŒŒ์ผ์„ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

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;
}

๋น„๋””์˜ค, ์˜ค๋””์˜ค, ์ž๋ง‰ ํƒ€์ž…์˜ ์ŠคํŠธ๋ฆผ๋งŒ remuxํ•  ๊ฒƒ์ด๋ฉฐ ์‚ฌ์šฉํ•˜๊ฒŒ๋  ์ŠคํŠธ๋ฆผ์„ ์ธ๋ฑ์Šค ๋ฐฐ์—ด์— ๋“ค๊ณ  ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค.

number_of_streams = input_format_context->nb_streams;
streams_list = av_mallocz_array(number_of_streams, sizeof(*streams_list));

ํ•„์š”ํ•œ๋งŒํผ์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•œ ํ›„, ๋ชจ๋“  ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•ด ๊ฐ๊ฐ ๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ avformat_new_stream ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ถœ๋ ฅ ํฌ๋งท ์ปจํ…์ŠคํŠธ์—๋‹ค๊ฐ€ ์ƒˆ๋กœ์šด ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋น„๋””์˜ค, ์˜ค๋””์˜ค, ์ž๋ง‰์ด ์•„๋‹Œ ๋ชจ๋“  ์ŠคํŠธ๋ฆผ๋“ค์— ๋Œ€ํ•ด์„œ๋Š” ๋งˆํ‚น์„ ํ•ด์„œ ๋‚˜์ค‘์— ์Šคํ‚ตํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

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;
  }
}

์ด์ œ ์ถœ๋ ฅ ํŒŒ์ผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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) ์”๋‹ˆ๋‹ค.

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];
  /* copy packet */
  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 ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ŠคํŠธ๋ฆผ ํŠธ๋ ˆ์ผ๋Ÿฌ(trailer)๋ฅผ ์ถœ๋ ฅ ๋ฏธ๋””์–ด ํŒŒ์ผ์— ์”๋‹ˆ๋‹ค.

av_write_trailer(output_format_context);

์ด์ œ ํ…Œ์ŠคํŠธํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ฒซ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ๋Š” MP4์—์„œ MPEG-TS ๋น„๋””์˜ค ํŒŒ์ผ๋กœ์˜ ํฌ๋งท (๋น„๋””์˜ค ์ปจํ…Œ์ด๋„ˆ) ๋ณ€ํ™˜์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ffmpeg input.mp4 -c copy output.ts ๋ช…๋ น์ค„์„ libav๋ฅผ ์ด์šฉํ•ด ๋งŒ๋“  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

make run_remuxing_ts

๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค!!! ์ ˆ ๋ฏฟ์ง€ ์•Š์•˜๋‚˜์š”?! ๊ทธ๋Ÿฌ์‹œ๋ฉด ์•ˆ๋˜์ฃ , ffprobe๋กœ ํ•œ๋ฒˆ ํ™•์ธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

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์˜ ๋™์ž‘ ๋ฐฉ์‹์— ๋Œ€ํ•œ ์•„์ด๋””์–ด๋ฅผ ๋‹ค์‹œ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๋ฉด ์ฝ”๋ฑ ๋ถ€๋ถ„๋งŒ ๊ฑด๋„ˆ๋›ด๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

remuxing libav components

์ด ์ฑ•ํ„ฐ๋ฅผ ๋๋‚ด๊ธฐ ์ „์— ๋ฆฌ๋จน์‹ฑ(remuxing) ํ”„๋กœ์„ธ์Šค์˜ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์„ ๋ณด์—ฌ๋“œ๋ฆฌ๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค, muxer์— ์˜ต์…˜์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ธ๋ฐ์š”. ๋งŒ์•ฝ์— ์ „์†ก์„ MPEG-DASH ํฌ๋งท์œผ๋กœ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด MPEG-TS๋‚˜ ๊ธฐ๋ณธ MPEG-4 ๋Œ€์‹  (fmp4๋ผ๊ณ  ๋ถ€๋ฅด๋Š”) fragmented mp4๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋ช…๋ น์ค„๋กœ๋Š” ์ด๋ ‡๊ฒŒ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ffmpeg -i non_fragmented.mp4 -movflags frag_keyframe+empty_moov+default_base_moof fragmented.mp4

libav ๋ฒ„์ „๋„ ๊ฑฐ์˜ ๋ช…๋ น์ค„ ๋งŒํผ์ด๋‚˜ ์‰ฝ์Šต๋‹ˆ๋‹ค. ํŒจํ‚ท์„ ๋ณต์‚ฌํ•˜๊ธฐ ๋ฐ”๋กœ ์ „, ์ถœ๋ ฅ ํ—ค๋”๋ฅผ ์“ธ๋•Œ ํ•ด๋‹น ์˜ต์…˜์„ ๋„˜๊ฒจ์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

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 ํŒŒ์ผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

make run_remuxing_fragmented_mp4

์ œ๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„๊ป˜ ๊ฑฐ์ง“๋งํ•˜๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋Š”๊ฑธ ๋ณด์—ฌ๋“œ๋ฆฌ์ฃ . ๊ฒฐ๊ณผ๋ฌผ์˜ ์ฐจ์ด๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด gpac/mp4box.js ํ˜น์€ http://mp4parser.com/ ๊ฐ™์€ ์•„์ฃผ ํ›Œ๋ฅญํ•œ ์‚ฌ์ดํŠธ/ํˆด์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋‹จ "common" mp4 ํŒŒ์ผ์„ ๋กœ๋“œํ•ด๋ณด์„ธ์š”.

mp4 boxes

๋ณด์‹œ๋‹ค์‹œํ”ผ ๋‹จ ํ•˜๋‚˜์˜ mdat ๋ฐ•์Šค(atom)๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค, ์—ฌ๊ธฐ์— ๋น„๋””์˜ค์™€ ์˜ค๋””์˜ค ํ”„๋ ˆ์ž„์ด ๋‹ด๊ฒจ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ์—” fragmented mp4๋ฅผ ๋กœ๋“œํ•ด์„œ mdat ๋ฐ•์Šค๊ฐ€ ์–ด๋–ป๊ฒŒ ํฉ์–ด์ ธ์žˆ๋Š”์ง€ ๋ณด์‹œ๊ฒ ์Šต๋‹ˆ๋‹ค.

fragmented mp4 boxes

์ฑ•ํ„ฐ 3 - ํŠธ๋žœ์Šค์ฝ”๋”ฉ (transcoding)

TLDR; ์ฝ”๋“œ๋ž‘ ์‹คํ–‰ํ•˜๋Š”๊ฑฐ๋‚˜ ๋ณด์—ฌ์ฃผ์„ธ์š”.

$ make run_transcoding

์ข€ ์ƒ์„ธํ•œ ๋ถ€๋ถ„์€ ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค, ๊ทธ๋Ÿฌ๋‚˜ ๊ฑฑ์ •ํ•˜์ง„ ๋งˆ์„ธ์š”: ์†Œ์Šค ์ฝ”๋“œ๋Š” github์— ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ์ฑ•ํ„ฐ์—์„œ๋Š” ์•„์ฃผ ๊ฐ„๋‹จํ•œ ํŠธ๋žœ์Šค์ฝ”๋”๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค, C๋กœ ์ž‘์„ฑํ•  ๊ฒƒ์ด๊ณ , ์ด๊ฒƒ์œผ๋กœ H264๋กœ ์ธ์ฝ”๋”ฉ๋œ ๋น„๋””์˜ค๋ฅผ H265๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค. FFmpeg/libav ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ํŠนํžˆ libavcodec, libavformat, libavutil๋ฅผ ์ด์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

media transcoding flow

๋น ๋ฅด๊ฒŒ ๋ณต์Šตํ•ด๋ณด๋ฉด: AVFormatContext๋Š” ์ปจํ…Œ์ด๋„ˆ (ex: MKV, MP4, Webm, TS) ๊ฐ™์€ ๋ฏธ๋””์–ด ํŒŒ์ผ ํฌ๋งท์— ๋Œ€ํ•œ ์ถ”์ƒํ™” ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. AVStream๋Š” ์ฃผ์–ด์ง„ ํฌ๋งท (ex: ์˜ค๋””์˜ค, ๋น„๋””์˜ค, ์ž๋ง‰, ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ)์— ๋Œ€ํ•œ ๊ฐ ๋ฐ์ดํ„ฐ ์œ ํ˜•์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. AVPacket์€ AVStream์œผ๋กœ๋ถ€ํ„ฐ ์–ป์–ด์ง„ ์••์ถ•๋œ ๋ฐ์ดํ„ฐ์˜ ์กฐ๊ฐ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ AVCodec (ex: av1, h264, vp9, hevc)์— ์˜ํ•ด ๋””์ฝ”๋”ฉ๋˜์–ด AVFrame๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” raw ๋ฐ์ดํ„ฐ๋กœ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค.

ํŠธ๋žœ์Šค๋จน์‹ฑ (Transmuxing)

๊ฐ„๋‹จํ•œ ํŠธ๋žœ์Šค๋จน์‹ฑ ์ž‘์—…์„ ์‹œ์ž‘ํ•ด๋ด…์‹œ๋‹ค. ๊ทธ๋ฆฌ๊ณ ๋‚˜์„œ ์ด ์ฝ”๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค. ์ฒซ๋ฒˆ์งธ ๋‹จ๊ณ„๋Š” ์ž…๋ ฅ ํŒŒ์ผ ๋กœ๋“œํ•˜๊ธฐ์ž…๋‹ˆ๋‹ค.

// Allocate an AVFormatContext
avfc = avformat_alloc_context();
// Open an input stream and read the header.
avformat_open_input(avfc, in_filename, NULL, NULL);
// Read packets of a media file to get stream information.
avformat_find_stream_info(avfc, NULL);

์ด์ œ ๋””์ฝ”๋”๋ฅผ ์„ค์ •ํ•  ๊ฒƒ์ธ๋ฐ, AVFormatContext๊ฐ€ ๋ชจ๋“  AVStream ์ปดํฌ๋„ŒํŠธ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ๊ฐ์˜ ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•ด์„œ, AVCodec์„ ๊ฐ€์ ธ์™€์„œ ํŠน์ • AVCodecContext๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ์ฃผ์–ด์ง„ ์ฝ”๋ฑ์„ ์—ด๊ฒŒ๋˜๊ณ  ๋””์ฝ”๋”ฉ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

AVCodecContext๋Š” ๋น„ํŠธ๋ ˆ์ดํŠธ, ํ”„๋ ˆ์ž„ ์†๋„, ์ƒ˜ํ”Œ๋ ˆ์ดํŠธ, ์ฑ„๋„, ๋†’์ด ๋“ฑ๊ณผ ๊ฐ™์€ ๋ฏธ๋””์–ด ์„ค์ •์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

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 ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ถœ๋ ฅ์œผ๋กœ ์“ฐ๊ธฐ ์œ„ํ•œ ํŒŒ์ผ์„ ์—ด๊ณ  ํ—ค๋”๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

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 ์ด ํ•จ์ˆ˜ ์ด๋ฆ„์ด "write frame"๋ผ๊ณ  ๋˜์–ด์žˆ๊ธด ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ ํŒจํ‚ท์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค . ์ด์ œ ํŒŒ์ผ์— ์ŠคํŠธ๋ฆผ ํŠธ๋ ˆ์ผ๋Ÿฌ๋ฅผ ์“ฐ๋ฉด์„œ ํŠธ๋žœ์Šค๋จน์‹ฑ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

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
  • libx265๋ผ๊ณ  ํ•˜๋Š” AVCodec์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค, avcodec_find_encoder_by_name
  • ์ƒ์„ฑํ•œ ์ฝ”๋ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ AVCodecContext๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค,avcodec_alloc_context3
  • ํŠธ๋žœ์Šค์ฝ”๋”ฉ ์„ธ์…˜์— ๋Œ€ํ•ด ๊ธฐ๋ณธ์ ์ธ ์†์„ฑ๋“ค์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค, ๊ทธ๋ฆฌ๊ณ 
  • ์ฝ”๋ฑ์„ ์—ด๊ณ  ์ปจํ…์ŠคํŠธ์—์„œ ์ŠคํŠธ๋ฆผ์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. avcodec_open2, avcodec_parameters_from_context
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";
// we're going to use internal options for the x265
// it disables the scene change detection and fix then
// GOP on 60 frames.
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);
// encoder codec params
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];
// control rate
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
  • ์••์ถ•์ด ํ•ด์ œ๋œ AVFrame๋ฅผ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค, avcodec_receive_frame
  • ์ด raw ํ”„๋ ˆ์ž„์˜ ํŠธ๋žœ์Šค์ฝ”๋”ฉ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค,
  • raw ํ”„๋ ˆ์ž„์„ (์ธ์ฝ”๋”์—) ๋ณด๋‚ด๊ณ , avcodec_send_frame
  • ์ฝ”๋ฑ์— ๋งž๊ฒŒ ์••์ถ•๋œ AVPacket์„ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค, avcodec_receive_packet
  • ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ์„ค์ •ํ•˜๊ณ , av_packet_rescale_ts
  • ํŒจํ‚ท์„ ์ถœ๋ ฅ ํŒŒ์ผ์— ์”๋‹ˆ๋‹ค. av_interleaved_write_frame
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);

// used function
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;
}

์•„์‹œ๋‹ค์‹œํ”ผ h265 ๋ฒ„์ „์˜ ๋ฏธ๋””์–ด ํŒŒ์ผ์ด h264๋ณด๋‹ค ์‚ฌ์ด์ฆˆ๊ฐ€ ์ž‘๊ธฐ ๋•Œ๋ฌธ์— ๋ฏธ๋””์–ด ์ŠคํŠธ๋ฆผ์„ h264์—์„œ h265๋กœ ๋ณ€ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ž‘์„ฑํ•œ ํ”„๋กœ๊ทธ๋žจ์€ ๋‹ค์Œ์˜ ์ž‘์—…๋“ค๋„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:


  /*
   * H264 -> H265
   * Audio -> remuxed (untouched)
   * 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 (fixed gop)
   * Audio -> remuxed (untouched)
   * 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 (fixed gop)
   * Audio -> remuxed (untouched)
   * 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 (fixed 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";

  /* WIP :P  -> it's not playing on VLC, the final bit rate is huge
   * 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";

์ด์ œ์„œ์•ผ ์†”์งํžˆ ๋งํ•˜์ž๋ฉด, ์ œ๊ฐ€ ์ƒ๊ฐํ–ˆ๋˜ ๊ฒƒ๋ณด๋‹ค ๋” ์‚ฝ์งˆํ–ˆ๋Š”๋ฐ์š”. FFmpeg ๋ช…๋ น์ค„ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ํŒŒ๋ด์•ผํ–ˆ๊ณ  ํ…Œ์ŠคํŠธ๋„ ์—„์ฒญ ๋Œ๋ ค๋ดค์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ œ๊ฐ€ ๋ญ”๊ฐ€ ๋†“์น˜๋Š”๊ฒŒ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ์š”, ์™œ๋ƒํ•˜๋ฉด force-cfr์„ ๊ฐ•์ œ๋กœ ๋„ฃ์–ด์ค˜์•ผ์ง€๋งŒ h264๊ฐ€ ์ž‘์šฉํ•˜๊ณ  warning messages (forced frame type (5) at 80 was changed to frame type (3)) ๊ฐ™์€ ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€๋„ ์—ฌ์ „ํžˆ ๋‚˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

๊ด€๋ จ ํ”„๋กœ์ ํŠธ ์‚ดํŽด๋ณด๊ธฐ