宮のレーザー加工所

レーザー加工機・3Dプリンタ・卓上旋盤・卓上フライスなどなど。。趣味で集めた機械での遊びを発信するブログ。工作機械全般の話になってきたので題名変更しました。

OpenShotを動画自動化の実行エンジンにする——音声・字幕・タイムラインを再現可能に組み立てる方法

動画編集を続けていると、映像そのものよりも、素材を並べる、音声を合わせる、字幕を表示する、少しだけ位置を直す、といった反復作業に時間を取られます。しかも、同じ形式の動画を何本も作る場合、手作業では微妙なズレや設定漏れが積み重なります。

そこで、動画編集ソフトを人間が毎回操作するのではなく、編集内容をデータとして組み立て、ソフトに適用する方法を試しました。本稿では、その実験で得られたOpenShot自動化の考え方と構成を、特定の個人・企業・業務に依存しない形で整理します。

まず、なぜ自動化したかったのか

動画制作で本当に難しいのは、1本を完成させることだけではありません。同じルールで何度も作り直せること、途中で失敗しても安全に復旧できること、別の素材に置き換えても同じ手順を使えることが重要です。

手作業中心の編集には、次のような問題があります。

  • 音声と字幕の開始位置を毎回合わせる必要がある
  • 字幕が長いと、画面端からはみ出す
  • クリップを途中で分割すると、後続の字幕や効果の位置調整が必要になる
  • 大量の素材を一度に操作すると、見落としやクリックミスが起きる
  • 失敗したとき、どの操作まで完了したか分かりにくい

この問題を「編集者の熟練度」で解決するのではなく、「編集計画を生成し、適用し、検証する仕組み」に置き換えるのが狙いでした。

ベースソフトにOpenShotを選んだ理由

自動化の土台にはOpenShotを使いました。理由は、一般的な動画編集ソフトとしての使いやすさと、プロジェクトをタイムラインとして扱えることのバランスがよかったからです。

完全なノンリニア編集を自動生成する専用レンダラーを最初から作ると、映像・音声・字幕・エフェクト・プレビュー・保存など、非常に多くの機能を自前で持つ必要があります。一方、OpenShotを実行エンジンとして使えば、人間が必要に応じて微調整できる編集画面を残しながら、反復作業だけを機械に任せられます。

ここでのポイントは、OpenShotを単なるGUIとしてではなく、最終編集状態を保持するタイムラインの実行先として捉えたことです。自動化側は「何を、いつ、どのレイヤーに置くか」を計算し、OpenShot側はそれを再生・表示・保存・書き出しにつなげます。

全体構成:素材ではなく編集計画を受け渡す

構成は、次のように分けました。

  1. 入力素材を準備する
  2. 音声や字幕に時間情報を付ける
  3. 編集計画を中間データとして生成する
  4. OpenShotへ計画を適用する
  5. タイムラインを検証する
  6. プレビューや保存状態を確認する

重要なのは、各段階を直接つなげず、中間データを残すことです。たとえば、音声セグメントの一覧をマニフェストとして保持し、その各項目に開始時刻、終了時刻、テキスト、ファイル名などを持たせます。

この形式にすると、次の処理は「マニフェストを読み、必要なクリップと字幕を生成する」だけになります。LLMや別の自動化プログラムが途中から参加しても、自然言語の会話履歴ではなく、構造化されたデータを入力にできます。

音声と字幕を同じ時間モデルで扱う

音声と字幕のズレを防ぐため、両者を同じ開始時刻・終了時刻から生成します。

概念的には、次のようなデータです。


{
  "index": 1,
  "start": "00:00:00.000",
  "end": "00:00:02.000",
  "text": "ここに字幕の文章が入ります。",
  "audio": "segment-001.wav"
}

この項目から、音声クリップは音声レイヤーへ、字幕クリップは字幕レイヤーへ、同じ start を基準に配置します。字幕の表示時間も end - start から求めるため、音声だけ、字幕だけを個別に手修正する必要が減ります。

また、動画本体、効果音、合成音声、字幕をレイヤーとして分けました。レイヤーの役割を固定すると、後から「音声だけを差し替える」「字幕だけを再生成する」といった部分更新がしやすくなります。

JSON編集計画で操作を小さく分ける

自動化の操作は、巨大な一括処理にせず、小さな命令の集合として表現します。たとえば、次のような操作です。


{
  "operation": "apply_edit_plan",
  "edits": [
    {"operation": "add_clip", "path": "video.mp4", "position": 0, "layer": 0},
    {"operation": "add_clip", "path": "narration.wav", "position": 2.0, "layer": 2},
    {"operation": "add_clip", "path": "caption.svg", "position": 2.0, "duration": 2.5, "layer": 3}
  ]
}

