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でビデオ録画機能を作る時に気をつけたいこと