NGINX에서 RTMP와 FFmpeg 라이브러리를 이용한 HLS 송신 예제

NGINX에서 RTMP와 FFmpeg 라이브러리를 이용한 HLS 송신 예제

 

제목이 좀 거창하네..

 

쉽게 요약하면 "우분투에 있는 영상하나를 실시간으로 브라우저 player에서 볼 수 있도록하기" 입니다.

 

일단 구현할 예제는 하나입니다.

 

나의 우분투 서버에 있는 영상을 어떤 웹브라우저의 player에서 볼수 있게 하기!

 

말은 간단한데 여기에 필요한 사전 지식들이 너무 너무 많습니다.

 

일단 필요한 준비물을 작성해 보겠습니다.

 

[1] ubuntu 18

[2] nginx

[3] nginx-rtmp 모듈

[4] HLS - HTTP Live Streaming 프로토콜 

[4] FFmpeg

[5] 영상

[6] 영상을 실시간으로 받아서 재생할 player 기능이 있는 브라우저

 

 

위의 준비물에 대해서 간단하게 설명을 하고 넘어가겠습니다.

 

[1] ubuntu 18 서버

- 쉽게말해서 컴퓨터라고 생각하시면 됩니다. cpu, memory, hardDisk 로 이루어진 유닉스 운영체제를 사용하는 기계입니다.

 

[2] nginx

- 아주 가벼운 웹서버 입니다. 클라이언트가 파일을 요청하면 요청에 맞는 정적 파일을 응답해주는 웹서버로 사용되기도 하고, 

리버스 프록시 서버로서 로드 밸런서 역할도 하는 아주 만능(?)에 가까운 프로그램입니다. 

 

[3] nginx-rtmp 모듈

- nginx에서 rtmp 프로토콜을 지원하기 위해 만들어진 모듈(라이브러리)입니다.

이 모듈을 이용해서 영상을 rtmp 형식으로 송출할 수 있습니다.

참고로 rtmp 란 real time messaging protocol 의 약자로서 실시간으로 데이터를 전송할 수 있는 프로토콜입니다.

nginx-rtmp 모듈은 영상을 rtmp 프로토콜로 전송하면 flv 형태로 서버에 전송되는데 이 영상을 클라이언트의 브라우저에 실시간으로 스트리밍 하기 위해서는 ts 파일로 변환이 필요합니다. 

 

[4] HLS - HTTP Live Streaming 프로토콜 

- HTTP 프로토콜 기반의 스트리밍 프로토콜입니다.

하나의 영상 파일을 일정한 구간별로 잘라서 ts 파일로 만들어서 전송을 합니다.

ts 파일들의 메타 정보는 m3u8 확장자의 파일이 가지고 있습니다.

클라이언트는 m3u8 파일을 참조해서 전달받은 ts파일을 순서대로 재생을 시킵니다.

(즉, 전체 영상을 모두 받지 않아도 바로 영상을 재생할 수 있게하는 프로토콜입니다.)

비슷한 프로토콜로는 mpeg-dash 가 있습니다.

 

[4] FFmpeg

- 코덱과 트랜스코딩을 할 수 있는 프로그램입니다.

코덱이란 인코딩과 디코딩 기능이 가능한 프로그램을 뜻합니다.

쉽게말해서 인코딩은 영상을 압축하는것. 디코딩은 압축한것을 풀어버리는 것입니다.

 

트랜스코딩이란 인코딩 형식을 바꾸는 것입니다. 즉 압축 형식을 바꾸는 것으로

mp4로 압축(인코딩)된 영상을 풀어서(디코딩) 다시 avi 형식으로 압축(인코딩)하는 것을 말합니다.

 

[5] 영상

- 말그대로 영상 파일 입니다.

 

[6] 영상을 실시간으로 받아서 재생할 player 기능이 있는 브라우저

-아래에서 실시간 영상 송출을 테스트 할 수 있는 브라우저 url을 공유하겠습니다.

 

실시간 영상 송출 시스템 구조

 

 

필요한 모듈 설치

 

nginx 설치하기

sudo apt-get update

sudo apt-get upgrade -y

sudo apt-get install nginx 

 

설치후 nginx 조작법 및 설정파일 관련한 내용은 이 포스팅을 참고해주세요.

 

