こんにちは、IoTLTアドベントカレンダー1日目の記事です!
YouTubeの広告って中々うざいですよね。
そんな広告の煩わしさを(多少)解消するデバイスを作ってみました。
まずは動画。動画だけでも見てくれると嬉しいです。
以下、細かい背景部分と試行錯誤の話にお付き合い下さいませ。
Nintendo Switchで(TV版)YouTubeを見るときのCM問題
僕は普段家でNintendo SwitchをTVに繋げてYouTubeを見ています。
基本的にYouTubeは垂れ流しで他の作業とかスマホ触ったりして、プロコン(スイッチのコントローラー)はどっかに置きっぱなしだったりします。
スプラとかゲームもたまにするのとHDMIポート数節約的にもYouTubeはスイッチで見ちゃうんですよね。
TVだと長尺なYouTube広告
PCやスマホで見る時と違って、何故かTVで流すYouTubeはやたら広告時間が長い ように感じます。正確なエビデンスは調べてないですが90秒とか120秒とか普通に出てきます。(ググると同じようなユーザーの声はありますね。)
TVで動画流しっぱなしというのもあり広告が長いのは仕方ないにしても、基本は他の何かをしつつ、YouTubeは垂れ流しにしてるのでプロコンは触ってない状態が多いです。
広告スキップタイミングを逃して次の広告が流れがち
YouTube広告の基本ですが、広告時間が終わったり、ある程度の時間が経つと スキップ可能ボタン が表示されます。このタイミングでボタンを押すと広告を終了できますが、 このタイミングを逃すと次の広告が流れ始めてしまう場合もあります。
ただ、このタイミングではプロコンは触ってないため、 スキップ可能タイミングを逃して次の広告に遷移させてしまいがちです。
YouTube側もこれを狙ってる気がしますね。より広告を流してくれるTVでの動画垂れ流しユーザーの隙をついて次の広告をスッと流す。
YouTubeの策略にまんまとハマっています。
「"ボタンひとつ押せばええやん"が面倒」なので自動化チャレンジ
まぁボタンひとつ押せばええやんなんですが、そのためにプロコンへ意識をやるのもしんどいので、自動的にスキップしてくれるデバイスを作ってみたいと思います。
Nintendo SwitchのボタンはArduinoなどでエミュレート可能
これは田中みそさん( @miso_develop )がIoTLTで話をしていたのでM5のAtom系やArduino系デバイスでやれそうな雰囲気でした。
USBシリアルでマイコンボードとスイッチを接続し、HIDデバイスとして認識させることでプロコンのエミュレートが可能です。
"広告スキップ可能なタイミングを検知"したらエミュレートしたプロコンもどきでAボタンを押すが出来れば今回のミッション達成です。
スキップ可能なタイミングの数秒を検知できるのか?
というのが今回の実装のポイントです。
YouTube広告が流れているときはこんな雰囲気ですね。
広告が流れているかどうか?やスキップ可能か?を何で判定するか
2025年現在のYouTubeだとスキップ可能な時はこのように "白枠に黒字のスキップボタン" が表示されます。
今回のアプローチは、 このボタン周りを機械学習によって画像判定をすることにしました。
「広告 or 通常動画か? は音声による判定ももしかしたら出来るのかも?」と一瞬思ったのですが、 スキップ可能状態かどうかは視覚的にしか分からない ので今回は TVの画面そのものを撮影 しての画像判定を用いることにしました。
ATOMS3R-CAMが手元にあったのでこれを使って制作していきます。
このカメラデバイスは写真を撮影してプログラムで何か処理ができる + Nintendo Switchのプロコンとしてエミュレート可能なので、以下のようにTV画面を撮影して処理をさせます。
生成AIでは速度とコスパ的に厳しい
この際にテレビの画面を撮影した画像を生成AIに投げたら判定してくれるかも?とも思いますが
体感かつ動画にもよりますが 広告スキップ可能なタイミングは数秒〜50秒の間くらいです。現在の生成AI(VLMなど)だと判定するのに割と時間が掛かってしまい、このタイミングの判定には厳しそうでした。
あと早くて性能が良いモデルだとコストもかかりますし。
ということで、事前に"スキップ可能な状態"と"スキップ不可の状態"を画像で学習させておくことで判定することにしました。
YouTube広告(動画)の3つの状態を学習させる
YouTube広告は右下にスキップ可能かどうかが表示されます。ここを撮影した画像を学習させて、判定時も同じ画角で撮影して判定させます。
今回、YouTubeの動画は広告動画の場合も含めて3つの状態を持っていると仮定しました。
-
1.広告が流れているけどスキップ不可の状態 -
2.広告が流れていて、スキップ可能な状態 -
3.広告ではなく通常の動画状態
これらの状態の画像をたくさん撮影して学習させ、 YouTube広告スキップ可能判定モデルを作ります。
3つの状態をad(広告中でスキップ不可)、skip(広告中だけどスキップ可)、none(通常動画)のラベルにして、skipのラベルを検知させる流れです。
1. スキップ不可の状態(ad)の画像例
いわゆる広告が流れている最中の画面(の右下)です。
実際に学習させた画面の一例ですが、"黒背景のボックスに白文字" + "黄色の文字でプログレス表示"がこの状態です。
この状態はスキップ不可なのでボタンを押してもスキップされません。実際の判定でも特に何もしません。
2. スキップ可能な状態(skip)の画像例(本名)
今回の本名です。 広告は流れているけどスキップ可能 な状態の画面(の右下)です。
この状態の時にボタンを押せたら無事にスキップできます。
3. 通常の動画が流れている状態 (none)の画像例
ここは通常に動画が流れている状態です。色々な動画を流して撮影をしておきます。
Teachable Machineでの学習
エッジインパルスやSeeedのSenseCraftなども試したのですが、うまく使いこなせず慣れ親しんでいるTeachable Machineで学習させました。
学習用にYouTube画面の画像をたくさん撮影...
システム構成とプログラムの実装
軽く後述してますが、全てをデバイス側でやりたかったのですが、メモリ都合で上手くいかず、判定は別途Node.jsでAPIサーバーを立ててその中で判定してデバイスへ返すようにしています。
ざっくり構成。
画像判定のAPI
Node.jsでサーバーを作るようにしています。実際のコードはこちら
- /predict: 判定用 画像を受けて判定結果を返す
- /save: 画像保存 学習用
- /mode: 判定モードか画像保存モードかの状態を返す
AtomS3R-CAM側では画像を撮影して/predictのエンドポイントに送信するという感じで判定ロジックはAPIサーバー側に逃しています。(メモリと僕の実装力問題ですね 泣)
Nintendo Switchの制御
こちらのみそさんの記事にあるsorasen2020/SwitchControllerESP32のライブラリを利用して実装しました。
スイッチの最初のコントローラー認識ではコントローラーのL+Rを押す必要があるのでその部分も一応入れています。
pushButton2(Button::L, 200, 0, 1);
pushButton2(Button::R, 200, 0, 1);
ただ、聞いた話によるとUSBシリアルでスイッチと接続する場合だとここは要らないっぽいです。
これは単体で動くPIO版サンプルも作ったので置いておきます。
#include <Arduino.h>
#include "SwitchControllerESP32.h"
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n=================================");
Serial.println("ATOMS3-CAM Switch Controller");
Serial.println("=================================\n");
// Switch コントローラーとして初期化
switchcontrolleresp32_init();
USB.begin(); // USBデバイスを開始(重要!)
switchcontrolleresp32_reset();
Serial.println("Switch controller initialized!");
// USB列挙を待つ
delay(3000);
// L+Rを同時押し(Switchに認識させる)
Serial.println("Pressing L+R buttons...");
pushButton2(Button::L, 200, 0, 1);
pushButton2(Button::R, 200, 0, 1);
delay(3000);
// Aボタンを押して確定
Serial.println("Pressing A button to confirm...");
pushButton(Button::A, 100, 1);
Serial.println("Connect to Nintendo Switch...");
Serial.println("LED ON - Device is ready!");
}
void loop() {
// 1秒ごとにAボタンを押す
Serial.println("Pressing A button...");
pushButton(Button::A, 100, 1); // Aボタンを100ms押して離す
delay(1000);
}
試行錯誤とボツ案など
色々ありましたメモです。
-
教師データの難しさ
- 初めはPCやTVのカメラで撮影した画像を学習させたり、PCでの画面など色々なパターンを学習させたけど全てノイズデータのような感じでうまくいかなかった
- => 学習データ自体もATOMS3R-CAMで撮影することに
- => 学習データを撮影するための撮影&学習モードを実装して判定モードと分けるように
- プロコンに触らずスキップできたら良いので最初はジェスチャー判定も試してみていたがうまくいかず
- 初めはPCやTVのカメラで撮影した画像を学習させたり、PCでの画面など色々なパターンを学習させたけど全てノイズデータのような感じでうまくいかなかった
-
画角固定
- ガムテ固定だとちょっとズレると画像判定が怪しくなる => 台を置いて固定(これはどっかで変えたい)
-
ATOMS3R-CAMで全てやりたかったけどうまくいかず
- Teachable Machineのエッジデバイス向け => メモリが足りなくてエラーに
- エッジインパルス => これもメモリ足りなくてエラーに
- どちらもプロコンエミュレートのプログラムと同時に入れると厳しい感じに
- 結果的に別でサーバーを立てて判定はNode.jsのプログラムで、ATOMS3R-CAMには結果だけを返す感じに
- これによってちょっと時間のロスがあるけど2秒くらいで判定してくれる
-
Gyazoにアップロードして判定したかったけど、ずっと起動させておくプログラムで定期的に送信して画像を送るのを外部サービスにやるのは気が引けるので却下
-
若干怪しい状態がたまにある
- 動画によっては通常動画なのにskip状態のようみ見えるものもあるのでここは学習がたりてない状態
-
Nintendo SwitchのエミュレートをUSBシリアルにするとデバッグコンソールが利用できないため、BLEで接続したかったがBLE接続がスイッチ側とうまくつながらなかった
- PCとはうまく接続できたので認識させるプロセス(L+R同時押し的な)周りな気がしている
-
初手から公式サンプル動かず
-
"カメラ撮影 => 判定サーバーに飛ばす" + "プロコンエミュレートでAボタン" でそれぞれを実装してマージ
-
家の中にあるJetson Nanoを判定サーバーにしたかったけどJetson NanoのNode.jsが古すぎてそのままだとエラーでまくる
-
仕組みが完成しても良いデモ動画を撮るのが難しい
- しっかりスキップされるのでタイミングを逃しがち
- デモ撮影用に広告が流れて欲しいときに限ってなかなか広告が流れない
実際に動いてる様子
まだ試していて怪しい部分はあるが割とちゃんと動いてくれています。
学習データのチューニングによって割と良い精度で判定してくれています。
ただ、学習方法の問題な気がしますが、若干怪しい認識がまだあります。
このように結構迷いながらSKIP判定になってしまっている箇所もあります。3~5本くらい動画流して1回程度なのでそこまで気にならないですが
終わりに
読んでいただきありがとうございました。
AtomS3R-CAMをメイカーフェアTokyoで衝動買いしたのでこのカメラデバイスを使いたいなというモチベから入りましたが、ローカル画像認識をするときのリソース問題的にはReCameraとかもっとリッチなものでやった方がシンプルだと思います。
一応コードも公開しておきます。
TV + Nintendo SwitchでYouTube見てるんですよーって話をしたら @miso_develop さんに「珍しいですね。」と言われたので真似したい人はいないかもですが、規約的にグレーな気もするのでもし 試す際は自己責任でお願いします。
そもそも YouTubeプレミアムに登録すると広告は流れない ので、皆さんはこんなことせずに素直に課金するのが早いし安全ですね。






















