MLTマルチメディアフレームワークとmeltとPythonバインディング

はじめに

先日kdenlive映像エフェクトレンダリングデモ一覧の記事を書いたわけですが、あれらのエフェクトはkdenliveなどのノンリニア動画編集ソフトを使用しないと実現できないものなのでしょうか?つまりコマンドラインから編集はできないのでしょうか?

はい、映像編集もコマンドラインから可能です。

例えば画像編集ソフトであるGIMPは高機能ですが、簡単な編集であればImageMagickで代用できますよね。
同じように、kdenliveShotcutのような高機能な映像編集ソフトを使わなくても、簡単なエフェクト処理であればMeltというコマンドで行うことができます。

GUICLI
GIMPImageMagick
kdenliveMelt

またImageMagickMeltは必ずしもワンライナーで書かなくてはいけないという制限はありません。
好きなだけ複雑なスクリプトを書けるわけですが、先程例えに出したImageMagickMeltでは、そのスクリプトに違いがあります。

どういうことでしょうか?

ImageMagickの場合、convertmogrifyなどをシェルスクリプトの形にすれば複雑な編集が可能です。
Meltの場合も同様、シェルスクリプトに書くことができます。
しかしMeltの場合はMLT XMLという、XMLで記述するところに真骨頂があります。
そしてそのXMLファイル作成用のPythonバインディングが存在します。

応用範囲がものすごく広がりそうだと思いませんか?

今回の記事では、Meltの紹介はもとより、その背後のMLTマルチメディアフレームワークの仕組みにも触れて、コマンドラインからの映像編集がどのようなものか、さらにPythonバインディングまで紹介したいと思います。

元画像
元画像

MLTマルチメディアフレームワークとはなにか?

ノンリニア動画編集ソフトであるShotcut, kdenliveOpenShotでは背後にMLTマルチメディアフレームワークが使われています。
MLTマルチメディアフレームワークは、動画編集およびメディア処理のためのオープンソースフレームワークです。
メディアファイルの処理、エフェクトの適用、トランジションの追加、オーディオ処理などを定義出来ます。
動画編集ソフトウェアのバックエンドとして使用されていて、KdenliveShotcutは、MLTマルチメディアフレームワークを使用する代表的な動画編集ソフトウェアです[^1]。
[^1]: ShotcutMLTと開発者が同じ人(Dan Dennedy)。

MLTエンジン、MLTサービス、MLTプロデューサー、MLTコンシューマーなどのコンポーネントから構成されます。

以下に、MLTマルチメディアフレームワークのコンポーネントと構造要素の概念をそれぞれ示します。

コンポーネント

コンポーネント説明
MLTエンジンメディア処理のコアとなるエンジン。メディアファイルの読み込み、エフェクトの適用、トランジションの処理を行う
MLTサービス各種エフェクトやフィルタを提供するプラグイン形式のサービス。frei0rmovitなどのエフェクトサービス、soxなどのフィルタサービス、mixなどのトランジションサービスが存在します
MLTプロデューサーメディアファイルを読み込み、タイムライン上で再生するためのコンポーネント。ローカルからメディアファイルを読み込むファイルプロデューサー、ハードウェアデバイスからメディアを取り込むデバイスプロデューサー、ネットワークからメディアデータを取得するネットワークプロデューサーなどがあります
MLTコンシューマーレンダリング結果を出力するためのコンポーネント。ファイルに保存したり、リアルタイムプレビューを表示したりします。ファイルを出力、保存するファイルコンシューマー、処理結果をリアルタイムで画面に表示するスクリーンコンシューマー、処理結果をネットワークにストリーミングするストリームコンシューマーがあります。

構造要素

構造要素説明
プロファイルプロジェクトの全体設定(解像度、フレームレート、カラースペースなど)を定義します。
プレイリスト複数のメディアクリップを管理し、タイムライン上での再生順序を決定します。
トラクタータイムライン管理を行い、複数のトラックやトランジションを含むコンポーネント
フィルターメディアデータに対してエフェクトやフィルタを適用するコンポーネント
トランジション異なるメディアクリップ間の切り替えをスムーズに行うためのコンポーネント

どのように記述されるか

