요즘은 바야흐로 영상의 시대라고 할 정도입니다. 미디어 세대라고 불릴 정도로 문자 위주의 검색이 아닌 동영상 위주의 검색이 활발하다고 합니다. 이러다가는 기존 검색 업체들이 문을 닫는 것은 아닐까 싶기도 합니다.  Raspberry Pi 와 nginx-rtmp 를 조합하여 초간단(?) 실시간 스트리밍 서버를 구축해 보겠습니다.

※ 준비물:

참고로, PiCam 은 RPi 에 편케이블로 직접 연결하는 형태의 카메라입니다. ‘PiCam 설치’ 로 검색해 보시면 관련한 글이 많이 있기 때문에 본 글에서는 굳이 언급하지 않겠습니다.

Camera Module V2
RPi Camera Module V2

서론

인터넷으로 영상을 송수신하는 과정은 상당히 많은 기술에 대한 이해를 필요로 합니다. 우선, 카메라 영상을 인터넷을 통해 송수신하기 위해서는 디지털 인코딩(encoding)/디코딩(decoding) 하는 과정이 필요합니다. 디지털 영상 신호를 주고 받기 위한 통신 규약(protocol)에도 RTP/RTSP, RTMP, HLS, MPEG-Dash 등 다양한 방식이 존재합니다. 영상을 보는 방법도 영상 파일을 다운로드 받아서 재생해서 보던 방식에서 벗어나 초고속 인터넷과 스마트폰 시대가 도래하면서 실시간 스트리밍 방식으로 변화하고 있습니다. 이전에는 파일 전송을 위한 전용 FTP 서버가 필요했다면 현재는 전체 파일 다운로드 없이 실시간 스트리밍으로 영상을 송출할 수 있는 서버가 필요합니다. 하나의 영상 소스를  다양한 재생 기기에 맞도록 실시간 변환해서 스트리밍을 하기 위해서는 어떻게 해야 하는 것일까요?  이럴 때 필요한 것이 ‘스트리밍 서버’ 입니다. 가장 대표적으로 Wowza 가 있습니다만 2백만원이 넘는 고가입니다. 저렴한 대안으로 nginx-rtmp 모듈이 있습니다. 기존의 nginx 웹 서버에 추가하여 RTMP 실시간 스트리밍이 가능하도록 하는 확장 기능 모듈입니다.

본 글에서는 Docker 와 nginx-rtmp 모듈을 이용하여 스트리밍 서버를 구축하는 방법을 정리해 보았습니다. 이를 위해서 docker 가 필요한데 아직 설치하지 않으셨다면 “Raspberry PI 에서 docker 설치와 실행하기” 를 보시고 docker 설치를 먼저 진행 하시기 바랍니다.

전체 과정 요약

  1. 준비
  2. Dockerfile 수정
  3. nginx-rtmp build
  4. nginx-rtmp 기동
  5. ffmpeg 설치
  6. 실시간 스트리밍 서비스 개시
  7. 시험 영상 확인

준비

RPi 용 nginx-rtmp docker 이미지를 찾지 못했기에 Linux 용 Dockerfile 을 변환해서 설치하도록 합니다. 우선, Linux 용 nginx-rtmp docker 소스를 다운 받습니다. 소스 배포처는 https://github.com/brocaar/nginx-rtmp-dockerfile 입니다.

$ wget https://github.com/brocaar/nginx-rtmp-dockerfile/archive/master.zip

압축을 해제하고, nginx-rtmp 폴더 내부에 config 폴더를 새로 만듭니다.

$ unzip master.zip
$ cd nginx-rtmp-dockerfile-master
$ mkdir config
$ ls -la
total 536
drwxr-xr-x 2 pi pi   4096 Dec 13 11:31 config
-rw-r--r-- 1 pi pi   1311 Dec 12 09:23 Dockerfile
-rw-r--r-- 1 pi pi   1521 Dec 12 09:23 nginx.conf
-rw-r--r-- 1 pi pi   1005 Dec 12 09:23 README.md
drwxr-xr-x 2 pi pi   4096 Jul 18  2017 static

Docker instance 의 timezone 설정을 위해 config 폴더에 RPi 의 /etc/localtime 파일을 복사합니다. 이 파일이 없는 경우, 기본 timezone 이 UTC 로 설정되어 Log 발생 시간 등을 확인하기 어렵습니다. Localtime 인 KTS 로 변경하기 바랍니다.

$ cp /etc/localtime `pwd`/config

nginx.conf 파일 내용을 수정하여 config 폴더에 복사합니다. 참고로, 원 소스의 nginx.conf 는 nginx-rtmp instance 내부에서 영상 변환을 하게 되어 있지만  본 글 내용은 외부에서 영상 변환을 하는 것을 다룰 것이기 때문에 nginx 내부 영상 변환 부분을 제외한 수정본을 사용합니다.

아래 내용이 수정한 nginx.conf 파일입니다. 설정 내용에 ‘application hls’, ‘application live’ 가 있는데 live 가 RTMP 재전송을 위한 설정이고, hls 는 RTMP=>HLS 전송을 위한 설정입니다. 참고로, RTMP 가 HLS 보다 원본 영상과 재생 영상간 시간 지연이 짧습니다. 모바일 기기의 불안정한 네트워크 환경을 고려해서 만들어진 HLS 규약에 따라 HLS는 기본 30초 지연 재생입니다.

daemon  off;

events {
    worker_connections 1024;
}

error_log stderr;

rtmp {
    server {
        listen 1935;
        chunk_size 4000;

        application live {
            live on;
       }

        application hls {
            live on;
            hls on;
            hls_fragment_naming system;
	    hls_playlist_length 2s; 
	    hls_fragment 1s;
            hls_path /data/hls;
            hls_nested on;
        }
    }
}

