Androidアプリ開発

OSCインタフェースを実装する

この記事は約21分で読めます。
記事内に広告が含まれています。
スポンサーリンク

この記事はAndroidスマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Javaの開発経験、XML構文規則、Androidのアプリ開発経験がある方を対象としています。
Androidのアプリ開発でお役にたててれば、嬉しいです。
(これからAndroidのアプリ開発やJavaでの開発を始めたい方への案内は、
記事の最後で紹介します)

この記事のテーマ


Open Spherical Camera APIで球面カメラをリモート操作する

Open Spherical Camera APIを実装している球面カメラ(360°カメラ)です。

ポイント

Open Spherical Camera APIは360°撮影できる球面カメラを操作するためのコマンドセットです。
JSONデータでコマンドの受け渡しを行うため、HttpプロトコルをハンドリングできるOkHttpを使用します。

Open Spherical Camera API

OkHttp

コマンドを実行するにあたり、デバイスとネットワーク接続する必要があります。
Wi-Fiデバイスにネットワーク接続はこちら↓↓↓

OscHelperクラス

Open Spherical Camera APIのコマンド実行はOscHelperクラスとして実装します。
操作コマンドをJSONデータで生成、OkHttpでコマンドを実行します。

build.gradle(Module :app)

dependencies {
    implementation  "com.squareup.okhttp3:okhttp:5.3.2" 
    :
}

コマンドはデバイスのIPアドレス宛てにPOSTリクエストを行います。
OscHelperクラスにステータス取得、録画開始、コマンド実行、ダウンロード、削除のメソッドを用意します。
リクエストの応答を待つ必要があるため、非同期処理として実装する必要があります。

public class OscHelper {
    public static final String      START_CAPTURE = "camera.startCapture";
    public static final String      STOP_CAPTURE = "camera.stopCapture";
    private final OkHttpClient      client = new OkHttpClient();
    private final Handler           handler = new Handler(Looper.getMainLooper());
    private final ObjectMapper      mapper = new ObjectMapper();
    private final Context           context;
    private final String            info;
    private final String            stat;
    private final String            exec;
    private final String            stus;
    public String[]                 response;
    public State[]                  state;
    public List<String>             deletes = new ArrayList<>();
    public String                   fileUrls;
    public OscHelper(Context context, int slot, String ip, int port) {
        this.context = context;
        this.response = new String[slot];
        this.state = new State[slot];
        this.stat = "http://" + ip + ":" + port + "/osc/state";
        this.exec = "http://" + ip + ":" + port + "/osc/commands/execute";
        this.stus = "http://" + ip + ":" + port + "/osc/commands/status";
    }

    // ステータス取得 //
    public void state(int no) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            MediaType type = MediaType.get("application/json; charset=utf-8");
            RequestBody body = RequestBody.create("", type);
            Request request = new Request.Builder()
                    .url(stat)
                    .post(body)
                    .build();
            try (Response res = client.newCall(request).execute()) {
                if (!res.isSuccessful()) {
                    throw new IOException("Unexpected code " + res);
                }
                response[no] = res.body().string();
            } catch (IOException e) {
                // エラー処理を記述する
                :
            }
            handler.post(() -> {
                // ここに処理を記述する
                :
            });
        });
    }

    // 録画開始 //
    public void recording(List<String> name, List<String> values, int slot) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            StringBuilder payload = new StringBuilder();
            payload.append("{\"name\": \"camera.setOptions\", \"parameters\": {\"options\": {");
            for (int i = 0; i < Math.min(name.size(), values.size()); i++) {
                payload.append(String.format(i > 0? ",\"%s\":\"%s\"" : "\"%s\":\"%s\"", name.get(i), values.get(i)));
            }
            payload.append("}}}");
            MediaType type = MediaType.get("application/json; charset=utf-8");
            RequestBody body = RequestBody.create(payload.toString(), type);
            Request request = new Request.Builder()
                    .url(exec)
                    .post(body)
                    .build();
            try (Response res = client.newCall(request).execute()) {
                if (!res.isSuccessful()) {
                    throw new IOException("Unexpected code " + res);
                }
                String response = res.body().string();
                state[slot] = mapper.readValue(response, State.class);
            } catch (IOException e) {
                // エラー処理を記述する
                :
            }
            handler.post(() -> {
                if (state[slot].state.equals("done")) {
                    fileUrls = null;
                    execute(START_CAPTURE, slot, false);
                } else {
                    // エラー処理を記述する
                    :
                }
            });
        });
    }

    // コマンド実行 //
    public void execute(String command, int slot, boolean cancel) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            String payload = String.format("{\"name\":\"%s\"}", command);
            MediaType type = MediaType.get("application/json; charset=utf-8");
            RequestBody body = RequestBody.create(payload, type);
            Request request = new Request.Builder()
                    .url(exec)
                    .post(body)
                    .build();
            try (Response res = client.newCall(request).execute()) {
                if (!res.isSuccessful()) {
                    throw new IOException("Unexpected code " + res);
                }
                String response = res.body().string();
                if (command.equals(STOP_CAPTURE)) {
                    JsonNode node = mapper.readTree(response);
                    state[slot] = new State();
                    state[slot].name = node.findValue("name").asText();
                    state[slot].state = node.findValue("state").asText();
                    fileUrls = node.at("/results/fileUrls").get(0).asText();
                } else {
                    state[slot] = mapper.readValue(response, State.class);
                }
            } catch (IOException e) {
                // エラー処理を記述する
                :
            }
            handler.post(() -> {
                // ここに処理を記述する
                :
            });
        });
    }

    // ダウンロード //
    public void download(String url, Uri uri, boolean stopSelf, int slot) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            Request request = new Request.Builder()
                    .url(url)
                    .build();
            try (Response response = client.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    ResponseBody body = response.body();
                    try (InputStream inputStream = body.byteStream();
                         OutputStream outputStream = new FileOutputStream(Objects.requireNonNull(context.getContentResolver().openFileDescriptor(uri, "wt")).getFileDescriptor())) {
                        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = Objects.requireNonNull(inputStream).read(buffer)) != -1) {
                            bufferedOutputStream.write(buffer, 0, len);
                        }
                        bufferedOutputStream.close();
                    } catch (IOException e) {
                        // エラー処理を記述する
                        :
                    }
                } else {
                    // エラー処理を記述する
                    :
                }
            } catch (IOException e) {
                // エラー処理を記述する
                :
            }
            handler.post(() -> {
                // ここに処理を記述する
                :
            });
        });
    }
 
    // 削除 //
    public void delete(int no) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("{\"name\": \"camera.delete\", \"parameters\": { \"fileUrls\": [ ");
            for (int i = 0; i < deletes.size(); i++) {
                stringBuilder.append(String.format(i > 0? ",\"%s\"" : "\"%s\"", deletes.get(i)));
            }
            stringBuilder.append(" ] }}");
            String payload = stringBuilder.toString();
            MediaType type = MediaType.get("application/json; charset=utf-8");
            RequestBody body = RequestBody.create(payload, type);
            Request request = new Request.Builder()
                    .url(exec)
                    .post(body)
                    .build();
            try (Response res = client.newCall(request).execute()) {
                if (!res.isSuccessful()) {
                    throw new IOException("Unexpected code " + res);
                }
                response[no] = res.body().string();
            } catch (IOException e) {
                // エラー処理を記述する
                :
            }
            handler.post(() -> {
                // ここに処理を記述する
                :
            });
        });
    }
}

