leandromoreira/ffmpeg-libav-tutorial
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
OriginalFFmpeg์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ฒ๋ผ(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)๋ฅผ ์ด์ฉํ์ฌ - ์๋ ๋ก๊ทธ ์ ํธ๋ฅผ ๋์งํ ์ ํธ๋ก ๋ณํํฉ๋๋ค.
![]()
์ฝ๋ฑ - ๋ฐ์ดํฐ๋ฅผ ์ค์ด๊ธฐ
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๋ฅผ ์ปจํ
์ด๋๋ก ์ฌ์ฉํ๋ ๋น๋์ค๊ฒ ์ฃ .

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}, ์ฌ๊ธฐ์:
- ์ ์ญ ์ต์
- ์ ๋ ฅ ํ์ผ ์ต์
- ์ ๋ ฅ url
- ์ถ๋ ฅ ํ์ผ ์ต์
- ์ถ๋ ฅ 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)

๋ฌด์์ธ๊ฐ? ์คํธ๋ฆผ (์ค๋์ค ๋๋ ๋น๋์ค) ์ค์ ํ๋๋ฅผ ๊ธฐ์กด ์ฝ๋ฑ์์ ๋ค๋ฅธ ์ฝ๋ฑ์ผ๋ก ๋ณํํ๋ ์์ .
์? ๊ฐ๋ ์ด๋ค ์ฅ์น๋ค์ (ํ ๋ ๋น์ , ์ค๋งํธํฐ, ์ฝ์ ๋ฑ) X๋ ์ง์ํ์ง ์์ง๋ง Y๋ฅผ ์ง์ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ์๋ก์ด ์ฝ๋ฑ๋ค์ ๋ ๋์ ์์ถ๋ฅ ์ ์ ๊ณตํ๊ธฐ๋ ํฉ๋๋ค.
์ด๋ป๊ฒ? H264 (AVC) ๋น๋์ค๋ฅผ H265 (HEVC)๋ก ๋ณํํ๊ธฐ.
$ ffmpeg \
-i bunny_1080p_60fps.mp4 \
-c:v libx265 \
bunny_1080p_60fps_h265.mp4
ํธ๋์ค๋จน์ฑ (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)