実際のファイル名やパスは環境ごとに異なるため、記事では一般化しています。大切なのは、命令を「素材の追加」「位置の指定」「レイヤーの指定」「表示時間の指定」のように分解することです。

処理が大きい場合は、編集計画を複数のチャンクに分けます。少数のクリップを適用して状態を保存し、次のチャンクを適用する方式です。これにより、1回の失敗で全体を失うリスクを下げ、どの範囲まで反映されたかを確認できます。

字幕は文字数ではなく画面上の幅で分割する

字幕では、単純に「何文字で改行する」というルールだけでは不十分です。日本語、英数字、記号では文字幅が異なり、縁取りを付けるとさらに表示領域を消費します。

そこで、字幕生成では次の条件を使いました。

  • 動画フレームの左右に安全領域を設ける
  • 使用フォントとフォントサイズを固定する
  • 縁取りの幅を含めて文字列の実測幅を求める
  • 文節や句読点など、意味のまとまりを壊さずに改行する
  • 2行で収まらない場合だけ3行を検討する

候補となる改行を複数生成し、実測幅、安全領域への収まり、行の長さのバランス、意味の区切りを評価して選びます。この方法では、「文字数は少ないのに画面からはみ出す」「単語の途中で不自然に切れる」といった問題を減らせます。

字幕を画像やSVGなどの独立した素材として作る方式と、OpenShotの字幕エフェクトを使う方式も検討しました。前者は見た目を厳密に管理しやすく、後者は編集画面での再利用性が高いという違いがあります。どちらを選ぶ場合も、字幕の内容・時刻・表示領域を別データとして管理しておくと、再生成が容易です。

失敗しやすい操作には状態と検証を持たせる

タイムライン編集では、見た目には正しくても内部状態が期待と違うことがあります。たとえば、クリップの分割後に後続要素の時刻がずれている、音声が別レイヤーに入っている、字幕が意図しない長さで表示されている、といった問題です。

そのため、操作ごとに次の情報を扱います。

  • 適用前のリビジョンや状態
  • 実行した操作の種類
  • 適用後の状態
  • 期待するクリップ数、レイヤー、時刻範囲
  • 保存済みかどうか
  • プレビューで確認した結果

特に重要なのは、編集後に「成功したように見える」だけで終わらせないことです。タイムライン検証、特定時刻へのシーク、プレビュー画像の確認、保存後の再オープンなどを組み合わせます。

クロスフェードや分割のように、周辺クリップへ影響する操作は、単独のテスト用タイムラインで先に確認します。実際のプロジェクトへ適用する前に、境界位置、連続性、音声の有無、字幕の追従を検証しておくと安全です。

LLMにとって扱いやすい自動化にする設計原則

LLMを自動化の中心に置く場合、自然言語だけで状態を管理しないことが重要です。LLMは計画の生成や例外への対応が得意ですが、長いタイムラインの全要素を会話だけで正確に記憶する用途には向きません。

そこで、次のような契約を設けます。

  • 入力:素材一覧、時間情報、字幕テキスト、プロジェクト設定
  • 計画:追加・分割・移動・削除などの編集命令
  • 期待結果:時刻範囲、レイヤー、字幕数、保存状態
  • 検証結果:成功・失敗、差分、プレビュー確認結果
  • 再実行条件:同じ計画を再適用してよいか、リビジョンが一致するか

この構造なら、LLMは「次に何をすべきか」を判断し、プログラムは「正確な数値とファイルを扱う」役割を担えます。人間は、最終的な見た目や内容の妥当性を確認できます。

得られたメリット

この方式のメリットは、単なる作業時間の短縮だけではありません。

再現性が上がる

編集計画と入力データが残るため、同じ条件なら同じタイムラインを再構築できます。担当者の操作順や記憶に依存しにくくなります。

部分修正がしやすい

字幕だけ、音声だけ、特定区間だけを再生成できます。最初から全編集をやり直す必要がありません。

品質基準をコード化できる

字幕の安全領域、行数、音声と字幕の同期、レイヤー構成などを検査条件として明文化できます。

失敗から復旧しやすい

小さなチャンク、状態保存、リビジョン確認、再オープン確認を組み合わせることで、途中状態から安全に続行できます。

人間とLLMの役割分担が明確になる

LLMは素材の意味理解、編集計画、例外の説明を担当し、決められた数値操作と検証は機械的に実行できます。

注意点と限界

自動化しても、すべての判断が不要になるわけではありません。字幕の自然さ、音声の聞きやすさ、映像との意味的な一致、演出としてのテンポは、最終的に人間が確認した方がよい領域です。

