2
2

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 5 years have passed since last update.

【MESHカスタムタグ】THETA Vと連動する

Last updated at Posted at 2018-08-09

できること

MESHのSDKでThetaVと連携できるカスタムタグを作成しました。下記の3つの機能がMESH上のタグと組み合わせて利用する事が出来ます。
① 360 Live Photos:指定された時間だけ動画が撮影されます。
② 写真撮影:写真を撮影します。
③ 動画撮影:動画の開始・停止をコントロールできます。

360 Live Photos機能は、iPhoneのLive Photos機能が好きで良く使うので、あえて動画撮影機能と分けて実装しました。

名称未設定-2.png

きっかけ

ThetaVで撮影をしていると本体ボタンを押している手や、スマホ越しにリモート撮影している自分が映ってしまうのがどうしても嫌だったので、色んなトリガーを設定できて、その場で調整が手軽にできるMESHと繋げてその問題を解決しようと思い、今回MESH用のThetaV連携タグを作成しました。

実装方法

とりあえず使ってみたい方は、下記のJSON DATAをコピーしてimportしてご利用ください。
https://gloryroad0713.github.io/mesh.github.io/mesh_thetav.html

360 Live Photos

IMG_1856.jpg

360 Live Photos機能は、大まかに下記の流れで処理を実行しています。
① 撮影前状態の取得
② ビデオモード状態か否かの確認
③ もし写真モードだったら、ビデオモードに切り替える
④ 動画の撮影開始
⑤ 指定時間経過後、動画の撮影停止

360LivePhotos_Execute.js
//Theta Endpoint URL の設定(デフォルトは192.168.1.1:80)
var endPointURL = "http://192.168.1.1:80"

wait().then((v) => {
    log("wait " + v + " millisecond");	
	closeCaptureSession(endPointURL) ;
	callbackSuccess( {
		resultType : "continue"
	} );
});

function sleep() {
	return new Promise(resolve => setTimeout(resolve,properties.millisecond));
}

async function wait() {
	checkState(endPointURL);
	//非同期処理が完了するまで待機
	await sleep();
	
	return properties.millisecond.toString();
}