๋ฌด์์ธ๊ฐ? ๋นํธ๋ ์ดํธ๋ฅผ ๋ณํํ๊ฑฐ๋ ๋ค๋ฅธ ๋ณํ๋ณธ(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)์ ์ฃผ๋ก ํธ๋์ค๋ ์ดํ (transrating)๊ณผ ํจ๊ป ์ฌ์ฉ๋ฉ๋๋ค.
์? ํธ๋์ค๋ ์ดํ (transrating)์์์ ์ด์ ์ ๋์ผํจ.
์ด๋ป๊ฒ? 1080p์ ํด์๋๋ฅผ 480p๋ก ๋ณํํ๊ธฐ.
$ ffmpeg \
-i bunny_1080p_60fps.mp4 \
-vf scale=480:-1 \
bunny_1080p_60fps_transsizing_480.mp4
๋ณด๋์ค: ์ ์ํ ์คํธ๋ฆฌ๋ฐ (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 ์ํคํ ์ฒ๊ฐ ์ด๋ป๊ฒ ๋์ํ๋์ง ์ด๊ฒ๋ค์ ์ปดํฌ๋ํธ๋ค์ด ์๋ก ์ด๋ป๊ฒ ํต์ ํ๋์ง๋ฅผ ๋ฐฐ์๋ด ์๋ค.
์ฌ๊ธฐ ๋น๋์ค๊ฐ ๋์ฝ๋ฉ๋๋ ํ๋ก์ธ์ค๋ฅผ ๋ด์ ๋ค์ด์ด๊ทธ๋จ์ด ํ๋ ์์ต๋๋ค.

์ฐ์ ์ฌ๋ฌ๋ถ์ ๋ฏธ๋์ด ํ์ผ์ 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์ง๋ฆฌ ํ๋ฐฑ ์ด๋ฏธ์ง๋ฅผ ์ป์ด๋์ต๋๋ค:

์ฑํฐ 1 - ์ค๋์ค์ ๋น๋์ค ๋๊ธฐํ
ํ๋ ์ด์ด๊ฐ ๋์ธ์ - ์ ๊ท MSE ๋น๋์ค ํ๋ ์ด์ด๋ฅผ ์์ฑ ์ค์ธ ์ ์ JS ๊ฐ๋ฐ์
ํธ๋์ค์ฝ๋ฉ ์์ ์ฝ๋๋ก ๋์ด๊ฐ๊ธฐ ์ ์ ํ์ด๋ฐ ํน์ ์ด๋ป๊ฒ ๋น๋์ค ํ๋ ์ด์ด๊ฐ ํ๋์ ํ๋ ์์ ์ ์๊ฐ์ ์ฌ์ํด์ผํ๋์ง์ ๋ํด์ ์ด์ผ๊ธฐํด๋ด ์๋ค.
์ง๋ ์์ ์์, ์ฐ๋ฆฌ๋ ์ด๋ ๊ฒ ๋ณด์ด๋ ํ๋ ์๋ค์ ์ ์ฅํ์ต๋๋ค.

๋น๋์ค ํ๋ ์ด์ด๋ฅผ ๋์์ธ ํ ๋ ๊ฐ ํ๋ ์์ ์ฃผ์ด์ง ์๋์ ์ฌ์ํด์ผํฉ๋๋ค, ๊ทธ๋ ์ง ์์ผ๋ฉด ๋๋ฌด ๋น ๋ฅด๊ฑฐ๋ ๋๋ฌด ๋๋ฆฌ๊ฒ ์ฌ์๋๊ธฐ ๋๋ฌธ์ ๋น๋์ค๋ฅผ ์ ๋๋ก ์ฆ๊ธฐ๊ธฐ ์ด๋ ค์ธ ๊ฒ์ ๋๋ค.
๊ทธ๋์ ๋ญ๊ฐ ํ๋ ์์ ์ํํ๊ฒ ์ฌ์ํ ์ ์๋ ๋ก์ง์ ์๊ฐํ ํ์๊ฐ ์์ต๋๋ค. ์ด ์ด์๋ฅผ ์ํด, ๊ฐ ํ๋ ์์ ํ๋ฆฌ์ ํ ์ด์ ํ์์คํฌํ (PTS)๋ฅผ ๊ฐ๊ฒ ๋๋๋ฐ ์ด๊ฒ์ ํ๋ ์์๋(fps) ๋ก ๋๋์ด์ง๋ ํ์๋ฒ ์ด์ค(timebase) ๋ผ๊ณ ํ๋ ์ ๋ฆฌ์(๋ถ๋ชจ๊ฐ ํ์์ค์ผ์ผ(timescale) ๋ก ์๋ ค์ง)๋ก ๊ตฌ์ฑ๋(factored) ์ฆ๊ฐํ๋ ์ซ์์ ๋๋ค.
์์ ๋ฅผ ์ข ๋ณธ๋ค๋ฉด ์ดํด๊ฐ ๋ ์ฌ์ธ ๊ฒ์ ๋๋ค, ๋ช๊ฐ์ ์๋๋ฆฌ์ค๋ฅผ ์๋ฎฌ๋ ์ด์ ํด์ฃ .
fps=60/1 ์ด๊ณ timebase=1/60000 ๋ผ๋ฉด ๊ฐ PTS๋ timescale / fps = 1000๋ฅผ ์ฆ๊ฐํ ๊ฒ ์
๋๋ค. ๊ทธ๋์ ๊ฐ ํ๋ ์์ PTS ์ค์ ์๊ฐ์ ์ด๋ ๊ฒ ๋ฉ๋๋ค (0๋ถํฐ ์์ํ๋ค๊ณ ํ๋ฉด):
frame=0, PTS = 0, PTS_TIME = 0frame=1, PTS = 1000, PTS_TIME = PTS * timebase = 0.016frame=2, PTS = 2000, PTS_TIME = PTS * timebase = 0.033
๋์ผํ ์๋๋ฆฌ์ค์ง๋ง ํ์๋ฒ ์ด์ค๊ฐ 1/60์ด๋ผ๋ฉด.
frame=0, PTS = 0, PTS_TIME = 0frame=1, PTS = 1, PTS_TIME = PTS * timebase = 0.016frame=2, PTS = 2, PTS_TIME = PTS * timebase = 0.033frame=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 = 0frame=1, PTS = 3, PTS_TIME = PTS * timebase = 0.04frame=2, PTS = 6, PTS_TIME = PTS * timebase = 0.08frame=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๋ฅผ ํตํด ์ ๊ณตํฉ๋๋ค.
- fps =
AVStream->avg_frame_rate - tbr =
AVStream->r_frame_rate - tbn =
AVStream->time_base
ํธ๊ธฐ์ฌ์ ๋ณด์๋ฉด, ์ฐ๋ฆฌ๊ฐ ์ ์ฅํ๋ ํ๋ ์๋ค์ 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์ ์ผ๋ฐ์ ์ธ ์ฌ์ฉ๋ฒ์ ์๋ ํจํด/์ํคํ ์ฒ ๋๋ ์ํฌํ๋ก์ฐ๋ฅผ ๋ฐ๋ฆ ๋๋ค:
- ํ๋กํ ์ฝ ๋ ์ด์ด -
input์ ๋ฐ์ (์๋ฅผ๋ค๋ฉดfile์ด์ง๋งrtmp๋๋HTTP์ ๋ ฅ๋ ๊ฐ๋ฅ). - ํฌ๋งท ๋ ์ด์ด - ์ปจํ
์ธ ๋ฅผ
demuxes, ๋๋ถ๋ถ ๋ฉํ๋ฐ์ดํฐ์ ์คํธ๋ฆผ์ ์ด์ด๋ด - ์ฝ๋ฑ ๋ ์ด์ด - ์์ถ๋ ์คํธ๋ฆผ ๋ฐ์ดํฐ๋ฅผ
decodesoptional - ํฝ์
๋ ์ด์ด - raw ํ๋ ์์ ๋ํด (๋ฆฌ์ฌ์ด์ง ๊ฐ์)
filters๋ฅผ ์ ์ฉํ ์๋ ์์ optional - and then it does the reverse path
- ์ฝ๋ฑ ๋ ์ด์ด - raw ํ๋ ์์
encodes(๋๋re-encodesํน์transcodes๊น์ง๋) optional - ํฌ๋งท ๋ ์ด์ด - raw ์คํธ๋ฆผ (์์ถ๋ ๋ฐ์ดํฐ)๋ฅผ
muxes(๋๋remuxes) - ํ๋กํ ์ฝ ๋ ์ด์ด - ๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก muxed๋ ๋ฐ์ดํฐ๋ฅผ
output์ผ๋ก ์ ์ก (๋๋ค๋ฅธ ํ์ผ ํน์ ๋คํธ์ํฌ ์๊ฒฉ ์๋ฒ์ผ ์๋ ์์)

์ด ๊ทธ๋ํ๋ 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) ํ๋ก์ธ์ค์ ์ค์ํ ๋ถ๋ถ์ ๋ณด์ฌ๋๋ฆฌ๊ณ ์ ํฉ๋๋ค, 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 ํ์ผ์ ๋ก๋ํด๋ณด์ธ์.

