ESP32-CAMで作る監視カメラ (RTSPストリーミング編)

ESP32-CAMで作る監視カメラ RTSPストリーミング編

ESP32-CAMを購入してダミーカメラに納めるという記事を公開した後、なぜかそれを使うところの記事をすっかり失念していました。
前回: ESP32-CAMで作るリビングカメラ (工作編)

ESP32-CAM DEVキットで作る監視カメラ 工作編 22 ダミーカメラにESP32-CAMを取り付けているところ。(写真は前回の工作編の使いまわし)
ESP32-CAM DEVキットで作る監視カメラ 工作編 25 完成したところ。(写真は前回の工作編の使いまわし)
AliExpressで販売されているESP32-CAMの1つ。このページに掲載されているESP32-CAMとは仕様が異なることがあります。

ESP32-CAMで作る監視カメラ RTSPストリーミング編 1
ESP32-CAMのプログラムのサンプルはArduino IDEにあるのですぐに使えます。
Arduino IDEのメニューバーから「ファイル」「スケッチ例」「ESP32」「Camera」「CameraWebServer」を辿ります。

ESP32CAMで作る監視カメラ RTSPストリーミング編 2
ボード(シングルボードコンピュータ)を選択します。
Arduino IDEのメニューバーから「ツール」「ボード(使用中のボードの名称)」「ESP32 Arduino」「AI Thinker ESP32-CAM」を辿ります。
上の画像では「ツール」の「シリアルポート」が灰色の無効状態になっていますが、ボードをPCにUSBケーブルで接続して通信できる状態になるとシリアルポートの設定ができるようになります。適切な通信速度を指定してください。

ESP32-CAMで作る監視カメラ RTSPストリーミング編 3
先に選択した「CameraWebServer」のソースの最初の方でボードの種類に応じてそのボード用の行を非コメント化します。
今回はESP32-CAMなので #define CAMERA_MODEL_AI_THINKER の行の行頭の「//」を削除します。
また、LANのWi-Fiに接続できるよう数行下の const char* ssid = "******" と const char* password = "******" の *****の部分にLANのWi-Fi APのSSIDとパスワードを入力します。
コンパイルして送信するとESP32-CAMが自動的に再起動して操作可能なウェブカメラサーバになります。LAN側でDHCPサーバが稼働していること。
Arduino IDEのメニューバーから「ツール」「シリアルモニタ」を辿りシリアルモニタを開きます。ESP32-CAMが起動してWi-Fiに接続するとシリアルモニタにIPアドレスが表示されます。ブラウザのURL欄にそのIPアドレスを入力してウェブカメラサーバを表示します。

ESP32-CAMで作る監視カメラ サンプルスケッチ画面
(前回の記事から画面を流用)
このサンプルスケッチは、高機能なのでいろいろ楽しめるものの監視カメラとして使うには不向きです。

Micro-RTSPを使ってRTSP対応監視カメラにする

RTSPストリームを実現するMicro-RTSP - https://github.com/geeksville/Micro-RTSPというライブラリがあります。
そのMicro-RTSPを使ったサンプルスケッチとしてESP32CAM_RTSP - https://github.com/Chihhao/ESP32CAM_RTSPが簡単で良さそうです。 GitHubからESP32CAM_RTSPのソースを取得し、ESP32CAM_RTSPディレクトリは丸ごとコピーしてArduino IDEのデータディレクトリの中に置きます。Arduino IDEのデータディレクトリはユーザーディレクトリの中のArduino (/home/ユーザー名/Arduino)など。環境によって違うかも。 Micro-RTSPはESP32CAM_RTSPに含まれているので別途入手する必要はありません。ESP32CAM_RTSPディレクトリのESP32CAM_RTSPディレクトリを丸ごとコピーしてArduino IDEのデータディレクトリの中のlibrariesディレクトリ下に置きます。

Arduino IDEを起動し、ESP32CAM_RTSPディレクトリの中の「ESP32CAM_RTSP.ino」を開きます。

ESP32CAM_RTSP.ino (編集)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//https://github.com/geeksville/Micro-RTSP

#include <WiFi.h>
#include <WebServer.h>
#include <WiFiClient.h>
#include "OV2640.h"
#include "SimStreamer.h"
#include "OV2640Streamer.h"
#include "CRtspSession.h"

char *ssid = "****************";          // Put your SSID here
char *password = "****************";  // Put your PASSWORD here

WebServer server(80);
WiFiServer rtspServer(554);
OV2640 cam;
CStreamer *streamer;

