Skip to content

๐Ÿš€ HLS ํ”„๋กœํ† ์ฝœ์— ๊ด€ํ•œ ์ •๋ฆฌ ๋ฐ FFmpeg ์‚ฌ์šฉ๊ธฐ

Yoonseo Han edited this page Dec 3, 2024 · 1 revision

HLS์˜ ์ฃผ์š” ํŠน์ง•

  • Apple์—์„œ ๋งŒ๋“  HTTP protocol ์œ„์— ๋™์ž‘ํ•˜๋Š” ๋น„๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ ํ”„๋กœํ† ์ฝœ
  • HTTP์˜ ํŠน์ง•๋“ค (ex: caching)์„ ์‚ฌ์šฉํ•  ์ˆ˜ ๊ฐ€ ์žˆ์Œ
  • ๋„คํŠธ์›Œํฌ ์ƒํƒœ์— ๋™์ ์œผ๋กœ ๋ฐ˜์‘ํ•˜์—ฌ ๋™์ž‘ ๊ธฐ๋Šฅ์„ adaptiveํ•˜๊ฒŒ ๋งž์ถ”์–ด์คŒ

์ „๋ฐ˜์ ์ธ ๋™์ž‘ ํ๋ฆ„ ์ •๋ฆฌ ๋ฐ ๊ตฌ์กฐ

image

์ถœ์ฒ˜: https://developer.apple.com/streaming/

HLS์™€ ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”„๋กœํ† ์ฝœ๊ณผ์˜ ๋น„๊ต

ํ”„๋กœํ† ์ฝœ ์ง€์—ฐ์‹œ๊ฐ„ ํ™•์žฅ์„ฑ CDN ํ˜ธํ™˜์„ฑ ์ ์‘ํ˜• ์ŠคํŠธ๋ฆฌ๋ฐ ๋””๋ฐ”์ด์Šค ํ˜ธํ™˜์„ฑ
HLS ์ค‘๊ฐ„(2-30์ดˆ) ๋งค์šฐ ๋†’์Œ ๋งค์šฐ ์ข‹์Œ ์ง€์› ๋งค์šฐ ๋†’์Œ
DASH ์ค‘๊ฐ„(2-30์ดˆ) ๋†’์Œ ์ข‹์Œ ์ง€์› ๋†’์Œ
RTMP ๋‚ฎ์Œ(1-5์ดˆ) ๋‚ฎ์Œ ์ œํ•œ์  ๋ฏธ์ง€์› ์ œํ•œ์ 

HLS์™€ ์‹ค์‹œ๊ฐ„ ์˜จ๋ผ์ธ ์ŠคํŠธ๋ฆฌ๋ฐ์˜ ์ ํ•ฉ์„ฑ

์žฅ์  ์„ค๋ช…
HTTP ๊ธฐ๋ฐ˜ ์ „์†ก - ๊ธฐ์กด ์›น ์ธํ”„๋ผ ํ™œ์šฉ ๊ฐ€๋Šฅ
- ๋ฐฉํ™”๋ฒฝ ํ†ต๊ณผ ์šฉ์ด
- CDN ํ™•์žฅ์„ฑ ์šฐ์ˆ˜
์ ์‘ํ˜• ์ŠคํŠธ๋ฆฌ๋ฐ - ๋„คํŠธ์›Œํฌ ์ƒํƒœ์— ๋”ฐ๋ผ ํ’ˆ์งˆ ์ž๋™ ์กฐ์ ˆ
- ๋‹ค์–‘ํ•œ ๋Œ€์—ญํญ ํ™˜๊ฒฝ ์ง€์›
๋†’์€ ์•ˆ์ •์„ฑ - ์„ธ๊ทธ๋จผํŠธ ๊ธฐ๋ฐ˜ ์ „์†ก์œผ๋กœ ์˜ค๋ฅ˜ ๋ณต์› ์šฉ์ด
- ๋ฒ„ํผ๋ง ์ตœ์†Œํ™”
๋””๋ฐ”์ด์Šค ํ˜ธํ™˜์„ฑ - iOS/Android ๋ชจ๋‘ ๋„ค์ดํ‹ฐ๋ธŒ ์ง€์›
- ๋Œ€๋ถ€๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ € ์ง€์›
ํ™•์žฅ์„ฑ - ์ˆ˜๋งŒ ๋ช…์˜ ๋™์‹œ ์‹œ์ฒญ์ž ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
- CDN ํ™œ์šฉ์œผ๋กœ ๊ธ€๋กœ๋ฒŒ ์„œ๋น„์Šค ์šฉ์ด