使用方法

レスポンスを受ける配列数、接続デバイスのIPアドレス、ポートでOscHelperクラスのインスタンス化します。
Open Spherical Camera APIで録画開始前にcamera.setOptionscaptureModeを設定する必要があります。
録画停止のレスポンスに録画したファイルの情報がありますので、ダウンロードまたは、削除で使用します。
接続デバイスにもよりますが、Wi-Fi接続で通信がない場合にWi-Fi自動オフ機能が働くことがあります。
Wi-Fi自動オフ機能を無効化するために一定間隔でキープアライブ通信を実行します。
キープアライブはステータス取得を使用します。

    private OscHelper               oscHelper;
    :
    @Override
    public void onCreate() {
        super.onCreate();
        :

        oscHelper = new OscHelper(context, 5, IP, 80);
        :
   }

    // 録画開始
    List<String> name = new ArrayList<>();
    List<String> vales = new ArrayList<>();
    name.add("captureMode");
    vales.add("video");
    oscHelper.recording(name, vales, 1);
    :

    // 録画停止
    oscHelper.execute(OscHelper.STOP_CAPTURE, 2, false);
    :

    // ダウンロード
    Uri uri = "ダウンロードファイルの出力先Uri"; 
    oscHelper.download(oscHelper.fileUrls, uri, true, 3);
    :

    // ファイル削除
    // deletesに削除対象のUriをセットする
    oscHelper.delete(4);
    :

    // キープアライブ
    Handler handler = new Handler(Looper.getMainLooper());
    Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    if (!recording) okHttpHelper.state(0);
                    handler.postDelayed(this, 30000);
                }
            };
    handler.post(runnable);
    :

今回は、ここまでです。

OSCインタフェースで球面カメラをリモート操作しているAndroidアプリです。

誤字脱字、意味不明でわかりづらい、
もっと詳しく知りたいなどのご意見は、
このページの最後にある
コメントか、
こちらから、お願いいたします♪

ポチッとして頂けると、
次のコンテンツを作成する励みになります♪

ブログランキング・にほんブログ村へ

これからAndroidのアプリ開発やJavaでの開発を始めたい方へ

アプリケーション開発経験がない方や、アプリケーション開発経験がある方でも、JavaやC#などのオブジェクト指向言語が初めての方は、Androidのアプリ開発ができるようになるには、かなりの時間がかかります。
オンラインスクールでの習得を、強くおススメします。

未経験者からプログラマーを目指すのに最適です。まずは無料カウンセリングから♪

ゲーム系に強いスクール、UnityやUnrealEngineを習得するのに最適です。まずは無料オンライン相談から♪

参考になったら、💛をポッチとしてね♪

スポンサーリンク
シェアする
msakiをフォローする
スポンサーリンク

コメント欄

タイトルとURLをコピーしました