dely Tech Blog

クラシル・TRILLを運営するdely株式会社の開発ブログです

新サービスの動画変換にAWS MediaConvertを利用した話

こんにちは。クラシル開発部でバックエンドエンジニアをしているyamanoiです

この記事は dely Advent Calendar 2021 19日目の記事です。

昨日はparayaさんの「delyにAndroidエンジニアとして入社して5ヶ月が経ちました」という記事でした

今回は先日発表させていただいた新サービス「クラシルショート」の動画変換にAWS MediaConvertを利用してみたので Rubyから利用する方法や、実際に使ってみた感想を書きたいと思います!

prtimes.jp

aws.amazon.com

なぜAWS MediaConvertを選択したのか

最初になぜ動画変換にAWS MediaConvertを利用したのかを軽く説明すると
クラシルには動画変換を行う自社開発された基盤が存在しているのですが、 自由度が高い一方で開発されてから時間が経過しているため、当初作ったエンジニアが存在しなかったり内部の処理がブラックボックスになっていたりと、メンテナンスが困難な状態にありました。

先日発表したクラシルショートでも動画変換処理を行う必要が出てきたのですが、AWS MediaConvertでやりたい事が実現できそう & 今後はできるだけマネージドサービスで完結できるようにしたいという想いから利用する選択をしました。

AWSにはElasticTranscoderという似たようなサービスも存在しますが、あまり開発が活発でない & 公式もMediaConvertの利用を推奨しているように感じました。

クラシルでの使い方

クラシルではどんな感じに利用しているかを書きたいと思います

Rubyからジョブを作成する

弊社はバックエンドはRubyを利用しており、 RubyにもAWS MediaConvertのSDKが存在するため、こちらを使ってジョブを作成しています。

require 'bundler/inline'
gemfile do
  source "https://rubygems.org"

  gem 'aws-sdk-mediaconvert'
end

endpoint = 'endpoint name'
queue_name = 'queue name'
role_name = 'role name'

settings = {}

client = Aws::MediaConvert::Client.new(endpoint: endpoint)
client.create_job({
  queue: queue_name,
  role: role_name,
  settings: settings
})

ジョブを作成する際に、ジョブの定義を渡す必要があります。

ジョブの定義はAWSのマネジメントコンソール上でジョブの設定を行い、「ジョブのJSONを表示」を押すことで定義をJSONとして吐き出すことができます。

ただこれをそのまま利用することができず、RubyのネイティブHashに置き換える必要があります。 ドキュメントにオプションが記載されているので、これと睨めっこしながら置き換えていく必要があります

{
  "Settings": {
    "Inputs": [
      {
        "TimecodeSource": "ZEROBASED",
        "VideoSelector": {},
        "AudioSelectors": {
          "Audio Selector 1": {
            "DefaultSelection": "DEFAULT"
          }
        },
        "FileInput": "s3://some-s3-bucket/input.mp4"
      }
    ]
  }
}

settings = {
  inputs: [
    {
      "timecode_source": "ZEROBASED",
      "video_selector": {},
      "audio_selectors": {
        "Audio elector 1": {
          "default_selection": "DEFAULT"
        }
      },
      "file_input": "s3://some-s3-bucket/input.mp4"
    }
  ]
}

https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/MediaConvert/Client.html#create_job-instance_method

JSONをそのまま突っ込めるように今後のアップデートに期待します

ジョブテンプレートの利用について

MediaConvertにはジョブテンプレートという概念があります ジョブの定義をテンプレートとして保存し、ジョブ作成時にテンプレート名を指定することで、ジョブ定義の記述を省略することが可能です。

クラシルでもジョブテンプレートの利用を検討したのですが、terraformがリソース対応しておらず、CloudFormation等の別の手段を検討する必要がありました。
そのため一旦ジョブテンプレートの利用はせず、ジョブを作成する際にジョブ定義を書く方針にしました。

ちなみにCloudFormation(CDK)で管理する場合はこんな感じでジョブテンプレートを作成できます

import cdk = require('@aws-cdk/core');
import { Construct } from 'constructs';
import * as mediaconvert from '@aws-cdk/aws-mediaconvert';

const json = `
{
  "Inputs": [
    {
      "TimecodeSource": "ZEROBASED",
      "VideoSelector": {},
      "AudioSelectors": {
        "Audio Selector 1": {
          "DefaultSelection": "DEFAULT"
        }
      }
    }
  ],
  "OutputGroups": [
    {
      "Name": "File Group",
      "OutputGroupSettings": {
        "Type": "FILE_GROUP_SETTINGS",
        "FileGroupSettings": {}
      },
      "Outputs": [
        {
          "VideoDescription": {
            "CodecSettings": {
              "Codec": "H_264",
              "H264Settings": {
                "RateControlMode": "VBR",
                "MaxBitrate": 10000,
                "Bitrate": 10000
              }
            }
          },
          "AudioDescriptions": [
            {
              "CodecSettings": {
                "Codec": "AAC",
                "AacSettings": {
                  "Bitrate": 96000,
                  "CodingMode": "CODING_MODE_2_0",
                  "SampleRate": 48000
                }
              }
            }
          ],
          "ContainerSettings": {
            "Container": "MP4",
            "Mp4Settings": {}
          }
        }
      ]
    }
  ],
  "TimecodeConfig": {
    "Source": "ZEROBASED"
  }
}
`
export class CdkSampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new mediaconvert.CfnJobTemplate(this, 'sample', {
      settingsJson: JSON.parse(json),
      description: "sample description",
      name: "sample"
    })
  }
}

作成したジョブテンプレートは以下のように利用できます

require 'aws-sdk-mediaconvert'

client = Aws::MediaConvert::Client.new(endpoint: endpoint)
client.create_job({
  queue: queue_name,
  role: role_name,
  job_template: 'sample'
})

変換結果を受け取る

動画の変換が終わったかどうかをアプリケーションが知る必要があるため、Cloudwatchイベントを利用して ステータスの変更を受け取っています。 変更通知はlambdaを経由してアプリケーションに通知されるようになっています。

EventBridgeを使うことでLambdaを咬ますこと無くエンドポイント通知できるようになったので、今後はこちらを使おうと思います aws.amazon.com

最後に

今回はMediaConvertについて紹介しました! MediaConvertには動画変換に必要な基本的な機能に合わせて、独自の拡張機能も多く大変便利なサービスです 少しでも気になった方は是非試してみてください

delyではエンジニア、デザイナー、PdMを積極採用しています。ご応募お待ちしております!

dely.jp