summaryrefslogtreecommitdiffstats
path: root/sl4a/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java
diff options
context:
space:
mode:
Diffstat (limited to 'sl4a/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java')
-rw-r--r--sl4a/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java385
1 files changed, 0 insertions, 385 deletions
diff --git a/sl4a/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java b/sl4a/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java
deleted file mode 100644
index b984828..0000000
--- a/sl4a/Common/src/com/googlecode/android_scripting/webcam/WebCamFacade.java
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * Copyright (C) 2016 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.googlecode.android_scripting.webcam;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-
-import android.app.Service;
-import android.graphics.ImageFormat;
-import android.graphics.Rect;
-import android.graphics.YuvImage;
-import android.hardware.Camera;
-import android.hardware.Camera.Parameters;
-import android.hardware.Camera.PreviewCallback;
-import android.hardware.Camera.Size;
-import android.util.Base64;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.WindowManager;
-import android.view.SurfaceHolder.Callback;
-
-import com.googlecode.android_scripting.BaseApplication;
-import com.googlecode.android_scripting.FutureActivityTaskExecutor;
-import com.googlecode.android_scripting.Log;
-import com.googlecode.android_scripting.SingleThreadExecutor;
-import com.googlecode.android_scripting.SimpleServer.SimpleServerObserver;
-import com.googlecode.android_scripting.facade.EventFacade;
-import com.googlecode.android_scripting.facade.FacadeManager;
-import com.googlecode.android_scripting.future.FutureActivityTask;
-import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
-import com.googlecode.android_scripting.rpc.Rpc;
-import com.googlecode.android_scripting.rpc.RpcDefault;
-import com.googlecode.android_scripting.rpc.RpcOptional;
-import com.googlecode.android_scripting.rpc.RpcParameter;
-
-/**
- * Manages access to camera streaming.
- * <br>
- * <h3>Usage Notes</h3>
- * <br><b>webCamStart</b> and <b>webCamStop</b> are used to start and stop an Mpeg stream on a given port. <b>webcamAdjustQuality</b> is used to ajust the quality of the streaming video.
- * <br><b>cameraStartPreview</b> is used to get access to the camera preview screen. It will generate "preview" events as images become available.
- * <br>The preview has two modes: data or file. If you pass a non-blank, writable file path to the <b>cameraStartPreview</b> it will store jpg images in that folder.
- * It is up to the caller to clean up these files after the fact. If no file element is provided,
- * the event will include the image data as a base64 encoded string.
- * <h3>Event details</h3>
- * <br>The data element of the preview event will be a map, with the following elements defined.
- * <ul>
- * <li><b>format</b> - currently always "jpeg"
- * <li><b>width</b> - image width (in pixels)
- * <li><b>height</b> - image height (in pixels)
- * <li><b>quality</b> - JPEG quality. Number from 1-100
- * <li><b>filename</b> - Name of file where image was saved. Only relevant if filepath defined.
- * <li><b>error</b> - included if there was an IOException saving file, ie, disk full or path write protected.
- * <li><b>encoding</b> - Data encoding. If filepath defined, will be "file" otherwise "base64"
- * <li><b>data</b> - Base64 encoded image data.
- * </ul>
- *<br>Note that "filename", "error" and "data" are mutual exclusive.
- *<br>
- *<br>The webcam and preview modes use the same resources, so you can't use them both at the same time. Stop one mode before starting the other.
- *
- * @author Damon Kohler (damonkohler@gmail.com) (probably)
- * @author Robbie Matthews (rjmatthews62@gmail.com)
- *
- */
-public class WebCamFacade extends RpcReceiver {
-
- private final Service mService;
- private final Executor mJpegCompressionExecutor = new SingleThreadExecutor();
- private final ByteArrayOutputStream mJpegCompressionBuffer = new ByteArrayOutputStream();
-
- private volatile byte[] mJpegData;
-
- private CountDownLatch mJpegDataReady;
- private boolean mStreaming;
- private int mPreviewHeight;
- private int mPreviewWidth;
- private int mJpegQuality;
-
- private MjpegServer mJpegServer;
- private FutureActivityTask<SurfaceHolder> mPreviewTask;
- private Camera mCamera;
- private Parameters mParameters;
- private final EventFacade mEventFacade;
- private boolean mPreview;
- private File mDest;
-
- private final PreviewCallback mPreviewCallback = new PreviewCallback() {
- @Override
- public void onPreviewFrame(final byte[] data, final Camera camera) {
- mJpegCompressionExecutor.execute(new Runnable() {
- @Override
- public void run() {
- mJpegData = compressYuvToJpeg(data);
- mJpegDataReady.countDown();
- if (mStreaming) {
- camera.setOneShotPreviewCallback(mPreviewCallback);
- }
- }
- });
- }
- };
-
- private final PreviewCallback mPreviewEvent = new PreviewCallback() {
- @Override
- public void onPreviewFrame(final byte[] data, final Camera camera) {
- mJpegCompressionExecutor.execute(new Runnable() {
- @Override
- public void run() {
- mJpegData = compressYuvToJpeg(data);
- Map<String,Object> map = new HashMap<String, Object>();
- map.put("format", "jpeg");
- map.put("width", mPreviewWidth);
- map.put("height", mPreviewHeight);
- map.put("quality", mJpegQuality);
- if (mDest!=null) {
- try {
- File dest=File.createTempFile("prv",".jpg",mDest);
- OutputStream output = new FileOutputStream(dest);
- output.write(mJpegData);
- output.close();
- map.put("encoding","file");
- map.put("filename",dest.toString());
- } catch (IOException e) {
- map.put("error", e.toString());
- }
- }
- else {
- map.put("encoding","Base64");
- map.put("data", Base64.encodeToString(mJpegData, Base64.DEFAULT));
- }
- mEventFacade.postEvent("preview", map);
- if (mPreview) {
- camera.setOneShotPreviewCallback(mPreviewEvent);
- }
- }
- });
- }
- };
-
- public WebCamFacade(FacadeManager manager) {
- super(manager);
- mService = manager.getService();
- mJpegDataReady = new CountDownLatch(1);
- mEventFacade = manager.getReceiver(EventFacade.class);
- }
-
- private byte[] compressYuvToJpeg(final byte[] yuvData) {
- mJpegCompressionBuffer.reset();
- YuvImage yuvImage =
- new YuvImage(yuvData, ImageFormat.NV21, mPreviewWidth, mPreviewHeight, null);
- yuvImage.compressToJpeg(new Rect(0, 0, mPreviewWidth, mPreviewHeight), mJpegQuality,
- mJpegCompressionBuffer);
- return mJpegCompressionBuffer.toByteArray();
- }
-
- @Rpc(description = "Starts an MJPEG stream and returns a Tuple of address and port for the stream.")
- public InetSocketAddress webcamStart(
- @RpcParameter(name = "resolutionLevel", description = "increasing this number provides higher resolution") @RpcDefault("0") Integer resolutionLevel,
- @RpcParameter(name = "jpegQuality", description = "a number from 0-100") @RpcDefault("20") Integer jpegQuality,
- @RpcParameter(name = "port", description = "If port is specified, the webcam service will bind to port, otherwise it will pick any available port.") @RpcDefault("0") Integer port)
- throws Exception {
- try {
- openCamera(resolutionLevel, jpegQuality);
- return startServer(port);
- } catch (Exception e) {
- webcamStop();
- throw e;
- }
- }
-
- private InetSocketAddress startServer(Integer port) {
- mJpegServer = new MjpegServer(new JpegProvider() {
- @Override
- public byte[] getJpeg() {
- try {
- mJpegDataReady.await();
- } catch (InterruptedException e) {
- Log.e(e);
- }
- return mJpegData;
- }
- });
- mJpegServer.addObserver(new SimpleServerObserver() {
- @Override
- public void onDisconnect() {
- if (mJpegServer.getNumberOfConnections() == 0 && mStreaming) {
- stopStream();
- }
- }
-
- @Override
- public void onConnect() {
- if (!mStreaming) {
- startStream();
- }
- }
- });
- return mJpegServer.startPublic(port);
- }
-
- private void stopServer() {
- if (mJpegServer != null) {
- mJpegServer.shutdown();
- mJpegServer = null;
- }
- }
-
- @Rpc(description = "Adjusts the quality of the webcam stream while it is running.")
- public void webcamAdjustQuality(
- @RpcParameter(name = "resolutionLevel", description = "increasing this number provides higher resolution") @RpcDefault("0") Integer resolutionLevel,
- @RpcParameter(name = "jpegQuality", description = "a number from 0-100") @RpcDefault("20") Integer jpegQuality)
- throws Exception {
- if (mStreaming == false) {
- throw new IllegalStateException("Webcam not streaming.");
- }
- stopStream();
- releaseCamera();
- openCamera(resolutionLevel, jpegQuality);
- startStream();
- }
-
- private void openCamera(Integer resolutionLevel, Integer jpegQuality) throws IOException,
- InterruptedException {
- mCamera = Camera.open();
- mParameters = mCamera.getParameters();
- mParameters.setPictureFormat(ImageFormat.JPEG);
- mParameters.setPreviewFormat(ImageFormat.JPEG);
- List<Size> supportedPreviewSizes = mParameters.getSupportedPreviewSizes();
- Collections.sort(supportedPreviewSizes, new Comparator<Size>() {
- @Override
- public int compare(Size o1, Size o2) {
- return o1.width - o2.width;
- }
- });
- Size previewSize =
- supportedPreviewSizes.get(Math.min(resolutionLevel, supportedPreviewSizes.size() - 1));
- mPreviewHeight = previewSize.height;
- mPreviewWidth = previewSize.width;
- mParameters.setPreviewSize(mPreviewWidth, mPreviewHeight);
- mJpegQuality = Math.min(Math.max(jpegQuality, 0), 100);
- mCamera.setParameters(mParameters);
- // TODO(damonkohler): Rotate image based on orientation.
- mPreviewTask = createPreviewTask();
- mCamera.startPreview();
- }
-
- private void startStream() {
- mStreaming = true;
- mCamera.setOneShotPreviewCallback(mPreviewCallback);
- }
-
- private void stopStream() {
- mJpegDataReady = new CountDownLatch(1);
- mStreaming = false;
- if (mPreviewTask != null) {
- mPreviewTask.finish();
- mPreviewTask = null;
- }
- }
-
- private void releaseCamera() {
- if (mCamera != null) {
- mCamera.release();
- mCamera = null;
- }
- mParameters = null;
- }
-
- @Rpc(description = "Stops the webcam stream.")
- public void webcamStop() {
- stopServer();
- stopStream();
- releaseCamera();
- }
-
- private FutureActivityTask<SurfaceHolder> createPreviewTask() throws IOException,
- InterruptedException {
- FutureActivityTask<SurfaceHolder> task = new FutureActivityTask<SurfaceHolder>() {
- @Override
- public void onCreate() {
- super.onCreate();
- final SurfaceView view = new SurfaceView(getActivity());
- getActivity().setContentView(view);
- getActivity().getWindow().setSoftInputMode(
- WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
- //view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- view.getHolder().addCallback(new Callback() {
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- setResult(view.getHolder());
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- }
- });
- }
- };
- FutureActivityTaskExecutor taskExecutor =
- ((BaseApplication) mService.getApplication()).getTaskExecutor();
- taskExecutor.execute(task);
- mCamera.setPreviewDisplay(task.getResult());
- return task;
- }
-
- @Rpc(description = "Start Preview Mode. Throws 'preview' events.",returns="True if successful")
- public boolean cameraStartPreview(
- @RpcParameter(name = "resolutionLevel", description = "increasing this number provides higher resolution") @RpcDefault("0") Integer resolutionLevel,
- @RpcParameter(name = "jpegQuality", description = "a number from 0-100") @RpcDefault("20") Integer jpegQuality,
- @RpcParameter(name = "filepath", description = "Path to store jpeg files.") @RpcOptional String filepath)
- throws InterruptedException {
- mDest=null;
- if (filepath!=null && (filepath.length()>0)) {
- mDest = new File(filepath);
- if (!mDest.exists()) mDest.mkdirs();
- if (!(mDest.isDirectory() && mDest.canWrite())) {
- return false;
- }
- }
-
- try {
- openCamera(resolutionLevel, jpegQuality);
- } catch (IOException e) {
- Log.e(e);
- return false;
- }
- startPreview();
- return true;
- }
-
- @Rpc(description = "Stop the preview mode.")
- public void cameraStopPreview() {
- stopPreview();
- }
-
- private void startPreview() {
- mPreview = true;
- mCamera.setOneShotPreviewCallback(mPreviewEvent);
- }
-
- private void stopPreview() {
- mPreview = false;
- if (mPreviewTask!=null)
- {
- mPreviewTask.finish();
- mPreviewTask=null;
- }
- releaseCamera();
- }
-
- @Override
- public void shutdown() {
- mPreview=false;
- webcamStop();
- }
-}