HLS ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ์‹คํ–‰์— ๊ด€ํ•œ ์ •๋ฆฌ

File extension์— ๊ด€ํ•œ ์ •๋ฆฌ

  • .m3u8: Segmented file์— ๊ด€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ธ๋ฑ์Šค ํŒŒ์ผ์˜ extension. ์ฃผ์š” ๋‚ด์šฉ์œผ๋กœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค
    • ๊ฐ .ts ํŒŒ์ผ์˜ URL โ†’ m3u8์„ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฝ๊ณ  ts ํŒŒ์ผ์— ๊ด€ํ•œ ์š”์ฒญ์„ ์ง„ํ–‰ํ•จ์œผ๋กœ ํ•ด๋‹น ๋‚ด์šฉ์€ ํ•„์ˆ˜์ !!!
    • ์˜์ƒ ํ’ˆ์งˆ/ํ•ด์ƒ๋„ ์ •๋ณด
    • ๋Œ€์—ญํญ ์ •๋ณด
  • .ts: ๋‚˜๋ˆ„์–ด์ง„ content๋ฅผ ๊ฐ์ž ์ €์žฅํ•˜๋Š” ํŒŒ์ผ
    • ๋ณดํ†ต ๋ช‡ ์ดˆ ๋‹จ์œ„๋กœ ๋ถ„ํ• ๋จ(์ผ๋ฐ˜์ ์œผ๋กœ 5-10์ดˆ)
    • ๋…๋ฆฝ์ ์œผ๋กœ ์žฌ์ƒ ๊ฐ€๋Šฅ
    • ๋„คํŠธ์›Œํฌ ์ƒํƒœ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํ’ˆ์งˆ์˜ ์กฐ๊ฐ์œผ๋กœ ์ „ํ™˜ ๊ฐ€๋Šฅ
    • ๋ฒ„ํผ๋ง ์—†์ด ๋Š๊น€ ์—†๋Š” ์žฌ์ƒ ๊ฐ€๋Šฅ

Express์—์„œ ํ•„์š”ํ•œ ํŒจํ‚ค์ง€ ์ •๋ฆฌ

yarn install fluent-ffmpeg @ffmpeg-installer/ffmpeg
yarn install hls-server

์ธ๋ฑ์Šค ํŒŒ์ผ๊ณผ ts ํŒŒ์ผ generation ์ˆœ์„œ

ffmpeg๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ HLS ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ ๋ฐ index file ์ƒ์„ฑ์„ ์ง€์›. ffmpeg ์—ญํ• :

  • ์›๋ณธ ๋น„๋””์˜ค/์˜ค๋””์˜ค๋ฅผ HLS ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜
  • .ts ์„ธ๊ทธ๋จผํŠธ ํŒŒ์ผ๋“ค๋กœ ๋ถ„ํ• 
  • .m3u8 ์žฌ์ƒ๋ชฉ๋ก ํŒŒ์ผ ์ƒ์„ฑ
  • ํ•„์š”์‹œ ์ปจํ…์ธ  ์•”ํ˜ธํ™”

HLS ํ…Œ์ŠคํŠธ ์ง„ํ–‰

ํ…Œ์ŠคํŠธ ์ง„ํ–‰ ๋ชฉ์ 

  • ์„œ๋ฒ„์—์„œ ์ €์žฅํ•œ ์Œ์› ํŒŒ์ผ์„ HLS ์–‘์‹์— ๋งž๊ฒŒ encryption์„ ์ง„ํ–‰ํ•˜๊ณ  ๋ฒ”์œ„์— ๋งž๋Š” segmentation์„ ์ง„ํ–‰ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด
  • ํด๋ผ์ด์–ธํŠธ์—์„œ ์š”์ฒญ์‹œ์— ์„œ๋ฒ„๊ฐ€ ๋ฐ˜ํ™˜์„ ๋ฌธ์ œ ์—†์ด ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด

FFmpeg ์ฝ”๋“œ : ํฌ๋งท ๋ณ€ํ™˜ ๋ฐ ํŒŒ์ผ ๋ถ„ํ•  ์ž‘์—… ์ง„ํ–‰

FfmpegService

async convertToHLS(inputPath: string, outputPath: string): Promise<void> {
  return new Promise((resolve, reject) => {
    ffmpeg(inputPath, { timeout: 432000 })
      .addOptions([
        '-c:a aac',
        '-b:a 128k',
        '-hls_time 10',
        '-hls_list_size 0',
        '-f hls',
      ])
      .output(outputPath)
      .on('progress', (progress) => {
        console.log('Processing: ', progress.percent, '% done');
      })
      .on('start', (commandLine) => {
        console.log('FFmpeg command:', commandLine);
      })
      .on('stderr', (stderrLine) => {
        console.log('FFmpeg stderr:', stderrLine);
      })
      .on('end', () => {
        console.log('Processing finished successfully');
        resolve();
      })
      .on('error', (err, stdout, stderr) => {
        console.error('FFmpeg Error:', err);
        console.error('FFmpeg stdout:', stdout);
        console.error('FFmpeg stderr:', stderr);
        reject(err);
      })
      .run();
  });
}
  • -c:a aac : audio codec AAC๋กœ ์„ค์ •
  • -b:a 128k: bit rate๋ฅผ 128k๋กœ ์„ค์ •
  • -hls_time 10: hls segment duration์„ 10์ดˆ๋กœ ์„ค์ •
  • -hls_list_size 0: maximum number of playlist entries๋ฅผ ์„ค์ • (0์€ ๋ฆฌ๋ฐ‹์ด ์—†๋‹ค๋Š” ๋œป)
  • -f hls: ์•„์›ƒ

์ถœ๋ ฅ๋˜๋Š” m3u8 ํŒŒ์ผ ํ˜•์‹ ๋ถ„์„

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.008778,
output0.ts
#EXTINF:10.008778,
output1.ts
#EXTINF:9.985556,
output2.ts
#EXTINF:10.008778,
output3.ts
#EXTINF:10.008778,
output4.ts
#EXTINF:9.985556,
output5.ts
#EXTINF:10.008778,
output6.ts
#EXTINF:10.008778,
output7.ts
#EXTINF:9.985556,
output8.ts
#EXTINF:8.061011,
output9.ts
#EXT-X-ENDLIST
  • EXTM3U: extended m3u file์ž„์„ ๋‚˜ํƒ€๋ƒ„
  • EXT-X-VERSION: ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ํŒŒ์ผ์˜ compatibility version์„ ์•Œ๋ ค์คŒ
  • EXT-X-TARGETDURATION: ๊ฐ ๋ฏธ๋””์–ด ํŒŒ์ผ์˜ duration ์ง€์ •
  • EXT-X-MEDIA-SEQUENCE:
    • ์ฒซ segment์˜ sequence number ํ‘œ์‹œ
    • VOD๋Š” 0์œผ๋กœ ํ‘œ์‹œ
  • #EXT-X-ENDLIST: ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ์ข…๋ฃŒ๋ฅผ ๋‚˜ํƒ€๋ƒ„

์ƒ์„ฑ๋œ ํŒŒ์ผ format:

image

inear

๊ธฐ์ˆ  ๊ณต์œ 

๐Ÿš€ ffmpeg๋Š” stderr๋กœ ๋””๋ฒ„๊น…์„ ํ•˜๋Š” ์ด์œ 
๐Ÿš€ HLS ํ”„๋กœํ† ์ฝœ์— ๊ด€ํ•œ ์ •๋ฆฌ ๋ฐ FFmpeg ์‚ฌ์šฉ๊ธฐ
๐Ÿš€ ๋น„ํŠธ๋Š” tsconfig.json์ด ์„ธ ๊ฐœ?
๐Ÿš€ NestJS ๊ธฐ๋ณธ ๊ฐœ๋… - Modules
๐Ÿš€ Socket.io ์ตœ(๊ฐ•)์ ํ™”
๐Ÿš€ ๋„์ปค์™€ nginx์˜ ์‚ฌ์šฉ๊ธฐ
๐Ÿš€ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด์ž

๊ฐœ๋ฐœ ์ผ์ง€