๋ณด์๋ค์ํผ ๋จ ํ๋์ mdat ๋ฐ์ค(atom)๊ฐ ์์ต๋๋ค, ์ฌ๊ธฐ์ ๋น๋์ค์ ์ค๋์ค ํ๋ ์์ด ๋ด๊ฒจ์์ต๋๋ค. ์ด๋ฒ์ fragmented mp4๋ฅผ ๋ก๋ํด์ mdat ๋ฐ์ค๊ฐ ์ด๋ป๊ฒ ํฉ์ด์ ธ์๋์ง ๋ณด์๊ฒ ์ต๋๋ค.

์ฑํฐ 3 - ํธ๋์ค์ฝ๋ฉ (transcoding)
TLDR; ์ฝ๋๋ ์คํํ๋๊ฑฐ๋ ๋ณด์ฌ์ฃผ์ธ์.
$ make run_transcoding์ข ์์ธํ ๋ถ๋ถ์ ๋์ด๊ฐ๊ฒ ์ต๋๋ค, ๊ทธ๋ฌ๋ ๊ฑฑ์ ํ์ง ๋ง์ธ์: ์์ค ์ฝ๋๋ github์ ์์ต๋๋ค.
์ด๋ฒ ์ฑํฐ์์๋ ์์ฃผ ๊ฐ๋จํ ํธ๋์ค์ฝ๋๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค, C๋ก ์์ฑํ ๊ฒ์ด๊ณ , ์ด๊ฒ์ผ๋ก H264๋ก ์ธ์ฝ๋ฉ๋ ๋น๋์ค๋ฅผ H265๋ก ๋ณํํ ์ ์์๊ฒ๋๋ค. FFmpeg/libav ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ํนํ libavcodec, libavformat, libavutil๋ฅผ ์ด์ฉํ๊ฒ ์ต๋๋ค.

๋น ๋ฅด๊ฒ ๋ณต์ตํด๋ณด๋ฉด: 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))๊ฐ์ ๊ฒฝ๊ณ ๋ฉ์์ง๋ ์ฌ์ ํ ๋๊ณ ์๊ธฐ ๋๋ฌธ์ด์ฃ .
๊ด๋ จ ํ๋ก์ ํธ ์ดํด๋ณด๊ธฐ
sindresorhus/awesome
๐ ์จ๊ฐ ํฅ๋ฏธ๋ก์ด ์ฃผ์ ์ ๊ดํ Awesome ๋ชฉ๋ก [์ฐธ๊ณ : ๊ธฐ์กด ํ ๋ฆฌํ์คํธ๋ฅผ ๋ฐ๋ผ์ก์ ์ฌ์ ๊ฐ ์๊ธธ ๋๊น์ง ํ ๋ฆฌํ์คํธ๊ฐ ์ผ์์ ์ผ๋ก ๋นํ์ฑํ๋์ด ์์ต๋๋ค]
533,855 ์คํEbookFoundation/free-programming-books
:books: ๋ฌด๋ฃ๋ก ์ด์ฉํ ์ ์๋ ํ๋ก๊ทธ๋๋ฐ ๋์
401,300 ์คํnilbuild/developer-roadmap
๊ฐ๋ฐ์์ ์ปค๋ฆฌ์ด ์ฑ์ฅ์ ๋๋ ์ธํฐ๋ํฐ๋ธ ๋ก๋๋งต, ๊ฐ์ด๋ ๋ฐ ๊ธฐํ ๊ต์ก ์ฝํ ์ธ .
371,566 ์คํvinta/awesome-python
โPython์ผ๋ก X๋ฅผ ํ๊ณ ์ถ์๋ฐ, ์ด๋ค ๋๊ตฌ๋ฅผ ์จ์ผ ํ ๊น?โ์ ๋ตํ๋ ๊ฒฐ์ ํ ๋ชฉ๋ก.
333,435 ์คํ