Skip to content

๐Ÿš€ ์•จ๋ฒ” ๋‹จ์œ„๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ํ•˜๊ธฐ (with HLS)

Kontae edited this page Dec 3, 2024 · 1 revision

๋ฌธ์ œ ์ƒํ™ฉ

HLS๋Š” ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”„๋กœํ† ์ฝœ๋กœ,

ํด๋ผ์ด์–ธํŠธ๋Š” ๋ฏธ๋””์–ด์— ๋Œ€ํ•œ playlist๋ฅผ ์š”์ฒญํ•˜๊ณ , ์„œ๋ฒ„๋Š” ์ด๋ฅผ ๋ณด๋‚ด์ค€๋‹ค.

ํด๋ผ์ด์–ธํŠธ๋Š” ๋ฐ›์€ playlist๋ฅผ ์ฝ์–ด ์‹œ๊ฐ„์— ๋งž๋Š” ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์„œ๋ฒ„์— ์š”์ฒญํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ๋Š” ์•จ๋ฒ” ๋‹จ์œ„๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์—ฌ๋Ÿฌ ๋…ธ๋ž˜๊ฐ€ ํ•˜๋‚˜์˜ ์„ธ์…˜์—์„œ ์ŠคํŠธ๋ฆฌ๋ฐ ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๊ณ ๋ฏผํ•œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

  1. ์•จ๋ฒ” ๋…ธ๋ž˜ ์ˆœ์„œ๋ฅผ ๋‹ด์€ master m3u8 ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํŒŒ์‹ฑํ•œ๋‹ค
    1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ master m3u8 ํŒŒ์ผ์„ ์š”์ฒญํ•œ๋‹ค
    2. ์„œ๋ฒ„๋Š” ์š”์ฒญ ๋ฐ›์€ ์‹œ๊ฐ„์— ๋งž์ถฐ (์ง€๋‚˜๊ฐ„ ๋…ธ๋ž˜๋ฅผ ์ œ์™ธํ•œ) master m3u8 ํŒŒ์ผ์„ ๋ณด๋‚ธ๋‹ค
    3. ํด๋ผ์ด์–ธํŠธ๋Š” ํ•ด๋‹น m3u8ํŒŒ์ผ์„ ํŒŒ์‹ฑํ•ด์„œ ์ฒซ๋ฒˆ์งธ ๋…ธ๋ž˜์˜ playlist m3u8 ์„ ์š”์ฒญํ•œ๋‹ค
    4. ์„œ๋ฒ„๋Š” ์š”์ฒญ ๋ฐ›์€ ์‹œ๊ฐ„์— ๋งž์ถฐ (์ง€๋‚˜๊ฐ„ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ œ์™ธ) ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ „์†กํ•œ๋‹ค.
  2. ํด๋ผ์ด์–ธํŠธ๊ฐ€ playlist๋ฅผ ์š”์ฒญํ•˜๋ฉด, ์„œ๋ฒ„์—์„œ ์ด ์•จ๋ฒ” ๊ธธ์ด์™€ ๋น„๊ตํ•˜z์—ฌ ์š”์ฒญ ์‹œ๊ฐ„์— ํ•ด๋‹นํ•˜๋Š” ๊ณก์˜ m3u8 ์„ ๋ณด๋‚ธ๋‹ค.
    1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ playlist๋ฅผ ์š”์ฒญํ•œ๋‹ค
    2. ์„œ๋ฒ„๋Š” ์ด ์•จ๋ฒ”์˜ ์‹œ๊ฐ„๊ณผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ธ ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•˜์—ฌ, ๋ช‡ ๋ฒˆ์งธ, ๋ช‡ ์ดˆ์ฏค์ธ์ง€ ๊ณ„์‚ฐํ•œ๋‹ค
    3. ์‹œ๊ฐ„์— ๋งž๋Š” ๋…ธ๋ž˜์˜ ์ง€๋‚˜๊ฐ„ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ œ์™ธํ•œ ์„ธ๊ทธ๋จผํŠธ ํŒŒ์ผ์„ ์ „์†กํ•œ๋‹ค.