๐Ÿš€ FSD ์‚ฌ์šฉ๊ธฐ, ๊ทผ๋ฐ ์ด์ œ ๋‚˜๋งŒ์˜ ๊ทœ์น™์„ ๊ณ๋“ค์ธ
๐Ÿš€ CICD ๊ตฌ์กฐ ์ˆ˜์ •
๐Ÿš€ ์•จ๋ฒ” ๋‹จ์œ„๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ํ•˜๊ธฐ (with HLS)
๐Ÿš€ HLS๋กœ ์Œ์•… ์ฃผ๊ณ ๋ฐ›๊ธฐ
๐Ÿš€ vite + react + typescript ํ™˜๊ฒฝ์—์„œ path alias ์„ค์ •
๐Ÿš€ React Scan์ด ๋ญ์ฃ ?
๐Ÿš€ ๋กœ์ปฌ ํ™˜๊ฒฝ ๊ฐœ๋ฐœ ๋ชจ๋“œ ๋ฐฐํฌ
๐Ÿš€ ์•จ๋ฒ” ์ „์ฒด๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐํ•œ๋‹ค๊ณ ? (with HLS)
๐Ÿš€ ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž

ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

๐Ÿš€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ HLS ERROR
๐Ÿš€ input ํƒœ๊ทธ์— ํ•œ๊ธ€ ์ž…๋ ฅ ํ›„, Enter๋ฅผ ๋ˆ„๋ฅด๋ฉด ํ•จ์ˆ˜๊ฐ€ ๋‘๋ฒˆ ํ˜ธ์ถœ๋˜๋Š” ์˜ค๋ฅ˜
๐Ÿš€ nginx proxy pass๋ฅผ ๋ฐ”๊ฟจ๋”๋‹ˆ ์ƒ๊ธด ์—๋Ÿฌ - ์Šค์›จ๊ฑฐ ์ธ์‹ ๋ฌธ์ œ
๐Ÿš€ ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ ํด๋ผ์ด์–ธํŠธ-์„œ๋ฒ„ WS handshake
๐Ÿš€ ๋ Œ๋”๋ง ๋ฒ”์ธ์€ ํ•˜๋‚˜!

ํ˜‘์—… ๊ทœ์น™

๐ŸŒˆ ๊ทธ๋ผ์šด๋“œ ๋ฃฐ
๐Ÿฅ” ํŒ€์› ์†Œ๊ฐœ
๐Ÿ”Ž ์ฝ”๋“œ & ๊นƒ ์ปจ๋ฒค์…˜
๐ŸŒณ ๊นƒ branch ์ „๋žต
๐Ÿ“Œ ๋…ธ์…˜ ๋ฌธ์„œ ์ €์žฅ์†Œ

ํ”„๋กœ์ ํŠธ ๊ธฐํš

๐ŸŽจ ํ”ผ๊ทธ๋งˆ
๐Ÿง‘โ€๐Ÿ’ป ๊ธฐํš ๊ณต์œ  ๋ฐœํ‘œ ์ž๋ฃŒ
๐ŸŽค 2์ฃผ์ฐจ ๋ฐœํ‘œ ์ž๋ฃŒ
๐Ÿ˜Ž ๋ฐฑ๋กœ๊ทธ

๋ฐ์ผ๋ฆฌ ์Šคํฌ๋Ÿผ

๐Ÿ“ 1์ฃผ์ฐจ
๐Ÿ“ 2์ฃผ์ฐจ
๐Ÿ“ 3์ฃผ์ฐจ
๐Ÿ“ 4์ฃผ์ฐจ
๐Ÿ“ 5์ฃผ์ฐจ

์ฃผ๊ฐ„ ๊ณ„ํš์„œ

๐Ÿ—“๏ธ 1์ฃผ์ฐจ
๐Ÿ—“๏ธ 2์ฃผ์ฐจ
๐Ÿ—“๏ธ 3์ฃผ์ฐจ
๐Ÿ—“๏ธ 4์ฃผ์ฐจ
๐Ÿ—“๏ธ 5์ฃผ์ฐจ

๊ทธ๋ฃน ํšŒ๊ณ 

โœจ 1์ฃผ์ฐจ
โœจ 2์ฃผ์ฐจ
โœจ 3์ฃผ์ฐจ
โœจ 4์ฃผ์ฐจ


view

Clone this wiki locally