14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

新しくなったSkyWayを使ってみよう!

無料でインターネットペットカメラを作ろう

Last updated at Posted at 2023-07-04

概要

仕事中に、家でお留守番しているペットを見たくなったことはありませんか?

この記事では、

使わなくなったスマホやRaspberry Piを自宅のペットカメラにして外出先からペットを見ることができるシステムを作ります。

このシステムの特徴は

  • 無料
  • 3分で利用開始できる
  • 自宅ネットワークの設定変更不要で、インターネットから見られる

です。

完成すると、この図のように、いつでもペットが見られます!
構成図

さぁ始めよう

この記事では

  1. SkyWayのセットアップ
  2. デモサーバーを使ってインターネットペットカメラを動かす
  3. SkyWayの説明
  4. ローカルでペットカメラを動かす
  5. GitHub Pagesを使ってホスティングする
  6. プログラムの解説

という流れで進めていきます。

1, 2だけでも、インターネットペットカメラとして使えます。
3以降は開発したい人向けです。

1. SkyWayのセットアップ

このシステムではSkyWayのプラットフォームを利用します。
SkyWayでは無料でアカウントを作成することができ、メアドのみ(クレジットカードの登録なし)でFreeプランが利用できます。

Freeプランは利用上限がありますが、個人向けペットカメラ用途ではFreeで大丈夫そうです。

まずユーザー登録をしてログインしましょう。

ログインしたら、プロジェクトを作成ボタンを押して、新しいプロジェクトを作成します。
make project

プロジェクト名は自由で構いません。

次にアプリケーションを作成ボタンを押して、新しいアプリケーションを作ります。

make app

このアプリケーション名も自由で構いません。

アプリケーションを作成すると、
アプリケーションIDシークレットキー
が表示されますので、大事に保管しておきましょう。

appid

2. デモサーバーを使ってインターネットペットカメラを動かす

早速ですが、インターネットペットカメラを動かしてみましょう。

デモサーバー(GitHub Pages)を用意しています。
動かすことができると、全体像がイメージがしやすくなりますよね!

ペットカメラ側(自宅)

カメラが付いているスマートフォンはありますか?
iPhoneであればiPhone6以降(iOS 11以降)のSafariで利用できます。
なければ、カメラ付きのノートPCやRaspberry Piでも構いません。

ブラウザから、カメラ用のURLを開いてみましょう。

すると「Input application ID」や「Input application Secret」と聞かれます。

さきほどSkyWayで取得した、あなたのアプリケーションIDシークレットキーを入力してください。

(このプロンプトがでない場合はブラウザでページをリロードをしましょう。ページのタブが表示されていないときは、プロンプトが出ない仕様のブラウザがあります。)

入力後、下の画像のような表示になり、カメラのストリーミングが開始されます。

IMG_1165.PNG

小さいですが、画面左上のボタンを押すとカメラを切り替えできます。

マイクをミュートしておきたい場合は、カメラ用のURLのaudioパラメータをfalseにしましょう。

ビューワー側(外出先)

スマホやパソコンのブラウザから、ビューワー用のURLを開いてみましょう。

カメラ側のときと同様に、あなたのアプリケーションIDシークレットキーの入力が必要です。

このような感じで、カメラ側から映像がストリーミングされます。

IMG_4861.PNG

ペットに癒やされましょう!

いちいちアプリケーションIDとシークレットキーを入力するのは面倒?

https://komasayuki.github.io/pet-cam/index.html?camera=true&audio=true&appId=YOUR_APPLICATION_ID

のようにappIdパラメータでアプリケーションIDを渡すことができます。そうするとアプリケーションIDの入力はスキップできます。

シークレットキーsecretパラメータで渡すことができますが、セキュリティ上推奨しません。ローカルのサーバーで試す場合のみ利用してください。

3. SkyWayの説明

このインターネットペットカメラを支えてくれているSkyWayの説明をします。

SkyWay JavaScript SDK