MLT XMLとして記述します。
このファイルには、プロジェクトの設定、使用するメディアファイル、適用するエフェクトやフィルタ、トラック構成などが記述されます。映像編集のレシピと考えて良さそうです。このレシピをもとにMLTエンジンが動作します。

MLT XMLの例

<mlt>
  <!-- ビデオの解像度、フレームレート、カラー空間などの設定を定義。プロファイル部分。 -->
  <profile description="HD 1080p 25 fps" width="1920" height="1080" progressive="1" frame_rate_num="25" frame_rate_den="1" colorspace="709"/>

  <!-- メディアファイルを定義。MLTプロデューサー部分。 -->
  <producer id="producer0" in="0" out="250">
    <property name="resource">input_video.mp4</property>
    <property name="mlt_service">avformat</property>
  </producer>

  <!-- トラック(プレイリスト)を定義。プロデューサーをトラックに追加。プレイリスト部分。 -->
  <playlist id="playlist0">
    <entry producer="producer0"/>
  </playlist>

  <!-- エフェクト(フィルタ)を定義。ここでは、フェードインエフェクトを追加。MLTサービス部分 -->
  <filter id="filter0" mlt_service="frei0r.alphaatop" in="0" out="50">
    <property name="0">0.0=0;1.0=1</property>
  </filter>

  <!-- タイムラインを定義。トラックやトランジションを含む。トラクター部分。 -->
  <tractor id="tractor0">
    <track producer="playlist0"/>
    <transition mlt_service="mix" in="0" out="250"/>
  </tractor>
</mlt>

MLTマルチメディアフレームワークの使用例

MLTマルチメディアフレームワークを使用して、動画編集を行いましょう。

Meltのインストール

コマンドラインから動画編集を可能にするMeltをインストールします。

sudo apt install -y melt
以下のパッケージが新たにインストールされます:
  libebur128-1 liblept5 libmlt++7 libmlt-data libmlt7 libmovit8 libopencv-calib3d4.5d libopencv-contrib4.5d libopencv-dnn4.5d
  libopencv-features2d4.5d libopencv-flann4.5d libopencv-highgui4.5d libopencv-ml4.5d libopencv-objdetect4.5d libopencv-video4.5d librtaudio6
  libtesseract4 melt

libmlt7などがMLT関連のライブラリです。

それでは動作確認をしましょう。

melt sample.mp4 -filter frei0r.emboss -consumer avformat:output_video.gif r=10
  • -filter frei0r.emboss
  • 入力ファイルに対してエフェクト処理をする
  • -consumer avformat
  • FFmpegavformatライブラリを使用して、出力ファイルを作成
  • r
  • フレームレートを指定。r=10は10fps。

動作確認終了です。

Meltで使用可能なフィルタサービス(エフェクト)

Meltで使用できるエフェクトは以下のようにして取得可能です。

melt -query "filters"
---
filters:
  - avcolour_space
  - avcolor_space
  - avdeinterlace
  - swscale
  - avfilter.abench
  - avfilter.acompressor
  - avfilter.acontrast
  - avfilter.acue
(後略)

このフィルタにはオーディオフィルタも含まれますが、450種類(!)くらいのフィルタが出力されます。
もし個々のフィルタの設定を知りたい場合は-query引数を用います。

# melt -query "filter=<フィルタ名>
melt -query "filter=frei0r.emboss"
---
schema_version: 0.3
title: emboss
version: 0.1
identifier: frei0r.emboss
description: Creates embossed relief image of source image
creator: Janne Liljeblad
type: filter
tags:
  - Video
parameters:
  - identifier: 0
    title: azimuth
    description: Light direction
    type: float
    minimum: 0
    maximum: 1
    default: 0.375
    mutable: yes
    widget: spinner
  - identifier: 1
    title: elevation
    description: Background lightness
    type: float
    minimum: 0
    maximum: 1
    default: 0.333333
    mutable: yes
    widget: spinner
  - identifier: 2
    title: width45
    description: Bump height
    type: float
    minimum: 0
    maximum: 1
    default: 0.25
    mutable: yes
    widget: spinner
...

これをみるとデフォルト値がいくつなのか、設定値の範囲はどれくらいかを確認できます。
この出力結果をみると、変更可能なパラメータはazimuth, elevation, width45だとわかります。azimuthは光の方向、elevationは背景の明るさ、width45はバンプの高さを表します。そしてこれらには個別のidentifierが割り振られています。例えばazimuthならidentifier0です。そして各設定値は:でつなげて指定します。
たとえば先程のembossフィルタではデフォルト値が使われたのでした。この値を以下のように変化させるとどうなるでしょうか。

