すべてのプロダクト
Search
ドキュメントセンター

:音声および動画処理のベストプラクティス

最終更新日:Apr 02, 2026

Function Compute の GPU インスタンスを使用すると、GPU ドライバー、CUDA バージョン、ハードウェアクラスターの管理を一切行わずに、FFmpeg を用いた大規模な動画トランスコーディングを実行できます。本ガイドでは、Serverless Devs と Python を使用して GPU インスタンス対応のトランスコーディング関数をデプロイする手順を説明します。例として MP4 から FLV への変換を紹介します。

GPU アクセラレーションを利用するタイミング

トランスコーディングジョブの実行時間が数分以上に及ぶ場合、または入力 1 件に対して複数の出力ストリームを生成する必要がある場合に、GPU アクセラレーションの効果が最も高まります。具体的なユースケースは以下のとおりです。

  • 1080p 以上の解像度でのトランスコーディング:2 分間の 1080p H.264 動画の場合、CPU では 3 分以上かかりますが、GPU を使用すると 10 秒未満で完了します(約 20 倍の高速化)。

  • マルチストリーム出力(1:N):3 種類の解像度を同時に出力する場合、CPU では約 6 分かかりますが、GPU を使用すると約 45 秒で完了します。

  • レイテンシに敏感なパイプライン:ソーシャルライブ配信、オンライン授業、遠隔医療など、リアルタイムまたはニアリアルタイムでの配信が求められるシナリオです。

CPU で 1 分未満で完了するジョブについては、GPU インスタンスのプロビジョニングにかかるオーバーヘッドが、速度向上によるメリットを上回る可能性があります。

サポートされるコーデックフォーマット

GPU インスタンスは Turing アーキテクチャをベースとし、NVIDIA T4 GPU を採用しています。

エンコーディング

フォーマット
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
HEVC 10-bit
HEVC B-frame

デコーディング

フォーマットビット深度
MPEG-18-bit
MPEG-28-bit
VC-18-bit
VP88-bit
VP98-bit、10-bit、12-bit
H.264 (AVCHD)8-bit、10-bit、12-bit
H.265 (HEVC) 4:2:08-bit、10-bit、12-bit
*H.265 (HEVC) 4:4:48-bit、10-bit、12-bit

パフォーマンスベンチマーク

以下のテストでは、2 分 5 秒の H.264 ソース動画(1920×1080、25 fps、4085 Kb/s;音声:aac (LC)、44100 Hz、ステレオ、fltp)を、CPU が同一(Xeon® Platinum 8163 4C、16 GB RAM)で、T4 GPU 搭載機と非搭載機で比較しました。FFmpeg のバージョンは git-2020-08-12-1201687 です。

1:1 トランスコーディング(入力 1 件、出力 1 件)

解像度GPU なしGPU あり
H264 1920×1080(フル HD、1080p)3 分 19.3 秒9.4 秒
H264 1280×720(HD、720p)2 分 3.7 秒5.8 秒
H264 640×480(480p)1 分 1.0 秒5.8 秒
H264 480×360(360p)44.4 秒5.7 秒

1:N トランスコーディング(入力 1 件、出力 3 件:1080p + 720p + 480p)

GPU なしGPU あり
5 分 58.7 秒45.3 秒

仕組み

この関数は、NVIDIA 対応 FFmpeg イメージをベースとしたカスタムコンテナ内で実行されます。呼び出されると、以下の処理を行います。

  1. Object Storage Service (OSS) バケットからソース動画をダウンロードします。

  2. FFmpeg を実行して動画をトランスコードします。GPU モードでは、FFmpeg が CUDA を使用して NVIDIA ハードウェア上でデコードおよびエンコードをオフロードします。-hwaccel cuda -hwaccel_output_format cuda フラグにより、デコード済みフレームが GPU メモリ内に保持され、エンコード前の CPU-GPU 間転送が回避されます。h264_nvenc フラグにより、FFmpeg はソフトウェアエンコーダーではなく、NVIDIA ハードウェアエンコーダーを H.264 出力に使用します。これらのフラグを指定しない場合、FFmpeg は CPU 上のソフトウェアエンコーディングにフォールバックします。

  3. トランスコード済みの出力を OSS にアップロードします。