また、OpenShotのバージョン、字幕エフェクトの仕様、利用するフォントやレンダリング環境によって表示結果が変わる場合があります。自動化の命令を固定的な魔法として扱わず、対象バージョンと検証結果を記録することが大切です。

まとめ

OpenShotの自動化で重要だったのは、GUI操作を無理に再現することではありません。音声・字幕・タイムラインを構造化し、編集計画として受け渡し、段階的に適用し、検証可能な状態として保存することです。

この考え方を採用すると、動画編集は「人が毎回クリックする作業」から、「入力データに対して再現可能な編集パイプラインを実行する作業」へ変わります。OpenShotはその実行先として、人間による最終確認と自動生成の間をつなぐ、扱いやすいベースソフトになります。

家庭用監視カメラをYouTubeに残す(1/4)全体構成とRTSP映像の確認

この取り組みを始めた背景

出発点は、家庭に設置された監視カメラの映像を見るために、古い親機のWeb管理画面とInternet Explorer互換機能へ頼らざるを得ないことでした。親機によってはActiveXなどIE固有の仕組みを前提としており、通常のEdgeでは画面の一部が正しく動作しません。

EdgeのIEモードは当面の閲覧手段にはなりますが、ブラウザーの互換機能、サイト登録、端末のポリシー設定といった特定環境に依存します。一般の利用者が自力で設定・維持しにくく、今後のブラウザーやOSの変更をまたいで同じ手順が使えるとも限りません。IEそのものを使い続ける方法も、安全な長期策とは言えません。ここでは「IEモードが近く廃止される」と予測するのではなく、旧式の管理画面を唯一の閲覧経路にしないことを目標にしました。

そこで親機のWeb画面を配信経路から切り離し、親機がLAN内に提供するRTSP映像をESP32-S3で直接受けてYouTubeへ送る方法を検証しました。カメラ親機や家庭内LANはそのまま活用し、常時起動PCや外部サーバーを置かず、配信イベントを分けて限定公開アーカイブへ残す構成です。旧管理画面は初期設定や録画確認の補助として残しつつ、日常的な映像閲覧・保存をIEモードだけに依存させないことが狙いです。

古い家庭用監視カメラの録画・閲覧環境を、専用PCを常時動かさずYouTubeの限定公開アーカイブへつなぐ構成を紹介します。全体は「親機が出すRTSP映像をESP32-S3が受け、YouTube Liveへ中継し、イベントごとにアーカイブする」という考え方です。

完成するデータ経路

監視カメラ → カメラ親機/NVR → 家庭内LAN → ESP32-S3 → インターネット → YouTube
                                       ├─ HTTPS:YouTube Live API
                                       └─ RTMP:映像送信

Windows PC:初期設定・OAuth認証・書込み・シリアル確認だけ
             本番時の常時中継には使わない

カメラからYouTubeへPC経由で映像を運ぶのではありません。カメラ親機が家庭内LANへ公開するRTSPストリームをESP32-S3が直接読み、ESP32自身がLive APIと映像送信を行います。そのため常時稼働サーバーは不要です。

なぜESP32-S3で試せたのか

ESP32-S3は一般的なLinux PCではなく、用途を絞ったマイコンです。今回の入力はH.264だったため、映像をデコードして別形式へ再エンコードするのではなく、RTPからFLV/RTMPへ組み替えて送ります。この経路では実行時FFmpegは必須ではありません。実証した設定はFHD(1920×1080)/4fpsですが、同じボード名でもPSRAM、Flash、無線環境、カメラ設定によって結果は変わります。

必要なもの

  • RTSPを提供するカメラ親機/NVRと、その設定を確認できる権限
  • PSRAM搭載のESP32-S3ボード
  • ESP32-S3が接続できる2.4GHz Wi-Fiと、親機・インターネットへ通信できる家庭内LAN
  • 初期設定とUSB書込みに使うWindows PC、データ通信対応USBケーブル
  • YouTube Liveが利用可能なチャンネルと、Google Cloud/OAuthの管理権限

ESP32-S3は2.4GHz帯のWi-Fiを使います。カメラ親機と同じLANへ無線参加できれば、有線LANアダプターは必須ではありません。ゲストSSIDやVLANの分離設定により相互通信が拒否される場合があるので注意します。

最初にカメラ親機のRTSP情報を調べる

メーカーのロゴや旧Web画面だけで、内部の録画機構やプロトコルを決めつけないようにします。親機の取扱説明書、管理画面、既存の閲覧ツールで、親機が実際に提供するRTSP映像を確認します。記録しておく値は、ホスト名またはIP、RTSPポート、ストリームパス、認証ユーザー、映像コーデック、解像度、フレームレートです。