//mjpeg串流
void handle_jpg_stream(void){
  WiFiClient client = server.client();
  String response = "HTTP/1.1 200 OK\r\n";
  response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n";
  server.sendContent(response);

  while (1)  {
    cam.run();
    if (!client.connected())
      break;
    response = "--frame\r\n";
    response += "Content-Type: image/jpeg\r\n\r\n";
    server.sendContent(response);

    client.write((char *)cam.getfb(), cam.getSize());
    server.sendContent("\r\n");
    if (!client.connected())
      break;
  }
}

//靜態影像
void handle_jpg(void){
  WiFiClient client = server.client();
  cam.run();
  if (!client.connected())
  {
    return;
  }
  String response = "HTTP/1.1 200 OK\r\n";
  response += "Content-disposition: inline; filename=capture.jpg\r\n";
  response += "Content-type: image/jpeg\r\n\r\n";
  server.sendContent(response);
  client.write((char *)cam.getfb(), cam.getSize());
}

//錯誤處理
void handleNotFound() {
  String message = "Server is running!\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  server.send(200, "text/plain", message);
}

//WiFi連線
void WifiConnect() {
  if(WiFi.status() == WL_CONNECTED) return;

  int t=0;
  WiFi.begin(ssid, password);
  while(true){
    if(WiFi.status() == WL_CONNECTED) break;
    delay(1000);
    t++;
    if(t>=5){
      t=0;
      WiFi.begin(ssid, password);
    }
  }
}

void setup(){
  //設定影像大小:UXGA(1600x1200),SXGA(1280x1024),XGA(1024x768),SVGA(800x600),VGA(640x480),CIF(400x296),QVGA(320x240),HQVGA(240x176),QQVGA(160x120)
  esp32cam_aithinker_config.frame_size = FRAMESIZE_XGA;
  esp32cam_aithinker_config.jpeg_quality = 10;
  cam.init(esp32cam_aithinker_config);

  //カメラ追加設定
  sensor_t * s = esp_camera_sensor_get();
  s->set_hmirror(s, 1); //左右反転 0無効 1有効
  s->set_vflip(s, 1); //上下反転 0無効 1有効
  //s->set_colorbar(s, 1); //カラーバー 0無効 1有効


  //開始WiFi連線
  WifiConnect();

  server.on("/", HTTP_GET, handle_jpg_stream);
  server.on("/jpg", HTTP_GET, handle_jpg);
  server.onNotFound(handleNotFound);
  server.begin();
  rtspServer.begin();
  streamer = new OV2640Streamer(cam);//啟動服務
}

void loop() {

  WifiConnect();

  server.handleClient();
  uint32_t msecPerFrame = 100;
  static uint32_t lastimage = millis();
  // If we have an active client connection, just service that until gone
  streamer->handleRequests(0); // we don't use a timeout here,
  // instead we send only if we have new enough frames
  uint32_t now = millis();
  if (streamer->anySessions()) {
    if (now > lastimage + msecPerFrame || now < lastimage) { // handle clock rollover
      streamer->streamImage(now);
      lastimage = now;

      // check if we are overrunning our max frame rate
      now = millis();
      if (now > lastimage + msecPerFrame) {
        printf("warning exceeding max frame rate of %d ms\n", now - lastimage);
      }
    }
  }
  WiFiClient rtspClient = rtspServer.accept();
  if (rtspClient) {
    streamer->addSession(rtspClient);
  }
}

シリアル通信の部分は運用時には不要なので取り除いています。代わりに画面反転とカラーバー表示を追加しています。(92-96行目)
Wi-FiのSSIDとパスワードを記入するだけで使用できます。Wi-FiのSSIDとパスワードを入力します。(11-12行目) カメラの映像が上下逆な場合は上下反転+左右反転も必要です。(94-95行目)。この反転については次へ。

カメラの映像は2通りで表示可能です。

  • http://IPアドレス/
  • rtsp://IPアドレス:554/mjpeg/1

前者は普通にネットワーク動画対応アプリやブラウザで表示可能ですが、後者は観ることができないかもしれません。また、両ストリームは同時再生できません。

ESP32CAMで作る監視カメラ RTSPストリーミング編 4
映像の上下が逆の場合、「回転」できる場合は180°回転させるだけです。
「回転」がない場合、「上下反転」を行うと一見目的達成のように見えますが、左右が逆になってしまいます。更に左右反転も行うと正常な上下逆になります。
なお、カメラ側の映像が上下逆さまであってもプレーヤー側で回転させて観ることができる場合があります。監視カメラ用のNVRアプリケーションでも映像の回転させて録画する機能を有することが多いでしょう。

Agent DVRへのカメラ登録

NVRのAgent DVRにRTSP対応のESP32-CAMを登録してみた。

