この記事は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.setOptionsでcaptureModeを設定する必要があります。
録画停止のレスポンスに録画したファイルの情報がありますので、ダウンロードまたは、削除で使用します。
接続デバイスにもよりますが、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を習得するのに最適です。まずは無料オンライン相談から♪

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





コメント欄