接続URIへユーザー名とパスワードを埋め込んだ状態で共有・記録しないでください。認証情報はローカル設定へ分離します。カメラの映像プロファイルがH.264であることも確認します。MJPEG/JPEGしか得られない場合は今回のH.264パススルー構成と同じではなく、圧縮・映像形式変換の別設計が必要です。

ESP32自身で受信試験する

PCのブラウザーで親機ページが開くことだけでは、ESP32から映像を取れる証明になりません。まずRTSP probe用ファームウェアをESP32へ書き込み、段階別に確認します。

  1. Wi-Fiに接続し、親機へLAN到達できる。
  2. RTSP Digest認証とSDP取得が成功する。
  3. SETUP/PLAY後にRTP over RTSP/TCPのフレームが増える。
  4. 受信した映像が想定したH.264プロファイルで、PSRAMに必要なバッファを確保できる。

ここまで合格したら、次の記事の開発環境とローカル秘密設定へ進みます。旧IEモード画面は、必要なら初期設定・能力確認の補助として使えますが、この配信経路の必須部品ではありません。

連載の目次

  1. 全体構成とRTSP映像の確認(この記事)
  2. ESP32-S3開発環境と秘密設定
  3. Google Cloud・OAuth・再生リスト準備
  4. 配信の切替・アーカイブ・運用確認
  5. 付録:実機で遭遇しやすい課題と安全上の注意

この連載は実証環境の設計と再構築手順をまとめたものです。ファームウェアのソース一式や個別環境の認証設定は公開していません。ソースを別途入手できない場合、本文のビルド手順だけで同一のファームウェアを再現できるわけではありません。

付録:実機で遭遇しやすい課題と安全上の注意 — 家庭用監視カメラからYouTubeへ

この記事は、家庭用カメラ親機のRTSP映像をESP32-S3からYouTubeへ送る連載の付録です。主手順ではなく、旧管理画面、代替機器、障害切り分け、セキュリティと保存上の注意をまとめます。

旧カメラ管理画面とIEモード

古いカメラ親機のWeb管理画面では、ActiveXなどInternet Explorer固有の部品に依存する場合があります。通常のEdge/Chromiumでは映像表示や再生画面が動かず、IEモードで初めて表示されることがあります。IEモードは設定確認の補助手段として残し、長期の映像中継経路には組み込まない設計にしました。

Windows用の独立GUIは、JPEG一枚取得、JPEGの周期更新プレビュー、親機に保存済みの録画・画像の検索とPCへの保存を担当できます。一方、JPEGポーリングはRTSP動画と同じフレームレートや遅延を示しません。親機に録画を開始させたり、すべての旧Web UI機能を置き換えたりするものでもありません。

iPhoneや別の小型機器で代用できるか

iPhoneでLAN内のRTSP映像を見られることと、その外部映像をRTMP/RTMPSでYouTubeへ再配信できることは別機能です。アプリごとに入力プロトコル、出力先、バックグラウンド動作、コーデックと課金条件を確認します。iPhone内蔵カメラを配信できても、ネットワークカメラを中継できるとは限りません。

ESP32-S3はLinuxを動かすSBCではなく、専用ファームウェアを実行するマイコンです。今回の用途ではカメラのH.264を再エンコードせず、RTPからFLV/RTMPへ組み替える構成にしています。トランスコードや複数入力、複雑な映像合成が必要なら、Linux SBCやサーバーを別途検討します。

よくある症状の切り分け

症状最初に確認すること次の対応
RTSP認証後に映像が来ない親機とESP32のLAN到達性、RTSPパス、SDP、H.264、RTP over TCPRTSP probeで認証・SETUP・PLAY・フレーム受信の段階を分けて確認
PSRAM確保に失敗するボードのFlash/PSRAM仕様とArduinoのボード設定適切なPSRAMモードを選び、起動ログを確認してから再試験
録画検索でXMLエラー親機が受け付けるISAPI検索XML、検索条件、識別子とページング別機種・ファームウェアでは要求形式を改めて実機確認
録画ダウンロードがHTTP 403APIメソッド、URL、認証、親機の応答段階403を直ちにパスワード誤りと決めず、実機が要求するHTTPメソッドを確認
YouTube Live APIが403OAuth主体と対象チャンネル、ライブ資格、既存イベント、エラー理由、利用制限新規イベント作成を連打せず、状態照会とバックオフで原因を特定
終了後もアーカイブが処理中Broadcastの完了状態とStudioの処理状態時間をおいて再確認。状態不明のイベントを重複作成しない

画質・アーカイブと運用上の限界