前提条件

開始する前に、以下の条件を満たしていることを確認してください。

  • DingTalk グループへの参加(グループ ID:11721331)。参加には、組織名、Alibaba Cloud アカウント ID、対象リージョン(例:中国 (深セン))、連絡先情報をご提供ください。

  • GPU インスタンスと同じリージョンにある Container Registry Enterprise Edition インスタンス。 Personal Edition も使用できますが、Enterprise Edition を推奨します。 詳細については、「Container Registry Enterprise Edition インスタンスを作成する」をご参照ください。

  • そのインスタンス内の名前空間およびイメージリポジトリ。Container Registry ドキュメントの「名前空間の作成」をご参照ください。その後、同じインスタンスに「イメージリポジトリ」を作成します。

  • NVIDIA GPU 対応でコンパイルされた FFmpeg バイナリを準備します。以下のいずれかを選択してください。

  • オーディオおよびビデオファイルは、同一リージョン内の OSS バケットにアップロードされます。この関数には、バケットのオブジェクトに対する読み取りおよび書き込みアクセスが必要です。アップロード手順については、「オブジェクトのアップロード」をご参照ください。また、権限設定については、「バケットの ACL の変更」をご参照ください。

FFmpeg トランスコーディングコマンド

以下のすべての例では、willprice/nvidia-ffmpeg Docker イメージを使用します。

GPU アクセラレーションなし