libnginx-mod-rtmp 설치하기

sudo apt update

sudo apt install libnginx-mod-rtmp

 

만약 설치가 안된다면 이 글을 참고해주세요.

 

위의 글을 대략 요약하면 아래와 같습니다.

 

먼저 nginx 안정 저장소를 추가

sudo add-apt-repository ppa:nginx/stable

 

그런 다음 apt 업데이트를 실행

sudo apt-get update

 

그리고 nginx geoip 모듈을 가져오기

sudo apt-get install nginx-module-geoip

 

 

nginx 서버에 rtmp 프로토콜 설정해주기

sudo vi /etc/nginx/nginx.conf


rtmp {
        server {
                listen 1935;
                chunk_size 4096;
                allow publish 127.0.0.1;
                deny publish all;

                application live {
                        live on;
                        record off;
                }
        }
}

 

rtmp 사용하는 1935 포트 방화벽 열고 nginx 재시작 하기

sudo ufw allow 1935/tcp
sudo systemctl reload nginx.service

 

유튜브에서 테스트할 영상 하나 다운로드 받기

sudo apt install python3-pip

pip install youtube-dl

youtube-dl https://www.youtube.com/watch?v=iom_nhYQIYk

 

실시간 스트리밍을 위한 FFmpeg 다운받기

sudo apt install ffmpeg

 

다운 받은 영상 확인하기

 

다운받은 영상 송출하기

아래와 같은 명령어로 영상을 송출합니다.

ffmpeg -re -i "Introducing App Platform by DigitalOcean-iom_nhYQIYk.mkv" -c:v copy -c:a aac -strict -2 -ar 44100 -ac 1 -f flv rtmp://localhost/live/obs_stream

 

HLS 적용하기

웹 브라우저를 사용하여 서버에서 비디오를 직접 스트리밍할 수 있도록 스트리밍 프로토콜에 대한 지원을 추가해보겠습니다. # 추가 부분을 보시면 되고 중요한것은 hls_path 에 보시면 해당 경로 즉, hls 폴더에 ts파일과 m3u8 파일이 생성이 될것입니다.

sudo vi /etc/nginx/nginx.conf

rtmp {
        server {
                application live {
                    	live on;
                    	record off;
                        # 추가 부분
                        hls on;
                        hls_path /var/www/html/stream/hls;
                        hls_fragment 3;
                        hls_playlist_length 60;

                        dash on;
                        dash_path /var/www/html/stream/dash;
                }
        }
}

 

전체 nginx 설정 파일

/etc/nginx/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
	worker_connections 768;
	# multi_accept on;
}

# 영상을 미디어 서버로 보낼때 사용하는 프로토콜
# nginx rtmp 설정
rtmp {
        server {
		# rtmp 포트 번호
                listen 1935;
		
		# rtmp 가 4k 블록으로 데이터 전송
                chunk_size 4096;
		
		# 동일한 서버에만 비디오 게시
                allow publish 127.0.0.1;

		allow publish all;
		

		#deny publish all;

		
		#  HLS 형식으로 변환 (트랜스먹싱 혹은 패킷타이징이라고 함)		
		# 스트림이 기본적으로 디스크에 저장안되게 처리
                application live {
                        live on;
                        record off;
               		
			#HLS
			hls on;
                        hls_path /var/www/html/stream/hls;
                        hls_fragment 3;
                        hls_playlist_length 60;

                        dash on;
                        dash_path /var/www/html/stream/dash;
		 }
        }
}