SkyWayのSDKは、現在

  • JavaScript
  • iOS
  • Android

の3種類が用意されています。
今回はブラウザ向けですので、JavaScript SDKを使います。

SkyWayは2023年1月31日から新バージョンを公開していて、
本記事では新しいバージョンを使っています。

Room

JavaScript SDKでは2種類のRoomを作ることができ、ユーザーが選択できます。

  • P2P Room (4人以下の少人数向け)
  • SFU Room (多人数向け、SFUサーバーを経由して通信する)

の2種類があります。
個人用のペットカメラでは視聴者はわずかですから、P2P Roomが最適です。

Stream

Roomで送受信できるメディアはStreamと呼ばれます。
やり取りできる情報の種類のことです。

  • VideoStream (ビデオ映像)
  • AudioStream (オーディオ音声、マイク音声)
  • DataStream (任意のメッセージ、P2P Roomでのみ使える)

本ペットカメラでは、VideoStreamとAudioStreamだけ使います。
チャットやコマンドなどを送りたい場合は、DataStreamが役に立ちます。

Channel

RoomにはChannelを作ることができます。Channelは通話しているユーザーグループの集まりです。ChannelはRoomの中に複数作ることができ、このChannelに参加することで実際に通話を開始できます。

Roomに動画などStreamを公開することをPublish、
Roomから受信することをSubscribeと言います。

本ペットカメラでは、Channelは1つでいいのでmy-lovely-petsという固定文字列をChannel名として使っています。

認証

認証はJWT(ジョットと読みます)で行います。JSON Web Tokenのことです。

が、認証に関してはあまり気にしなくてもSkyWayのSDK任せで大丈夫です。

WebRTC

WebRTCはWeb Real-Time Communicationのことで、ブラウザを使って動画や音声をやり取りするのに使える技術です。

各ブラウザのWebRTCサポート状況でブラウザがどのバージョンからWebRTCがサポートしているか確認できます。
ここ5年ぐらいのブラウザバージョンなら、サポートされている感じですね。

iOSだとiOS 11以降(iPhone6以降)、AndroidだとAndroid 5以降から使えるようです。

4. ローカルでペットカメラを動かす

SkyWay JavaScript SDKリポジトリにはサンプルコードが含まれており、p2p room exampleという、本件に最適なサンプルがあります。

サンプルをベースにインターネットペットカメラ用途に書き換えたソフトをGitHubで公開しました。

ビルド

以下の方法でコードを取得してビルドできます。
nodeのインストールが必要です。


# clone pet-cam repository from GitHub
% git clone https://github.com/komasayuki/pet-cam

% cd pet-cam

# first time only
% npm install

# ビルド ./distに生成される
% npm run build

※SkyWayのサンプルではnpm run buildにBabelとParcelを併用していたので、Parcelのみで動くように変更しています。

実行

実行するためにはWebサーバーが必要です。
chromeではhttpでもWebRTCが使えるので、パソコンで実行してみましょう。
(iOSではWebRTCの利用はhttps必須です)

http-servernpxを使ってWebサーバーを立ち上げます。
静的ファイルをホストするだけなので、Webサーバーになら何でもいいです。

% npx http-server ./dist
Available on:
  http://127.0.0.1:8080

ポート8080でWebサーバーが立ち上がりますので、

カメラ側: http://127.0.0.1:8080/index.html?camera=true

ビューワー側: http://127.0.0.1:8080/index.html?camera=false

をPCのブラウザで開いてください。
プロンプトが出たら、SkyWayで取得したApplication IDとSecretを入力しましょう。

うまく動きましたか?

この方法では、distフォルダのファイルを
インターネットに公開しなくても、
それぞれのPC環境でWebサーバーを立ち上げて、
それぞれのPC環境で127.0.0.1にアクセスしても、
インターネットを超えてカメラに繋がります。

5. GitHub Pagesを使ってホスティングする

GitHubは、GitHub Pagesという無料でhttpsでホスティングできる仕組みを提供しています。