単一出力(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

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

主なパラメーター

パラメーター説明
-hwaccel cudaデコードに CUDA ハードウェアアクセラレータを選択します
-hwaccel_output_format cudaデコード済みフレームを GPU メモリ内に保持し、エンコード前の CPU-GPU 間転送を回避します
-c:v h264_nvencNVIDIA ハードウェア H.264 エンコーダーを使用します
-c:a copy音声ストリームを再エンコードせずにコピーします
-b:v 5M出力ビットレートを 5 Mb/s に設定します

Serverless Devs を使用した GPU 関数のデプロイ

開始する前に、Serverless Devs と Docker をインストールし、次に手順に従って認証情報を使用して Serverless Devs を設定してください。

操作手順

  1. プロジェクトを作成します。

    fc-gpu-prj
    ├── code
    │   ├── app.py        # 関数コード
    │   └── Dockerfile    # イメージのDockerfile
    ├── README.md
    └── s.yaml            # デプロイ構成

    生成されるプロジェクト構造は以下のとおりです。

    fc-gpu-prj
    ├── code
    │   ├── app.py        # 関数コード
    │   └── Dockerfile    # イメージの Dockerfile
    ├── README.md
    └── s.yaml            # デプロイメント構成
  2. プロジェクトディレクトリに移動します。

    cd fc-gpu-prj
  3. 構成ファイルを編集します。s.yaml — リージョン、サービス名、コンテナイメージ URI をご環境に合わせて更新します。主要な GPU 関連パラメーターは instanceType: fc.gpu.tesla.1 および gpuMemorySize: 8192 です。

    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. Container Registry で「demo」という名前空間および「gpu-transcoding_s」というイメージリポジトリを作成します。
              # 2. 更新および再デプロイ時にタグをインクリメントします(例:v0.1 → v0.2)。
              image: registry.cn-shanghai.aliyuncs.com/demo/gpu-transcoding_s:v0.1
            codeUri: ./code

    YAML パラメーターの完全な一覧については、「YAML 仕様」をご参照ください。app.py — この関数は、OSS からソース動画をダウンロードし、FFmpeg を使用してトランスコードし(TRANS-MODE ヘッダーに基づいて GPU または CPU を使用)、結果を OSS に再アップロードします。src_url および dst_url のプレースホルダー値を、実際の OSS オブジェクト URL に置き換えてください。

    # -*- coding: utf-8 -*-
    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 オブジェクト URL(読み取りアクセス権限が必要)に置き換えてください
            dst_url = "https://your.domain/output.flv" # 実際の OSS オブジェクト URL(書き込みアクセス権限が必要)に置き換えてください
            src_path = "/tmp/input_c.flv"
            dst_path = "/tmp/output_c.mp4"
    
            if enable_gpu:
                src_url = "https://your.domain/input.mp4"  # 実際の OSS オブジェクト URL(読み取りアクセス権限が必要)に置き換えてください
                dst_url = "https://your.domain/output.flv" # 実際の OSS オブジェクト URL(書き込みアクセス権限が必要)に置き換えてください
                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 — NVIDIA FFmpeg ベースイメージからコンテナイメージを構築し、Python ランタイムを追加します。

    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 を実行して、ローカル構成を使用してください。
    s deploy
  6. コールドスタートによる遅延を回避し、GPU 関数が即座にリクエストを処理できるよう、プロビジョニング済みインスタンスを作成します。

    s provision put --target 1 --qualifier LATEST
  7. プロビジョニング済みインスタンスが準備完了していることを確認します。

    s provision get --qualifier LATEST

    current の値が 1 になった時点で、インスタンスは準備完了です。出力例:

    [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. 作業が完了したら、課金を停止するためにプロビジョニング済みインスタンスを解放します。

    s provision put --target 0 --qualifier LATEST

コンソールからの GPU 関数のデプロイ

  1. コンテナイメージの準備

    1. Container Registry Enterprise Edition インスタンス(推奨)または Personal Edition インスタンスを作成します。詳細については、「Container Registry Enterprise Edition インスタンスの作成」をご参照ください。

    2. インスタンスに名前空間とイメージリポジトリを作成します。Container Registry ドキュメントの名前空間の作成をご参照ください。次に、手順に従って同じインスタンスにイメージリポジトリを作成します。

    3. Container Registry コンソール の Docker 指示に従い、Serverless Devs の /code ディレクトリにある app.py および Dockerfile をイメージリポジトリにプッシュします。

    db-acr-docker

  2. サービスを作成する。サービスを作成する」のセクションをご参照ください。

  3. 関数を作成します。カスタムコンテナ関数を作成する」をご参照ください。

    インスタンスタイプGPU インスタンス に、リクエストハンドラータイプHTTP リクエストの処理 に設定します。
  4. 実行タイムアウト期間の延長。2 分間の動画の CPU トランスコーディングには 100 秒以上かかるため、デフォルトの 60 秒のタイムアウト期間では不十分です。

    1. 関数を検索し、構成 をクリックします(アクション 列)。

    2. 環境情報 セクションで、実行タイムアウト期間 を延長し、保存 をクリックします。

    db-gpu-time

  5. プロビジョニング済み GPU インスタンスの構成。作成後にルール一覧を確認し、現在の予約済みインスタンス数 が目標数と一致していることを確認します。

    1. 関数の詳細ページで、自動スケーリング タブをクリックし、ルールの作成 をクリックします。

    2. GPU インスタンスのプロビジョニングに必要なパラメーターを構成し、作成 をクリックします。「自動スケーリングルールの構成」で詳細をご確認ください。

    db-gpu-reserved

  6. cURL を使用した関数のテスト

    1. 関数の詳細ページで、トリガー タブをクリックし、トリガーエンドポイントを取得します。

    2. 以下のコマンドを実行します。

      デプロイ済みのバージョンを確認:

      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}

結果

OSS ドメイン名を使用して、ブラウザからトランスコード済みの出力にアクセスします。例:

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

ドメイン名を実際の OSS エンドポイントに置き換えてください。

次のステップ