Search code examples
androidvideo-capture

android video recording with duration limit and low quality


I need to record video for 8 seconds in my app. I am using MediaStore.ACTION_VIDEO_CAPTURE intent for video recording and using these parameters (MediaStore.EXTRA_VIDEO_QUALITY,MediaStore.EXTRA_DURATION_LIMIT) to set quality and duration limit of video recording. but i encountered a very interesting bug i.e. when i set duration limit to 8 seconds and video quality to 1, its working fine and is recording video for 8 seconds but as soon i changes the video quality to 0 and keeping everything same, the video is now recorded for 21 seconds. I am using sony Xperia phone for testing, but when i shift to HTC, duration limit not working in any case neither on setting video quality to 1 nor on setting it to 0.

So i don't know what is happening right now. In severe need. please help. Thanks in advance.

Here is the code I am using..

private void recordVideo() {
    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

    File f = null;

    try {
        f = setUpVideoFile();
        filePath = f.getAbsolutePath();
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
    } catch (IOException e) {
        e.printStackTrace();
        f = null;
        filePath = null;
    }
    objectGlobal.setFilepath(filePath);
    intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
    intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 8);
    startActivityForResult(intent, CAMERA_CAPTURE_VIDEO);
}

Solution

  • I know its bit late to answer this question, but i think i have to, so that if others are facing this problem, they can easily deal with it.

    Actually the problem is when you lower the video quality via this

     intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
    

    the camera intent no longer works as a video recorder. It actually creates a mms when we try lowering the quality. As far as time is concerned, I don't know whether it is a bug in Android or may be some memory constraints. So the solution I adopted is to make custom video recording class using SurfaceView. So I am pasting the code of Video Recording from one of my projects.Also the video recorded is almost playable in all android/iOS devices, i have tested so far. Here is my Video Recording Class

     package com.mukesh.videorecordingsample;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.hardware.Camera;
    import android.hardware.Camera.Size;
    import android.media.CamcorderProfile;
    import android.media.MediaRecorder;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.view.KeyEvent;
    import android.view.Surface;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.Window;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class RecordVideoPostsActivity extends Activity implements
            SurfaceHolder.Callback, OnClickListener {
    
        protected static final int RESULT_ERROR = 0x00000001;
    
        private static final int MAX_VIDEO_DURATION = 8 * 1000;
        private static final int ID_TIME_COUNT = 0x1006;
    
        private SurfaceView mSurfaceView;
        private ImageView iv_cancel, iv_ok, iv_record;
        private TextView tv_counter;
    
        private SurfaceHolder mSurfaceHolder;
        private MediaRecorder mMediaRecorder;
        private Camera mCamera;
    
        private List<Size> mSupportVideoSizes;
    
        private String filePath;
    
        private boolean mIsRecording = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            this.requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_recordvideo);
    
            initView();
        }
    
        private void initView() {
    
            mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
    
            iv_record = (ImageView) findViewById(R.id.iv_record);
            iv_cancel = (ImageView) findViewById(R.id.iv_cancel);
            iv_ok = (ImageView) findViewById(R.id.iv_ok);
            iv_record.setImageResource(R.drawable.btn_video_start);
    
            tv_counter = (TextView) findViewById(R.id.timer);
            tv_counter.setVisibility(View.GONE);
    
            iv_cancel.setOnClickListener(this);
            iv_ok.setOnClickListener(this);
            iv_record.setOnClickListener(this);
    
            mSurfaceHolder = mSurfaceView.getHolder();
            mSurfaceHolder.addCallback(this);
    
        }
    
        private void exit(final int resultCode, final Intent data) {
            if (mIsRecording) {
                new AlertDialog.Builder(RecordVideoPostsActivity.this)
                        .setTitle("Video Recorder")
                        .setMessage("Do you want to exit?")
                        .setPositiveButton("yes",
                                new DialogInterface.OnClickListener() {
    
                                    @Override
                                    public void onClick(DialogInterface dialog,
                                                        int which) {
                                        stopRecord();
                                        if (resultCode == RESULT_CANCELED) {
                                            if (filePath != null)
                                                deleteFile(new File(filePath));
                                        }
                                        setResult(resultCode, data);
                                        finish();
                                    }
                                })
                        .setNegativeButton("no",
                                new DialogInterface.OnClickListener() {
    
                                    @Override
                                    public void onClick(DialogInterface dialog,
                                                        int which) {
    
                                    }
                                }).show();
                return;
            }
            if (resultCode == RESULT_CANCELED) {
                if (filePath != null)
                    deleteFile(new File(filePath));
            }
            setResult(resultCode, data);
            finish();
        }
    
        private void deleteFile(File delFile) {
            if (delFile == null) {
                return;
            }
            final File file = new File(delFile.getAbsolutePath());
            delFile = null;
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    if (file.exists()) {
                        file.delete();
                    }
                }
            }.start();
        }
    
        private Handler mHandler = new Handler() {
    
            @Override
            public void handleMessage(android.os.Message msg) {
                switch (msg.what) {
                    case ID_TIME_COUNT:
                        if (mIsRecording) {
                            if (msg.arg1 > msg.arg2) {
                                // mTvTimeCount.setVisibility(View.INVISIBLE);
                                tv_counter.setText("00:00");
                                stopRecord();
                            } else {
                                tv_counter.setText("00:0" + (msg.arg2 - msg.arg1));
                                Message msg2 = mHandler.obtainMessage(ID_TIME_COUNT,
                                        msg.arg1 + 1, msg.arg2);
                                mHandler.sendMessageDelayed(msg2, 1000);
                            }
                        }
                        break;
    
                    default:
                        break;
                }
            }
    
            ;
    
        };
    
        private void openCamera() {
            try {
                this.mCamera = Camera.open();
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setRotation(90);
                System.out.println(parameters.flatten());
                parameters.set("orientation", "portrait");
                mCamera.setParameters(parameters);
                mCamera.lock();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
                    try {
                        mCamera.setDisplayOrientation(90);
                    } catch (NoSuchMethodError e) {
                        e.printStackTrace();
                    }
                }
                mSupportVideoSizes = parameters.getSupportedVideoSizes();
                if (mSupportVideoSizes == null || mSupportVideoSizes.isEmpty()) {
                    String videoSize = parameters.get("video-size");
                    Log.i(EmBazaarConstants.APP_NAME, videoSize);
                    mSupportVideoSizes = new ArrayList<Camera.Size>();
                    if (!RecordVideoPostsActivity.isEmpty(videoSize)) {
                        String[] size = videoSize.split("x");
                        if (size.length > 1) {
                            try {
                                int width = Integer.parseInt(size[0]);
                                int height = Integer.parseInt(size[1]);
                                mSupportVideoSizes.add(mCamera.new Size(width,
                                        height));
                            } catch (Exception e) {
                                Log.e(EmBazaarConstants.APP_NAME, e.toString());
                            }
                        }
                    }
                }
                for (Size size : mSupportVideoSizes) {
                    Log.i(EmBazaarConstants.APP_NAME, size.width + "<>" + size.height);
                }
            } catch (Exception e) {
                Log.e(EmBazaarConstants.APP_NAME, "Open Camera error\n" + e.toString());
            }
        }
    
        private boolean initVideoRecorder() {
            if (mCamera == null) {
                mCamera = Camera.open();
                mCamera.unlock();
            } else {
                mCamera.unlock();
            }
            mMediaRecorder = new MediaRecorder();
    
            mMediaRecorder.setCamera(mCamera);
    
            try {
                mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            try {
                CamcorderProfile lowProfile = CamcorderProfile
                        .get(CamcorderProfile.QUALITY_LOW);
                CamcorderProfile hightProfile = CamcorderProfile
                        .get(CamcorderProfile.QUALITY_HIGH);
                if (lowProfile != null && hightProfile != null) {
                    lowProfile.audioCodec = MediaRecorder.AudioEncoder.AAC;
                    lowProfile.duration = hightProfile.duration;
                    lowProfile.videoCodec = MediaRecorder.VideoEncoder.H264;
                    lowProfile.videoFrameRate = hightProfile.videoFrameRate;
                    lowProfile.videoBitRate = 1500000 > hightProfile.videoBitRate ? hightProfile.videoBitRate
                            : 1500000;
                    if (mSupportVideoSizes != null && !mSupportVideoSizes.isEmpty()) {
                        int width = 640;
                        int height = 480;
                        Collections.sort(mSupportVideoSizes, new SizeComparator());
                        int lwd = mSupportVideoSizes.get(0).width;
                        for (Size size : mSupportVideoSizes) {
                            int wd = Math.abs(size.width - 640);
                            if (wd < lwd) {
                                width = size.width;
                                height = size.height;
                                lwd = wd;
                            } else {
                                break;
                            }
                        }
                        lowProfile.videoFrameWidth = width;
                        lowProfile.videoFrameHeight = height;
                    }
    
                    mMediaRecorder.setProfile(lowProfile);
                }
            } catch (Exception e) {
                try {
                    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                try {
                    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                if (mSupportVideoSizes != null && !mSupportVideoSizes.isEmpty()) {
                    Collections.sort(mSupportVideoSizes, new SizeComparator());
                    Size size = mSupportVideoSizes.get(0);
                    try {
                        mMediaRecorder.setVideoSize(size.width, size.height);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                } else {
                    try {
                        mMediaRecorder.setVideoSize(640, 480);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                e.printStackTrace();
            }
    
            File f = null;
            try {
                f = setUpVideoFile();
                filePath = f.getAbsolutePath();
            } catch (IOException e) {
                e.printStackTrace();
                f = null;
                filePath = null;
            }
            mMediaRecorder.setOutputFile(filePath);
    
            mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
                try {
                    mMediaRecorder.setOrientationHint(90);
                } catch (NoSuchMethodError e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
            try {
                mMediaRecorder.prepare();
            } catch (IllegalStateException e) {
                Log.d("VideoPreview",
                        "IllegalStateException preparing MediaRecorder: "
                                + e.getMessage());
                releaseMediaRecorder();
                return false;
            } catch (IOException e) {
                Log.d("VideoPreview",
                        "IOException preparing MediaRecorder: " + e.getMessage());
                releaseMediaRecorder();
                return false;
            } catch (Exception e) {
                releaseMediaRecorder();
                e.printStackTrace();
            }
            return true;
        }
    
        private void releaseMediaRecorder() {
            if (mMediaRecorder != null) {
                mMediaRecorder.reset();
                mMediaRecorder.release();
                mMediaRecorder = null;
                mCamera.lock();
            }
        }
    
        private void releaseCamera() {
            if (mCamera != null) {
                mCamera.release();
                mCamera = null;
            }
        }
    
        private void startRecord() {
            try {
                if (initVideoRecorder()) {
                    mMediaRecorder.start();
                    iv_record.setImageResource(R.drawable.btn_video_stop);
                } else {
                    releaseMediaRecorder();
                    iv_record.setImageResource(R.drawable.btn_video_start);
                }
                tv_counter.setVisibility(View.VISIBLE);
                tv_counter.setText("00:0" + (MAX_VIDEO_DURATION / 1000));
                Message msg = mHandler.obtainMessage(ID_TIME_COUNT, 1,
                        MAX_VIDEO_DURATION / 1000);
                mHandler.sendMessage(msg);
                mIsRecording = true;
            } catch (Exception e) {
                showShortToast("problem while capturing video");
                e.printStackTrace();
                exit(RESULT_ERROR, null);
            }
        }
    
        private void stopRecord() {
            try {
                mMediaRecorder.stop();
            } catch (Exception e) {
                if (new File(filePath) != null
                        && new File(filePath).exists()) {
                    new File(filePath).delete();
                }
            }
            releaseMediaRecorder();
            mCamera.lock();
            iv_record.setImageResource(R.drawable.btn_video_start);
            mIsRecording = false;
    
            iv_record.setVisibility(View.GONE);
            iv_cancel.setVisibility(View.VISIBLE);
            iv_ok.setVisibility(View.VISIBLE);
        }
    
        public static void setCameraDisplayOrientation(Activity activity,
                                                       int cameraId, Camera camera) {
            Camera.CameraInfo info = new Camera.CameraInfo(); // Since API level 9
            Camera.getCameraInfo(cameraId, info);
            int rotation = activity.getWindowManager().getDefaultDisplay()
                    .getRotation();
            int degrees = 0;
            switch (rotation) {
                case Surface.ROTATION_0:
                    degrees = 0;
                    break;
                case Surface.ROTATION_90:
                    degrees = 90;
                    break;
                case Surface.ROTATION_180:
                    degrees = 180;
                    break;
                case Surface.ROTATION_270:
                    degrees = 270;
                    break;
            }
    
            int result;
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                result = (info.orientation + degrees) % 360;
                result = (360 - result) % 360;
            } else {
                result = (info.orientation - degrees + 360) % 360;
            }
            camera.setDisplayOrientation(result);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            openCamera();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            releaseCamera();
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            if (mCamera != null) {
                try {
                    mCamera.setPreviewDisplay(holder);
                    mCamera.startPreview();
                } catch (Exception e) {
                }
            }
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                                   int height) {
    
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            if (mCamera != null) {
                try {
                    mCamera.stopPreview();
                } catch (Exception e) {
                }
            }
        }
    
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                exit(RESULT_CANCELED, null);
                return true;
            }
            return super.onKeyDown(keyCode, event);
        }
    
        @Override
        public void onClick(View arg0) {
            switch (arg0.getId()) {
                case R.id.iv_ok:
                    Intent data = new Intent();
                    if (filePath != null) {
                        data.putExtra("videopath", filePath);
                    }
                    exit(RESULT_OK, data);
                    break;
                case R.id.iv_cancel:
                    exit(RESULT_CANCELED, null);
                    break;
                case R.id.iv_record:
                    if (mIsRecording) {
                        stopRecord();
                    } else {
                        startRecord();
                    }
                    break;
                default:
                    break;
            }
        }
    
        protected void showShortToast(String text) {
            Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
        }
    
        private File setUpVideoFile() throws IOException {
    
            File videoFile = null;
    
            if (Environment.MEDIA_MOUNTED.equals(Environment
                    .getExternalStorageState())) {
    
                File storageDir = new File(
                        EmBazaarConstants.LOCAL_STORAGE_BASE_PATH_FOR_POSTED_VIDEOS)
                        .getParentFile();
    
                if (storageDir != null) {
                    if (!storageDir.mkdirs()) {
                        if (!storageDir.exists()) {
                            Log.d("CameraSample", "failed to create directory");
                            return null;
                        }
                    }
                }
                videoFile = File.createTempFile(EmBazaarConstants.MP4_FILE_PREFIX
                                + System.currentTimeMillis() + "_",
                        EmBazaarConstants.MP4_FILE_SUFIX, storageDir);
            } else {
                Log.v(getString(R.string.app_name),
                        "External storage is not mounted READ/WRITE.");
            }
    
            return videoFile;
        }
    
        private class SizeComparator implements Comparator<Size> {
    
            @Override
            public int compare(Size lhs, Size rhs) {
                return rhs.width - lhs.width;
            }
        }
    
        public static boolean isEmpty(String str) {
            return str == null || "".equals(str.trim());
        }
    
    }
    

    and you can simply call this class in your Activity like this

    if (isDeviceSupportCamera()) {
                        startActivityForResult(new Intent(PostStatusActivity.this,
                                        yourActivity.class),
                                EmBazaarConstants.CAMERA_CAPTURE_VIDEO);
                    } else {
                        Toast.makeText(this, "Your device doesn't support camera",
                                Toast.LENGTH_LONG).show();
                    }
    

    and here is isDeviceSupportCamera() function

     private boolean isDeviceSupportCamera() {
            if (getApplicationContext().getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_CAMERA)) {
                return true;
            } else {
                return false;
            }
        }
    

    In your onActivityResult, you have to write this code

     @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    
            if (requestCode == EmBazaarConstants.CAMERA_CAPTURE_VIDEO
                    && resultCode == RESULT_OK) {
    
                if (data != null && data.getStringExtra("videopath") != null) 
                    videoFilePath= data.getStringExtra("videopath"); 
    }           
    
    }
    

    Hope this helps.

    ** Although we have new Camera2 APIs in android lollipop, but this code still works in android lollipop as well.But still you can change to new Camera2 APIs,if you want.