さきほどビルドした際にdistフォルダに生成されたファイルを公開するだけなのですが、今回はGitHubリポジトリからGitHub Actionsを使ってWebに公開されるようにしました。

まず、GitHubリポジトリのSettingsからGitHub Pagesの Sourceの設定を
GitHub Actionsに変更します。

Screenshot 2023-07-05 at 14.49.34.png

そしてリポジトリのActionsを開いて、左側のDeploy to GitHub Pagesを開き、右のRun workflowからRun workflowを実行します。

そうすると、ローカルPCで実行したnpm run buildで作られたファイルをGitHub Pagesとして公開してくれます。2分ぐらいかかります。

Screenshot 2023-07-05 at 17.29.01.png

公開されるURLはActionsの結果の画面で取得できます。
Screenshot 2023-07-05 at 17.31.26.png

あなた専用のインターネットペットカメラができました!

6. プログラムの解説

では最後に、プログラムの解説をしていきます。

ローカルでAuthトークン(JWT)の作成

SkyWayサーバーの認証はJWTで行います。
これは以下のように、SkyWayのJavaScript SDKで簡単に作ることができます。

const token = new SkyWayAuthToken({
  jti: uuidV4(),
  iat: nowInSec(),
  exp: nowInSec() + 60 * 60 * 24,
  scope: {
    app: {
      id: appId,
      turn: true,
      actions: ['read'],
      channels: [
        {
          id: '*',
          name: '*',
          actions: ['write'],
          members: [
            {
              id: '*',
              name: '*',
              actions: ['write'],
              publication: {
                actions: ['write'],
              },
              subscription: {
                actions: ['write'],
              },
            },
          ],
          sfuBots: [
            {
              actions: ['write'],
              forwardings: [
                {
                  actions: ['write'],
                },
              ],
            },
          ],
        },
      ],
    },
  },
}).encode(secret);

これはサンプルから変更ありません。
SkyWayのWebサイトで作成したappIdとsecretを渡すことでAuthトークンが生成されます。

このAuthトークンはJWTなので、jwt.ioなどでデコードできます。私が試したところでは有効期間が24時間のJWTになっていました。JWTをデコードすると、expという値に失効日時が含まれています。

また、このサンプルでは認可の設定が*だらけですが、特定のチャンネルだけ操作できる、など制限をかけたAuthトークンを生成することもできます。

SkyWayで認証、コンテキストの作成

作成したAuthトークンを使って、SkyWayのContextを作成します。
Contextは認証ができたら取得できるものぐらいの認識でOKです。

const context = await SkyWayContext.Create(token);

認証情報が間違っている場合などは、この関数コールで例外がThrowされます。try-catchしましょう。

Roomを作成して、ジョイン

contextが手に入ったら、p2pルームのmy-lovely-petsというチャンネルに参加します。

const channel = await SkyWayRoom.FindOrCreate(context, {
    type: 'p2p',
    name: 'my-lovely-pets',
});
const me = await channel.join();

ペットカメラなのでチャンネルは1種類でいいです。
ペットがたくさんいる幸せな方は、うまく切り替えしましょう。

これ以降の手順は、カメラ側とビューワー側で異なります。

カメラ側から動画/音声StreamをPublishする

ペットカメラは1方通行のストリーミングです。
自宅カメラ側から動画と音声を送信します。

まず、SkyWayStreamFactory.enumerateInputVideoDevices()でカメラ一覧を取得します。
取得したカメラ一覧の配列にはMediaDeviceが含まれており、idの他に人間がカメラを認識できるlabelという属性もあります。

const cameraDevices = await SkyWayStreamFactory.enumerateInputVideoDevices();

取得したカメラのidをdeviceIdとしてSkyWayStreamFactory.createCameraVideoStream()
に渡すと、videoのStreamが取得できます。

audioのStreamはSkyWayStreamFactory.createMicrophoneAudioStream()
で取得できます。

