Android
Android ビデオ録画機能を作る
Android ビデオを録画するサンプルコード
Androidでビデオをキャプチャするサンプルコードです。Intentとかで他のビデオアプリを使う簡単実装ではなく、ビデオアプリ自体をつくるやつです。
別に新しい話でもないし、気楽にできるかと思っていたらかなり時間がかかりましたので、ここにとりあえず出た結論を書いておきます。
「Androidのカメラは鬼門だ」と聞いてはいましたが、本当に、プレビューの画面が縦横が逆になったり、縦横比がおかしくなったり、録画した画像が縦横逆だったり、しょっちゅう落ちたり、と問題が多かったです。
ちなみに、GoogleのAndroidのサンプルコードが昔はあったらしいのですが、Android5(Lollipop)への対応だけのやつしか書いてない…。
それがコレ↓
http://developer.android.com/samples/Camera2Video/index.html
古いメソッドは、Deprecateされてしまっているので、注意が必要ですが、Android5だけに対応すればいい世の中って、2年は先だなぁ(現在2015年3月26日)…
で、色々とはまった結果、色々なサイトの参考情報を集結してこのようになりました。
このサンプルコードはビルドダーゲット4.3.1(APIレベル18)、最低対応バージョンが3.0(APIレベル11)です。
確認機種はGalaxy S3a(Android4.3), Experia SO-04D(Android4.1.2)です。
//VideoAvtivity.java public class VideoActivity extends Activity implements SurfaceHolder.Callback{ private SurfaceHolder surfaceHolder; private SurfaceView surfaceView; public MediaRecorder mrec = new MediaRecorder(); //private Button stopRecording = null; File video; private Camera mCamera; private final static String TAG = "VideoActivity"; private Context context; private Button startRecording; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.video); context = this; } @Override public void onResume(){ super.onResume(); int numCams = Camera.getNumberOfCameras(); if(numCams > 0){ try{ mCamera = Camera.open(); surfaceView = (SurfaceView) findViewById(R.id.surface_camera); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); } catch (RuntimeException ex){ Toast.makeText(context, "カメラがありません", Toast.LENGTH_LONG).show(); } } } @Override protected void onPause() { if(mCamera != null) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } super.onPause(); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, 0, 0, "StartRecording"); menu.add(0, 1, 0, "StopRecording"); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { //この辺は若干適当なので利用する際はアレンジして頂きたいですが、 //オプションのメニューアイテムで録画開始とストップをします。 switch (item.getItemId()) { case 0: try { startRecording(); } catch (Exception e) { String message = e.getMessage(); Log.i(null, "Problem Start"+message); mrec.release(); } break; case 1: //GoToAllNotes mrec.stop(); mrec.release(); mrec = null; break; default: break; } return super.onOptionsItemSelected(item); } protected void startRecording() throws IOException { mrec = new MediaRecorder(); // Works well mCamera.unlock(); mrec.setCamera(mCamera); mrec.setPreviewDisplay(surfaceHolder.getSurface()); mrec.setVideoSource(MediaRecorder.VideoSource.CAMERA); mrec.setAudioSource(MediaRecorder.AudioSource.MIC); //録画される画面の縦横を決める int degrees = getSurfaceDegrees(); Camera.CameraInfo camInfo = new Camera.CameraInfo(); int camera_id = findFrontFacingCameraID(); Camera.getCameraInfo(camera_id, camInfo); int cameraRotationOffset = camInfo.orientation; int displayRotation = getDisplayRotation(camInfo, cameraRotationOffset, degrees); mrec.setOrientationHint(displayRotation); mrec.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); Date date = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String file_name = sdf1.format(date)+".3gp"; mrec.setOutputFile("/sdcard/"+file_name); mrec.prepare(); mrec.start(); } protected void stopRecording() { mrec.stop(); mrec.release(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) { startPreview(holder); } @Override public void surfaceCreated(SurfaceHolder holder) { if (mCamera != null){ } else { Toast.makeText(getApplicationContext(), "Camera not available!", Toast.LENGTH_LONG).show(); finish(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { } public void startPreview(SurfaceHolder holder) { try { Log.i(TAG, "starting preview"); Camera.CameraInfo camInfo = new Camera.CameraInfo(); Camera.Parameters params = mCamera.getParameters(); int camera_id = findFrontFacingCameraID(); Camera.getCameraInfo(camera_id, camInfo); int cameraRotationOffset = camInfo.orientation; Camera.Parameters parameters = mCamera.getParameters(); List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes(); Camera.Size previewSize = null; float closestRatio = Float.MAX_VALUE; int targetPreviewWidth = isLandscape() ? getWidth() : getHeight(); int targetPreviewHeight = isLandscape() ? getHeight() : getWidth(); float targetRatio = targetPreviewWidth / (float) targetPreviewHeight; Log.v(TAG, "target size: " + targetPreviewWidth + " / " + targetPreviewHeight + " ratio:" + targetRatio); for (Camera.Size candidateSize : previewSizes) { float whRatio = candidateSize.width / (float) candidateSize.height; if (previewSize == null || Math.abs(targetRatio - whRatio) < Math.abs(targetRatio - closestRatio)) { closestRatio = whRatio; previewSize = candidateSize; } } int degrees = getSurfaceDegrees(); int displayRotation = getDisplayRotation(camInfo, cameraRotationOffset, degrees); Log.d(TAG, "displayRotation:" + displayRotation); mCamera.setDisplayOrientation(displayRotation); int rotate = getRotation(camInfo, cameraRotationOffset, degrees); Log.v(TAG, "rotate: " + rotate); Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height); parameters.setPreviewSize(previewSize.width, previewSize.height); parameters.setRotation(rotate); mCamera.setParameters(parameters); mCamera.setPreviewDisplay(holder); mCamera.startPreview(); Log.d(TAG, "preview started"); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } private boolean isFrontFacingCam(Camera.CameraInfo camInfo){ boolean isFrontFacingCam; if(camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){ isFrontFacingCam = true; }else{ isFrontFacingCam = false; } return isFrontFacingCam; } private int getWidth(){ DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int width = displayMetrics.widthPixels; return width; } private int getHeight(){ DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int height = displayMetrics.heightPixels; return height; } private boolean isLandscape(){ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){ return true; }else{ return false; } } private int getSurfaceDegrees(){ int rotation = getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; // Natural orientation case Surface.ROTATION_90: degrees = 90; break; // Landscape left case Surface.ROTATION_180: degrees = 180; break;// Upside down case Surface.ROTATION_270: degrees = 270; break;// Landscape right } return degrees; } private int getRotation(Camera.CameraInfo camInfo, int cameraRotationOffset, int degrees){ int rotate; if (isFrontFacingCam(camInfo)) { rotate = (360 + cameraRotationOffset + degrees) % 360; } else { rotate = (360 + cameraRotationOffset - degrees) % 360; } return rotate; } private int getDisplayRotation(Camera.CameraInfo camInfo, int cameraRotationOffset, int degrees){ int displayRotation; if (isFrontFacingCam(camInfo)) { displayRotation = (cameraRotationOffset + degrees) % 360; displayRotation = (360 - displayRotation) % 360; } else { // back-facing displayRotation = (cameraRotationOffset - degrees + 360) % 360; } return displayRotation; } private int findFrontFacingCameraID() { int cameraId = -1; // Search for the front facing camera int numberOfCameras = Camera.getNumberOfCameras(); for (int i = 0; i < numberOfCameras; i++) { CameraInfo info = new CameraInfo(); Camera.getCameraInfo(i, info); if (info.facing == CameraInfo.CAMERA_FACING_FRONT) { Log.d(TAG, "Camera found"); cameraId = i; break; } } return cameraId; } }
//video.xml <?xml version="1.0" encoding="UTF-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <SurfaceView android:id="@+id/surface_camera" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" /> /> </RelativeLayout>
参考にしたサイト
http://stackoverflow.com/questions/22105251/android-mediarecorder-aspect-ratio-incorrect
他stackoverflowさん様様です。
Androidでビデオ録画機能を作る時に気をつけたいこと