如何在 Android 应用程序中录制视频?

发布于 2024-08-12 19:16:11 字数 25 浏览 6 评论 0 原文

如何在 Android 上录制视频?

How can I capture a video recording on Android?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

檐上三寸雪 2024-08-19 19:16:11

这是一个使用 MediaRecorder 的简单视频录制示例:

public class VideoCapture extends Activity implements OnClickListener, SurfaceHolder.Callback {
    MediaRecorder recorder;
    SurfaceHolder holder;
    boolean recording = false;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        recorder = new MediaRecorder();
        initRecorder();
        setContentView(R.layout.main);

        SurfaceView cameraView = (SurfaceView) findViewById(R.id.CameraView);
        holder = cameraView.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        cameraView.setClickable(true);
        cameraView.setOnClickListener(this);
    }

    private void initRecorder() {
        recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);

        CamcorderProfile cpHigh = CamcorderProfile
                .get(CamcorderProfile.QUALITY_HIGH);
        recorder.setProfile(cpHigh);
        recorder.setOutputFile("/sdcard/videocapture_example.mp4");
        recorder.setMaxDuration(50000); // 50 seconds
        recorder.setMaxFileSize(5000000); // Approximately 5 megabytes
    }

    private void prepareRecorder() {
        recorder.setPreviewDisplay(holder.getSurface());

        try {
            recorder.prepare();
        } catch (IllegalStateException e) {
            e.printStackTrace();
            finish();
        } catch (IOException e) {
            e.printStackTrace();
            finish();
        }
    }

    public void onClick(View v) {
        if (recording) {
            recorder.stop();
            recording = false;

            // Let's initRecorder so we can record again
            initRecorder();
            prepareRecorder();
        } else {
            recording = true;
            recorder.start();
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        prepareRecorder();
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        if (recording) {
            recorder.stop();
            recording = false;
        }
        recorder.release();
        finish();
    }
}

它来自我的书:Pro Android Media: Developing适用于智能手机和平板电脑的图形、音乐、视频和富媒体应用

另外,不要忘记在清单中包含这些权限:

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

Here is a simple video recording example using the MediaRecorder:

public class VideoCapture extends Activity implements OnClickListener, SurfaceHolder.Callback {
    MediaRecorder recorder;
    SurfaceHolder holder;
    boolean recording = false;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        recorder = new MediaRecorder();
        initRecorder();
        setContentView(R.layout.main);

        SurfaceView cameraView = (SurfaceView) findViewById(R.id.CameraView);
        holder = cameraView.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        cameraView.setClickable(true);
        cameraView.setOnClickListener(this);
    }

    private void initRecorder() {
        recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);

        CamcorderProfile cpHigh = CamcorderProfile
                .get(CamcorderProfile.QUALITY_HIGH);
        recorder.setProfile(cpHigh);
        recorder.setOutputFile("/sdcard/videocapture_example.mp4");
        recorder.setMaxDuration(50000); // 50 seconds
        recorder.setMaxFileSize(5000000); // Approximately 5 megabytes
    }

    private void prepareRecorder() {
        recorder.setPreviewDisplay(holder.getSurface());

        try {
            recorder.prepare();
        } catch (IllegalStateException e) {
            e.printStackTrace();
            finish();
        } catch (IOException e) {
            e.printStackTrace();
            finish();
        }
    }

    public void onClick(View v) {
        if (recording) {
            recorder.stop();
            recording = false;

            // Let's initRecorder so we can record again
            initRecorder();
            prepareRecorder();
        } else {
            recording = true;
            recorder.start();
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        prepareRecorder();
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        if (recording) {
            recorder.stop();
            recording = false;
        }
        recorder.release();
        finish();
    }
}

It's from my book: Pro Android Media: Developing Graphics, Music, Video, and Rich Media Apps for Smartphones and Tablets

Also, do not forget to include these permissions in manifest:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
西瓜 2024-08-19 19:16:11

这是另一个正在工作的

public class EnregistrementVideoStackActivity extends Activity implements SurfaceHolder.Callback {
    private SurfaceHolder surfaceHolder;
    private SurfaceView surfaceView;
    public MediaRecorder mrec = new MediaRecorder();
    private Button startRecording = null;