確認した入力条件はFHD(1920×1080)/4fpsのH.264です。別カメラの解像度、フレームレート、ビットレート、GOPでは負荷もYouTube ingestとの互換性も変わります。成功した機器構成を他環境で保証するものではありません。

YouTubeのライブアーカイブは配信終了後すぐ視聴可能になるとは限らず、処理が非同期に続きます。公式ヘルプでは12時間未満のライブは自動アーカイブの対象になり得る一方、12時間を超えるライブはアーカイブされないと案内されています。重要な映像はローカル録画も検討してください。8時間ごとのイベント切替にも短い無記録区間が生じる可能性があります。

通信と秘密情報の注意

  • RTSP Digestは認証方式であり、映像ストリームの暗号化ではありません。
  • このファームウェアの映像用RTMPソケットはTLSで包まれていません。API用HTTPSのTLS検証が有効でも、RTMP映像まで暗号化されるわけではありません。
  • stream keyやOAuth tokenを含むローカル設定・コンパイル済みファームウェアを公開しないでください。漏えいが疑われた場合はYouTube Studioでstream keyを変更し、再書込みします。
  • SSID、カメラ認証情報、IPアドレス、OAuth応答本文、再生リストIDをシリアルログや公開記事へ貼らないでください。
  • 限定公開はURLを知る人が視聴・再共有できる方式です。特定アカウントだけに制限する場合は非公開動画の個別招待が必要です。

この連載は設計・構築手順の記録です。ファームウェアのソース一式や秘密設定は記事に添付していません。ソースを別途入手できない場合、記事中のビルドコマンドだけでは同じファームウェアを生成できません。

公式資料

主手順は4回の連載として順に掲載します。まずは「構成とカメラRTSP映像の確認」からお読みください。

主手順の連載はこちら:第1回:全体構成とRTSP映像の確認

家庭用監視カメラをYouTubeに残す(2/4)ESP32-S3開発環境と秘密設定

第2回では、ESP32-S3の開発環境を用意し、ボードとカメラ、Wi-Fi、YouTubeの設定値を安全に分けます。最初にRTSP受信専用の確認用ファームウェアを動かし、映像を取れることを確かめてから本番スケッチへ進みます。

PCとボードの役割

Windows PCはArduino CLIの導入、OAuthの初期認証、ビルド、USB経由の書込み、シリアルログの確認に使います。配信中にPCを常時起動したり、常時稼働サーバーを借りたりする構成ではありません。ESP32-S3はOSを起動せず、専用ファームウェアで動作します。

データ通信対応のUSBケーブルでボードを接続し、ポートを確認します。ボードにUSB端子が複数ある場合は、USB-UART/書込み機能とUSB-OTG機能の違いを製品資料で確認してください。電源専用ケーブルでは書込み・シリアル通信ができません。

Arduino CLIの準備

Arduino CLIとEspressif公式Arduino-ESP32 coreを導入します。JSON処理にはArduinoJsonを使います。

arduino-cli config add board_manager.additional_urls https://espressif.github.io/arduino-esp32/package_esp32_index.json
arduino-cli core update-index
arduino-cli core install esp32:esp32
arduino-cli lib install ArduinoJson
arduino-cli board list

CLIの導入手順はOSやリリースにより変わる場合があるため、Espressif公式の現行説明も参照します。

ボード仕様とPSRAMを照合

この実証で使ったESP32-S3 WROOM-1系の設定例は次のFQBNです。これは同じFlash/PSRAM構成のボード用で、すべてのESP32-S3にそのまま使える設定ではありません。

esp32:esp32:esp32s3:PSRAM=opi,FlashSize=16M,PartitionScheme=default_8MB

注文ページの「ESP32-S3」という名前だけで判断せず、搭載モジュール、Flash容量、PSRAM容量と種類を確認します。PSRAMが無効または認識されないと、動画用バッファを確保できません。起動時ログでPSRAM検出と確保量を確かめてください。

環境ごとの設定と秘密値を分離

設定項目をスケッチへ直書きする場合でも、公開リポジトリに秘密値を含めないローカル専用ヘッダーへ分けます。利用者ごとに設定するのは次のような値です。

  • 家庭内Wi-FiのSSIDとパスフレーズ
  • 親機のホスト、RTSPポート、ストリームパス、認証ユーザーとパスワード
  • Google OAuthクライアント情報、refresh token、再利用Live Stream ID
  • YouTube RTMP stream key、限定公開再生リストID
  • 配信タイトルに使う場所名、試験/本番サイクル設定

RTSP URIへID・パスワードを埋め込んだまま共有しないでください。ローカル設定ファイルをGit ignoreにするだけで安全が保証されるわけではありません。認証JSONはリポジトリ外へ置き、生成ヘッダーだけでなくコンパイル済みバイナリも秘密値を含む前提で保護します。ビルドログやシリアル出力にも認証情報、完全なURL、トークンを出さないでください。