http {

	# Basic Settings

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	# server_tokens off;

	# server_names_hash_bucket_size 64;
	# server_name_in_redirect off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	# SSL Settings

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	# Logging Settings

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	# Gzip Settings

	gzip on;


	# Virtual Host Configs

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

 

nginx 하위 설정 파일

/etc/nginx/sites-available/default

주석 #으로 미디어 서버 부분만 참고하면 됩니다.

 

# 미디어 서버
# rtmp -> hls 포트
server {
    listen 8088 ssl;

    ssl_certificate /etc/letsencrypt/live/서버도메인/fullchain.pem; 
    ssl_certificate_key /etc/letsencrypt/live/서버도메인/privkey.pem;

     location / {
        add_header Cache-Control no-cache;
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Expose-Headers' 'Content-Length';

	# 도메인:8088/ 의 root 경로임	
	root /var/www/html/stream;

        types {
            application/vnd.apple.mpegurl m3u8;
        }
       
	# alias /tmp/hls/;
      }
}


# 80번 포트로 들어오면 443 포트로 redirect
server {

	listen 80 default_server;
	listen [::]:80 default_server;
	server_name 서버도메인;

    if ($host = 서버도메인) {
        return 301 https://$host$request_uri;
    } 
}



# 443 포트로 들어오면 nodejs 3000 포트로 redirect
server {
    # 아마존 서버 경우 앞에 www. 붙여야됨.
    server_name 서버도메인;

    listen [::]:443 ssl ipv6only=on; 
    listen 443 ssl;

    ssl_certificate /etc/letsencrypt/live/서버도메인/fullchain.pem; 
    ssl_certificate_key /etc/letsencrypt/live/서버도메인/privkey.pem; 

	location / {
		proxy_pass http://127.0.0.1:3000;
		#try_files $uri $uri/ =404;
	}
}

 

8088포트열고 ts 파일 저장 디렉토리 생성하기

8088 포트를 HLS 실시간 영상 송출 포트로 사용했습니다. 따라서 ufw 로 8088 포트를 열어 주어야 합니다.

그리고 ts 파일이 생성될 디렉토리를 생성해줍니다. 그리고 nginx 서버를 재시작 합니다.

sudo ufw allow 8088/tcp

sudo mkdir /var/www/html/stream

sudo systemctl reload nginx

 

테스트

ffmpeg 모듈로 위에서 다운 받은 영상을 rtmp 프로토콜로 송신합니다.

ffmpeg -re -i "Introducing App Platform by DigitalOcean-iom_nhYQIYk.mkv" -c:v copy -c:a aac -strict -2 -ar 44100 -ac 1 -f flv rtmp://localhost/live/obs_stream

 

아래처럼 영상이 송출되는 것을 확인할 수 있습니다.

 

테스트 웹브라우저 - https://www.hlsplayer.net/

위 브라우저에 들어가셔서 m3u8 탭을 클릭하시고 서버 주소와 m3u8 파일이 있는 경로를 적어줍니다. 포트는 8088 입니다.

아래와 같이 우분투에서 실시간으로 송출하고 있는 영상을 웹브라우저에서도 실시간 영상으로 시청할 수 있습니다.

 

 

로컬에서 테스트

만약 로컬에서 테스트 하고 싶으시면 빈 html 파일을 만드신 후 아래 소스를 붙여넣기 해주세요.

그리고 videoSrc 부분만 본인이 구축한 서버 도메인으로 변경해 주시면 됩니다.

 

<!DOCTYPE html>
<html>

<head>
    <title>실시간 영상 송출</title>
</head>

<body>

    <video id="video" loop playsinline autoplay muted></video>

    <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>

    <script>
        var video = document.getElementById('video');
        var videoSrc = 'https://서버 도메인:8088/hls/obs_stream.m3u8';
        if (Hls.isSupported()) {
            var hls = new Hls();
            hls.loadSource(videoSrc);
            hls.attachMedia(video);
            hls.on(Hls.Events.MANIFEST_PARSED, function () {
                video.muted = 'muted';
                video.autoplay = 'autoplay';
                video.playsinline = 'true';
                video.play();
            });
        } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
            video.src = videoSrc;
            video.addEventListener('loadedmetadata', function () {
                video.muted = 'muted';
                video.autoplay = 'autoplay';
                video.playsinline = 'true';
                video.play();
            });
        }
    </script>

</body>

</html>

 

 

참고한 사이트

https://stackoverflow.com/questions/36554405/how-to-enable-dynamic-module-with-an-existing-nginx-installation

 

https://realizetoday.tistory.com/entry/Live-VOD-Streaming-Media-server-%EA%B5%AC%EC%B6%95-1-%EB%AF%B8%EB%94%94%EC%96%B4%EC%84%9C%EB%B2%84%EC%9D%98-%EC%9D%B4%ED%95%B4?category=909104 

 

https://www.digitalocean.com/community/tutorials/how-to-set-up-a-video-streaming-server-using-nginx-rtmp-on-ubuntu-20-04

 

https://code-developer.tistory.com/11?category=820998