function checkState(_endPointURL) {
	ajax( {
		url : _endPointURL + "/osc/state",
		type : "post",
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			checkCaptureMode(endPointURL);
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log(XMLHttpRequest + " | " + textStatus + " | " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	});
}

//Theta Vの撮影モードがvideoモードになっている確認
function checkCaptureMode(endPointURL) {
	var getOptionsJSONData = {
		name: "camera.getOptions",
		parameters: {
        optionNames: [
				"captureMode"
			]
        }
	}

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( getOptionsJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			var json_data = JSON.stringify(contents);
			var json_text = JSON.parse(json_data)
			log("checkCaptureMode = " + json_data);
			
			var mode = json_text["results"]["options"]["captureMode"];
			
			//写真モードだったら、動画モードに切り替える
			if(mode == "image"){
				setCaptureMode(endPointURL, "video");
			}
			else{
				startCaptureSession(endPointURL);
			}
				
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}


//Theta Vの撮影モードを変更する
function setCaptureMode(endPointURL, _mode) {
	var getOptionsJSONData = {
		name: "camera.setOptions",
		parameters: {
			options: {
				    captureMode: _mode
			}
		}
   	}	

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( getOptionsJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			log("動画モードに変更");
			startCaptureSession(endPointURL);
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

//Theta の動画の撮影をスタートする
function startCaptureSession(endPointURL) {
	var takePictureJSONData = {
		name: "camera.startCapture",
	} 

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( takePictureJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
				log("動画撮影スタート");
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

//動画の撮影を終了する
function closeCaptureSession(endPointURL) {
	var closeSessionJSONData = {
		name: "camera.stopCapture",
	} 

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( closeSessionJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			log("動画撮影ストップ");
			},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

return {
	resultType : "pause"
};

写真撮影

IMG_1857.jpg

写真撮影機能は、大まかに下記の流れで処理を実行しています。
① 撮影前状態の取得
② 写真モード状態か否かの確認
③ もしビデオモードだったら、写真モードに切り替える
④ 写真の撮影開始

Photo_Excute.js
//Theta Endpoint URL の設定(デフォルトは192.168.1.1:80)
var endPointURL = "http://192.168.1.1:80"

wait().then((v) => {
    log("wait " + v + " millisecond");	
	closeCaptureSession(endPointURL) ;
	callbackSuccess( {
		resultType : "continue"
	} );
});

function sleep() {
	return new Promise(resolve => setTimeout(resolve,properties.millisecond));
}

async function wait() {
	checkState(endPointURL);
	//非同期処理が完了するまで待機
	await sleep();
	
	return properties.millisecond.toString();
}

function checkState(_endPointURL) {
	ajax( {
		url : _endPointURL + "/osc/state",
		type : "post",
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			checkCaptureMode(endPointURL);
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log(XMLHttpRequest + " | " + textStatus + " | " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	});
}

//Theta Vの撮影モードがvideoモードになっている確認
function checkCaptureMode(endPointURL) {
	var getOptionsJSONData = {
		name: "camera.getOptions",
		parameters: {
        optionNames: [
				"captureMode"
			]
        }
	}

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( getOptionsJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			var json_data = JSON.stringify(contents);
			var json_text = JSON.parse(json_data)
			log("checkCaptureMode = " + json_data);
			
			var mode = json_text["results"]["options"]["captureMode"];
			
			//写真モードだったら、動画モードに切り替える
			if(mode == "image"){
				setCaptureMode(endPointURL, "video");
			}
			else{
				startCaptureSession(endPointURL);
			}
				
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}


//Theta Vの撮影モードを変更する
function setCaptureMode(endPointURL, _mode) {
	var getOptionsJSONData = {
		name: "camera.setOptions",
		parameters: {
			options: {
				    captureMode: _mode
			}
		}
   	}	

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( getOptionsJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			log("動画モードに変更");
			startCaptureSession(endPointURL);
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

//Theta の動画の撮影をスタートする
function startCaptureSession(endPointURL) {
	var takePictureJSONData = {
		name: "camera.startCapture",
	} 

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( takePictureJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
				log("動画撮影スタート");
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

//動画の撮影を終了する
function closeCaptureSession(endPointURL) {
	var closeSessionJSONData = {
		name: "camera.stopCapture",
	} 

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( closeSessionJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			log("動画撮影ストップ");
			},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

return {
	resultType : "pause"
};

動画撮影

IMG_1858.jpg

動画撮影機能は、大まかに下記の流れで処理を実行しています。
① 撮影前状態の取得
② ビデオモード状態か否かの確認
③ もし写真モードだったら、ビデオモードに切り替える
④ ビデオの撮影開始/停止

Movie_Initialize.js
return {
    runtimeValues : {
        inputIndex : 0,
		outputIndex:0
    },
    resultType : "continue"
};
Movie_Excute.js
//Theta Endpoint URL の設定(デフォルトは192.168.1.1:80)
var endPointURL = "http://192.168.1.1:80"

checkState(endPointURL);

function checkState(_endPointURL) {
	ajax( {
		url : _endPointURL + "/osc/state",
		type : "post",
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			checkCaptureMode(endPointURL);
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log(XMLHttpRequest + " | " + textStatus + " | " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	});
}

//Theta Vの撮影モードがvideoモードになっている確認
function checkCaptureMode(endPointURL) {
	var getOptionsJSONData = {
		name: "camera.getOptions",
		parameters: {
        optionNames: [
				"captureMode"
			]
        }
	}

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( getOptionsJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			var json_data = JSON.stringify(contents);
			var json_text = JSON.parse(json_data)
			log("checkCaptureMode = " + json_data);
			
			var mode = json_text["results"]["options"]["captureMode"];
			
			
			if(runtimeValues.inputIndex == 0){
				//写真モードだったら、動画モードに切り替える
				if(mode == "image"){
					setCaptureMode(endPointURL, "video");
				}
				else{
					startCaptureSession(endPointURL);
				}
			}
			else if(runtimeValues.inputIndex == 1){
				closeCaptureSession(endPointURL);			
			}
				
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}


//Theta Vの撮影モードを変更する
function setCaptureMode(endPointURL, _mode) {
	var getOptionsJSONData = {
		name: "camera.setOptions",
		parameters: {
			options: {
				    captureMode: _mode
			}
		}
   	}	

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( getOptionsJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			log("動画モードに変更");
			startCaptureSession(endPointURL);
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

//Theta の動画の撮影をスタートする
function startCaptureSession(endPointURL) {
	var takePictureJSONData = {
		name: "camera.startCapture",
	} 

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( takePictureJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			log("動画撮影スタート");
			
			runtimeValues.outputIndex = 0;
			
			callbackSuccess({		
				resultType: "continue"
			});
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

//動画の撮影を終了する
function closeCaptureSession(endPointURL) {
	var closeSessionJSONData = {
		name: "camera.stopCapture",
	} 

	ajax( {
		url : endPointURL + "/osc/commands/execute",
		type : "post",
		data : JSON.stringify( closeSessionJSONData),
		contentType: "application/json",
		dataType: "json",
		timeout : 5000,
		success : function (contents) {
			log("動画撮影ストップ");
			
			runtimeValues.outputIndex = 1;
				
			callbackSuccess({
					resultType: "continue"
			});
		},
		error : function (XMLHttpRequest, textStatus, errorThrown) {
			log("Error:" + textStatus + " " + errorThrown);		
			callbackSuccess({
				resultType: "stop"
			});
		}
	} );
}

return {
	resultType : "pause"
};
Movie_Result.js
return {
    indexes : [ runtimeValues.outputIndex ],
    resultType : "continue"
};

使ってみて

ボタンタグでリモート撮影や、マイクタグで盛り上がった時に自動撮影、明るさタグで手をかざして数秒後に撮影など、色々なトリガーを条件に撮影ができるのでより自然な360度写真/動画が撮影できた。ただ、トリガーが重なり命令が重なるとThetaVが上手くコントロールできず誤動作することがあったので、以前作った出力制御タグを活用しました。

参考

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?