まずRTSP probeを動かす

本番スケッチへ進む前にRTSP probeを書き込み、ボード自身から親機へ接続できることを確認します。確認順はWi-Fi接続、RTSP Digest認証、SDP/H.264情報、SETUP/PLAY、RTPフレーム受信です。PCから見えることだけでは、ESP32から同じLANへ到達できる証明になりません。

カメラとボードの間にゲストWi-Fi分離やAPクライアント分離がないかも確認します。映像がH.264でない、PSRAMが確保できない、RTPフレームが増えないといった問題を一つずつ解消してから本番用スケッチへ進みます。

ファームウェアソースについて

ここで示すCLIコマンドは、該当するファームウェアのソースツリーが手元にある前提です。この連載にはソース一式を添付していません。コードを別途入手できない場合、手順だけで同じ実装をコンパイルできるわけではありません。

公式資料

次に読む:第3回:Google Cloud・OAuth・再生リストの準備

家庭用監視カメラをYouTubeに残す(3/4)Google Cloud・OAuth・再生リスト準備

第3回では、YouTube Live APIを使うためのGoogle Cloud設定、OAuth認証、YouTubeチャンネルと再生リストの準備を行います。最初の認証・ファームウェア書込みにはPCを使いますが、本番の映像中継に常時PCやサーバーは必要ありません。運用時はESP32-S3がAPI呼出しと映像送信を担います。

1. Google CloudでAPIとOAuthクライアントを準備

  1. 自分が管理するGoogle Cloudプロジェクトを選び、YouTube Data API v3を有効にします。
  2. OAuth同意画面を設定します。公開ステータスがTestingの場合は、実際に認証するGoogleアカウントをテストユーザーへ登録します。
  3. アプリの実行形態に合うOAuthクライアントを作成します。初回認証をPCのブラウザーで行う構成ではDesktop app型を使います。
  4. ダウンロードしたクライアントJSONをリポジトリ外のローカル保護フォルダーに保存します。

Google OAuthは権限スコープを通じて操作範囲を許可します。YouTube Live APIや再生リストを更新できるスコープは強い権限を持つため、誰のアカウントを認証しているか、どのチャンネルを操作するかを慎重に確認してください。OAuthの設定、審査、トークン失効条件は変わり得るので、構築時に公式要件を確認します。

2. PCで対話認証し、refresh tokenを保護

PC上の認証ヘルパーからブラウザーを開き、意図したGoogleアカウントでログインしてアクセスを承認します。ローカルの一時コールバックが完了したら、refresh tokenを含むファイルをリポジトリや同期共有フォルダーの外へ保管します。ESP32はこのrefresh tokenで短命のaccess tokenを取得し、YouTube APIを呼びます。

認証画面で複数チャンネルを選べるアカウントでは、アカウント名だけでチャンネルを特定したと思い込まないでください。最初に作成したテストイベントが目的のチャンネルのYouTube Studioに現れることを確認してから運用へ進みます。

OAuth同意画面が外部ユーザー向けTesting状態の場合、YouTubeのような基本プロフィール以外のスコープを使うrefresh tokenは7日で失効する場合があります。無人運用の前に公開状態と検証要件を確認し、再認証の方法も用意してください。

3. 配信用Streamを用意する

YouTube StudioまたはLive APIで配信入力先となるLive Streamを準備し、以後のイベントで再利用します。ここには似た名前の別情報が出てきます。

  • Stream ID:Live APIがBroadcastとStreamを結び付けるためのリソース識別子。
  • stream key:RTMP送信元がYouTube ingestへ認証するための秘密値。

この二つは交換できません。現行ESP32コードはStream IDをAPI状態確認に用い、stream keyはローカル設定からRTMPに使います。APIのStream一覧取得からstream keyを読み出す設計ではありません。stream keyを記事、チャット、ログ、公開ソースへ貼らないでください。

4. 限定公開再生リストを作る

対象チャンネルでアーカイブ用の再生リストを作成し、公開範囲を「限定公開」にします。Playlist IDをローカル設定へ記録します。限定公開はURLを知る人が視聴・再共有できる設定で、特定Googleアカウントだけに絞るアクセス制御ではありません。厳密な個別制限が必要なら動画を非公開にして視聴者を招待します。

再生リストの権限と動画の公開状態は別々に確認します。限定公開動画を公開再生リストへ追加しないでください。

