【串流影音】大家一起來當直播主吧…

前幾年直播一直很熱門,一大票的直播主都紛紛的出現,不管是YouTube的,或者是一些交友APP,甚至是網路購物,之前很有很熱門的賣魚哥-王雷也是帶來了直播帶貨的熱潮。 其實本來是想學學WebRTC的,它主是要基於UDP協定,速度快,但是光看到它要自己編碼,還要裝一些有的沒的,很麻煩,再加上它的Server又不是很容易,最後還是選擇了基於HTTP的HLS協定,也就是大家在一般影音平臺常看到的.m3u8檔,而且nginx直接就支援,馬上難度就降低了許多了,這裡我們使用Docker來安裝nginx-rtmp這個帶有RTMP模組的image檔,安全又快速…

作業環境

項目 版本
macOS Sonoma 14.7.1
FFmpeg 7.1
OrbStack 1.9.4
nginx-rtmp 1.2.2

安裝ffmepg

  • 安裝ffmepg主要是要用它來產生.m3u8的功能,也就是.ts檔案的目錄檔…
brew install ffmpeg

下載nginx-rtmp

  • 這裡我們使用OrbStack來處理Docker的image檔…
  • 不過我想應該還是有很多人裝官方的Docker-Desktop,它是可以切換的…
docker context use orbstack       # Switch to OrbStack
docker context use desktop-linux  # Switch to Docker Desktop

  • 下載這個安裝設定檔docker-compose.yml執行就可以了…
  • 下載完成就會出現在OrbStack的列表內了…
services:

  nginx-rtmp:
    image: tiangolo/nginx-rtmp
    restart: always
    container_name: nginx-rtmp
    ports:
      - "1935:1935"
      - "8088:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./stat.xsl:/usr/local/nginx/html/stat.xsl
docker-compose up -d

播放串流影片

  • 在這裡呢,我們先準備一個在線的影片連接,一個.m3u8網址…
http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8
  • 這裡我們可以先使用ffmpeg內的ffplay來播放網路上的m3u8…
  • 當然,用其它的支援m3u8的放播器都可以…
ffplay http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8

影片串流

  • 接下來要像一般線上的影片一樣串流放送…
  • 要先把這個影片下載下來,放到資料夾中…
  • 這是一部約10分鐘的短片…
http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4

  • 然後利用ffmpeg指令來推動 / 播放串流…
  • 在推動的過程中,可以看到.m3u8 / .ts的檔案在產生,然後播完就不見了…
  • 其中test目錄名稱,就是m3u8的名字…
ffmpeg -re -stream_loop -1 -i BigBuckBunny.mp4 -c:v copy -c:a copy -f flv rtmp://localhost:1935/live/test
ffplay http://localhost:8088/test.m3u8
ffmpeg 
  -re                   # 讀取輸入來源(流)原生的 frame rate
  -stream_loop <count>  # 循環次數 / -1 (循環)
  -i <video>            # 輸入來源
  -c:v <format>         # 影片格式 (copy 代表與來源相同)
  -c:a <format>         # 聲音格式 (copy 代表與來源相同)
  -f <format>           # 轉檔格式
  rtmp://<url>          # 串流URL

加上濾鏡

  • 在這裡我們來加上浮水印的功能…
ffmpeg -re -i BigBuckBunny.mp4 -i William.jpg -filter_complex "overlay=W-w" -c:v libx264 -c:a aac -r 30 -preset ultrafast -f flv rtmp://localhost:1935/live/test_logo
ffplay http://localhost:8088/test_logo.m3u8
ffmpeg 
  re 
  -i BigBuckBunny.mp4 
  -i William.jpg                        # 濾鏡圖片
  -filter_complex "overlay=W-w"         # 設定影片的濾鏡,加在右上角
  -c:v libx264                          # 影片轉成h264
  -c:a aac                              # 聲音aac壓縮
  -r 30                                 # 30fps
  -preset ultrafast                     # 編碼速度
  -f flv                                # flv格式
  rtmp://localhost:1935/live/test_logo
  • 然後也可以來這裡看一下串流的狀態…
http://localhost:8088/stat

串流直播

  • 這裡就是我們的重頭戲啦,使用手機上的相機直播…
  • 首先,因為是macOS系統,所以是選用AVFoundation來做處理…
  • 先使用指令找出能用的WebCamera的編號列表…
  • 然後就來直播囉…
ffmpeg -f avfoundation -list_devices true -i ""
ffmpeg -f avfoundation -framerate 30 -video_size 640x480 -i "1" -c:v libx264 -preset ultrafast -f flv rtmp://localhost:1935/live/test_webcam
ffplay http://localhost:8088/test_webcam.m3u8

線上影音

  • 相信大家在看線上影音的時候,一定都是可以快轉的嘛…
  • 所以這些.ts一定要先轉出來才能有這個功能…
  • 我們可以利用指令/程式來處理…
  • 然後把轉好的檔案貼到Docker的對應資料夾中…
  • 最後記得改nginx.conf,加上hls_cleanup off,不然就只能播一次就會被刪掉了…
  • 然後就可以播放了,當然用手機也可以看到的…
ffplay http://localhost:8088/playlist.m3u8
package main

import "os/exec"

func main() {
	convertToHLS("BigBuckBunny.mp4", "./hls/")
}

func convertToHLS(inputFile, outputPath string) error {
	cmd := exec.Command("ffmpeg",
		"-i", inputFile,
		"-profile:v", "baseline",
		"-level", "3.0",
		"-start_number", "0",
		"-hls_time", "10",
		"-s", "640x480",
		"-hls_list_size", "0",
		"-f", "hls",
		outputPath+"/playlist.m3u8")

	return cmd.Run()
}
worker_processes auto;
rtmp_auto_push on;
events {}

rtmp {
    server {
        listen 1935;
        chunk_size 4096; # default 4096

        application live {
            live           on;
            interleave     on;

            # access
            allow play 127.0.0.1;
            deny play all;

            # hls
            hls            on;
            hls_path       /tmp/hls;
            hls_cleanup    off;         # 自動刪除關閉
       }
    }
}

http {
    include      mime.types;
    default_type application/octet-stream;
    keepalive_timeout  65;

    server {
        listen 80;
        listen [::]:80;

        location / {
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            root /tmp/hls;
        }

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

         location /stat.xsl {
            root html;
         }
    }
}

範例程式碼下載

後記

  • 總覺得自己技能樹越點越歪了,好像慢慢的往後端去了…
  • 其實啊,雖然前端難入門,但是學到後面除了畫面之外,難的就是跟硬體的互動了…
  • 但是後端啊,入門容易,就CRUD嘛,但是要學到深的話,除了一大堆的協定之外,還有速度 / 穩定度 / 多人使用的處理,這都是在手機上碰不到的問題,所以啊還是自己要懂一點,也比較可以跟後端的人溝通,不然手機端實在太依靠後端了,很沒有地位啊…