ESP32CAMで作る監視カメラ RTSPストリーミング編 5
Agent DVRの設定画面。
「新しいデバイス」をクリックします。

ESP32CAMで作る監視カメラ RTSPストリーミング編 6
右上隅のドロップダウンは、「全般的」を選択状態(新しいデバイスの初期値)。 「有効」をオン。ソースの種類は「IPカメラ」を選択し、右端の「 … 」をクリックする。

ESP32CAMで作る監視カメラ RTSPストリーミング編 7
右上隅のドロップダウンは、「ビデオソース」を選択状態(初期値)。
「ライブURL」に、「 rtsp://IPアドレス:554/mjpeg/1 」を入力する。この画面ではこの1項目だけ。右下の「OK」をクリックする。
1つ戻った画面になる。

ESP32CAMで作る監視カメラ RTSPストリーミング編 8
(カメラの映像で動態検知して自動録画開始する場合のみ)
右上隅のドロップダウンから「検出器」を選択する。
機能有効化のスイッチを「On(オン)」にする。
中央の大きな「領域」の直下にある「検出器」のドロップダウンメニューで「単純」を選択する。
右下の「OK」をクリックする。

RTSP対応カメラ設定の肝はこれだけです。

とても簡単にESP32-CAMをNVRに接続可能な普通の監視カメラにできました。
ただし、ESP32-CAMが性能不足なのか少しラグります。ラグの面でいえばRTSPストリーミングよりHTTPストリーミングの方がややマシなようです。

関連記事:

AdSenseより稼げる? 面倒だけどEzoicを導入してみる (広告設定)

Ezoicの広告設定

Ezoicの広告システムを利用すると、なぜAdSenseより稼げるようになる(可能性がある)のか。おそらくGAMとかHeader Bidding(ヘッダ入札)について触りだけでも知っていれば、「ああ、Ezoicは(広告枠の管理を)任せられる運用代行でそっち系も使えるのね」ということでわかるかと。逆に、「そっち系」を全く知らない人には説明がとても難しい。この記事でもそっち系の説明はウェブ1ページに収まるようなものではないので割愛です。

とりあえずEzoicの広告を使えるようにしてしまって、広告の仕組みあたりは後から自分でググって調べていただければ。Ezoicを申し込むとGAMの招待も得られるのでGAMの入口としてもどうぞ。

収益の支払い方法の指定

まだ収益を生み出す状態に至っていないので気が早い話ですが、忘れやすいことでもあるので先に収益の受け取り方法の設定をしておきます。

Ezoicの支払い方法 1
Ezoicのサイトにログインし、右上の(アカウント)をクリックし①、「Payments and Charges」②、「Payment Settings」③を辿ります。
下部のFORM OF PAYMENTの右にある「SELECT FORM」のドロップダウンメニューをクリックします。④

Ezoicの支払い方法 2
受取方法(支払い方法)は幾つかありますが、口座振替(米国のみ)と小切手(米国,カナダのみ)は選ばないでしょう。PayPalかPayoneer経由の銀行送金を選びます。PayPalは4%の手数料がかかります。Payoneerの日本宛の手数料は2%です。日本外の広告サービスやアフィリエイトを利用しているなら手数料の安いPayoneerを利用することが多いでしょう。今回、この記事ではPayoneerを選択します。
「Bank Transfer or Prepaid Card Via Payoneer」をクリックします。
中央下部に「Go To Payoneer」ボタンが表示されるのでクリックします。

Ezoicの支払い方法 3
Peyoneerのウェブサイトが開きます。
Payoneerのアカウントが無いということであれば「Bank Transfers」を選んで「SIGN UP」します。(この記事では以降割愛)
Payoneerのアカウントがあるなら「Already have a Payoneer Account? Sign in」をクリックします。

Ezoicの支払い方法 4
Payoneerのログイン画面が表示されるので、自身のアカウントIDとパスワードを入力して「SIGN IN」をクリックします。
パパッと幾つかの画面が自動的に遷移し自動的にEzoicからPayoneerに支払い方法が設定されます。(操作不要)
Peyoneerの画面は閉じます。

Ezoicの支払い方法 5
Ezoicの画面に戻ると下部の「FORM OF PAYMENT」のCurrent Payment Formが、「Bank Transfer or Prepaid Card Via Payoneer」になっています。

Ezoicの支払い方法 6
Payoneerからメールが届いている筈なので確認します。
「Ezoicがご利用のPayoneerアカウントと繋がったことをお知らせいたします。」云々と書かれていれば手続き完了といえます。