本番へ進む前の確認

  • Google Cloudの認証アカウントが対象チャンネルを操作できる。
  • YouTube Studio上でライブ配信機能が利用可能である。
  • Live Streamとstream keyを取り違えず、ローカル秘密設定に入れた。
  • OAuth token、client JSON、Playlist IDを公開物やログへ含めない。
  • テストイベントが意図したチャンネルに作成されることを確認した。

公式資料

次に読む:第4回:配信の切替・アーカイブ・運用確認

家庭用監視カメラをYouTubeに残す(4/4)配信の切替・アーカイブ・運用確認

連載第4回では、ESP32-S3からYouTube Liveへ映像を送り、8時間ごとに独立した配信イベントを完了させ、限定公開アーカイブを再生リストへ入れる運用をまとめます。第1回から第3回で説明したカメラRTSP、ボード設定、OAuth準備が済んでいることを前提にします。

配信イベントと配信ストリームは別のもの

YouTube Live APIでは、配信イベント(Broadcast)と映像入力先(Stream)は別リソースです。各サイクルで新しいBroadcastを作り、事前に用意したStreamを再利用して結び付けます。新しいイベントを短時間に何度も作るとチャンネル側の資格・制限に達することがあるため、試験イベントを連打しないでください。

起動からライブ開始まで

  1. ESP32の時刻をNTPで同期します。時刻が確定しない場合はイベントを作らないようにします。
  2. liveBroadcasts.insertでタイトル、開始時刻、限定公開設定を持つ新しいBroadcastを作ります。復旧に必要な識別子と状態はNVSに保存します。
  3. liveBroadcasts.bindでBroadcastを再利用Streamへ結び付けます。Stream IDはAPI上の識別子であり、RTMP認証に使うstream keyとは別です。
  4. ローカルの秘密設定にあるstream keyでYouTube ingestへRTMP接続し、RTSPから受けたH.264映像をFLV/RTMPとして送信します。この構成では映像をデコード・再エンコードしません。
  5. YouTubeがStreamをactiveと報告したことを確認してから、Broadcastをliveへ遷移させます。遷移中もメディア送信とRTMP ping応答を続けます。

APIのイベント作成・bind・状態遷移と、RTMPの接続・映像送信は別段階です。Studioで映像が見えることと、API状態がliveであることを両方確認します。

8時間ごとに別アーカイブにする

このファームウェアの本番周期は8時間です。YouTubeの案内では12時間未満のライブは自動アーカイブの対象になり得ますが、12時間を超えるライブはアーカイブされません。8時間単位に分けることで、一本の長時間配信に依存しない記録を目指します。ただし、各イベントの録画・公開が必ず成功する保証ではなく、YouTube側の処理時間も一定ではありません。

終了時は、まずRTMPメディア送信を停止し、Live APIでBroadcastをcompleteへ遷移させます。その後、動画処理状態を照会します。録画が「処理中」の間は、すぐに削除や再作成をせず時間をおいて確認します。イベント間に短い無記録時間が生じる可能性があります。

限定公開再生リストへ登録

対象チャンネルに限定公開の再生リストをあらかじめ作り、Playlist IDをローカル設定に保存します。現行実装は、ライブ開始後に配信動画を一度登録し、イベント完了後には一覧を調べて重複を避けてから未登録分を追加します。後処理は録画が利用可能になるまで待つ必要があり、即時に再生リストで視聴できるとは限りません。

ライブ中の再生リスト追加に失敗しても、映像配信自体は止めません。イベント完了・録画準備後の再試行結果を確認します。Playlist IDや限定公開URLは認証情報同様、公開ログや記事へ載せないでください。

再起動と失敗時の運用

  • 既存イベントの状態が分からないときは、新規イベントを重ねて作らず、保存済みIDでAPI照会します。
  • 電源断ではRTMPが突然切れ、YouTube側の終了検出や録画確定が遅れることがあります。再起動後に既存イベントを確認してから復旧します。
  • このファームウェアの通常失敗は2分から指数バックオフし、最大6時間まで延長します。APIレート制限時は最低1時間待機します。これらは本実装の値であり、YouTube共通の要件ではありません。
  • HTTP状態だけでなく、APIが返す理由、失敗段階、次回再試行時刻を確認します。認証情報を含み得るレスポンス本文はそのまま共有しません。

受入テスト

  1. RTSP probeで、ESP32からの認証・H.264受信・PSRAM確保を確認する。
  2. Live APIで意図したチャンネルにBroadcastが作られ、再利用Streamへbindされることを確認する。
  3. RTMP接続後、Studioで映像を確認し、Stream activeとBroadcast liveの両状態を照合する。
  4. タイトルが「場所 YYYY-MM-DD HH:mm」の日本時間になっていることを確認する。
  5. ライブ中のPlaylist登録と、失敗時の完了後再試行をログで確認する。
  6. 終了後にBroadcast complete、アーカイブ処理、再生リスト登録を読み戻して確認する。
  7. 8時間連続試験、電源再投入、ネットワーク断をそれぞれ分けて試験する。数時間の動作実績だけで長期無停止を保証しない。