ํ•ด๊ฒฐ๋ฐฉ์•ˆ ์„ ํƒ

1๋ฒˆ ๋ฐฉ๋ฒ•์˜ ๊ฒฝ์šฐ,

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ง์ ‘ ์ŠคํŠธ๋ฆฌ๋ฐ ํ๋ฆ„์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค
  • HlS.js master m3u8 ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
  • ํด๋ผ์ด์–ธํŠธ ์ธก ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•œ๋‹ค
  • ํŒŒ์‹ฑ ๋กœ์ง ๊ตฌํ˜„์˜ ์–ด๋ ค์›€์ด ์žˆ๋‹ค
  • ํ™•์žฅ์„ฑ์ด ์ œํ•œ์ ์ด๋‹ค

2๋ฒˆ ๋ฐฉ๋ฒ•์˜ ๊ฒฝ์šฐ,

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

โœ…ย 2๋ฒˆ ๋ฐฉ๋ฒ•์œผ๋กœ ์„ ํƒ

2๋ฒˆ ๋ฐฉ๋ฒ•์ด ์„œ๋ฒ„ ์ค‘์‹ฌ์œผ๋กœ ์ œ์–ดํ•จ์œผ๋กœ, ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ์ผ๊ด€๋˜๊ฒŒ ๋…ธ๋ž˜๊ฐ€ ์žฌ์ƒ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์–ด ์„ ํƒํ–ˆ๋‹ค.

๋˜, ํด๋ผ์ด์–ธํŠธ์—์„œ์˜ ํŒŒ์ผ ํŒŒ์‹ฑ์ด ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ์ด ๋” ๋†’๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์•จ๋ฒ” ๋‹จ์œ„๋กœ ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ ์š”์ฒญํ•˜๊ธฐ

2๋ฒˆ ๋ฐฉ๋ฒ• ์„ ํƒ์œผ๋กœ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ๋Š” playlist m3u8 ์„ ์š”์ฒญ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

ํ•˜๋‚˜์˜ ๋ฌธ์ œ๋Š” ๋‹ค์Œ ๋…ธ๋ž˜๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ, ๋‹ค์‹œ m3u8 ์„ ์š”์ฒญํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์ด ๋ฌธ์ œ๋Š” audio ํƒœ๊ทธ์˜ ended ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•œ๋‹ค.

  1. ์•จ๋ฒ”์— ๋Œ€ํ•œ playlist url ๋งŒ๋“ค๊ธฐ
const createStreamUrl = (roomId: string) =>`${import.meta.env.VITE_API_URL}/api/music/${roomId}/playlist.m3u8`;
  1. hls ์—ฐ๊ฒฐ
  const initializeHls = (audio: HTMLMediaElement, streamUrl: string) => {
    destroyHls();
    const hls = new Hls(DEFAULT_STREAMING_CONFIG);
    hlsRef.current = hls;
    hls.loadSource(streamUrl);
    hls.attachMedia(audio);

    hls.on(Hls.Events.MANIFEST_PARSED, () => setIsLoaded(true));
    hls.on(Hls.Events.ERROR, (error) =>
      console.error('Streaming error:', error),
    );
  };
  1. ๋‹ค์Œ ๋…ธ๋ž˜๋กœ ๋„˜์–ด๊ฐ€๊ฒŒ ์ฒ˜๋ฆฌ ( ended
  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;

    const handleEnded = () => {
      setIsLoaded(false);

      if (songIndex + 1 > totalSongs) {
        setSongIndex(1);
				...
        return;
      }
      setSongIndex((prev) => prev + 1);
      playStream();
    };
    audio.addEventListener('ended', handleEnded);
    return () => {
      audio.removeEventListener('ended', handleEnded);
    };
  }, [songIndex, totalSongs]);

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