melt sample.mp4 -filter frei0r.emboss:0=0.0:1=0.9:2=0.0 -consumer avformat:output_3.gif r=5

入力が静止画の場合、inoutを指定します。outがフレーム数となります。

melt example.png in=0 out=10 -filter oldfilm -consumer avformat:output_oldfilm.gif

※ 書式に間違いはないように思えますが、わたしの環境では-filterの引数が反映されませんでした。これはembossだけでなく、oldfilmにおいても同様でした。後述のMLT XML使用時は機能しました

melt example.png in=0 out=10 -filter oldfilm:delta=400:brightnessdelta_up=100:brightnessdelta_down=100 -consumer avformat:oldfilm02.gif

詳細はman meltにて確認できます。また付加情報はMelt Documentationにあります。試してはいませんがドキュメンテーションによると2トラック以上のエフェクトの追加など複雑なことが可能なようです。
ただし、ドキュメンテーションにあるように

  • コマンドライン解析時のエラーチェックが弱いこと
  • 複雑な処理には複雑なコマンドラインを構築しなければならないこと(非現実的)

とあります。

では、複雑なコマンドライン構築を避けて、なおかつ思い通りの映像エフェクトを実現できないものでしょうか?

それを解決するのが次に説明するMLT XMLの仕組み(MLT Multimedia Framework)とそのバインディングであるpython3-mltです。

MLTのインストール(python3-mlt)

というわけで、MLT Multimedia Frameworkを利用するためにパイソンバインディングをインストールしていきましょう。
meltをインストールしていない場合、MLTのPythonバインディングであるpython3-mltをインストールすると必要なライブラリ群(libmlt7など)が一緒にインストールされます。

  • libmlt7: MLTライブラリの主要な部分
  • libmlt++7: MLTライブラリのC++バインディング
  • libmlt-data: MLTライブラリに関連するデータ
  • その他、多数の依存パッケージ(例:libavcodec60libavformat60libavutil58など)、FFmpegなどに必要なライブラリ

リポジトリはUbuntu 22.04 LTSuniverseリポジトリになります。

はじめ、普段使用しているシステムにvenvによる仮想環境を作り、そこでMLTを試そうとしましたが、システムにライブラリをインストールしないといけないことがわかりました。

そこでここではGnome BoxesにインストールしたUbuntu 22.04 LTSpython3-mltをインストールします。
python3-mlt自体もuniverseセクションにあります。

# Gnome BoxesのUbuntu 22.04にインストール
sudo add-apt-repository universe
sudo apt update && apt upgrade -y
sudo apt install python3-mlt

インストールが完了したら、以下のPythonスクリプトを実行して、MLTが正しくインストールされているか確認します。

$ python3Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import mlt7
>>> mlt_version = mlt7.Factory().init()
No LADSPA plugins were found!
Check your LADSPA_PATH environment variable.
>>> print("MLT Version:", mlt_version)
MLT Version: <mlt7.Repository; proxy of <Swig Object of type 'Mlt::Repository *' at 0x7b9040195890> >
>>> 

このように表示されればインストールは完了です。

PythonでMLTを使用して動画にエフェクトを追加

まず、lxmlライブラリを使用してMLT XMLを生成し、それを使って動画にエフェクトを適用します。
前もってpipでインストールをしておきましょう。

# pipなどのアップグレード
pip install -U pip wheel setuptools
pip install lxml

それではPythonをつかってMLT XMLファイルを作成します。

下記のPythonスクリプトは下記のコマンドラインと同様の動作を定義するMLT XMLファイルを作成するものです。

melt example.png in=0 out=10 -filter oldfilm:delta=400:brightnessdelta_up=100:brightnessdelta_down=100 -consumer avformat:oldfilm02.gif
from lxml import etree


