全部產品
Search
文件中心

Function Compute:音視頻處理最佳實務

更新時間:Jul 06, 2024

您可以通過Function Compute控制台、SDK或Serverless Devs來體驗GPU執行個體的最佳實務。本文以Python語言為例,說明如何使用Serverless Devs開發工具,將原始視頻經過函數代碼的轉碼處理,從.mp4轉換為.flv格式。

應用情境和優勢

隨著越來越多的強互動應用情境的出現,例如社交直播、線上課堂以及遠程醫學等,互連網流量正在向即時、准即時的趨勢演化。視頻平台通常要將原始視頻內容根據碼率、解析度、渠道貼片、播放平台等維度,以1∶N的方式轉碼輸出多種分發視頻格式,以服務不同網路品質、各種播放平台的觀看者。視頻轉碼是視頻生產分發中的關鍵一環,理想的視頻轉碼解決方案需要在成本(人民幣/流)和功率效率(瓦/流)方面具有成本效益。

在不同的應用情境下,Function Compute提供的GPU執行個體與CPU相比所具備的優勢如下。

  • 即時、准即時的應用情境

    提供數倍於CPU的轉碼效率,從而快速將生產內容推向終端使用者。

  • 成本優先的GPU應用情境

    提供彈性預留模式,從而按需為您保留工作GPU執行個體,對比自購VM擁有較大成本優勢。

  • 效率優先的GPU應用情境

    屏蔽營運GPU叢集的繁重負擔(驅動/CUDA版本管理、機器運行管理、GPU壞卡管理),使得開發人員專註於代碼開發、聚焦營運目標的達成。

GPU執行個體的更多資訊,請參見執行個體類型及使用模式

效能對比

Function ComputeGPU執行個體基於的Turing架構支援以下編碼和解碼格式:

  • 編碼格式

    H.264 (AVCHD) YUV 4:2:0

    H.264 (AVCHD) YUV 4:4:4

    H.264 (AVCHD) Lossless

    H.265 (HEVC) 4K YUV 4:2:0

    H.265 (HEVC) 4K YUV 4:4:4

    H.265 (HEVC) 4K Lossless

    H.265 (HEVC) 8k

    HEVC10-bitsupport

    HEVCB Framesupport

  • 解碼格式

    MPEG-1

    MPEG-2

    VC-1

    VP8

    VP9

    H.264 (AVCHD)

    H.265 (HEVC) 4:2:0

    *H.265 (HEVC) 4:4:4

    8 bit

    10 bit

    12 bit

    8 bit

    10 bit

    12 bit

    8 bit

    10 bit

    12 bit

原始視頻資訊如下表所示。

視頻資訊

資料

時間長度

2分05秒

碼率

4085 Kb/s

視頻流資訊