http {
    server {
        listen 80;

        location /hls {
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            root /data;
            add_header Cache-Control no-cache;
            add_header Access-Control-Allow-Origin * always;
        }

        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet static/stat.xsl;
        }

        location /static {
            alias /static;
        }

        location /crossdomain.xml {
            default_type text/xml;
            return 200 '<?xml version="1.0"?>
                <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
                <cross-domain-policy>
                    <site-control permitted-cross-domain-policies="all"/>
                    <allow-access-from domain="*" secure="false"/>
                    <allow-http-request-headers-from domain="*" headers="*" secure="false"/>
                </cross-domain-policy>';
            expires 24h;
        }
    }
}

Dockerfile 수정

docker image 의 기본이 되는 OS 를 Trusty 에서 resin/rpi-raspbian 으로 변경합니다.

FROM resin/rpi-raspbian

ffmpeg build 부분을 주석 처리합니다.

RUN apt-get update && \
  apt-get upgrade -y && \
  apt-get clean && \
  apt-get install -y --no-install-recommends build-essential apt-utils \
  wget software-properties-common && \
# ffmpeg
#  add-apt-repository ppa:mc3man/trusty-media && \
#  apt-get update && \
#  apt-get install -y --no-install-recommends ffmpeg && \

nginx.conf 복사를 하지 않도록 주석 처리합니다.

#ADD nginx.conf /config/nginx.conf

nginx-rtmp build

원 소스에서 알려 준 방법대로 build 를 합니다.

$ docker build -t nginx_rtmp .

build 를 완료하면 nginx_rtmp image 파일이 local image repository 에 등록됩니다.

$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
nginx_rtmp           latest              1d0ae951699c        23 hours ago        398MB
resin/rpi-raspbian   latest              6e68cc6f3192        7 weeks ago         128MB

nginx-rtmp docker instance 실행

config 폴더에 복사한 2개의 파일을 mapping 에 추가하여 실행합니다.

$ docker run --name nginx-rtmp -p 1935:1935 -p 8080:80 \
-v `pwd`/config/nginx.conf:/config/nginx.conf \
-v `pwd`/config/localtime:/etc/localtime \
nginx_rtmp

docker instance 가 오류 없이 실행이 되었는지 확인해 봅니다.

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                          NAMES
06d1e56a12ab        nginx_rtmp          "/usr/bin/entry.sh /…"   23 hours ago        Up 2 seconds        0.0.0.0:1935->1935/tcp, 0.0.0.0:8080->80/tcp   nginx-rtmp

docker instance 내부에서 nginx.conf 와 시간 설정을 확인합니다.

$ docker exec -it nginx-rtmp /bin/bash
$ date
Fri Dec 14 13:37:23 KST 2018
$ cat /config/nginx.conf

ffmpeg 최신 설치 (OS 포함 버전이 2.x 인 경우)

ffmpeg 최신 Static 버전을 원 배포처인 https://johnvansickle.com/ffmpeg/  에서 다운로드하여 사용합니다. 2018년 12월 현재, 최신 버전은 4.1 입니다.  참고로, Raspberry PI 는 armhf 버전을 다운로드 해야 합니다.

$ wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-armhf-static.tar.xz
$ cd /usr/local
$ sudo xz -cd ffmpeg-release-armhf-static.tar.xz|tar xvf -
$ sudo ln -sf ffmpeg-4.1-armhf-32bit-static ffmpeg

실행이 쉽도록 PATH 환경 변수에 /usr/local/ffmpeg 을 추가합니다.

$ export PAHT=/usr/local/ffmpeg:${PATH}
$ which ffmpeg
/usr/local/ffmpeg/ffmpeg

실시간 스트리밍 서비스 개시

ffmpeg 을 이용해서 PiCam 영상을 nginx-rtmp 스트리밍 서버로 송출합니다.

RTMP 송출 시험

$ ffmpeg -nostdin -rtsp_transport tcp -i /dev/video0 -vcodec copy \
-an -pix_fmt yuv420p -f flv rtmp://localhost:1935/live/101

또는

$ sudo raspivid -w 1280 -h 720 -t 0 -b 1500000 -fps 20 -o - \
 | ffmpeg -y -f h264 -i - -c:v copy -an -f flv \
 -rtmp_buffer 100 -rtmp_live live rtmp://localhost:1935/live/101

※ raspivid 옵션 설명

– w 1280 -h 720: 해상도 지정 1280 x 720

– b 1500000: 전송 bitrate 1.5 Mbps

– fps 20: 초당 영상 프레임 20 frame/sec

또는 HLS 로 송출 시험

$ ffmpeg -nostdin -rtsp_transport tcp -i /dev/video0 -vcodec copy \
-an -pix_fmt yuv420p -f flv rtmp://localhost:1935/hls/101

또는

$ sudo raspivid -w 1280 -h 720 -t 0 -b 1500000 -fps 20 -o - \
| ffmpeg -y -f h264 -i - -c:v copy -an -f flv \
-rtmp_buffer 100 -rtmp_live live rtmp://localhost:1935/hls/101

RTMP 와 HLS 동시 송출이 가능합니다.

여기서, “101” 은 스트리밍 영상 이름으로 임의의 값으로 설정하면 됩니다.

영상 재생 시험

다음TV팟플레이를 실행하고, 영상 재생을 시험합니다.

  • RTMP 영상 재생의 경우, rtmp://<RPi’s IP>:1935/live/101
  • HLS 영상 재생의 경우, http://<RPi’s IP>:8080/hls/101/index.m3u8

영상이 잘 나온다면 성공!