const video = await SkyWayStreamFactory.createCameraVideoStream({deviceId: cameraDevices[0].id});
const audio = await SkyWayStreamFactory.createMicrophoneAudioStream();

HTMLでvideoというIDのvideoタグを用意してあります。
以下のコードを実行することで、videoタグではカメラのプレビューが見られるようになります。

const elm = document.getElementById('video') as HTMLVideoElement;
video.attach(elm);
await elm.play();

あとは、さきほど取得したme:LocalP2PRoomMemberに対して、publishするだけです。
これで動画/音声が送信が開始されます。
(実際には送信相手がいない場合はネットワークには流れません)

await me.publish(audio);
await me.publish(video);

iPhoneではデフォルトが前面カメラになったので、カメラの切り替え機能がないと使いにくいです。そのため、サンプルコードにはないカメラの切り替えを追加しています。

送信を開始してからカメラを切り替えるために

const publication = await me.publish(video);

のようにpublishの戻り値を保管しておき、新しく作ったvideoStreamをpublication.replaceStream()します。

const anotherCamera = await SkyWayStreamFactory.createCameraVideoStream({ deviceId: devices[1].id });
publication.replaceStream(anotherCamera);

ビューワー側から動画/音声StreamをSubscribeする

以下のコードでは、現在publishされているStreamと、これからPublishされるStreamでSubscribeを実行します。

これはサンプルコードのままです。
VideoStreamが来たら、videoタグで表示するようにして
AudioStreamが来たら、audioタグで再生できるようにします。

ペットカメラでは、一番最後にPublishされたStreamを無条件に最新のものとみなしています。

const subscribeAndAttach = async (publication) => {
  if (publication.publisher.id === me.id) return;

  const { stream } = await me.subscribe(publication.id);

  switch (stream.contentType) {
    case 'video':
      {
        const elm = document.getElementById('video') as HTMLVideoElement;
        stream.attach(elm);
      }
      break;
    case 'audio':
      {
        const elm = document.getElementById('audio') as HTMLAudioElement;
        stream.attach(elm);
      }
      break;
  }
};

channel.publications.forEach(subscribeAndAttach);
channel.onStreamPublished.add((e) => subscribeAndAttach(e.publication));

再接続

短時間の切断などはSkyWayのSDKによって自動的に再接続されますが、長時間の場合は手動での再接続が必要となります。

context.onFatalError.add(async () => {
  context.dispose();

  const newContext = await createContext(token, true);
  //context作成以降のフローをやり直し
});

contextに対してErrorハンドラーを追加できますので、この中でcontextを破棄して、contextの作成からやり直しが必要です。

ペット愛に溢れているなら、再接続の実装は欠かせませんね!

セキュリティについて

ブラウザのクライアントサイドのみで動くシステムとして実装しています。

もし、appIdやsecretを入力するのが面倒で、main.tsなどにハードコードしてしまうとアクセスした人からsecretが見えてしまいます。

そのような改造を行う場合は、自分専用のサービス利用に限定し、Webページ自体への認証を確実に行いましょう。

SkyWayのドキュメントではバックエンドでのAuthトークン生成を推奨しています。

URLパラメータで認証情報(シークレット)を渡すのは、セキュリティ上危険とされています。
通信経路上では漏れないものの、Webサーバーのログに残ったり、ブックマークを誰かに見られたり、ショルダーハックされるリスクがあるからです。

注意して設計しましょう。

補足

ペットカメラ側はデバイスが起動しっぱなしになると思いますがビューワーが接続されていない時間は、カメラからのビデオの送信は止まっていますのでネットワーク的な負荷は低いです。

最後に

SkyWayのプラットフォームを使うことで、とても簡単にインターネットペットカメラを作ることができました。とてもシンプルですね。

同様のプラットフォームにはAmazon Kinesis Video streamsなどありますがそれと比較しても簡単でした。

わずか100行ちょっとで実現できるなんて素敵ですね。

この記事を読んでくれた人が、これで何か作りたい!と思ってくれたら幸いです。

14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?