def create_mlt_xml_with_oldfilm(input_image, output_xml):
    # MLT XMLのルート要素を作成
    mlt = etree.Element('mlt', version="7.4.0")

    # プロファイルを追加
    profile = etree.SubElement(mlt, 'profile', {
        'description': 'NTSC 720x480 10fps',  # プロファイルの説明
        'width': '720',  # 横幅720px
        'height': '480',  # 縦幅480px
        'progressive': '1',  # プログレッシブスキャン
        'frame_rate_num': '10',  # フレームレートの分子
        'frame_rate_den': '1',  # フレームレートの分母
        'colorspace': '601'  # カラースペース(オプション)
    })

    # プロデューサーを追加
    producer = etree.SubElement(mlt, 'producer', id="producer0")
    property_element = etree.SubElement(producer, 'property', name="resource")
    property_element.text = input_image
    property_element = etree.SubElement(producer, 'property', name="mlt_service")
    property_element.text = 'qimage'

    # プレイリストを追加
    playlist = etree.SubElement(mlt, 'playlist', id="playlist0")
    entry = etree.SubElement(playlist, 'entry', producer="producer0", **{"in": "0", "out": "10"})

    # oldfilmエフェクトを追加
    filter_element = etree.SubElement(playlist, 'filter', id="filter0")
    filter_element.set('mlt_service', 'oldfilm')
    filter_element.set('in', '0')
    filter_element.set('out', '10')

    param_delta = etree.SubElement(filter_element, 'property', name="delta")
    param_delta.text = '400'
    param_brightness_up = etree.SubElement(filter_element, 'property', name="brightnessdelta_up")
    param_brightness_up.text = '100'
    param_brightness_down = etree.SubElement(filter_element, 'property', name="brightnessdelta_down")
    param_brightness_down.text = '100'

    # タイムラインを追加
    tractor = etree.SubElement(mlt, 'tractor', id="tractor0")
    track = etree.SubElement(tractor, 'track', producer="playlist0")
    transition = etree.SubElement(tractor, 'transition', {
        'mlt_service': 'mix',
        'in': '0',
        'out': '10'
    })

    # XMLツリーをファイルに保存
    tree = etree.ElementTree(mlt)
    tree.write(output_xml, pretty_print=True, xml_declaration=True, encoding='UTF-8')

if __name__ == "__main__":
    # 使用例
    create_mlt_xml_with_oldfilm("assets/example.png", "output_with_oldfilm.mlt")

このスクリプトを実行した結果が以下になります。

<?xml version='1.0' encoding='UTF-8'?>
<mlt version="7.4.0">
  <profile description="NTSC 720x480 10fps" width="720" height="480" progressive="1" frame_rate_num="10" frame_rate_den="1" colorspace="601"/>
  <producer id="producer0">
    <property name="resource">assets/example.png</property>
    <property name="mlt_service">qimage</property>
  </producer>
  <playlist id="playlist0">
    <entry producer="producer0" in="0" out="10"/>
    <filter id="filter0" mlt_service="oldfilm" in="0" out="10">
      <property name="delta">400</property>
      <property name="brightnessdelta_up">100</property>
      <property name="brightnessdelta_down">100</property>
    </filter>
  </playlist>
  <tractor id="tractor0">
    <track producer="playlist0"/>
    <transition mlt_service="mix" in="0" out="10"/>
  </tractor>
</mlt>

このファイルがMLT XMLファイルです。このファイルがレシピとなります。
ではこのレシピをもとにファイルの変換を行いましょう。

melt output_with_oldfilm.mlt -consumer avformat:output_oldfilm.gif

出来上がったファイルが以下になります。mltコマンドでは引数が動作しないバグ?がありましたが、こちらはきちんと設定値が反映されているようです。

最後に

今回は例を示しただけですので、もしかしたら「せっかくMLT XMLファイルを作成してもmeltコマンドとできることが同じならmeltコマンドだけで良くない?」と思われる方もいらっしゃると思います。
ではなぜMLT XMLの仕組みがあるのかと言えば、もっとずっと複雑な指示が記載できるからです。たとえばkdenliveShotcutなどでは内部でMLT XMLを利用しています。複数トラックのコンポジションやトランジション、映像だけでなくオーディオのエフェクト、どのようなファイル形式で書き出すかなど、ありとあらゆることができるようになります。
逆に今回の例のように簡単なものであればmeltコマンド単体のほうが簡単です。
あるいはPythonスクリプトでMLT XMLファイルを作成できるようにしておくことで、再利用性が上がりますし、複数のファイルを一度に処理できたり、条件分岐したりと汎用性をもたせることもできます。利用範囲次第、ということですね。

