DaVinci Resolve 18でゆっくり動画っぽいものをPythonで効率的に作ろうとしたけど断念した話
はじまり




効率化のツールの概要
まず、ゆっくり動画のテンプレを作ります。
テンプレの構成は以下の感じです。
- 左下と右下にキャラクターの画像を配置して、
- キャラクターの音声を再生すると同時に、
- キャラクターの画像および字幕を切り替えます。

このテンプレで、1.は制作者がそのフレームの状況を鑑みて画像を選択すれば良いのですが、
2.と3.に関しては、どこでその音声を流して、字幕を表示させるかどうかは一意に決まっています。
そのため、2.で音声ファイル(.wavファイル)を設置する処理を、
3.でテキストを自動で設置してその内容を自動入力する試みを、今回行いました。

試したこと
今回、自動化を試みた環境は以下のとおりです。
- DaVinci Resolve 18(DaVinci Resolve Studio 18 ではない。)
- macOS Monterey
- Python3
DaVinci ResolveでのPythonの実行方法
まず、DaVinci Resolve Studio 18ではないので、外部ファイルからのResolve向けのスクリプトの実行ができません。そのため、DaVinci Resolve画面上のConsoleからPythonを実行します。

ConsoleからPy3を選んだときに「Python not found」的なメッセージが出たら、Python3をここからインストールします。(僕は既にanacondaが入っているときにその「Python not found」的なメッセージ食らったのですが、Resolveがどの環境変数を読み取っているのかよく分からなかったので、渋々新しくPythonをインストールしました。)

そして、Python3がResolve上で使えるようになったら早速スクリプトを組んでいきます。
参考にした資料は、以下になります。
音声メディアをインポートする。
まず、2.のwavファイルをタイムラインに配置する自動化を試しました。
Console上では、resolve = GetResolve()は記述する必要はなく、既にresolveはクラスとして使える状態になっています。
そして、以下のような記述をして、1つのフォルダに格納されている音声メディア達をメディアプールに入れます。
project_manager = resolve.GetProjectManager()current_project = project_manager.GetCurrentProject(voice_path = "/Users/hogehoge/Downloads/20220912_ghostoftsushima_02/voices"
media_storage = resolve.GetMediaStorage()clips_added = media_storage.AddItemListToMediaPool(voice_path)そして、そのメディアプールに入れた音声メディアをタイムライン上にマーカーで印を付けたところにインポート出来ないかどうかを試しました。
すると、マーカーの取得自体は可能で、返り値のdict型オブジェクトのキーが、frame値になっているので、そのframeをタイムコードに変換してあげれば、音声ファイルの配置箇所の指定は可能そうです。
current_timeline = current_project.GetCurrentTimeline()markers = current_timeline.GetMarkers()次に、音声メディアを指定した場所に配置することが出来るかどうかを試してみました。
結論から言うと、この部分が出来なさそうでした・・・ 以下のようなスクリプトを実行してみましたが、なぜか音声メディアを既にタイムライン上にある最後のメディアの直後に置いてしまうようで、これだとあまり効率化にはなりません・・・
def FRAME_VALUE(time_unit : str) -> int: if type(time_unit) != str: raise TypeError("time_unit must be str.") return_value = 0 FRAME_OF_SEC = 30 FRAME_OF_MINUTE = FRAME_OF_SEC * 60 FRAME_OF_HOUR = FRAME_OF_MINUTE * 60 if time_unit == "sec": return_value = FRAME_OF_SEC elif time_unit == "minute": return_value = FRAME_OF_MINUTE elif time_unit == "hour": return_value = FRAME_OF_HOUR else: raise ValueError("time_unit must be 'sec', 'minute' or 'hour'.") return return_value
def timecode_to_frame(timecode : str, start_frame : int = 0) -> int: COLON = ":" if type(timecode) != str: raise TypeError("timecode must be str.") if type(start_frame) != int: raise TypeError("start_frame must be int.") if COLON not in timecode: raise TypeError("timecode must contain colon.") time_list = timecode.split(COLON) if len(time_list) != 4: raise TypeError("timecode must be \"hour : minute : second : frame\".") frame = sum([int(time_list[0]) * FRAME_VALUE("hour") , int(time_list[1]) * FRAME_VALUE("minute") , int(time_list[2]) * FRAME_VALUE("sec") , int(time_list[3]) ]) frame = frame + start_frame return frame
for i in range(0, len(clips_added), 1): tmp_duration = clips_added[i].GetClipProperty()["Duration"] if i == 0: current_timeline.SetCurrentTimecode('01:18:29:13') if i == 1: current_timeline.SetCurrentTimecode('01:18:33:13') start_frame = 1000 end_frame = start_frame + timecode_to_frame(tmp_duration) print_log(tmp_duration) print_log(end_frame) media_pool.AppendToTimeline([{ "mediaPoolItem": clips_added[i] , "startFrame": start_frame , "endFrame": end_frame }])AppendToTimeline()関数は、引数に開始フレーム位置および開始タイムコードを取りません。
Wiki上では、"startFrame"と記述がありますが、これは「メディア内の」開始フレーム位置なので、タイムライン上の開始フレーム位置を指定することは、この関数上では出来ません。
そのため、SetCurrentTimecode()関数で開始タイムコードが指定できるのかと思い試しましたが、どうやら違うようです。(右下の方に音声メディアが配置できるかなあ?)

既にタイムライン上にある最後のメディアの直後に置いてしまっています・・・失敗・・・(右下ではなく、その左の方に置かれてしまっている。)

以上を試して、他にタイムライン上に音声メディアを指定した位置に配置する術が他に見つからず、断念しました・・・
(ImportIntoTimeline()という関数もあるのですが、この関数はどうやらAAF拡張子のファイルのみが対象になるようで、wavファイルを使って実行したところ、何も配置されませんでした。)
テキストをタイムラインに配置する。
3.のテキストの配置の自動化ですが、こちらは試しませんでした!
2.が出来ないと分かった時点で、効率化の美味みが半分くらい消失していましたし、テキストも音声メディアと同じ理由で配置する場所を指定できるかどうかが疑わしかったため、調査も行っていません。
テキストのプロパティを変える手段としては、以下のような記述になるかと思います。 SetProperty()で編集できるプロパティは、こちらに載っていたので試してみたい方はご参考までに。
timeline_items = current_timeline.GetItemListInTrack("video", 1)timeline_items[0].SetProperty('ZoomX', 2.0)おしまい


以上になります!
記事を共有
この記事が役に立ったなら、ぜひ他の人と共有してください!