h264 (High), yuv420p (progressive), 1920x1080 [SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 1k tbn, 50 tbc

音視頻資訊

aac (LC), 44100 Hz, stereo, fltp

CPU和GPU的測試機器指標如下表所示。

對比項

CPU機型

GPU機型

CPU

CPU Xeon® Platinum 8163 4C

CPU Xeon® Platinum 8163 4C

RAM

16 GB

16 GB

GPU

N/A

T4

FFmpeg

git-2020-08-12-1201687

git-2020-08-12-1201687

視頻轉碼(1∶1)

效能測試:1路輸入、1路輸出

解析度

CPU轉碼耗時

GPU轉碼耗時

H264∶1920x1080 (1080p) (Full HD)

3分19.331秒

0分9.399秒

H264∶1280x720 (720p) (Half HD)

2分3.708秒

0分5.791秒

H264∶640x480 (480p)

1分1.018秒

0分5.753秒

H264∶480x360 (360p)

44.376秒

0分5.749秒

視頻轉碼(1∶N)

效能測試:1路輸入、3路輸出

解析度

CPU轉碼耗時

GPU轉碼耗時

H264∶1920x1080 (1080p) (Full HD)

5分58.696秒

0分45.268秒

H264∶1280x720 (720p) (Half HD)

H264∶640x480 (480p)

轉碼命令

  • CPU轉碼命令

    • 單路轉碼(1∶1)

      docker run --rm -it --volume $PWD:/workspace --runtime=nvidia willprice/nvidia-ffmpeg -y -i input.mp4 -c:v h264 -vf scale=1920:1080 -b:v 5M output.mp4
    • 多路轉碼(1∶N)

      docker run --rm -it --volume $PWD:/workspace --runtime=nvidia willprice/nvidia-ffmpeg \
      -y -i input.mp4 \
      -c:a copy -c:v h264 -vf scale=1920:1080 -b:v 5M output_1080.mp4 \
      -c:a copy -c:v h264 -vf scale=1280:720 -b:v 5M output_720.mp4 \
      -c:a copy -c:v h264 -vf scale=640:480 -b:v 5M output_480.mp4
    表 1. 參數說明

    參數

    說明

    -c:a copy

    無需任何重新編碼即可複製音頻流。

    -c:v h264

    為輸出資料流選擇軟體H.264編碼器。

    -b:v 5M

    將輸出位元速率設定為5 Mb/s。

  • GPU轉碼命令

    • 單路轉碼(1∶1)

      docker run --rm -it --volume $PWD:/workspace --runtime=nvidia willprice/nvidia-ffmpeg -y -hwaccel cuda -hwaccel_output_format cuda -i input.mp4 -c:v h264_nvenc -vf scale_cuda=1920:1080:1:4 -b:v 5M output.mp4
    • 多路轉碼(1∶N)

      docker run --rm -it --volume $PWD:/workspace --runtime=nvidia willprice/nvidia-ffmpeg \
      -y -hwaccel cuda -hwaccel_output_format cuda -i input.mp4 \
      -c:a copy -c:v h264_nvenc -vf scale_npp=1920:1080 -b:v 5M output_1080.mp4 \
      -c:a copy -c:v h264_nvenc -vf scale_npp=1280:720 -b:v 5M output_720.mp4 \
      -c:a copy -c:v h264_nvenc -vf scale_npp=640:480 -b:v 5M output_480.mp4
    表 2. 參數說明

    參數

    說明

    -hwaccel cuda

    選擇合適的硬體加速器。

    -hwaccel_output_format cuda

    將解碼的幀儲存在GPU記憶體中。

    -c:v h264_nvenc

    選擇NVIDIA硬體加速H.264編碼器。

準備工作

  • 使用GPU執行個體過程中,為了確保您的業務正常進行,請加入DingTalk使用者群(DingTalk群號:11721331),並提供以下資訊。

    • 組織名稱,例如您所在的公司名稱。

    • 您的阿里雲帳號ID。

    • 您期望使用GPU執行個體的地區,例如華南1(深圳)。

    • 連絡方式,例如您的手機號、郵箱或DingTalk帳號等。

  • 在GPU執行個體所在地區,完成以下操作:

  • 編譯FFmpeg。

    FFmpeg需要自行編譯以使用GPU加速,編譯方式如下:

  • 將需處理的音視頻資源上傳至在GPU執行個體所在地區的OSS Bucket中,且您對該Bucket中的檔案有讀寫權限。具體步驟,請參見控制台上傳檔案。許可權相關說明,請參見修改儲存空間讀寫權限

通過Serverless Devs部署GPU應用

前提條件

操作步驟

  1. 建立專案。

    s init devsapp/start-fc-custom-container-event-python3.9 -d fc-gpu-prj

    建立的專案目錄如下所示。

    fc-gpu-prj
    ├── code
    │   ├── app.py        # 函數代碼
    │   └── Dockerfile    # Dockerfile:將代碼打包成鏡像的Dockerfile
    ├── README.md
    └── s.yaml            # 專案配置:包含了鏡像如何部署在Function Compute
  2. 進入專案所在目錄。

    cd fc-gpu-prj
  3. 按實際情況修改目錄檔案的參數配置。

    • 編輯s.yaml檔案。

      YAML檔案的參數詳解,請參見YAML規範

      edition: 1.0.0
      name: container-demo
      access: default
      vars:
        region: cn-shenzhen
      services:
        customContainer-demo:
          component: devsapp/fc
          props:
            region: ${vars.region}
            service:
              name: tgpu_ffmpeg_service
              internetAccess: true
            function:
              name: tgpu_ffmpeg_func
              description: test gpu for ffmpeg
              handler: not-used
              timeout: 600
              caPort: 9000
              instanceType: fc.gpu.tesla.1
              gpuMemorySize: 8192
              cpu: 4
              memorySize: 16384
              diskSize: 512
              runtime: custom-container
              customContainerConfig:
                # 1. 請檢查阿里雲ACR容器鏡像倉庫已提前建立相應的命名空間(namespace:demo)與倉庫(repo:gpu-transcoding_s)。
                # 2. 後續更新函數時,請修改此處的tag,由v0.1修改為v0.2後,重新執行s build && s deploy。
                image: registry.cn-shanghai.aliyuncs.com/demo/gpu-transcoding_s:v0.1
              codeUri: ./code
    • 編輯app.py檔案。

      樣本如下:

      # -*- coding: utf-8 -*-
      # python2 and python3
      
      from __future__ import print_function
      from http.server import HTTPServer, BaseHTTPRequestHandler
      import json
      import sys
      import logging
      import os
      import time
      import urllib.request
      import subprocess
      
      class Resquest(BaseHTTPRequestHandler):
          def download(self, url, path):
              print("enter download:", url)
              f = urllib.request.urlopen(url)
              with open(path, "wb") as local_file:
                  local_file.write(f.read())
      
          def upload(self, url, path):
              print("enter upload:", url)
              headers = {
                  'Content-Type': 'application/octet-stream',
                  'Content-Length': os.stat(path).st_size,
              }
              req = urllib.request.Request(url, open(path, 'rb'), headers=headers, method='PUT')
              urllib.request.urlopen(req)
      
          def trans(self, input_path, output_path, enable_gpu):
              print("enter trans input:", input_path, " output:", output_path, " enable_gpu:", enable_gpu)
      
              cmd = ['ffmpeg', '-y', '-i', input_path, "-c:a", "copy", "-c:v", "h264", "-b:v", "5M", output_path]
              if enable_gpu:
                  cmd = ["ffmpeg", "-y", "-hwaccel", "cuda", "-hwaccel_output_format", "cuda", "-i", input_path, "-c:v", "h264_nvenc", "-b:v", "5M", output_path]
      
              try:
                  subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
              except subprocess.CalledProcessError as exc:
                  print('\nreturncode:{}'.format(exc.returncode))
                  print('\ncmd:{}'.format(exc.cmd))
                  print('\noutput:{}'.format(exc.output))
                  print('\nstderr:{}'.format(exc.stderr))
                  print('\nstdout:{}'.format(exc.stdout))
      
          def trans_wrapper(self, enable_gpu):
              src_url = "https://your.domain/input.mp4"  # 需替換為您個人阿里雲帳號下的OSS,且您有可讀寫的許可權。
              dst_url = "https://your.domain/output.flv" # 需替換為您個人阿里雲帳號下的OSS,且您有可讀寫的許可權。
              src_path = "/tmp/input_c.flv"
              dst_path = "/tmp/output_c.mp4"
      
              if enable_gpu:
                  src_url = "https://your.domain/input.mp4"  # 需替換為您個人帳號下的OSS,且您有可讀寫的許可權。
                  dst_url = "https://your.domain/output.flv" # 需替換為您個人帳號下的OSS,且您有可讀寫的許可權。
                  src_path = "/tmp/input_g.flv"
                  dst_path = "/tmp/output_g.mp4"
      
              local_time = time.time()
              self.download(src_url, src_path)
              download_time = time.time() - local_time
      
              local_time = time.time()
              self.trans(src_path, dst_path, enable_gpu)
              trans_time = time.time() - local_time
      
              local_time = time.time()
              self.upload(dst_url, dst_path)
              upload_time = time.time() - local_time
      
              data = {'result':'ok', 'download_time':download_time, 'trans_time':trans_time, 'upload_time':upload_time}
              self.send_response(200)
              self.send_header('Content-type', 'application/json')
              self.end_headers()
              self.wfile.write(json.dumps(data).encode())
      
          def pong(self):
              data = {"function":"trans_gpu"}
              self.send_response(200)
              self.send_header('Content-type', 'application/json')
              self.end_headers()
              self.wfile.write(json.dumps(data).encode())
      
          def dispatch(self):
              mode = self.headers.get('TRANS-MODE')
      
              if mode == "ping":
                  self.pong()
              elif mode == "gpu":
                  self.trans_wrapper(True)
              elif mode == "cpu":
                  self.trans_wrapper(False)
              else:
                  self.pong()
      
          def do_GET(self):
              self.dispatch()
      
          def do_POST(self):
              self.dispatch()
      
      if __name__ == '__main__':
          host = ('0.0.0.0', 9000)
          server = HTTPServer(host, Resquest)
          print("Starting server, listen at: %s:%s" % host)
          server.serve_forever()
    • 編輯Dockerfile檔案。

      樣本如下:

      FROM registry.cn-shanghai.aliyuncs.com/serverless_devs/nvidia-ffmpeg:latest
      WORKDIR /usr/src/app
      RUN apt-get update --fix-missing
      RUN apt-get install -y python3
      RUN apt-get install -y python3-pip
      COPY . .
      ENTRYPOINT [ "python3", "-u", "/usr/src/app/app.py" ]
      EXPOSE 9000
  4. 構建鏡像。

    s build --dockerfile ./code/Dockerfile
  5. 部署代碼至Function Compute

    s deploy
    說明

    服務名稱和函數名稱不變,重複執行以上命令時,請選擇本地配置,即use local

  6. 配置預留模式的執行個體。

    s provision put --target 1 --qualifier LATEST
  7. 查詢預留模式的執行個體是否就緒。

    s provision get --qualifier LATEST

    如果查詢到current參數為1,則說明GPU執行個體的預留模式已就緒,樣本如下。

    [2021-12-14 08:45:24] [INFO] [S-CLI] - Start ...
    [2021-12-14 08:45:24] [INFO] [FC] - Getting provision: tgpu_ffmpeg_service.LATEST/tgpu_ffmpeg_func
    customContainer-demo:
     serviceName:      tgpu_ffmpeg_service
     functionName:      tgpu_ffmpeg_func
     qualifier:       LATEST
     resource:        188077086902****#tgpu_ffmpeg_service#LATEST#tgpu_ffmpeg_func
     target:         1
     current:        1
     scheduledActions:    (empty array)
     targetTrackingPolicies: (empty array)
     currentError:     
  8. 調用函數。

    • 查看線上函數版本

      s invoke
    • 使用CPU進行轉碼

      s invoke -e '{"method":"GET","headers":{"TRANS-MODE":"cpu"}}'
    • 使用GPU進行轉碼

      s invoke -e '{"method":"GET","headers":{"TRANS-MODE":"gpu"}}'
  9. 釋放GPU執行個體。

    s provision put --target 0 --qualifier LATEST

通過Function Compute控制台部署GPU應用

  1. 部署鏡像。

    1. 建Container Registry的企業版執行個體或個人版執行個體。

      推薦您建立企業版執行個體。具體操作步驟,請參見建立企業版執行個體

    2. 建立命名空間和鏡像倉庫。

    3. Container Registry控制台,根據介面提示完成Docker相關操作步驟。然後將上述樣本app.pyDockerfile推送至執行個體鏡像倉庫,檔案資訊,請參見通過ServerlessDevs部署GPU應用時/code目錄中的app.pyDockerfile

      db-acr-docker

  2. 建立服務。具體操作步驟,請參見建立服務

  3. 建立函數。具體操作步驟,請參見建立Custom Container函數

    說明

    執行個體類型選擇GPU執行個體請求處理常式類型選擇處理 HTTP 要求

  4. 修改函數的執行逾時時間。

    1. 單擊目標服務下目標函數右側操作列的配置

    2. 環境資訊地區,修改執行逾時時間,然後單擊儲存

      db-gpu-time

    說明

    CPU轉碼耗時會超過預設的60s,因此建議您修改執行逾時時間為較大的值。

  5. 配置GPU預留執行個體。

    1. 在函數詳情頁面,單擊彈性管理頁簽,然後單擊建立規則

    2. 在建立Auto Scaling限制規則頁面,按需配置參數,預留GPU執行個體,然後單擊建立

      關於配置預留執行個體的具體操作,請參見配置Auto Scaling規則db-gpu-reserved

      配置完成後,您可以在規則列表查看預留的GPU執行個體是否就緒。即當前預留執行個體數是否為設定的預留執行個體數。

  6. 使用cURL測試函數。

    1. 在函數詳情頁面,單擊觸發器管理頁簽,查看觸發器的配置資訊,擷取觸發器的訪問地址。

    2. 在命令列執行如下命令,調用GPU函數。

      • 查看線上函數版本

        curl -v "https://tgpu-ff-console-tgpu-ff-console-ajezot****.cn-shenzhen.fcapp.run"
        {"function": "trans_gpu"}
      • 使用CPU進行轉碼

        curl "https://tgpu-ff-console-tgpu-ff-console-ajezot****.cn-shenzhen.fcapp.run" -H 'TRANS-MODE: cpu'
        {"result": "ok", "upload_time": 8.75510573387146, "download_time": 4.910430669784546, "trans_time": 105.37688875198364}
      • 使用GPU進行轉碼

        curl "https://tgpu-ff-console-tgpu-ff-console-ajezotchpx.cn-shenzhen.fcapp.run" -H 'TRANS-MODE: gpu'
        {"result": "ok", "upload_time": 8.313958644866943, "download_time": 5.096682548522949, "trans_time": 8.72346019744873}

執行結果

您可通過在瀏覽器中訪問以下網域名稱,查看轉碼後的視頻:

https://cri-zbtsehbrr8******-registry.oss-cn-shenzhen.aliyuncs.com/output.flv

本網域名稱僅為樣本,需以實際情況為準。