如何在我的 Android 应用中录制视频?
Posted
技术标签:
【中文标题】如何在我的 Android 应用中录制视频?【英文标题】:How can I record a video in my Android app? 【发布时间】:2010-12-21 12:34:52 【问题描述】:如何在 android 上捕获视频记录?
【问题讨论】:
我们可以在现有的视频轨道中录制音频吗?? 另一种感谢方式是检查有用的答案 【参考方案1】:这是一个使用 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 Graphics, Music, Video, and Rich Media Apps for Smartphones and Tablets
另外,不要忘记在清单中包含这些权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
【讨论】:
这是一个很好的例子。一开始我很难让它工作。我的错误与 0 字节的文件输出大小有关。我开始调试,结果发现罪魁祸首是record.prepare()。我没有为录像机设置新的输出文件,所以每次我触摸屏幕停止录像时它都会覆盖我现有的视频。希望这对其他人有帮助 :-) 顺便提一下,您还需要在清单中添加 RECORD_AUDIO、CAMERA 和 WRITE_EXTERNAL_STORAGE 权限才能使此示例代码正常工作。 我完全无法通过在录制开始前显示的预览来可靠地完成这项工作。是否有机会进行编辑以显示此内容? 这是上面带有相机预览的示例:github.com/vanevery/Custom-Video-Capture-with-Preview 警告!如果您将使用此示例,请不要忘记从表面破坏中删除 finish() 方法!它关闭了您从其他活动返回此活动的机会。我在那里迷路了2个多小时) @vanever 这个代码的大问题是.. 它创建了两个文件,一个是很好的记录,另一个是在我们重新准备记录器时创建.. 如果我们不这样做,有没有办法删除该文件记录下来..【参考方案2】:这是另一个有效的例子
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_
android:layout_
android:orientation="vertical" >
<SurfaceView
android:id="@+id/surface_camera"
android:layout_
android:layout_
android:layout_weight="1" />
<Button
android:id="@+id/buttonstart"
android:layout_
android:layout_
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" />
【讨论】:
嗨,请检查:***.com/questions/10594553/… 谢谢,vanevery的例子是存储0字节大小的文件,没用 通过单击硬件菜单,将显示开始和停止录制。但在 Android 平板电脑中没有硬件菜单。在那种情况下,我该如何开始和停止视频捕获? 为了编译示例而进行了一些调整,但这对我来说非常有用。谢谢! @Milos 你能告诉我如何将标记图像嵌入到表面视频中吗??【参考方案3】:您使用相同的 MediaRecorder 类录制音频和视频。这很简单。 这是example。
【讨论】:
MediaRecorder 中的视频实现中存在一些细微的错误,这些错误会因不可预知的原因导致分段错误。我怀疑这就是@Vishnuparsad 首先发布这个问题的原因。 那么他应该提到它:) 录制视频需要更多的工作,因为您必须处理预览表面。【参考方案4】:这个演示会对你有所帮助....
video.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="vertical" >
<ToggleButton
android:id="@+id/toggleRecordingButton"
android:layout_
android:layout_
android:layout_alignParentTop="true" />
<SurfaceView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/surface_camera"
android:layout_
android:layout_
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...
【讨论】:
嗨。我正在尝试在我的项目中实现您的代码,但从未调用过 surfaceCreated 函数? @REJH,在创建surface时调用,因为mHolder.addCallback(this);
这一行附加了Activity类来处理surface事件。可能是您的 IDE 无法识别此调用,但应该调用它。【参考方案5】:
查看此示例相机预览代码,CameraPreview
。这将帮助您开发视频预览的视频录制代码,创建MediaRecorder
对象,并设置视频录制参数。
【讨论】:
正确的网址是:developer.android.com/resources/samples/ApiDemos/src/com/…。自从 piyshnp 发布他的答案后,情况可能已经改变 再一次:Controlling the Camera【参考方案6】:为了搜索者的利益,此示例将为您提供一个活动预览,并带有一个用于录制的开始/停止按钮。它是从这个 android blog 修改而来的,看起来相当可靠。
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_
android:layout_
android:layout_centerInParent="true"
android:layout_weight="1"
>
<RelativeLayout
android:orientation="vertical"
android:layout_
android:layout_
>
<FrameLayout
android:id="@+id/videoview"
android:layout_
android:layout_/>
<Button
android:id="@+id/mybutton"
android:layout_
android:layout_
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:text="REC"
android:textSize="12dp"/>
</RelativeLayout>
</RelativeLayout>
【讨论】:
在 Android Studio 中运行项目会在 .xml 文件中产生以下错误消息:Error:(29) Error parsing XML: no element found where 29 is the line对应于跨度> @bergercookie - 你找到/解决问题了吗?我从我的工作代码中提取了这个(使用 Android Studio)。如果有问题,请随时编辑我的帖子。 @bergercookie - xml 示例只是缺少一个关闭的 RelativeLayout 标记。我修复了格式,现在结束标记正确显示【参考方案7】:附带说明 - Android API 中似乎存在错误或文档错误,或者我可能只是愚蠢。谷歌文档清楚地说明了以下内容:
注意:从 Android 4.0(API 级别 14)开始,Camera.lock() 和 Camera.unlock() 调用会自动为您管理。
见:http://developer.android.com/guide/topics/media/camera.html
好像不是这样!
在没有任何成功和许多小问题(例如“启动失败”)之后,我决定手动实现锁定和 BAM!一切正常。
我将 genymotion 模拟器用于最小 sdk 为 14 的 4.1.1 设备。
【讨论】:
【参考方案8】:如果您使用后置摄像头,上述示例将有效。如果你使用前置摄像头,你将不得不调整一些东西:
首先,您需要在清单中添加新权限。
<uses-feature android:name="android.hardware.camera.front" android:required="false" />
在您的 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
是为后置摄像头保留的。
您还必须设置 mediarecorder 的视频大小,因为它在您的表面视图中。
以下是从前置摄像头录制视频并带有小预览显示的完整示例:
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_
android:layout_
tools:context="CameraActivity">
<SurfaceView
android:layout_
android:layout_
android:id="@+id/surfaceView"/>
<Button
android:layout_
android:layout_
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();
【讨论】:
这不起作用。它通过关闭 VM 使我的应用程序崩溃。 有效!但录制的视频是颠倒的。此外,当我尝试在宽度上进行 match_parent 时,预览不可见。 :// 全屏预览设置宽高为wrap_content。 如何设置 PORTRAIT 变体?【参考方案9】:截至 2017 年 12 月,已经有一些更新,例如现在不推荐使用android.hardware.Camera
。虽然较新的 android.hardware.camera2
带有方便的东西,例如 CameraManager
。
我个人非常喜欢这个示例,它利用了当前的 API 并且像一个魅力一样工作: https://github.com/googlesamples/android-Camera2Video
它还包括在开始时询问用户所需的权限,并在开始视频录制之前提供视频预览功能。
(另外,我觉得代码真的很漂亮(这对我来说非常罕见^^),但这只是我的主观意见。)
【讨论】:
不适用于三星 J2 :( 生成的视频文件无法播放【参考方案10】:您可以使用 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);
【讨论】:
你测试过这个吗? @pookie 我想是的,但是因为已经 1.5 年了所以忘记了。实际上你可以看看那些 GitHub repo 链接,因为它们会有一些你可以遵循的“快速入门”。 @pookie 如果您觉得有帮助,请点赞:)以上是关于如何在我的 Android 应用中录制视频?的主要内容,如果未能解决你的问题,请参考以下文章
如何从我的 Android 设备直接在 Helix 服务器上录制视频?