無音カメラ開発日誌 その1


先日twitterで開発者さんとこんなやりとりがありまして…… 


 
さくっと15分くらいで作ってみました! 保存処理をちょこっと工夫しています。 

○○.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(○○.getDrawingCache());

「○○」の部分にViewを入れると、そのViewのキャプチャが取れるので、カメラを展開しているSurfaceViewを当てはめています。 
 
[AndroidManifest.xml]

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

カメラを使うのと、撮影した画像をSDカードに保存するので、このパーミッションを追加してください。 
 
[MainActivity.java]

public class MainActivity extends Activity {
	private SurfaceView mySurfaceView;
	private Camera myCamera;

	private String PARENT_FOLDER_NAME = "gucci1208";

	private int default_camera_angle;
	private int default_image_angle;

	private SurfaceHolder.Callback mSurfaceListener = new SurfaceHolder.Callback() {
		public void surfaceCreated(SurfaceHolder holder) {
			// TODO Auto-generated method stub
			myCamera = Camera.open();

			// カメラ機能が横向き固定なので変更
			myCamera.setDisplayOrientation(default_camera_angle);

			try {
				myCamera.setPreviewDisplay(holder);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		public void surfaceDestroyed(SurfaceHolder holder) {
			// TODO Auto-generated method stub
			myCamera.release();
			myCamera = null;
		}

		public void surfaceChanged(SurfaceHolder holder, int format, int width,
				int height) {
			myCamera.stopPreview();

			Parameters mParam = myCamera.getParameters();

			//撮った画像も縦向きに
			mParam.setRotation(default_image_angle);

			// Set orientation
			boolean portrait = isPortrait();
			if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
				// 2.1 and before
				if (portrait) {
					mParam.set("orientation", "portrait");
				} else {
					mParam.set("orientation", "landscape");
				}
			} else {
				// 2.2 and later
				if (portrait) {
					myCamera.setDisplayOrientation(90);
				} else {
					myCamera.setDisplayOrientation(0);
				}
			}

			// Set width & height
			int previewWidth = width;
			int previewHeight = height;
			if (portrait) {
				previewWidth = height;
				previewHeight = width;
			}

			List<Size> sizes = mParam.getSupportedPreviewSizes();
			int tmpHeight = 0;
			int tmpWidth = 0;
			for (Size size : sizes) {
				if ((size.width > previewWidth)
						|| (size.height > previewHeight)) {
					continue;
				}
				if (tmpHeight < size.height) {
					tmpWidth = size.width;
					tmpHeight = size.height;
				}
			}
			previewWidth = tmpWidth;
			previewHeight = tmpHeight;

			mParam.setPreviewSize(previewWidth, previewHeight);

			// Adjust SurfaceView size
			ViewGroup.LayoutParams layoutParams = mySurfaceView.getLayoutParams();

			float layoutHeight, layoutWidth;
			if (portrait) {
			    layoutHeight = previewWidth;
			    layoutWidth = previewHeight;
			} else {
			    layoutHeight = previewHeight;
			    layoutWidth = previewWidth;
			}

			float factH, factW, fact;
			factH = height / layoutHeight;
			factW = width / layoutWidth;
			// Select smaller factor, because the surface cannot be set to the size larger than display metrics.
			if (factH < factW) {
			    fact = factH;
			} else {
			    fact = factW;
			}
			layoutParams.height = (int)(layoutHeight * fact);
			layoutParams.width = (int)(layoutWidth * fact);

			mySurfaceView.setLayoutParams(layoutParams);

			myCamera.setParameters(mParam);
			myCamera.startPreview();
		}
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.lo_camera);

		//タッチすると撮影するよメッセージ
		android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(MainActivity.this);
		builder.setMessage("画面をタッチすると撮影します").setPositiveButton("OK", null).show();

		default_camera_angle = 270;
		default_image_angle = 90;

		mySurfaceView = (SurfaceView) findViewById(R.id.surface_view);
		SurfaceHolder holder = mySurfaceView.getHolder();
		holder.addCallback(mSurfaceListener);
		holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (event.getAction() == MotionEvent.ACTION_DOWN) {
			if (myCamera != null) {
				//保存処理
				SaveScreen();
			}
		}
		return true;
	}

	private void SaveScreen() {
		mySurfaceView.setDrawingCacheEnabled(true);
		Bitmap bitmap = Bitmap.createBitmap(mySurfaceView.getDrawingCache());

		// SDカードにJPEGデータを保存する
		if (bitmap != null) {
			try {
				// 「/sdcard」を取得
				File directory = Environment.getExternalStorageDirectory();
				// SDカードに書き込める状態かチェック
				if (directory.exists()) {
					// ⇒書き込める状態の場合
					if (directory.canWrite()) {
						// 画像を保存するフォルダを作成します。
						File file1 = new File(directory.getAbsolutePath()
								+ "/" + PARENT_FOLDER_NAME);
						file1.mkdir();
						File file2 = new File(directory.getAbsolutePath()
								+ "/" + PARENT_FOLDER_NAME + "/"
								+ getPackageName());
						file2.mkdir();
					}
				}
				// フルパスを取得
				String folderpath = directory.getAbsolutePath() + "/"
						+ PARENT_FOLDER_NAME + "/" + getPackageName() + "/";

				// sdcardフォルダを指定
				File root = new File(folderpath);
				// 日付でファイル名を作成
				String filename = System.currentTimeMillis() + ".jpg";

				// 保存処理開始
				FileOutputStream fos = null;
				fos = new FileOutputStream(new File(root, filename));
				// jpegで保存
				bitmap.compress(CompressFormat.JPEG, 100, fos);
				// 保存処理終了
				fos.close();

				finish();
			} catch (Exception e) {
				android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(MainActivity.this);
				builder.setMessage("撮影した画像の保存に失敗しました").setPositiveButton("OK", null).show();
			}
		}
	}

	protected boolean isPortrait() {
		return (MainActivity.this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
	}
}

カメラ機能をSurfaceViewを使って実現しています。 
本来なら「SaveScreen();」の部分で、「Camera.ShutterCallback」を呼び出してイメージを生成したりするのですが、そこでSurfaceViewのキャプチャを取得してSDカードに保存しています。 
 
[activity_main.xml]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:text="" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="vertical" >
        <SurfaceView
            android:id="@+id/surface_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center" />
    </LinearLayout>

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:text="" />

</LinearLayout>

レイアウトxmlはこのようになります。単純ですね。 
 
これで[SDカード]-[gucci1208]-[com.gucci1208.silentcamera]フォルダに画像が保存されたはずです。 
さて、撮影された画像がこちら。 
1389961117651
なんだよこれ…… 
 
どうやらSurfaceViewから派生しているVideoViewのビットマップは取得できないようです。なんと! 
 
無音カメラの開発、思ったより難しいみたい。 
その2に続く。

カテゴリー: Android, Eclipse, Java, XML, プログラム

コメントを残す

メールアドレスが公開されることはありません。