以上です。ありがとうございました。

参考文献


補足

本文に入れるには冗長な情報を以下に記述します。

MLTマルチメディアフレームワークの歴史

  1. 2002年 – プロジェクト開始
  • MLT (Media Lovin’ Toolkit) プロジェクトは、Dan Dennedyによって開始されました。彼は当時、放送業界での使用を目的としたメディア編集フレームワークを開発していました。
  1. 2003年 – 初期開発とリリース
  • 初期バージョンが公開され、基本的なメディア処理機能が実装されました。MLTは、当初からモジュール化されたプラグインアーキテクチャを持ち、さまざまなメディア形式とエフェクトをサポートしました。
  1. 2005年 – Kdenliveとの統合
  • Kdenlive(KDE Non-Linear Video Editor)との統合が進みました。これにより、KdenliveはMLTをバックエンドとして使用することで、より強力な動画編集機能を提供できるようになりました。
  1. 2007年 – プロジェクトの成長
  • MLTは、より多くのエフェクト、トランジション、フィルタをサポートするように成長しました。また、コミュニティの貢献が増え、フレームワークの機能が拡張されました。
  1. 2010年 – OpenShotとの連携
  • OpenShot Video EditorもMLTをバックエンドとして採用しました。これにより、OpenShotも多くのMLTの機能を利用できるようになり、ユーザーに対して強力な動画編集機能を提供できるようになりました。
  1. 2012年 – MLTの成熟
  • MLTは、バージョン0.8をリリースし、安定性とパフォーマンスが大幅に向上しました。また、クロスプラットフォーム対応が強化され、WindowsやmacOSでも利用できるようになりました。
  1. 2015年 – 新しいエフェクトとフィルタの追加
  • コミュニティの継続的な貢献により、新しいエフェクトやフィルタが追加されました。特に、映像とオーディオの処理機能が強化され、プロフェッショナルな編集作業にも対応できるようになりました。
  1. 2020年 – 現在
  • MLTは、引き続き活発に開発されています。最新バージョンでは、より多くのメディア形式のサポート、高度なエフェクト、リアルタイムプレビュー機能などが強化されています。また、KdenliveやShotcutなどの動画編集ソフトウェアのバックエンドとして広く使用されています。

MLT、GStreamer、FFmpegの関係

技術説明
MLT非線形ビデオ編集エンジン。複数のオーディオおよびビデオトラックを管理し、エフェクトやトランジションを適用。
GStreamerマルチメディアフレームワーク。リアルタイム処理やストリーミングに強みがあり、MLTのプラグインとして利用されることがある。
FFmpegマルチメディア処理ライブラリ。エンコード/デコード、フィルタリング、エフェクトの適用においてMLTで広く利用される。

MLTの主な特徴

特徴説明
モジュール化プラグインを通じて機能を拡張できる。
クロスプラットフォームLinux、Windows、macOSで動作。
オープンソースソースコードが公開されており、自由に利用および改良が可能。
他のライブラリとの統合FFmpegやGStreamerと統合し、広範なメディアフォーマットをサポート。

MLTを使用しているアプリケーション

アプリケーションプラットフォーム説明
KdenliveLinux, Windows, macOSKDEプロジェクトの一部であり、強力な非線形ビデオ編集ソフトウェア。
ShotcutLinux, Windows, macOSクロスプラットフォームのオープンソース動画編集ソフトウェア。
FlowbladeLinuxマルチトラック非線形動画編集ソフトウェア。
OpenShotLinux, Windows, macOS使いやすさに重点を置いたオープンソース動画編集ソフトウェア。

非線形ビデオ編集エンジンの機能

機能説明
マルチトラック編集複数のビデオおよびオーディオトラックを扱い、タイムライン上で自由に配置して編集できる。
リアルタイムプレビューエフェクトやトランジションをリアルタイムでプレビューしながら編集できる。
エフェクトとフィルタ映像や音声に対してさまざまなエフェクトやフィルタを適用できる。
トランジション異なるクリップ間のシームレスな切り替えを提供するトランジション効果をサポート。
レンダリング最終的な編集結果をファイルとして出力するためのレンダリング機能を提供。