最終的にEzoicで得た収益はPayoneerからあなたの(日本の)銀行口座に送金することになるので、別途Payoneerにログインして送金先銀行口座を登録しておく必要があります。Payoneer利用者なら登録済みだと思われますが。

EzoicAdsの設定

Ezoicの広告設定 1
WordPressの管理パネルのEzoicプラグインで広告についての設定を見てみます。
右下の「LET'S GO!」から他のメニューに進めますが、そもそもプレースホルダーが完成していないことが指摘されています。先ずは広告を表示する場所を準備してや必要がありそうです。それはEzoic側で行います。

Ezoicの広告設定 2
Ezoicのウェブサイトにアクセスし、上部に並んでいるアイコンから「EzoicAds」をクリックします。

Ezoicの広告設定 3
EzoicAdsの画面です。
左列のメニューから「Ad Positions」をクリックします。①
右列で、タイトル下に並ぶタブから「PlaceHolders」をクリックします。②
右に表示される「NEW PLACEHOLDER」をクリックします。③

Ezoicの広告設定 4
「Where on the page is this ad?」のドロップダウンメニューでウェブページ内の何処に貼る広告にするかを選択します。サイドバーの上やタイトルの下など、ある程度わかり易い位置から選択できます。なお、必ず「選択した位置」で貼らなければならないということではないようです。
また、この選択によりその下の方にある広告サイズのチェックボックスのチェックの有無が自動で変更になります。
「Placeholder Name?」は広告場所の名前などを任意で指定できます。基本的にはメモ扱いです。
右下の「SAVE」をクリックして保存します。

Ezoicの広告設定 5
ポップアップで広告コードが表示されるので、「Copy code to clipboard」でコードをコピーし、それをウェブ側に貼り付けます。 この広告コードはAdSenseの広告コードと同じように扱えば良さそうです。
「CLOSE」でポップアップを閉じます。

Ezoicの広告設定 6
追加したプレースホルダーがリストに1行追加されます。上の画像では4つのプレースホルダーが登録されています。
」でそのプレースホルダーの広告コードが表示されます。
」でそのプレースホルダーを変更できます。
」は、コピーではなく「テンプレートの作成」ということです。
」は、そのプレースホルダーの削除です。
ページの種類に応じて適所に貼れるだけのプレースホルダーを用意すれば良さそうです。が、ページ内の特定の場所が広告だらけにならないよう計画して作成しましょう。なお、プレースホルダーをたくさん用意してコードをページ中に貼りまくってもすべてのプレースホルダーに同時に広告が表示されるということはないようです。名目上はそのページで最適なプレースホルダーに広告が出ることになっています。
また、作成するプレースホルダーの数は任意ということになっていますが、広告の配信をスタートさせるためには最低10個必要です。作成したプレースホルダーのコードの全てをページのどこかに入れなければならないということではないようなので、必要で作ったプレースホルダーが10に満たなければ使わないプレースホルダーを追加してとりあえず10個にしましょう。

Ezoicの広告設定 7
上部のタブから「Ad Types」を選択します。このタブでは広告の種類別の設定を行います。
2段めのタブの左端にある「Native Ads」は「ネイティブ広告」の設定です。
「ネイティブ広告は、通常、投稿の下部またはサイドバーに配置される編集およびスポンサー コンテンツ ストーリーです。ユーザーがサイトを離れようとしているときに追加の広告収入をもたらすことができるため、そのような場所で非常に人気があります.」と、Ezoicのドキュメントに書かれています。この広告を利用するには「ネイティブ広告」用のプレースホルダーを追加する必要があるようです。
「ネイティブ広告」を利用するなら右にあるスイッチを「ON」にします。オプションでPG-13とPGを選択できますが、初期値のPG-13で良さそうです。

Ezoicの広告設定 8
2段めのタブの「Vignette Ads」はヴィネット広告(全画面広告)です。
PC向け、スマホ向け、タブレット向けにそれぞれ全画面広告のオン/オフを指定できます。
全画面広告は収益に貢献してくれる種類の広告ですが、閲覧者には嫌われます。収益が1円でも多くなれば閲覧者のことなどどうでもよいということでなければオフにしておいた方が無難かもしれません。