    File video;
    private Camera mCamera;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.camera_surface);
        Log.i(null , "Video starting");
        startRecording = (Button)findViewById(R.id.buttonstart);
        mCamera = Camera.open();
        surfaceView = (SurfaceView) findViewById(R.id.surface_camera);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @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); 

        mrec.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
        mrec.setPreviewDisplay(surfaceHolder.getSurface());
        mrec.setOutputFile("/sdcard/zzzz.3gp"); 

        mrec.prepare();
        mrec.start();
    }

    protected void stopRecording() {
        mrec.stop();
        mrec.release();
        mCamera.release();
    }

    private void releaseMediaRecorder(){
        if (mrec != null) {
            mrec.reset();   // clear recorder configuration
            mrec.release(); // release the recorder object
            mrec = null;
            mCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (mCamera != null){
            mCamera.release();        // release the camera for other applications
            mCamera = null;
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (mCamera != null){
            Parameters params = mCamera.getParameters();
            mCamera.setParameters(params);
        }
        else {
            Toast.makeText(getApplicationContext(), "Camera not available!", Toast.LENGTH_LONG).show();
            finish();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mCamera.stopPreview();
        mCamera.release();
    }
}

camera_surface.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" />

<Button
    android:id="@+id/buttonstart"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/record_start" />

</RelativeLayout>

示例,当然在清单中包含这些权限:

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

Here is another example which is working

public class EnregistrementVideoStackActivity extends Activity implements SurfaceHolder.Callback {
    private SurfaceHolder surfaceHolder;
    private SurfaceView surfaceView;
    public MediaRecorder mrec = new MediaRecorder();
    private Button startRecording = null;

    File video;
    private Camera mCamera;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.camera_surface);
        Log.i(null , "Video starting");
        startRecording = (Button)findViewById(R.id.buttonstart);
        mCamera = Camera.open();
        surfaceView = (SurfaceView) findViewById(R.id.surface_camera);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @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); 

        mrec.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
        mrec.setPreviewDisplay(surfaceHolder.getSurface());
        mrec.setOutputFile("/sdcard/zzzz.3gp"); 

        mrec.prepare();
        mrec.start();
    }

    protected void stopRecording() {
        mrec.stop();
        mrec.release();
        mCamera.release();
    }

    private void releaseMediaRecorder(){
        if (mrec != null) {
            mrec.reset();   // clear recorder configuration
            mrec.release(); // release the recorder object
            mrec = null;
            mCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (mCamera != null){
            mCamera.release();        // release the camera for other applications
            mCamera = null;
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (mCamera != null){
            Parameters params = mCamera.getParameters();
            mCamera.setParameters(params);
        }
        else {
            Toast.makeText(getApplicationContext(), "Camera not available!", Toast.LENGTH_LONG).show();
            finish();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mCamera.stopPreview();
        mCamera.release();
    }
}

camera_surface.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" />

<Button
    android:id="@+id/buttonstart"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/record_start" />

</RelativeLayout>

And of course include these permission in manifest:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
愁以何悠 2024-08-19 19:16:11

这个演示将对您有所帮助......

video.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<ToggleButton
    android:id="@+id/toggleRecordingButton"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true" />

<SurfaceView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/surface_camera"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_centerInParent="true"
    android:layout_weight="1" >
</SurfaceView>

您的主要活动:Video.java

 public class Video extends Activity implements OnClickListener,
    SurfaceHolder.Callback {

private static final String TAG = "CAMERA_TUTORIAL";

private SurfaceView mSurfaceView;
private SurfaceHolder mHolder;
private Camera mCamera;
private boolean previewRunning;
private MediaRecorder mMediaRecorder;
private final int maxDurationInMs = 20000;
private final long maxFileSizeInBytes = 500000;
private final int videoFramesPerSecond = 20;
Button btn_record;
boolean mInitSuccesful = false;
File file;
ToggleButton mToggleButton;

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

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

    mSurfaceView = (SurfaceView) findViewById(R.id.surface_camera);
    mHolder = mSurfaceView.getHolder();
    mHolder.addCallback(this);
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    mToggleButton = (ToggleButton) findViewById(R.id.toggleRecordingButton);
    mToggleButton.setOnClickListener(new OnClickListener() {
        @Override
        // toggle video recording
        public void onClick(View v) {
            if (((ToggleButton) v).isChecked())
                mMediaRecorder.start();
            else {
                mMediaRecorder.stop();
                mMediaRecorder.reset();
                try {
                    initRecorder(mHolder.getSurface());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    });
}

private void initRecorder(Surface surface) throws IOException {
    // It is very important to unlock the camera before doing setCamera
    // or it will results in a black preview
    if (mCamera == null) 
    {
        mCamera = Camera.open();
        mCamera.unlock();
    }

    if (mMediaRecorder == null)
        mMediaRecorder = new MediaRecorder();

    mMediaRecorder.setPreviewDisplay(surface);
    mMediaRecorder.setCamera(mCamera);

    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);

    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);

    mMediaRecorder.setOutputFile(this.initFile().getAbsolutePath());

    // No limit. Don't forget to check the space on disk.
    mMediaRecorder.setMaxDuration(50000);
    mMediaRecorder.setVideoFrameRate(24);
    mMediaRecorder.setVideoSize(1280, 720);
    mMediaRecorder.setVideoEncodingBitRate(3000000);
    mMediaRecorder.setAudioEncodingBitRate(8000);

    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

    try {
        mMediaRecorder.prepare();
    } catch (IllegalStateException e) {
        // This is thrown if the previous calls are not called with the
        // proper order
        e.printStackTrace();
    }

    mInitSuccesful = true;
}

private File initFile() {
    // File dir = new
    // File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
    // this
    File dir = new File(Environment.getExternalStorageDirectory(), this
            .getClass().getPackage().getName());


    if (!dir.exists() && !dir.mkdirs()) {
        Log.wtf(TAG,
                "Failed to create storage directory: "
                        + dir.getAbsolutePath());
        Toast.makeText(Video.this, "not record", Toast.LENGTH_SHORT);
        file = null;
    } else {
        file = new File(dir.getAbsolutePath(), new SimpleDateFormat(
                "'IMG_'yyyyMMddHHmmss'.mp4'").format(new Date()));
    }
    return file;
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    try {
        if (!mInitSuccesful)
            initRecorder(mHolder.getSurface());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

private void shutdown() {
    // Release MediaRecorder and especially the Camera as it's a shared
    // object that can be used by other applications
    mMediaRecorder.reset();
    mMediaRecorder.release();
    mCamera.release();

    // once the objects have been released they can't be reused
    mMediaRecorder = null;
    mCamera = null;
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    shutdown();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {
    // TODO Auto-generated method stub

}

@Override
public void onClick(View v) {
    // TODO Auto-generated method stub

}

}

MediaMetadataRetriever 类

public class MediaMetadataRetriever {

 static {
        System.loadLibrary("media_jni");
        native_init();
    }

    // The field below is accessed by native methods
    @SuppressWarnings("unused")
    private int mNativeContext;

    public MediaMetadataRetriever() {
        native_setup();
    }

    /**
     * Call this method before setDataSource() so that the mode becomes
     * effective for subsequent operations. This method can be called only once
     * at the beginning if the intended mode of operation for a
     * MediaMetadataRetriever object remains the same for its whole lifetime,
     * and thus it is unnecessary to call this method each time setDataSource()
     * is called. If this is not never called (which is allowed), by default the
     * intended mode of operation is to both capture frame and retrieve meta
     * data (i.e., MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY).
     * Often, this may not be what one wants, since doing this has negative
     * performance impact on execution time of a call to setDataSource(), since
     * both types of operations may be time consuming.
     * 
     * @param mode The intended mode of operation. Can be any combination of 
     * MODE_GET_METADATA_ONLY and MODE_CAPTURE_FRAME_ONLY:
     * 1. MODE_GET_METADATA_ONLY & MODE_CAPTURE_FRAME_ONLY: 
     *    For neither frame capture nor meta data retrieval
     * 2. MODE_GET_METADATA_ONLY: For meta data retrieval only
     * 3. MODE_CAPTURE_FRAME_ONLY: For frame capture only
     * 4. MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY: 
     *    For both frame capture and meta data retrieval
     */
    public native void setMode(int mode);

    /**
     * @return the current mode of operation. A negative return value indicates
     * some runtime error has occurred.
     */
    public native int getMode();

    /**
     * Sets the data source (file pathname) to use. Call this
     * method before the rest of the methods in this class. This method may be
     * time-consuming.
     * 
     * @param path The path of the input media file.
     * @throws IllegalArgumentException If the path is invalid.
     */
    public native void setDataSource(String path) throws IllegalArgumentException;

    /**
     * Sets the data source (FileDescriptor) to use.  It is the caller's
     * responsibility to close the file descriptor. It is safe to do so as soon
     * as this call returns. Call this method before the rest of the methods in
     * this class. This method may be time-consuming.
     * 
     * @param fd the FileDescriptor for the file you want to play
     * @param offset the offset into the file where the data to be played starts,
     * in bytes. It must be non-negative
     * @param length the length in bytes of the data to be played. It must be
     * non-negative.
     * @throws IllegalArgumentException if the arguments are invalid
     */
    public native void setDataSource(FileDescriptor fd, long offset, long length)
            throws IllegalArgumentException;

    /**
     * Sets the data source (FileDescriptor) to use. It is the caller's
     * responsibility to close the file descriptor. It is safe to do so as soon
     * as this call returns. Call this method before the rest of the methods in
     * this class. This method may be time-consuming.
     * 
     * @param fd the FileDescriptor for the file you want to play
     * @throws IllegalArgumentException if the FileDescriptor is invalid
     */
    public void setDataSource(FileDescriptor fd)
            throws IllegalArgumentException {
        // intentionally less than LONG_MAX
        setDataSource(fd, 0, 0x7ffffffffffffffL);
    }

    /**
     * Sets the data source as a content Uri. Call this method before 
     * the rest of the methods in this class. This method may be time-consuming.
     * 
     * @param context the Context to use when resolving the Uri
     * @param uri the Content URI of the data you want to play
     * @throws IllegalArgumentException if the Uri is invalid
     * @throws SecurityException if the Uri cannot be used due to lack of
     * permission.
     */
    public void setDataSource(Context context, Uri uri)
        throws IllegalArgumentException, SecurityException {
        if (uri == null) {
            throw new IllegalArgumentException();
        }

        String scheme = uri.getScheme();
        if(scheme == null || scheme.equals("file")) {
            setDataSource(uri.getPath());
            return;
        }

        AssetFileDescriptor fd = null;
        try {
            ContentResolver resolver = context.getContentResolver();
            try {
                fd = resolver.openAssetFileDescriptor(uri, "r");
            } catch(FileNotFoundException e) {
                throw new IllegalArgumentException();
            }
            if (fd == null) {
                throw new IllegalArgumentException();
            }
            FileDescriptor descriptor = fd.getFileDescriptor();
            if (!descriptor.valid()) {
                throw new IllegalArgumentException();
            }
            // Note: using getDeclaredLength so that our behavior is the same
            // as previous versions when the content provider is returning
            // a full file.
            if (fd.getDeclaredLength() < 0) {
                setDataSource(descriptor);
            } else {
                setDataSource(descriptor, fd.getStartOffset(), fd.getDeclaredLength());
            }
            return;
        } catch (SecurityException ex) {
        } finally {
            try {
                if (fd != null) {
                    fd.close();
                }
            } catch(IOException ioEx) {
            }
        }
        setDataSource(uri.toString());
    }

    /**
     * Call this method after setDataSource(). This method retrieves the 
     * meta data value associated with the keyCode.
     * 
     * The keyCode currently supported is listed below as METADATA_XXX
     * constants. With any other value, it returns a null pointer.
     * 
     * @param keyCode One of the constants listed below at the end of the class.
     * @return The meta data value associate with the given keyCode on success; 
     * null on failure.
     */
    public native String extractMetadata(int keyCode);

    /**
     * Call this method after setDataSource(). This method finds a
     * representative frame if successful and returns it as a bitmap. This is
     * useful for generating a thumbnail for an input media source.
     * 
     * @return A Bitmap containing a representative video frame, which 
     *         can be null, if such a frame cannot be retrieved.
     */
    public native Bitmap captureFrame();

    /**
     * Call this method after setDataSource(). This method finds the optional
     * graphic or album art associated (embedded or external url linked) the 
     * related data source.
     * 
     * @return null if no such graphic is found.
     */
    public native byte[] extractAlbumArt();

    /**
     * Call it when one is done with the object. This method releases the memory
     * allocated internally.
     */
    public native void release();
    private native void native_setup();
    private static native void native_init();

    private native final void native_finalize();

    @Override
    protected void finalize() throws Throwable {
        try {
            native_finalize();
        } finally {
            super.finalize();
        }
    }

    public static final int MODE_GET_METADATA_ONLY  = 0x01;
    public static final int MODE_CAPTURE_FRAME_ONLY = 0x02;

    /*
     * Do not change these values without updating their counterparts
     * in include/media/mediametadataretriever.h!
     */
    public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
    public static final int METADATA_KEY_ALBUM           = 1;
    public static final int METADATA_KEY_ARTIST          = 2;
    public static final int METADATA_KEY_AUTHOR          = 3;
    public static final int METADATA_KEY_COMPOSER        = 4;
    public static final int METADATA_KEY_DATE            = 5;
    public static final int METADATA_KEY_GENRE           = 6;
    public static final int METADATA_KEY_TITLE           = 7;
    public static final int METADATA_KEY_YEAR            = 8;
    public static final int METADATA_KEY_DURATION        = 9;
    public static final int METADATA_KEY_NUM_TRACKS      = 10;
    public static final int METADATA_KEY_IS_DRM_CRIPPLED = 11;
    public static final int METADATA_KEY_CODEC           = 12;
    public static final int METADATA_KEY_RATING          = 13;
    public static final int METADATA_KEY_COMMENT         = 14;
    public static final int METADATA_KEY_COPYRIGHT       = 15;
    public static final int METADATA_KEY_BIT_RATE        = 16;
    public static final int METADATA_KEY_FRAME_RATE      = 17;
    public static final int METADATA_KEY_VIDEO_FORMAT    = 18;
    public static final int METADATA_KEY_VIDEO_HEIGHT    = 19;
    public static final int METADATA_KEY_VIDEO_WIDTH     = 20;
    public static final int METADATA_KEY_WRITER          = 21;
    // Add more here...
}

This demo will helpful for you....

video.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<ToggleButton
    android:id="@+id/toggleRecordingButton"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true" />

<SurfaceView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/surface_camera"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_centerInParent="true"
    android:layout_weight="1" >
</SurfaceView>

Your Main Activity: Video.java

 public class Video extends Activity implements OnClickListener,
    SurfaceHolder.Callback {

private static final String TAG = "CAMERA_TUTORIAL";

private SurfaceView mSurfaceView;
private SurfaceHolder mHolder;
private Camera mCamera;
private boolean previewRunning;
private MediaRecorder mMediaRecorder;
private final int maxDurationInMs = 20000;
private final long maxFileSizeInBytes = 500000;
private final int videoFramesPerSecond = 20;
Button btn_record;
boolean mInitSuccesful = false;
File file;
ToggleButton mToggleButton;

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

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

    mSurfaceView = (SurfaceView) findViewById(R.id.surface_camera);
    mHolder = mSurfaceView.getHolder();
    mHolder.addCallback(this);
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    mToggleButton = (ToggleButton) findViewById(R.id.toggleRecordingButton);
    mToggleButton.setOnClickListener(new OnClickListener() {
        @Override
        // toggle video recording
        public void onClick(View v) {
            if (((ToggleButton) v).isChecked())
                mMediaRecorder.start();
            else {
                mMediaRecorder.stop();
                mMediaRecorder.reset();
                try {
                    initRecorder(mHolder.getSurface());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    });
}

private void initRecorder(Surface surface) throws IOException {
    // It is very important to unlock the camera before doing setCamera
    // or it will results in a black preview
    if (mCamera == null) 
    {
        mCamera = Camera.open();
        mCamera.unlock();
    }

    if (mMediaRecorder == null)
        mMediaRecorder = new MediaRecorder();

    mMediaRecorder.setPreviewDisplay(surface);
    mMediaRecorder.setCamera(mCamera);

    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);

    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);

    mMediaRecorder.setOutputFile(this.initFile().getAbsolutePath());

    // No limit. Don't forget to check the space on disk.
    mMediaRecorder.setMaxDuration(50000);
    mMediaRecorder.setVideoFrameRate(24);
    mMediaRecorder.setVideoSize(1280, 720);
    mMediaRecorder.setVideoEncodingBitRate(3000000);
    mMediaRecorder.setAudioEncodingBitRate(8000);

    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

    try {
        mMediaRecorder.prepare();
    } catch (IllegalStateException e) {
        // This is thrown if the previous calls are not called with the
        // proper order
        e.printStackTrace();
    }

    mInitSuccesful = true;
}

private File initFile() {
    // File dir = new
    // File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
    // this
    File dir = new File(Environment.getExternalStorageDirectory(), this
            .getClass().getPackage().getName());


    if (!dir.exists() && !dir.mkdirs()) {
        Log.wtf(TAG,
                "Failed to create storage directory: "
                        + dir.getAbsolutePath());
        Toast.makeText(Video.this, "not record", Toast.LENGTH_SHORT);
        file = null;
    } else {
        file = new File(dir.getAbsolutePath(), new SimpleDateFormat(
                "'IMG_'yyyyMMddHHmmss'.mp4'").format(new Date()));
    }
    return file;
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    try {
        if (!mInitSuccesful)
            initRecorder(mHolder.getSurface());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

private void shutdown() {
    // Release MediaRecorder and especially the Camera as it's a shared
    // object that can be used by other applications
    mMediaRecorder.reset();
    mMediaRecorder.release();
    mCamera.release();

    // once the objects have been released they can't be reused
    mMediaRecorder = null;
    mCamera = null;
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    shutdown();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {
    // TODO Auto-generated method stub

}

@Override
public void onClick(View v) {
    // TODO Auto-generated method stub

}

}

MediaMetadataRetriever Class

public class MediaMetadataRetriever {

 static {
        System.loadLibrary("media_jni");
        native_init();
    }

    // The field below is accessed by native methods
    @SuppressWarnings("unused")
    private int mNativeContext;

    public MediaMetadataRetriever() {
        native_setup();
    }

    /**
     * Call this method before setDataSource() so that the mode becomes
     * effective for subsequent operations. This method can be called only once
     * at the beginning if the intended mode of operation for a
     * MediaMetadataRetriever object remains the same for its whole lifetime,
     * and thus it is unnecessary to call this method each time setDataSource()
     * is called. If this is not never called (which is allowed), by default the
     * intended mode of operation is to both capture frame and retrieve meta
     * data (i.e., MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY).
     * Often, this may not be what one wants, since doing this has negative
     * performance impact on execution time of a call to setDataSource(), since
     * both types of operations may be time consuming.
     * 
     * @param mode The intended mode of operation. Can be any combination of 
     * MODE_GET_METADATA_ONLY and MODE_CAPTURE_FRAME_ONLY:
     * 1. MODE_GET_METADATA_ONLY & MODE_CAPTURE_FRAME_ONLY: 
     *    For neither frame capture nor meta data retrieval
     * 2. MODE_GET_METADATA_ONLY: For meta data retrieval only
     * 3. MODE_CAPTURE_FRAME_ONLY: For frame capture only
     * 4. MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY: 
     *    For both frame capture and meta data retrieval
     */
    public native void setMode(int mode);

    /**
     * @return the current mode of operation. A negative return value indicates
     * some runtime error has occurred.
     */
    public native int getMode();

    /**
     * Sets the data source (file pathname) to use. Call this
     * method before the rest of the methods in this class. This method may be
     * time-consuming.
     * 
     * @param path The path of the input media file.
     * @throws IllegalArgumentException If the path is invalid.
     */
    public native void setDataSource(String path) throws IllegalArgumentException;

    /**
     * Sets the data source (FileDescriptor) to use.  It is the caller's
     * responsibility to close the file descriptor. It is safe to do so as soon
     * as this call returns. Call this method before the rest of the methods in
     * this class. This method may be time-consuming.
     * 
     * @param fd the FileDescriptor for the file you want to play
     * @param offset the offset into the file where the data to be played starts,
     * in bytes. It must be non-negative
     * @param length the length in bytes of the data to be played. It must be
     * non-negative.
     * @throws IllegalArgumentException if the arguments are invalid
     */
    public native void setDataSource(FileDescriptor fd, long offset, long length)
            throws IllegalArgumentException;

    /**
     * Sets the data source (FileDescriptor) to use. It is the caller's
     * responsibility to close the file descriptor. It is safe to do so as soon
     * as this call returns. Call this method before the rest of the methods in
     * this class. This method may be time-consuming.
     * 
     * @param fd the FileDescriptor for the file you want to play
     * @throws IllegalArgumentException if the FileDescriptor is invalid
     */
    public void setDataSource(FileDescriptor fd)
            throws IllegalArgumentException {
        // intentionally less than LONG_MAX
        setDataSource(fd, 0, 0x7ffffffffffffffL);
    }

    /**
     * Sets the data source as a content Uri. Call this method before 
     * the rest of the methods in this class. This method may be time-consuming.
     * 
     * @param context the Context to use when resolving the Uri
     * @param uri the Content URI of the data you want to play
     * @throws IllegalArgumentException if the Uri is invalid
     * @throws SecurityException if the Uri cannot be used due to lack of
     * permission.
     */
    public void setDataSource(Context context, Uri uri)
        throws IllegalArgumentException, SecurityException {
        if (uri == null) {
            throw new IllegalArgumentException();
        }

        String scheme = uri.getScheme();
        if(scheme == null || scheme.equals("file")) {
            setDataSource(uri.getPath());
            return;
        }

        AssetFileDescriptor fd = null;
        try {
            ContentResolver resolver = context.getContentResolver();
            try {
                fd = resolver.openAssetFileDescriptor(uri, "r");
            } catch(FileNotFoundException e) {
                throw new IllegalArgumentException();
            }
            if (fd == null) {
                throw new IllegalArgumentException();
            }
            FileDescriptor descriptor = fd.getFileDescriptor();
            if (!descriptor.valid()) {
                throw new IllegalArgumentException();
            }
            // Note: using getDeclaredLength so that our behavior is the same
            // as previous versions when the content provider is returning
            // a full file.
            if (fd.getDeclaredLength() < 0) {
                setDataSource(descriptor);
            } else {
                setDataSource(descriptor, fd.getStartOffset(), fd.getDeclaredLength());
            }
            return;
        } catch (SecurityException ex) {
        } finally {
            try {
                if (fd != null) {
                    fd.close();
                }
            } catch(IOException ioEx) {
            }
        }
        setDataSource(uri.toString());
    }

    /**
     * Call this method after setDataSource(). This method retrieves the 
     * meta data value associated with the keyCode.
     * 
     * The keyCode currently supported is listed below as METADATA_XXX
     * constants. With any other value, it returns a null pointer.
     * 
     * @param keyCode One of the constants listed below at the end of the class.
     * @return The meta data value associate with the given keyCode on success; 
     * null on failure.
     */
    public native String extractMetadata(int keyCode);

    /**
     * Call this method after setDataSource(). This method finds a
     * representative frame if successful and returns it as a bitmap. This is
     * useful for generating a thumbnail for an input media source.
     * 
     * @return A Bitmap containing a representative video frame, which 
     *         can be null, if such a frame cannot be retrieved.
     */
    public native Bitmap captureFrame();

    /**
     * Call this method after setDataSource(). This method finds the optional
     * graphic or album art associated (embedded or external url linked) the 
     * related data source.
     * 
     * @return null if no such graphic is found.
     */
    public native byte[] extractAlbumArt();

    /**
     * Call it when one is done with the object. This method releases the memory
     * allocated internally.
     */
    public native void release();
    private native void native_setup();
    private static native void native_init();

    private native final void native_finalize();

    @Override
    protected void finalize() throws Throwable {
        try {
            native_finalize();
        } finally {
            super.finalize();
        }
    }

    public static final int MODE_GET_METADATA_ONLY  = 0x01;
    public static final int MODE_CAPTURE_FRAME_ONLY = 0x02;

    /*
     * Do not change these values without updating their counterparts
     * in include/media/mediametadataretriever.h!
     */
    public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
    public static final int METADATA_KEY_ALBUM           = 1;
    public static final int METADATA_KEY_ARTIST          = 2;
    public static final int METADATA_KEY_AUTHOR          = 3;
    public static final int METADATA_KEY_COMPOSER        = 4;
    public static final int METADATA_KEY_DATE            = 5;
    public static final int METADATA_KEY_GENRE           = 6;
    public static final int METADATA_KEY_TITLE           = 7;
    public static final int METADATA_KEY_YEAR            = 8;
    public static final int METADATA_KEY_DURATION        = 9;
    public static final int METADATA_KEY_NUM_TRACKS      = 10;
    public static final int METADATA_KEY_IS_DRM_CRIPPLED = 11;
    public static final int METADATA_KEY_CODEC           = 12;
    public static final int METADATA_KEY_RATING          = 13;
    public static final int METADATA_KEY_COMMENT         = 14;
    public static final int METADATA_KEY_COPYRIGHT       = 15;
    public static final int METADATA_KEY_BIT_RATE        = 16;
    public static final int METADATA_KEY_FRAME_RATE      = 17;
    public static final int METADATA_KEY_VIDEO_FORMAT    = 18;
    public static final int METADATA_KEY_VIDEO_HEIGHT    = 19;
    public static final int METADATA_KEY_VIDEO_WIDTH     = 20;
    public static final int METADATA_KEY_WRITER          = 21;
    // Add more here...
}
別甾虛僞 2024-08-19 19:16:11

您可以使用相同的 MediaRecorder 类来录制音频和视频。这很简单。
这是一个示例

You record audio and video using the same MediaRecorder class. It's pretty simple.
Here's an example.

得不到的就毁灭 2024-08-19 19:16:11

查看此示例相机预览代码, 相机预览。这将帮助您开发用于视频预览的视频录制代码、创建 MediaRecorder 对象并设置视频录制参数。

Check out this Sample Camera Preview code, CameraPreview. This would help you in devloping video recording code for video preview, create MediaRecorder object, and set video recording parameters.

坏尐絯 2024-08-19 19:16:11

为了搜索者的利益,此示例将为您提供活动预览,并带有用于录制的开始/停止按钮。它是从这个 android 博客 修改而来的,似乎相当可靠。

java类(VideoWithSurfaceVw)

package <<your packagename here>>;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

public class VideoWithSurfaceVw extends Activity{

    // Adapted from http://sandyandroidtutorials.blogspot.co.uk/2013/05/android-video-capture-tutorial.html


    private Camera myCamera;
    private MyCameraSurfaceView myCameraSurfaceView;
    private MediaRecorder mediaRecorder;

    Button myButton;
    SurfaceHolder surfaceHolder;
    boolean recording;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        recording = false;

        setContentView(R.layout.activity_video_with_surface_vw);

        //Get Camera for preview
        myCamera = getCameraInstance();
        if(myCamera == null){
            Toast.makeText(VideoWithSurfaceVw.this,
                    "Fail to get Camera",
                    Toast.LENGTH_LONG).show();
        }

        myCameraSurfaceView = new MyCameraSurfaceView(this, myCamera);
        FrameLayout myCameraPreview = (FrameLayout)findViewById(R.id.videoview);
        myCameraPreview.addView(myCameraSurfaceView);

        myButton = (Button)findViewById(R.id.mybutton);
        myButton.setOnClickListener(myButtonOnClickListener);
    }

    Button.OnClickListener myButtonOnClickListener
            = new Button.OnClickListener(){

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

            try{
                if(recording){
                    // stop recording and release camera
                    mediaRecorder.stop();  // stop the recording
                    releaseMediaRecorder(); // release the MediaRecorder object

                    //Exit after saved
                    //finish();
                    myButton.setText("REC");
                    recording = false;
                }else{

                    //Release Camera before MediaRecorder start
                    releaseCamera();

                    if(!prepareMediaRecorder()){
                        Toast.makeText(VideoWithSurfaceVw.this,
                                "Fail in prepareMediaRecorder()!\n - Ended -",
                                Toast.LENGTH_LONG).show();
                        finish();
                    }

                    mediaRecorder.start();
                    recording = true;
                    myButton.setText("STOP");
                }
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }};

    private Camera getCameraInstance(){
        // TODO Auto-generated method stub
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }

    private String getFileName_CustomFormat() {
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH_mm_ss");
        Date now = new Date();
        String strDate = sdfDate.format(now);
        return strDate;
    }


    private boolean prepareMediaRecorder(){
        myCamera = getCameraInstance();
        mediaRecorder = new MediaRecorder();

        myCamera.unlock();
        mediaRecorder.setCamera(myCamera);

        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

        mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));



        mediaRecorder.setOutputFile("/sdcard/" + getFileName_CustomFormat() + ".mp4");
        //mediaRecorder.setOutputFile("/sdcard/myvideo1.mp4");
        mediaRecorder.setMaxDuration(60000); // Set max duration 60 sec.
        mediaRecorder.setMaxFileSize(50000000); // Set max file size 50M

        mediaRecorder.setPreviewDisplay(myCameraSurfaceView.getHolder().getSurface());

        try {
            mediaRecorder.prepare();
        } catch (IllegalStateException e) {
            releaseMediaRecorder();
            return false;
        } catch (IOException e) {
            releaseMediaRecorder();
            return false;
        }
        return true;

    }

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private void releaseMediaRecorder(){
        if (mediaRecorder != null) {
            mediaRecorder.reset();   // clear recorder configuration
            mediaRecorder.release(); // release the recorder object
            mediaRecorder = new MediaRecorder();
            myCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (myCamera != null){
            myCamera.release();        // release the camera for other applications
            myCamera = null;
        }
    }

    public class MyCameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback{

        private SurfaceHolder mHolder;
        private Camera mCamera;

        public MyCameraSurfaceView(Context context, Camera camera) {
            super(context);
            mCamera = camera;

            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed.
            mHolder = getHolder();
            mHolder.addCallback(this);
            // deprecated setting, but required on Android versions prior to 3.0
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int weight,
                                   int height) {
            // If your preview can change or rotate, take care of those events here.
            // Make sure to stop the preview before resizing or reformatting it.

            if (mHolder.getSurface() == null){
                // preview surface does not exist
                return;
            }

            // stop preview before making changes
            try {
                mCamera.stopPreview();
            } catch (Exception e){
                // ignore: tried to stop a non-existent preview
            }

            // make any resize, rotate or reformatting changes here

            // start preview with new settings
            try {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();

            } catch (Exception e){
            }
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            // The Surface has been created, now tell the camera where to draw the preview.
            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            } catch (IOException e) {
            }
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub

        }
    }
}

活动(activity_video_with_surface_vw)

<RelativeLayout android:id="@+id/surface_camera"     
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
android:layout_weight="1"
>

<RelativeLayout
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <FrameLayout
        android:id="@+id/videoview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
    <Button
        android:id="@+id/mybutton"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:text="REC"
        android:textSize="12dp"/>
</RelativeLayout>

</RelativeLayout>

For the benefit of searchers, this example will give you an active preview, with a start/stop button for recording. It was modified from this android blog and seems fairly reliable.

java class (VideoWithSurfaceVw)

package <<your packagename here>>;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

public class VideoWithSurfaceVw extends Activity{

    // Adapted from http://sandyandroidtutorials.blogspot.co.uk/2013/05/android-video-capture-tutorial.html


    private Camera myCamera;
    private MyCameraSurfaceView myCameraSurfaceView;
    private MediaRecorder mediaRecorder;

    Button myButton;
    SurfaceHolder surfaceHolder;
    boolean recording;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        recording = false;

        setContentView(R.layout.activity_video_with_surface_vw);

        //Get Camera for preview
        myCamera = getCameraInstance();
        if(myCamera == null){
            Toast.makeText(VideoWithSurfaceVw.this,
                    "Fail to get Camera",
                    Toast.LENGTH_LONG).show();
        }

        myCameraSurfaceView = new MyCameraSurfaceView(this, myCamera);
        FrameLayout myCameraPreview = (FrameLayout)findViewById(R.id.videoview);
        myCameraPreview.addView(myCameraSurfaceView);

        myButton = (Button)findViewById(R.id.mybutton);
        myButton.setOnClickListener(myButtonOnClickListener);
    }

    Button.OnClickListener myButtonOnClickListener
            = new Button.OnClickListener(){

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

            try{
                if(recording){
                    // stop recording and release camera
                    mediaRecorder.stop();  // stop the recording
                    releaseMediaRecorder(); // release the MediaRecorder object

                    //Exit after saved
                    //finish();
                    myButton.setText("REC");
                    recording = false;
                }else{

                    //Release Camera before MediaRecorder start
                    releaseCamera();

                    if(!prepareMediaRecorder()){
                        Toast.makeText(VideoWithSurfaceVw.this,
                                "Fail in prepareMediaRecorder()!\n - Ended -",
                                Toast.LENGTH_LONG).show();
                        finish();
                    }

                    mediaRecorder.start();
                    recording = true;
                    myButton.setText("STOP");
                }
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }};

    private Camera getCameraInstance(){
        // TODO Auto-generated method stub
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }

    private String getFileName_CustomFormat() {
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH_mm_ss");
        Date now = new Date();
        String strDate = sdfDate.format(now);
        return strDate;
    }


    private boolean prepareMediaRecorder(){
        myCamera = getCameraInstance();
        mediaRecorder = new MediaRecorder();

        myCamera.unlock();
        mediaRecorder.setCamera(myCamera);

        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

        mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));



        mediaRecorder.setOutputFile("/sdcard/" + getFileName_CustomFormat() + ".mp4");
        //mediaRecorder.setOutputFile("/sdcard/myvideo1.mp4");
        mediaRecorder.setMaxDuration(60000); // Set max duration 60 sec.
        mediaRecorder.setMaxFileSize(50000000); // Set max file size 50M

        mediaRecorder.setPreviewDisplay(myCameraSurfaceView.getHolder().getSurface());

        try {
            mediaRecorder.prepare();
        } catch (IllegalStateException e) {
            releaseMediaRecorder();
            return false;
        } catch (IOException e) {
            releaseMediaRecorder();
            return false;
        }
        return true;

    }

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private void releaseMediaRecorder(){
        if (mediaRecorder != null) {
            mediaRecorder.reset();   // clear recorder configuration
            mediaRecorder.release(); // release the recorder object
            mediaRecorder = new MediaRecorder();
            myCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (myCamera != null){
            myCamera.release();        // release the camera for other applications
            myCamera = null;
        }
    }

    public class MyCameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback{

        private SurfaceHolder mHolder;
        private Camera mCamera;

        public MyCameraSurfaceView(Context context, Camera camera) {
            super(context);
            mCamera = camera;

            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed.
            mHolder = getHolder();
            mHolder.addCallback(this);
            // deprecated setting, but required on Android versions prior to 3.0
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int weight,
                                   int height) {
            // If your preview can change or rotate, take care of those events here.
            // Make sure to stop the preview before resizing or reformatting it.

            if (mHolder.getSurface() == null){
                // preview surface does not exist
                return;
            }

            // stop preview before making changes
            try {
                mCamera.stopPreview();
            } catch (Exception e){
                // ignore: tried to stop a non-existent preview
            }

            // make any resize, rotate or reformatting changes here

            // start preview with new settings
            try {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();

            } catch (Exception e){
            }
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            // The Surface has been created, now tell the camera where to draw the preview.
            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            } catch (IOException e) {
            }
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub

        }
    }
}

activity (activity_video_with_surface_vw)

<RelativeLayout android:id="@+id/surface_camera"     
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
android:layout_weight="1"
>

<RelativeLayout
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <FrameLayout
        android:id="@+id/videoview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
    <Button
        android:id="@+id/mybutton"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:text="REC"
        android:textSize="12dp"/>
</RelativeLayout>

</RelativeLayout>
〆凄凉。 2024-08-19 19:16:11

目前(2022 年)最具前瞻性的解决方案应该是 Android CameraX 库。 https://developer.android.com/jetpack/androidx/releases/camera
示例代码可以在这里找到:https://github.com/android/camera-samples

它仍处于 Alpha 状态,但对我来说,示例代码在 Pixel 4a 上运行没有问题

Currently (in the Year 2022) the Solution that should be most future-proof is the Android CameraX Library. https://developer.android.com/jetpack/androidx/releases/camera
Sample Code can be found here: https://github.com/android/camera-samples

It is still in Alpha Status but for me the sample code works no problem on a Pixel 4a

温暖的光 2024-08-19 19:16:11

作为旁注 - Android API 中似乎存在错误或错误的文档,或者也许我只是愚蠢的。谷歌文档明确指出以下内容:

注意:从 Android 4.0(API 级别 14)开始,Camera.lock() 和
系统会自动为您管理 Camera.unlock() 调用。

请参阅:http://developer.android.com/guide/topics/media/camera .html

情况似乎并非如此!

经过几天的战斗,没有取得任何成功,并且出现了许多小问题,例如“无法启动”之类的错误,我决定手动实现锁定和嘭!一切都很好。

我在 4.1.1 设备上使用 genymotion 模拟器,最小 sdk 为 14。

As a side note - there seems to be a bug in the Android API or faulty documentation or maybe I am just plain stupid. The google docs clearly states the following:

Note: Starting with Android 4.0 (API level 14), the Camera.lock() and
Camera.unlock() calls are managed for you automatically.

See: http://developer.android.com/guide/topics/media/camera.html

This does not seem to be the case!

After batteling for literaly days without any success and many little problems like "failed to start" kinda errors I decided to manually implement the locking and BAM! everything worked fine.

Im using the genymotion emulator for a 4.1.1 device with a min sdk of 14.

寄风 2024-08-19 19:16:11

如果您使用后置摄像头,上面的示例将有效。如果您使用前置摄像头,则必须调整一些内容:

首先,您需要在清单中添加新权限。

在您的 initRecorder 方法中,而不是

CamcorderProfile cpHigh = CamcorderProfile
                .get(CamcorderProfile.QUALITY_HIGH);
recorder.setProfile(cpHigh);

您需要使用:

CamcorderProfile profile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_FRONT, CamcorderProfile.QUALITY_LOW);
recorder.setProfile(profile);

因为CamcorderProfile.QUALITY_HIGH是为后置摄像头保留的。

您还必须设置媒体记录器的视频大小,因为它在表面视图中。

以下是使用小预览显示从前置摄像头录制视频的完整示例:

Android.manifest

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.front" android:required="false" />

activity_camera.xml >

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="CameraActivity">

    <SurfaceView
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:id="@+id/surfaceView"/>

    <Button
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="REC"
        android:id="@+id/btnRecord"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="25dp" />
</RelativeLayout>

CameraActivity.java

public class SongVideoActivity extends BaseActivity implements SurfaceHolder.Callback {

    private int mCameraContainerWidth = 0;
    private SurfaceView mSurfaceView = null;
    private SurfaceHolder mSurfaceHolder = null;

    private Camera mCamera = null;
    private boolean mIsRecording = false;

    private int mPreviewHeight;
    private int mPreviewWidth;

    MediaRecorder mRecorder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_song_video);

        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable ex) {
                releaseMediaRecorder();
                releaseCamera();
            }
        });

        mCamera = getCamera();

        //camera preview
        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        mCameraContainerWidth = mSurfaceView.getLayoutParams().width;

        findViewById(R.id.btnRecord).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mIsRecording) {
                    stopRecording();
                } else {
                    // initialize video camera
                    if (prepareVideoRecorder()) {

                        // Camera is available and unlocked, MediaRecorder is prepared,
                        // now you can start recording
                        mRecorder.start();

                        // inform the user that recording has started
                        Toast.makeText(getApplicationContext(), "Started recording", Toast.LENGTH_SHORT).show();
                        mIsRecording = true;
                    } else {
                        // prepare didn't work, release the camera
                        releaseMediaRecorder();
                        // inform user
                    }
                }
            }
        });
    }

    private void stopRecording() {
        mRecorder.stop();  // stop the recording
        releaseMediaRecorder(); // release the MediaRecorder object
        mCamera.lock();         // take camera access back from MediaRecorder

        // inform the user that recording has stopped
        Toast.makeText(this, "Recording complete", Toast.LENGTH_SHORT).show();
        mIsRecording = false;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private Camera getCamera() {

        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                try {
                    return mCamera = Camera.open(camIdx);
                } catch (RuntimeException e) {
                    Log.e("cameras", "Camera failed to open: " + e.getLocalizedMessage());
                }
            }
        }
        return null;
    }

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private Camera.Size getBestPreviewSize(Camera.Parameters parameters) {
        Camera.Size result=null;

        for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
            if(size.width < size.height) continue; //we are only interested in landscape variants

            if (result == null) {
                result = size;
            }
            else {
                int resultArea = result.width*result.height;
                int newArea = size.width*size.height;

                if (newArea > resultArea) {
                    result = size;
                }
            }
        }

        return(result);
    }

    private boolean prepareVideoRecorder(){
        mRecorder = new MediaRecorder();

        // Step 1: Unlock and set camera to MediaRecorder
        mCamera.unlock();
        mRecorder.setCamera(mCamera);

        // Step 2: Set sources
        mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        mRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
        //recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

        // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
        // Customise your profile based on a pre-existing profile
        CamcorderProfile profile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_FRONT, CamcorderProfile.QUALITY_LOW);
        mRecorder.setProfile(profile);

        // Step 4: Set output file
        mRecorder.setOutputFile(new File(getFilesDir(), "movie-" + UUID.randomUUID().toString()).getAbsolutePath());
        //recorder.setMaxDuration(50000); // 50 seconds
        //recorder.setMaxFileSize(500000000); // Approximately 500 megabytes

        mRecorder.setVideoSize(mPreviewWidth, mPreviewHeight);

        // Step 5: Set the preview output
        mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());

        // Step 6: Prepare configured MediaRecorder
        try {
            mRecorder.prepare();
        } catch (IllegalStateException e) {
            Toast.makeText(getApplicationContext(), "exception: " + e.getMessage(), Toast.LENGTH_LONG).show();
            releaseMediaRecorder();
            return false;
        } catch (IOException e) {
            Toast.makeText(getApplicationContext(), "exception: " + e.getMessage(), Toast.LENGTH_LONG).show();
            releaseMediaRecorder();
            return false;
        }
        return true;
    }

    private void releaseMediaRecorder(){
        if (mRecorder != null) {
            mRecorder.reset();   // clear recorder configuration
            mRecorder.release(); // release the recorder object
            mRecorder = null;
            mCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (mCamera != null){
            mCamera.release();        // release the camera for other applications
            mCamera = null;
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setRecordingHint(true);
            Camera.Size size = getBestPreviewSize(parameters);
        mCamera.setParameters(parameters);

            //resize the view to the specified surface view width in layout
            int newHeight = size.height / (size.width / mCameraContainerWidth);
            mSurfaceView.getLayoutParams().height = newHeight;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mPreviewHeight = mCamera.getParameters().getPreviewSize().height;
        mPreviewWidth = mCamera.getParameters().getPreviewSize().width;

        mCamera.stopPreview();
        try {
            mCamera.setPreviewDisplay(mSurfaceHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        mCamera.startPreview();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mIsRecording) {
            stopRecording();
        }

        releaseMediaRecorder();
        releaseCamera();
    }
}

The above example will work if you are using rear camera. If you are using front camera, you will have to adjust some things:

First off, you will need to add new permission in the manifest.

<uses-feature android:name="android.hardware.camera.front" android:required="false" />

In your initRecorder method, instead of

CamcorderProfile cpHigh = CamcorderProfile
                .get(CamcorderProfile.QUALITY_HIGH);
recorder.setProfile(cpHigh);

You need to use:

CamcorderProfile profile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_FRONT, CamcorderProfile.QUALITY_LOW);
recorder.setProfile(profile);

because CamcorderProfile.QUALITY_HIGH is reserved for the rear camera.

You will also have to set the video size for mediarecorder as it is in your surface view.

Here is the full example of recording video from front camera with a small preview display:

Android.manifest

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.front" android:required="false" />

activity_camera.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="CameraActivity">

    <SurfaceView
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:id="@+id/surfaceView"/>

    <Button
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="REC"
        android:id="@+id/btnRecord"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="25dp" />
</RelativeLayout>

CameraActivity.java

public class SongVideoActivity extends BaseActivity implements SurfaceHolder.Callback {

    private int mCameraContainerWidth = 0;
    private SurfaceView mSurfaceView = null;
    private SurfaceHolder mSurfaceHolder = null;

    private Camera mCamera = null;
    private boolean mIsRecording = false;

    private int mPreviewHeight;
    private int mPreviewWidth;

    MediaRecorder mRecorder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_song_video);

        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable ex) {
                releaseMediaRecorder();
                releaseCamera();
            }
        });

        mCamera = getCamera();

        //camera preview
        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        mCameraContainerWidth = mSurfaceView.getLayoutParams().width;

        findViewById(R.id.btnRecord).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mIsRecording) {
                    stopRecording();
                } else {
                    // initialize video camera
                    if (prepareVideoRecorder()) {

                        // Camera is available and unlocked, MediaRecorder is prepared,
                        // now you can start recording
                        mRecorder.start();

                        // inform the user that recording has started
                        Toast.makeText(getApplicationContext(), "Started recording", Toast.LENGTH_SHORT).show();
                        mIsRecording = true;
                    } else {
                        // prepare didn't work, release the camera
                        releaseMediaRecorder();
                        // inform user
                    }
                }
            }
        });
    }

    private void stopRecording() {
        mRecorder.stop();  // stop the recording
        releaseMediaRecorder(); // release the MediaRecorder object
        mCamera.lock();         // take camera access back from MediaRecorder

        // inform the user that recording has stopped
        Toast.makeText(this, "Recording complete", Toast.LENGTH_SHORT).show();
        mIsRecording = false;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private Camera getCamera() {

        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                try {
                    return mCamera = Camera.open(camIdx);
                } catch (RuntimeException e) {
                    Log.e("cameras", "Camera failed to open: " + e.getLocalizedMessage());
                }
            }
        }
        return null;
    }

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private Camera.Size getBestPreviewSize(Camera.Parameters parameters) {
        Camera.Size result=null;

        for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
            if(size.width < size.height) continue; //we are only interested in landscape variants

            if (result == null) {
                result = size;
            }
            else {
                int resultArea = result.width*result.height;
                int newArea = size.width*size.height;

                if (newArea > resultArea) {
                    result = size;
                }
            }
        }

        return(result);
    }

    private boolean prepareVideoRecorder(){
        mRecorder = new MediaRecorder();

        // Step 1: Unlock and set camera to MediaRecorder
        mCamera.unlock();
        mRecorder.setCamera(mCamera);

        // Step 2: Set sources
        mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        mRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
        //recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

        // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
        // Customise your profile based on a pre-existing profile
        CamcorderProfile profile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_FRONT, CamcorderProfile.QUALITY_LOW);
        mRecorder.setProfile(profile);

        // Step 4: Set output file
        mRecorder.setOutputFile(new File(getFilesDir(), "movie-" + UUID.randomUUID().toString()).getAbsolutePath());
        //recorder.setMaxDuration(50000); // 50 seconds
        //recorder.setMaxFileSize(500000000); // Approximately 500 megabytes

        mRecorder.setVideoSize(mPreviewWidth, mPreviewHeight);

        // Step 5: Set the preview output
        mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());

        // Step 6: Prepare configured MediaRecorder
        try {
            mRecorder.prepare();
        } catch (IllegalStateException e) {
            Toast.makeText(getApplicationContext(), "exception: " + e.getMessage(), Toast.LENGTH_LONG).show();
            releaseMediaRecorder();
            return false;
        } catch (IOException e) {
            Toast.makeText(getApplicationContext(), "exception: " + e.getMessage(), Toast.LENGTH_LONG).show();
            releaseMediaRecorder();
            return false;
        }
        return true;
    }

    private void releaseMediaRecorder(){
        if (mRecorder != null) {
            mRecorder.reset();   // clear recorder configuration
            mRecorder.release(); // release the recorder object
            mRecorder = null;
            mCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (mCamera != null){
            mCamera.release();        // release the camera for other applications
            mCamera = null;
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setRecordingHint(true);
            Camera.Size size = getBestPreviewSize(parameters);
        mCamera.setParameters(parameters);

            //resize the view to the specified surface view width in layout
            int newHeight = size.height / (size.width / mCameraContainerWidth);
            mSurfaceView.getLayoutParams().height = newHeight;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mPreviewHeight = mCamera.getParameters().getPreviewSize().height;
        mPreviewWidth = mCamera.getParameters().getPreviewSize().width;

        mCamera.stopPreview();
        try {
            mCamera.setPreviewDisplay(mSurfaceHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        mCamera.startPreview();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mIsRecording) {
            stopRecording();
        }

        releaseMediaRecorder();
        releaseCamera();
    }
}
一向肩并 2024-08-19 19:16:11

截至 2017 年 12 月,已有一些更新,例如现在已弃用 android.hardware.Camera 的使用。虽然较新的 android.hardware.camera2 附带了一些方便的东西,例如 CameraManager

我个人非常喜欢这个例子,它利用了当前的 API,并且工作起来很有魅力:
https://github.com/googlesamples/android-Camera2Video

它还包括询问用户开始时所需的权限以及开始视频录制之前的视频预览功能。

(另外,我发现代码真的很漂亮(这对我来说非常罕见^^),但这只是我的主观意见。)

As of December 2017, there have been some updates, e.g. the usage of android.hardware.Camera is deprecated now. While the newer android.hardware.camera2 comes with handy things like a CameraManager.

I personally like this example a lot, which makes use of this current API and works like a charm:
https://github.com/googlesamples/android-Camera2Video

It also includes asking the user for the required permissions at start and features video preview before starting the video recording.

(In addition, I find the code really beautiful (and this is very rare for me ^^), but that's just my subjective opinion.)

奢欲 2024-08-19 19:16:11

您可以使用 GitHub 上的库,而不是根据草图编写代码。例如: https://github.com/CameraKit/camerakit-android (或 https://github.com/google/cameraview,或 https://github.com/hujiaweibujidao/CameraView 等)。那么你只需要:

private CameraKitView cameraKitView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    cameraKitView = findViewById(R.id.camera);
}

@Override
protected void onStart() {
    super.onStart();
    cameraKitView.onStart();
}

@Override
protected void onResume() {
    super.onResume();
    cameraKitView.onResume();
}

@Override
protected void onPause() {
    cameraKitView.onPause();
    super.onPause();
}

@Override
protected void onStop() {
    cameraKitView.onStop();
    super.onStop();
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    cameraKitView.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

Instead of writing code from the sketch, you can use a library on GitHub. For instance: https://github.com/CameraKit/camerakit-android (or https://github.com/google/cameraview, or https://github.com/hujiaweibujidao/CameraView and so on). Then you only need to:

private CameraKitView cameraKitView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    cameraKitView = findViewById(R.id.camera);
}

@Override
protected void onStart() {
    super.onStart();
    cameraKitView.onStart();
}

@Override
protected void onResume() {
    super.onResume();
    cameraKitView.onResume();
}

@Override
protected void onPause() {
    cameraKitView.onPause();
    super.onPause();
}

@Override
protected void onStop() {
    cameraKitView.onStop();
    super.onStop();
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    cameraKitView.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文