GoPro をライブカメラにしてターミナルから操作する

👉 【無料】使わなくなったスマホをライブカメラにしておじいちゃんを監視したい 

GoProはパソコンとWiFi接続で接続できますよね。

このとき、
10.5.5.9 をルータとしたローカルネットワークとなり、
パソコン側は、10.5.5.10? のアドレスが割り振られます。

非SSL通信です。

録画を開始・停止する

今、このページを見ているパソコンをGoProにWiFi接続すると

以下のただのリンクをタップして操作できます。

まず、ストリーミングを(再)起動して、

[→ (再)起動]


http://10.5.5.9/gp/gpControl/execute?p1=gpStream&a1=proto_v2&c1=restart

録画開始して、

[→ 録画開始]


http://10.5.5.9/gp/gpControl/command/shutter?p=1

そして、録画停止。

[→ 録画停止]


http://10.5.5.9/gp/gpControl/command/shutter?p=0

これだけで、録画開始・停止はできるのですが、ライブ画像を見たいところです。

ライブ画像を見る

ffplay (ffmpeg) で見ることができます。


ffplay -loglevel panic -an -fflags nobuffer -f:v mpegts -probesize 8192 udp://:8554

実際には、ストリーミング接続を保持させる処理が必要となります。


#!/usr/bin/env python3

import sys
import socket
from urllib.request import urlopen
import subprocess
from time import sleep
import datetime
import signal
import http
import multiprocessing

# GPH 6/0.160
GOPRO_IP = "10.5.5.9"
UDP_PORT = 8554
KEEP_ALIVE = 2.5
MESSAGE = "_GPHD_:0:0:2:0.000000"

URL_PREFIX       = f"http://{GOPRO_IP}/gp/gpControl"
URL_STREAM       = f"{URL_PREFIX}/execute?p1=gpStream&a1=proto_v2&c1=restart"
URL_RECORD_START = f"{URL_PREFIX}/command/shutter?p=1"
URL_RECORD_STOP  = f"{URL_PREFIX}/command/shutter?p=0"

CMD_VIEW = f"ffplay -loglevel panic -fflags nobuffer -f:v mpegts -probesize 8192 udp://:{UDP_PORT}"

def live():

    urlopen(URL_STREAM).read()
    subprocess.Popen(CMD_VIEW, shell=True)
    multiprocessing.Process(target=keep_alive).start()

    print("Press Ctl+C to quit.")
    print("Press ENTER to start/stop recording.")

    on = False
    while True:
        input()
        on = not on
        record(on)

def keep_alive():

    while True:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.sendto(bytes(MESSAGE, "utf-8"), (GOPRO_IP, UDP_PORT))
        sleep(KEEP_ALIVE)

def record(on):

    urlopen(URL_RECORD_START if on else URL_RECORD_STOP).read()
    print(f"Record {str(on)} {datetime.datetime.today()}")


def quit_gopro(signal, frame):

    sys.exit(0)

if __name__ == '__main__':

    signal.signal(signal.SIGINT, quit_gopro)
    live()

パソコン・スマホ共にアプリが公式に公開はされていますが、サクッと見たいですよね。

いろんな使い方ができそうです。

👉 【無料】使わなくなったスマホをライブカメラにしておじいちゃんを監視したい 
Live Hero5 Session with WiFi - GOPRO SUPPORT HUB


すばやく理解する「Room x RxJava 」

いい記事があったので。

Room 🔗 RxJava – Google Developers – Medium

まずは、Room で Dao.


@Query(“SELECT * FROM Users WHERE id = :userId”)
User getUserById(String userId);

ここまでで問題なのは、

1. 同期呼び出しでブロッキング。
2. データ変更時に再度呼び出す必要がある。

ということで、RxJava を使いたくなります。

Room は RxJava2.x に対応しています。

Adding Components to your Project | Android Developers

どのように使うのか?

Maybe


@Query(“SELECT * FROM Users WHERE id = :userId”)
Maybe<User> getUserById(String userId);

1. 該当ユーザがなければ、何も返さずに complete。
2. 該当ユーザがあれば、onSuccess となり complete。
3. Maybe が complete されたあとにユーザー情報が更新されても何もしない。

Single


@Query(“SELECT * FROM Users WHERE id = :userId”)
Single<User> getUserById(String userId);

1. 該当ユーザがなければ、何も返さず onError(EmptyResultException)。
2. 該当ユーザがあれば、onSuccess。
3. Single が complete されたあとにユーザー情報が更新されても何もしない。

Flowable


@Query(“SELECT * FROM Users WHERE id = :userId”)
Flowable<User> getUserById(String userId);

1. 該当ユーザはなければ、何も返さず emit もされない。当然、onNext も onError も呼ばれない。
2. ユーザが存在すれば、onNext。
3. ユーザ情報が更新されるたびに、自動で emit されるので、UI上を最新データに更新させることが可能になる。

 

まとめ

これだけ数行でデータベース、非同期処理を簡潔明快に説明できる Room x RxJava の組み合わせ。

おまけに Observable から細分化された RxJava2.x の主役たちの使い方も理解することができます。

素晴らしいですよね。


「アプリにActivityはひとつでいい」という神のお告げ

左からの NavigationDrawer が初回に起動する Activity にある場合, 気持ち悪いと思ってましたよね.

あのAndroidの神と言われている Jake Warthon さんが言い切ってます.

アプリにActivity一つで複数のFragmentを使う。ただFragmentのバックスタックは使わない。クソなので。

UI周りでいえば Activity起動時のコストを考えてみれば理にかなってるようにも思えます.

確かに, 「Fragment のバックスタック」周りで混乱する様子はだれもが見てきました。

Reddit でも話題になっており, この意見に同意する人も多い雰囲気.

In Droidcon NYC 2017, Jake Wharton says you should use a single-activity for the whole app, and you can use fragments but don't use the fragment backstack because it's bad : androiddev

で, いまどきのストラクチャーでどのような構成にするのか.

Android: the Single Activity, Multiple Fragments pattern | One Activi…

このスライドでは, 画面の数だけ「Presenter + View(Fragment)」のペアを用意する という形の記述となっていますが, Fragmentの特性上これが自然な気がしていますが.