package org.deviceconnect.android.deviceplugin.theta.core; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import org.deviceconnect.android.deviceplugin.theta.core.sensor.DefaultHeadTracker; import org.deviceconnect.android.deviceplugin.theta.core.sensor.HeadTracker; import org.deviceconnect.android.deviceplugin.theta.core.sensor.HeadTrackingListener; import org.deviceconnect.android.deviceplugin.theta.utils.BitmapUtils; import org.deviceconnect.android.deviceplugin.theta.utils.Quaternion; import org.deviceconnect.android.deviceplugin.theta.utils.Vector3D; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Spherical View API. * * <h2>Usage</h2> * <code> * // Obtain Spherical View API. * ThetaDeviceApplication app = (ThetaDeviceApplication) getApplication(); * SphericalViewApi api = app.getSphericalViewApi(); * * // Start Image View. * api.startImageView(picture, param, renderer); * * // Change Image View Settings. * api.updateImageView(newParam); * * // Stop Image View. * api.stopImageView(); * </code> */ public class SphericalViewApi implements HeadTrackingListener { private State mState; private SphericalViewParam mParam; private SphericalViewRenderer mRenderer; private final HeadTracker mHeadTracker; private Bitmap mTexture; private LivePreviewTask mLivePreviewTask; private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); public SphericalViewApi(final Context context) { mHeadTracker = new DefaultHeadTracker(context); } @Override public void onHeadRotated(final Quaternion rotation) { if (isRunning()) { SphericalViewRenderer.Camera currentCamera = mRenderer.getCamera(); SphericalViewRenderer.CameraBuilder newCamera = new SphericalViewRenderer.CameraBuilder(currentCamera); newCamera.rotate(rotation); mRenderer.setCamera(newCamera.create()); } } public synchronized void startLiveView(final LiveCamera camera, final SphericalViewParam param, final SphericalViewRenderer renderer) { if (isRunning()) { throw new IllegalStateException("SphericalViewApi is already running."); } mParam = param; if (param.isVRMode()) { mHeadTracker.registerTrackingListener(this); mHeadTracker.start(); } mRenderer = renderer; mRenderer.setScreenSettings(param.getWidth(), param.getHeight(), param.isStereo()); mLivePreviewTask = new LivePreviewTask(camera) { @Override protected void onFrame(final byte[] frame) { Bitmap texture = BitmapFactory.decodeByteArray(frame, 0, frame.length); // Fix texture size to power of two. texture = BitmapUtils.resize(texture, 512, 256); mRenderer.setTexture(texture); } }; mExecutor.execute(mLivePreviewTask); mState = State.RUNNING; } public synchronized void startImageView(final Bitmap picture, final SphericalViewParam param, final SphericalViewRenderer renderer) { if (isRunning()) { throw new IllegalStateException("SphericalViewApi is already running."); } mParam = param; mRenderer = renderer; mTexture = BitmapUtils.resize(picture, 2048, 1024); renderer.setTexture(mTexture); if (param.isVRMode()) { mHeadTracker.registerTrackingListener(this); mHeadTracker.start(); } SphericalViewRenderer.CameraBuilder camera = new SphericalViewRenderer.CameraBuilder(mRenderer.getCamera()); camera.setFov((float) param.getFOV()); // TODO Enable to change other parameters. mRenderer.setCamera(camera.create()); mRenderer.setScreenSettings(param.getWidth(), param.getHeight(), param.isStereo()); mState = State.RUNNING; } public synchronized void startImageView(final byte[] picture, final SphericalViewParam param, final SphericalViewRenderer renderer) { if (isRunning()) { throw new IllegalStateException("SphericalViewApi is already running."); } Bitmap texture = BitmapFactory.decodeByteArray(picture, 0, picture.length); startImageView(texture, param, renderer); } public synchronized void updateImageView(final SphericalViewParam param) { if (!isRunning()) { throw new IllegalStateException("SphericalViewApi is not running."); } if (!mParam.isVRMode() && param.isVRMode()) { mHeadTracker.registerTrackingListener(this); mHeadTracker.start(); } else if (mParam.isVRMode() && !param.isVRMode()) { mHeadTracker.stop(); mHeadTracker.unregisterTrackingListener(this); } SphericalViewRenderer.CameraBuilder camera = new SphericalViewRenderer.CameraBuilder(mRenderer.getCamera()); camera.setFov((float) param.getFOV()); camera.setPosition(new Vector3D((float) param.getCameraX(), (float) param.getCameraY(), (float) param.getCameraZ())); if (!param.isVRMode()) { camera.rotateByEulerAngle( (float) param.getCameraRoll(), (float) param.getCameraYaw(), (float) param.getCameraPitch() ); } mRenderer.setCamera(camera.create()); mRenderer.setSphereRadius((float) param.getSphereSize()); mRenderer.setScreenSettings(param.getWidth(), param.getHeight(), param.isStereo()); mParam = param; } public void resetCameraDirection() { mHeadTracker.reset(); } public synchronized void stop() { if (isState(State.STOPPED)) { throw new IllegalStateException("SphericalViewApi has already stopped."); } if (mTexture != null) { mTexture.recycle(); } if (mLivePreviewTask != null) { mLivePreviewTask.stop(); mLivePreviewTask = null; } mHeadTracker.stop(); mHeadTracker.unregisterTrackingListener(this); mState = State.STOPPED; } public synchronized void pause() { if (isRunning()) { mState = State.PAUSED; mHeadTracker.stop(); } } public synchronized void resume() { if (isPaused()) { mState = State.RUNNING; mHeadTracker.start(); } } public boolean isRunning() { return isState(State.RUNNING); } public boolean isPaused() { return isState(State.PAUSED); } private boolean isState(State state) { return mState == state; } private enum State { STOPPED, RUNNING, PAUSED } }