この実証構成ではFHD(1920×1080)/4fpsで映像を送信できました。機種やルーターが異なる場合は入力条件から再試験します。ファームウェア全ソースはこの連載に添付していません。

公式資料

次に読む:付録:実機で遭遇しやすい課題と安全上の注意

Codexからはてなブログに記事を投稿・公開する方法 — AtomPub API実践

Codexを使って、はてなブログに記事を投稿し、下書き確認を経て公開する方法をまとめます。ブラウザー画面を自動操作するのではなく、はてなブログ公式のAtomPub APIをHTTPS経由で利用する手順です。

結論:下書き投稿と公開を段階に分ける

安全に運用する基本形は、接続確認 → 下書き作成 → 内容確認 → 公開 → 公開状態の再確認です。APIには記事の新規投稿・取得・更新の機能があり、公開状態も記事データで指定できます。

1. 用意する情報

  • ルートエンドポイント:投稿対象ブログの管理画面にある「設定 → 詳細設定 → AtomPub」で確認します。
  • はてなID:Basic認証のユーザー名として使います。
  • APIキー:Basic認証のパスワードに相当します。アカウント設定で確認し、通常のログインパスワードとは別に扱います。

この環境では、エンドポイントとAPIキーを hatena_root と hatena_api という環境変数から読み込みました。値をソースコード、記事、会話、ログに書かないのが重要です。環境変数も秘密情報を完全に保護する保管庫ではないため、共有端末では利用範囲を絞り、リポジトリへ登録しないでください。認証はHTTPSで行います。

2. まず読み取りで接続を確かめる

ルートエンドポイントに認証付きのGETを送り、HTTP 200とAtomPubのサービス文書が返ることを確認します。サービス文書から記事コレクションのURLを確認できます。認証情報を表示せずに、接続先・応答形式・投稿先が意図したブログかを確かめてから書き込みます。

3. 最初の投稿は必ず下書きにする

新規記事は、記事コレクションにAtom XMLをPOSTして作成します。記事タイトルと本文に加え、app:control/app:draft を yes に明示します。省略すると下書きではない扱いになるため、テスト時に省略してはいけません。

POST {hatena_root}/entry
Content-Type: application/atom+xml;charset=utf-8;type=entry

<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:app="http://www.w3.org/2007/app">
  <title>記事タイトル</title>
  <author><name>はてなID</name></author>
  <content type="text/html">&lt;p&gt;記事本文&lt;/p&gt;</content>
  <app:control><app:draft>yes</app:draft></app:control>
</entry>

成功時はHTTP 201 Createdが返り、レスポンスのLocationヘッダーに作成した記事のメンバーURIが含まれます。そのURIを記録し、GETでタイトルと下書き状態を確認します。投稿リクエストがタイムアウトした場合も、すぐ再POSTせず、まず記事一覧を取得して同じ記事ができていないか確認すると重複を防げます。

4. 人が確認してから公開する

記事の文章、リンク、個人情報、公開範囲を確認した後、対象記事のメンバーURIへPUTします。本文とタイトルを含めた記事データを送り、app:draft を no にします。成功時はHTTP 200が返ります。最後に記事をGETし、下書き状態が no になったことと、レスポンスのalternateリンクを確認します。

この操作は記事を公開状態にします。ブログ自体の公開範囲が限定されている場合は、そのブログ設定も反映されます。Codexに記事作成を任せても、公開は内容を確認してから明示的に依頼する運用が安全です。

5. 権限と失敗時の確認

  • HTTP 401などの場合:HTTPSのURL、はてなID、APIキー、認証ヘッダーを確認します。キーそのものはログへ出しません。
  • HTTP 403などの場合:対象ブログでの権限を確認します。公式仕様では「寄稿者」権限は下書きの作成・編集に限られ、APIから公開できません。
  • 投稿結果が不明な場合:再投稿より先に、記事一覧をタイトルで照合します。
  • 記事記法:本文のcontent typeはブログの編集モードに合わせます。HTML編集のブログではHTMLとして本文を送ります。

今回確認できたこと

2026年9月23日に、AtomPub APIでテスト記事を下書きとして新規作成し、一覧から下書き状態を確認しました。その後、同じ記事をPUTで公開状態へ変更し、再取得して下書きではないことを確認できました。今回の記事は、そのテスト投稿を手順記事に差し替えたものです。

公式資料