Ezoicの広告設定 9
2段めのタブの「Auto-Insert Ads」は自動広告です。
「Disclosure」は自動広告で表示される広告が閲覧者に広告であると明示するためにラベル表示するものです。任意のテキストは指定できず、ドロップダウンから選択します。幾つかの言語が用意されていて日本語の「広告」もあります。
「AI Placeholders」はAIが広告を置く場所として最適な位置を試すようです。
Enhanced Placeholdersは、AIがサイト上のプレースホルダを自動的に挿入、変更、または移動する新機能ということです。よくわかりませんがプレースホルダ(の位置を)自動調整するもののようです。閲覧者の気分を害しない程度に収益重視な配置を行うものと予想します。
下部の「CSS Filters」はAdSenseの自動広告にはない機能で、CSSのセレクタを登録することで、そのセレクタのある要素内には広告を表示しないもののようです。広告を表示したくないブロックなどを指定できるということになるので便利そうです。<div id="hoge">広告を表示したくないブロック</div>のようにセレクタ付きで書いて、この画面で #hoge を登録すれば良いと思われます。

Ezoicの広告設定 10
2段めのタブの「Anchor Ads」はアンカー広告です。スマホ向けに画面一番上あるいは一番下に表示されてスクロールしても居座るやつをよく見ます。
「Anchor Ads」のスイッチでアンカー広告自体を有効化/無効化できます。上の画像ではスクショを撮るために敢えてアンカー広告をオンにしていますがオフの方が無難でしょう。
また、有効化した場合は、PC向け, スマホ向け, タブレット向けをそれぞれオン/オフできます。
「Adaptice Sizing」はサイズに応じて表示を調整するもののようです。Ezoicでは大きなプレースホルダーには複数広告を詰め込んだりということが行われるようですが、アンカー広告ではどのように機能するかは不明です。
「Allow Fluid Sizing」を許可した場合の挙動は不明です。サイズを柔軟に調整するものかもしれません。

Ezoicの広告設定 11
2段めのタブの「Side Rails」はサイドレール広告です。AdSenseでも2022年12月中旬から提供が始まりましたが閲覧者だけでなくサイトオーナーも嫌って採用は少なめのようです。ページを表示したときに左右の余白に表示され、スクロールしても居座ります。アンカー広告が上下だとすると、その左右版といっても良いかもしれません。アンカー広告は縦長画面のスマホ向きですが、サイドレールは左右に広いPCやタブレット向けといえるでしょう。サイドレール広告を表示すると広告収入は増えることがあるかもしれませんが閲覧者には不評なので使用しないのが無難です。ページの見た目も悪くなります。
EzoicではSticky Sidebar(次)があるので、このサイドレール広告がスクロールしても居座るタイプであるかは不明です。

Ezoicの広告設定 12
2段めのタブの「Sticky Sidebar」は固定サイドバー広告です。スクロールしても居座るページ横の広告ですが、Side Railsとの違いはよくわかりません。閲覧者には嫌われそうなので使用しないのが無難でしょう。

Ezoicの広告設定 13
2段めのタブの「Floating Video」はフローティングビデオ広告です。画面の右下に表示されるようです。音声は閲覧者が操作するまでミュートということです。ビデオ広告は閲覧者に嫌われることがあります。特にモバイル通信のスマホユーザーには恐ろしく嫌われるでしょうからせめてスマホ向けだけでもオフにしておくのが無難でしょう。
ビデオ広告は

Ezoicの広告設定 14
左列メニューで「AdSense|Mediation」を選択し、右列上部のタブで「Link Your Adsense Account」を選択した状態です。
AdSenseアカウントとリンクすると、AdSense認定パートナープログラムの一部となって、AdSenseが他の広告パートナーと一緒に入札することになるようです。入札が増えるということは広告単価が向上する可能性があります。
AdSenseアカウントとのリンクは前回行っているのでこのページは右に「お客様のAdSenseアカウントは、AdSense認定パートナープログラムで管理されるようになりました。」と表示されています。ここではすることはありません。

Ezoicの広告設定 15
左列メニューで「Ad Restrictions」で広告の制限を行うことができます。広告を表示したくないページのURLを登録したり、嫌な広告を出す広告主を除外する、特定のカテゴリの広告を表示させないといった常識的な機能が利用できます。

Ezoicの広告設定 16
Ezoicのトップページに戻ります。
「Welcome to Ezoic!」の下にある「COMPLETE SETUP」ボタンをクリックします。

Ezoicの広告設定 17
「③Setup Ad Testing」の「Placeholders」を選択します。
「Placeholder Cound:」が10以上であれば、「Congrarulations, placeholder setup is complete!」になっている筈です。

Ezoicの広告設定 18
「③Setup Ad Testing」の「Turn traffic on」を選択します。
Ezoicはトラフィックデータをフィードすることで学習し、数週間かけて収益が60~150%程度向上するということです。そのために、デスクトップ,タブレット,スマホのフィード用のスイッチをオンにします。オンにするとスライドバーが表示され100%になります。この値は調整できますが、とりあえず100%で良さそうです。
下の「SAVE」をクリックして保存します。

続きます

関連記事:
Up