はじめに:Chromebookでプロ用動画編集に挑む
今回もまた、日常的にChromeOSを使用したい人向けの記事になります。
今回は、ハリウッド映画の制作現場やプロの映像クリエイターが広く使用している動画編集ソフト「DaVinci Resolve」のLinux版を、Chromebook上でどこまで動作させることができるのかを検証してみました。
ZIPファイルの解凍からインストール、発生したエラーの原因、解消に向けて取った方法、そして実際の画面を交えて紹介します。
結論としては、使えないことはない、って感じだと思います。
システム環境
まずラップトップ仕様の確認です。私の環境は、Profileでも紹介しています。
- ラップトップ:HP Dragonfly pro Chromebook
- CPU:12th Gen Intel(R) Core(TM) i5-1235U(スレッド数 12、4.400 GHz)
- GPU:Intel Iris Xe Graphics(CPU内蔵)
- メモリ:16GB
- OS: ChromeOS 上の Linux 仮想環境(Debian 13 “Trixie”)
- ソフトウェア: DaVinci Resolve 21.1.1(Linux無料版)
ChromeOSのLinux環境は、ChromeOSの上の仮想マシンの中で動いています。そのため、Linuxから見
えるGPUは本物のIntel GPUそのものではなく、仮想GPUです。 vulkaninfoで確認すると、Virtio-GPU
Venus (Intel(R) Graphics (ADL GT2))と表示されます。Venusは、仮想マシンの中から出されたVulkan
(GPUを使うための命令の規格)の命令を、ChromeOS側の本物のGPUドライバへ中継する仕組みで
す。
この「GPUが仮想」という点が、今回の話のほぼすべての出発点になります。
インストール
Blackmagic Design社の公式サイトからダウンロードしたLinux版は、ZIP形式で圧縮されています。解凍すると、拡張子が .run という形式のインストーラーが出てきます。
# 必要なライブラリを入れておく
sudo apt-get install -y libapr1 libaprutil1 libglu1-mesa libgstreamer1.0-0 \
libgstreamer-plugins-base1.0-0 libnuma1 libxcb-xinerama0 libxcb-cursor0 \
libxkbcommon-x11-0 libasound2-plugins libxcb-composite0 libxcb-damage0
unzip DaVinci_Resolve_21.1.1_Linux.zip
chmod +x DaVinci_Resolve_21.1.1_Linux.run
sudo SKIP_PACKAGE_CHECK=1 DISPLAY=:0 XAUTHORITY=$HOME/.Xauthority \
./DaVinci_Resolve_21.1.1_Linux.run -i -y
ポイントは2つあります。
- インストーラーは、決められたパッケージが入っているかを確認して、足りないと判定すると止まります。今回は必要なライブラリを先に入れたうえで、SKIP_PACKAGE_CHECK=1でこの確認を飛ばしました。
- 最初はSKIP_PACKAGE_CHECK=1 sudo …と書いてしまい、うまくいきませんでした。sudo は基本的に呼び出し元の環境変数を引き継がないので、sudoの後ろに書く必要があります。画面(ディスプレイ)を開けないというエラーも出たので、DISPLAY と XAUTHORITYも一緒に渡しています。
これで /opt/resolve にソフトウェア一式がインストールされました。
なお、Resolveに同梱されているGLib関連のライブラリ( libglib-2.0 、 libgio-2.0 、 libgmodule-2.0 )
は、新しめのLinuxでよく紹介されている対処法にならって、別のフォルダへ退避した状態で使っていま
す。
sudo mkdir -p /opt/resolve/libs/disabled-libraries
sudo mv /opt/resolve/libs/libglib* /opt/resolve/libs/libgio* \ /opt/resolve/libs/libgmodule* \
/opt/resolve/libs/disabled-libraries/
OpenCL環境の準備
DaVinci Resolveは、映像の処理にGPUを使います。Linux版ではCUDA(NVIDIA)かOpenCLが必要です
が、ChromeOSのLinux環境には、最初はOpenCLが入っていません。また、普通のPCのようにIntel公式
のOpenCLドライバを入れても、相手が仮想GPUなので使えません。
そこで、Mesa(Linuxのオープンソースのグラフィックスドライバ群)に含まれている、Rust製の
OpenCL実装 「Rusticl」 を使いました。
sudo apt-get install -y mesa-opencl-icd pocl-opencl-icd
(このとき、CPUで動くOpenCL実装のPoCLも一緒に入れています。これが後で関係してきます。)
Rusticlは、環境変数 RUSTICL_ENABLE で、どのドライバの上で動かすかを指定します。今回は Zink を指定しました。Zinkは本来「Vulkanの上でOpenGLを動かす」ためのMesaのドライバですが、RusticlはZinkを経由して、Vulkanが使えるGPUで計算を実行できます。
つまり、OpenCLの計算は次のような経路で、ホストのIris Xeまで届きます。
Resolve → OpenCL(Rusticl) → Zink → Vulkan(Venus) → ホストの Intel Iris Xe
RUSTICL_ENABLE=zink を付けて clinfo (OpenCLの情報を表示するコマンド)を実行すると、rusticl のデバイスとして zink Vulkan 1.3(Virtio-GPU Venus (Intel(R) Graphics (ADL GT2)) (MESA_VENUS)) が表示されます。これで、OpenCLからGPUが見えるようになりました。
最初の壁:「Loading Color Page」で止まる
RUSTICL_ENABLE=zink を付けて起動すると、Resolveのログには、GPUがきちんと検出されたことが出ていました。
GPUDetect | INFO | Detected 1 GPUs:
GPUDetect | INFO | - "Intel zink Vulkan 1.3(Virtio-GPU Venus ( Graphics (ADL GT2)) (MESA_VENUS))" <- Main
Display GPU
GPUDetect | INFO | Integrated, 7.7 GiB VRAM
ところが、起動画面は「Loading Color Page」で止まったまま進まなくなりました。
ログ( /opt/resolve/logs/rollinglog.txt )をさかのぼると、その手前でOpenCLの準備に失敗していまし
た(抜粋)。
GPU.SingleBoardMgr | ERROR | Failed to initialize OpenCL board manager for "Intel zink Vulkan 1.3(...)": Failed
to initialize OpenCL context
DVIP | ERROR | Failed to create OpenCL context:
DVIP Exception: OpenCL Context Manager failed to create context.
Main | ERROR | Failed to initialize OpenGL texture pool, no valid contexts available.
Main | INFO | Show splash screen message: Loading Color Page
GPUは見えているのに、OpenCLで計算を始めるための準備(OpenCLコンテキストの作成)でつまずい
ている、という状態です。
止まっているプロセスにデバッガ(GDB)をつないで、メインスレッドがどこで止まっているかも見てみました。
# ResolveのプロセスIDを調べる(このときは 14259 でした)
pidof resolve
# そのプロセスにGDBをつないで、メインスレッドの呼び出し履歴を表示する
sudo gdb -p 14259 -batch -ex "thread 1" -ex "bt 30"
#0 0x00007f07a5c9bbbb in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007f07a5ca1cb2 in pthread_mutex_lock () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00007f07ab560e36 in std::__1::mutex::lock() () from /opt/resolve/bin/../libs/libc++.so.1
#3 0x00000000053beee3 in ?? ()
...
メインスレッドは、ロック(mutex)が空くのを待ったまま止まっていました。Resolve本体には関数名
の情報(シンボル)が含まれていないので、どの処理で待っているのかまでは追えません。
互換レイヤー(通訳プログラム)を作る
Linuxには「LD_PRELOAD」という仕組みがあります。アプリがライブラリの関数を呼び出すとき、その
手前に自作の関数を割り込ませることができます。これを使って、ResolveとOpenCLの間に入る「通訳
役」のライブラリ( libresolve_gpu_compat.so )をC言語で作りました。
その1:デバイスの種類を「GPU」と答える
互換レイヤーは少しずつ機能を足していきました。最初の段階でしていたのは、次の2つだけです。
- ResolveがGPUのデバイスを探したら、種類を問わずすべてのデバイスを返す
- デバイスの種類を聞かれたら、常に「GPU」と答える
/* GPUを探しに来たら、種類を問わずすべてのデバイスを返す */
cl_int clGetDeviceIDs(cl_platform_id platform, cl_device_type device_type,
cl_uint num_entries, cl_device_id *devices, cl_uint *num_devices) {
if (device_type & CL_DEVICE_TYPE_GPU)
device_type = CL_DEVICE_TYPE_ALL;
return orig_clGetDeviceIDs(platform, device_type, num_entries, devices, num_devices);
}
このときは RUSTICL_ENABLE を付けずに起動していたので、OpenCLのデバイスとして見えていたのは、CPUで動く PoCL でした。この互換レイヤーを挟むと、PoCLのCPUデバイスが「GPU」として扱われます。その状態で、「Loading Color Page」を通過できました。
ただしこれは、OpenCLの計算をCPU(PoCL)にさせて動いた、ということです。このあと、GPU
(Rusticl)で処理する構成に切り替えていきます。
その2:プロジェクトマネージャーで落ちる問題
Color Pageを通過したあとは、プロジェクトマネージャーが開いた直後にResolveが終了してしまう問題
が出ました。これは、互換レイヤーに次のような対処を足して解消しました。
- strcmp などの文字列関数に、空のポインタ(NULL)が渡されても落ちないようにする
- ウィンドウの親子関係の指定( WM_TRANSIENT_FOR )を、設定しないようにする
このほかにも、作業を進める中で出てきたクラッシュに対して、ProRes RAW用のライブラリ
( libProResRAW.so )を何もしないダミーに差し替えるなど、細かい対処を重ねています。
その3:映像が映らない問題と「GL連携」の代役
起動できるようになったあとも、メディアをクリックすると落ちる、ビューアが真っ黒のまま、といった問題が続きました。
Resolveは、画面の表示にOpenGL、映像の計算にOpenCLを使います。そして、OpenCLで計算した結果
を、コピーせずにそのままOpenGLの画面に渡す仕組み(OpenCLの拡張機能 cl_khr_gl_sharing )を使うように作られています。
ところが、 clinfo で確認すると、Rusticl(Zink経由)のデバイスはこの機能に対応していません。そこ
で互換レイヤーに、この機能の「代役」を実装し、あわせてOpenCLの処理をGPU(Rusticl)で行う構成に切り替えました。直接は渡せないので、一度メインメモリ(RAM)を経由して受け渡します。
1.「対応しています」と答える:デバイスの拡張機能の一覧を聞かれたら、末尾にcl_khr_gl_sharing
を書き足して返す
2.OpenCLの準備を通す:OpenCLコンテキストを作るとき、OpenGL連携用の指定が含まれていれば
取り除いてから、ドライバに渡す
3.共有の代わりに、普通のOpenCL画像を用意する:ResolveがOpenGLのテクスチャをOpenCLで使いたいと言ってきたら( clCreateFromGLTexture )、テクスチャの大きさと形式をOpenGLに問い合わ
せ、同じ大きさの普通のOpenCL画像を作って対応表に登録する
4.計算が終わったら、RAM経由で画面に書き戻す:Resolveが「計算が終わったので画面に返す」と
言ってきたら( clEnqueueReleaseGLObjects )、OpenCL画像の中身をRAMに読み出し、glTexSubImage2DでOpenGLのテクスチャに書き込む
4番目の部分を簡略化すると、こんな流れです(実際のコードからエラー処理やログ出力などを省いてい
ます)。
cl_int clEnqueueReleaseGLObjects(cl_command_queue queue, cl_uint num_objects,
const cl_mem *mem_objects, cl_uint num_wait,
const cl_event *wait_list, cl_event *event) {
for (cl_uint i = 0; i < num_objects; i++) {
InteropObj *obj = find_interop(mem_objects[i]); /* 3で登録した対応表 */
if (!obj) continue;
/* OpenCL画像の中身をRAMへ読み出す(読み終わるまで待つ) */
size_t origin[3] = {0, 0, 0};
size_t region[3] = {obj->width, obj->height, 1};
orig_clEnqueueReadImage(queue, obj->mem, CL_TRUE, origin, region, 0, 0,
obj->pixels, num_wait, wait_list, NULL);
/* RAMの内容をOpenGLのテクスチャへ書き込む */
glBindTexture(obj->target, obj->gl_id);
glTexSubImage2D(obj->target, obj->miplevel, 0, 0, obj->width, obj->height,
obj->gl_fmt, obj->gl_type, obj->pixels);
}
complete_event_if_needed(queue, event); /* 完了済みのイベントを返す */
return CL_SUCCESS;
}
今回の用途では、OpenCL → OpenGLの方向だけを実装しています。
GPUで処理させるため、OpenCLの実装はRusticlだけが読み込まれるようにしました。PoCLや、Mesaの
古いOpenCL実装(Clover)も一緒に見えていると、Rusticl以外のデバイスが使われることがあるためで
す。Rusticlの設定ファイルだけを置いた専用のフォルダを作り、OpenCLのローダーにそのフォルダだけを読ませます。
sudo mkdir -p /etc/OpenCL/rusticl_only
echo "libRusticlOpenCL.so.1" | sudo tee /etc/OpenCL/rusticl_only/rusticl.icd
最終的に、Resolveは次の環境変数を付けて起動しています(アプリ一覧のアイコンからも、このスクリ
プトを呼ぶようにしました)。
export QT_QPA_PLATFORM=xcb
export MESA_GL_VERSION_OVERRIDE=4.6
export MESA_GLSL_VERSION_OVERRIDE=460
export LD_PRELOAD=/usr/local/lib/libresolve_gpu_compat.so
export RUSTICL_ENABLE=zink
export OCL_ICD_VENDORS=/etc/OpenCL/rusticl_only
exec /opt/resolve/bin/resolve "$@"
これらの対処を重ねた結果、GPU(Rusticl)で処理する構成のまま、ビューアにカラーの映像が表示さ
れるようになりました。
画面描画のトラブル(縦線ノイズと1コマ送り)
映像が映るようになったあと、実際に編集する上で、2つの描画トラブルが出ました。
ビューアにバーコード状の縦線が入る
ビューアを再生すると、映像全体に黒い縦線が等間隔で入り、すだれ越しに見ているような状態になりました。
互換レイヤーの中で、OpenCLから読み出した直後のフレームをファイルに保存して調べました。
1920×1080の画像で、1画素は RGBA の各色が16ビットの浮動小数点数(1画素あたり8バイト)です。
x=00: R=0x0000 G=0x0000 B=0x0000 A=0x3c00
x=01: R=0x33a4 G=0x3445 B=0x356a A=0x3c00
x=02: R=0x0000 G=0x0000 B=0x0000 A=0x3c00
x=03: R=0x342a G=0x3498 B=0x35bf A=0x3c00
x=04: R=0x0000 G=0x0000 B=0x0000 A=0x3c00
x=05: R=0x34e5 G=0x3553 B=0x367a A=0x3c00
偶数列(0, 2, 4…)は色の値がすべて0、奇数列(1, 3, 5…)には正常な色が入っていました( 0x3c00 は
浮動小数点数で 1.0、 0x33a4 は約 0.24 です)。色が入っている画素の数は 1,036,800 で、全画素
2,073,600 のちょうど半分。1列おきに抜けていることが、データの上でもはっきりしました。
読み出しのタイミングが早すぎる可能性も考えて、OpenCLの処理が全部終わるのを待ってから読み出す
ようにも変えてみましたが、縦線は消えませんでした。
根本原因の特定まではせず、今回は表示する側で補う方法をとりました。読み出したフレームで、偶数列の色が0なら、すぐ右隣の奇数列の画素をそのままコピーします。
/* 1画素 = 8バイトなので、64ビット整数として1画素ずつ扱う */
uint64_t *p64 = (uint64_t *)obj->pixels;
for (int y = 0; y < h; y++) {
uint64_t *row = p64 + y * w;
for (int x = 0; x < w; x += 2) {
/* 下位48ビット(RGB)が0の画素は、右隣の画素で埋める */
if ((row[x] & 0x0000FFFFFFFFFFFFULL) == 0 && (row[x + 1] & 0x0000FFFFFFFFFFFFULL) != 0)
row[x] = row[x + 1];
}
}
1画素がちょうど8バイトなので、64ビットの整数として扱えば、1回の代入で1画素を丸ごとコピーでき
ます。同じ処理を単体のプログラムにして、このChromebookで測ってみたところ、1080pの1フレーム
あたり約1.4ミリ秒でした。
これで縦線は見えなくなりました。ただし、偶数列は隣の画素のコピーなので、ビューアの横方向の細かさは実質半分になっています。普通に見る分には気になりませんが、細部をじっくり確認する用途には向かないです。
矢印キーの1コマ送りで、映像が前後に行き来する
キーボードの「→」キーで1コマずつ送ると、順番どおりに進まず、進んだり戻ったりをピョコピョコ繰
り返す現象が出ました。
ReleaseGL: tex 119 (1920x1080) read took 58.286 ms (num_objs 1)
ReleaseGL: tex 124 (1920x1080) read took 52.327 ms (num_objs 1)
ReleaseGL: tex 119 (1920x1080) read took 56.317 ms (num_objs 1)
ReleaseGL: tex 124 (1920x1080) read took 91.438 ms (num_objs 1)
テクスチャに書き込んだ直後に、 glFlush() を呼ぶようにしました。 glFlush() は、OpenGLにたまってい
る命令を、すぐにGPUへ送り出させる命令です。
glTexSubImage2D(obj->target, obj->miplevel, 0, 0, obj->width, obj->height,
obj->gl_fmt, obj->gl_type, obj->pixels);
glFlush(); /* 追加:たまっている命令をすぐにGPUへ送り出す */
これで、1コマ送りが順番どおりに進むようになりました。また、ProResの素材を再生して、スムーズに
再生できると感じられるところまで来ました(ただ、その後使っていく中では、再生がカクつく場面もありました)。
実戦編集とワークスペースの自動化
プレビューが安定したところで、実際の素材を読み込みます。ここで注意が必要なのが、Linux版の無料
版が対応している形式です。
Linux無料版について調べたところ、以下の仕様が分かりました。
- 映像:H.264/H.265 の MP4 は読み込めない。編集用によく使われる Apple ProResに対応。
- 音声:AAC 形式は読み込めない。非圧縮のLinear PCM(WAVなど)は対応。
Chromebookのレコーダーアプリで録音した音声( .webm )も、そのままでは使えません。
毎回手動で変換するのは大変なので、「指定のフォルダに入れるだけで、裏側で編集用形式に変換してくれる仕組み」を作りました。
- 01_Drop_Videos_Here:スマホやカメラの動画(mp4、movなど)や音声を入れるフォルダ
- 02_Ready_For_Resolve:変換済みのファイルが置かれるフォルダ。動画は ProRes 422(10ビット4:2:2)+ PCM音声、音声ファイルは WAV(48kHz)になります
- 03_Projects:プロジェクト用のフォルダ。レコーダーの音声(webm など)を入れると、同じ名前の WAV がその場に作られます
- 04_Exports:Resolveから書き出したファイルを置くフォルダ。 .mov を置くと、同じ名前の .mp4(H.264 / AAC)が自動で作られます
変換の中身は ffmpeg です。たとえば動画の変換は、次のようなコマンドです。
ffmpeg -y -i input.mp4 \
-c:v prores_ks -profile:v 2 -pix_fmt yuv422p10le \
-c:a pcm_s16le -ar 48000 \
output.mov
フォルダの監視は、 inotifywait (ファイルの変化を検知するコマンド)を使ったスクリプトを、
systemd のユーザーサービスとして常駐させています。コピー中のファイルを変換し始めないように、
ファイルサイズが変わらなくなってから変換するようにしています。
注意点が2つあります。
- ProResは編集しやすい代わりに、ファイルがかなり大きくなります。手元の 1080p の動画では、1分あたり約143MBだったものが、変換後は約1.1GBになりました。ストレージの空きには気をつけてください
- 04_Exports の自動変換では色の形式を指定していないため、ProResの 4:2:2 のまま H.264 になります(H.264 High 4:2:2)。この形式は再生できない機器もあるので、幅広く再生させたい場合は、ffmpegに-pix_fmt yuv420pを付けて4:2:0にするのが無難です
Resolveからの書き出しは、QuickTime 形式の ProRes(422 HQ)で行いました。それを04_Exportsに置けば、MP4が自動でできます。
実際の様子
以下が実際の様子です(画面録画、約2分15秒)。
画面録画を同時実行しているため、単体動作時よりは全体的なスムーズさが少し落ちています。
カット編集、トランジション、タイトル、カラーグレーディングまでは動き、書き出しまで完走もできたので、やろうと思えば編集できるかなと思いました。
おわりに
今回もChromeOSを日常使いしたい人向けの記事としてお送りしました。
今回の作業は、互換レイヤーのコードも、素材を変換するスクリプトも、すべてAntigravityに書いてもらいました。私がやったのは、実際にResolveを動かしてみて「ここで止まった」「縦線が出た」と伝え、返ってきた修正を試す、ということの繰り返しです。プロンプト一発でうまくいったわけではなく、やり取りを重ねて2日ほどかかりましたが、CのコードやOpenCLの仕組みを自分で一から調べていたら、ここまではたどり着けなかったと思います。
ChromebookでのDaVinci Resolveは、プレビューの動きなど万全とは言えないものの、カット編集からカラーグレーディング、書き出しまで、ひと通りの作業はこなせました。
専門知識がなくても、「やってみたいこと」があれば形にしていける時代になってきています。まさに一億総クリエイター時代ですね。
ちなみに、執筆時点ではまだ実機が届いていませんが、新しく登場したGooglebookはChromebookよりも、クリエイティブな作業のためのPCを目指しているのではないか、と個人的には感じています。

コメント