1 MediaRecorder 录制视频步骤

这篇文章介绍了Android录音MediaRecorder使用和SoundPool的使用 主要讲解了音频的录制和播放,这篇AudioRecord和AudioTrack更加底层的音频录制和播放今天这篇文章讲解录制视频,其实前面已经讲过了利用MediaCodec录制视频但考虑到简单开发用的比较多的还是MediaRecorder所以继续讲解视频的录制。
如何利用MediaRecorder录制视频?
在Camera的文档中写的很清楚:https://developer.android.google.cn/guide/topics/media/camera#capture-video
Capturing Videos。
不像利用Camera进行拍照,录制视频需要一个严格的调用顺序,你必须遵从下面的顺序才能很好地利用Camera在你的应用中录制出视频。

具体步骤:

  • 1打开Camera,利用Camera.open()函数打开相机。
  • 2设置预览,把Camera和SurfaceView连接,利用Camera.setPreviewDisplay()设置预览的SurfaceHolder。
  • 3开始预览,调用Camera.startPreview()开始预览
  • 4开始录制视频(严格按照下面的顺序进行操作):
    • a . unLock Camera,解锁相机,利用函数Camera.unlock(),这样Camera才能被MediaRecodrder使用,否则会报错。
    • b. 配置MediaRecorder,生成MediaRecorder实例
    • (1)利用setCamera()设置Camera被视频录制使用,
    • (2)setAudioSource(),设置音频来自哪里(从哪里获取音频),MediaRecorder.AudioSource.CAMCORDER
    • (3)setVideoSource() ,设置视频来源(从哪里获取视频),MediaRecorder.VideoSource.CAMERA
    • (4)设置输出视频文件的格式和编码,从Android 2.2及以上版本开始,利用MediaRecorder.setProfile方法进行设置,利用CamcorderProfile.get()可以获取CamcorderProfile对象,内部包含了系统封装好的一些配置。
      至少需要设置的参数:
      setOutputFormat() 设置输出格式,一般设置为默认或者MediaRecorder.OutputFormat.MPEG_4.
      setAudioEncoder() 设置编码类型一般为默认或者MediaRecorder.AudioEncoder.AMR_NB.
      setVideoEncoder() 设置视频编码类型一般是默认或者MediaRecorder.VideoEncoder.MPEG_4_SP.
    • (5)setOutputFile() 设置输出文件的存放位置。
    • (6)setPreviewDisplay() ,设置预览的SurfaceHolder。
    • c 准备,调用MediaRecorder.prepare() 应用上面的配置。
    • d 开始视频录制,MediaRecorder.start()
  • 5 停止录制
    • a 停止MediaRecorder,停止录制
    • b 重置 MediaRecorder.reset(),移除设置的参数
    • c 释放MediaRecorder
    • d Lock Camera,Camera.lock(),从Android 4.0 开始,lock操作不是必须的,除非MediaRecorder.prepare()失败。
  • 6 Camera 停止预览,Camera.stopPreview().
  • 7 释放Camera,Camera.release()。

代码示例:来自developer

private boolean prepareVideoRecorder(){mCamera = getCameraInstance();mMediaRecorder = new MediaRecorder();// Step 1: Unlock and set camera to MediaRecordermCamera.unlock();mMediaRecorder.setCamera(mCamera);// Step 2: Set sourcesmMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));// Step 4: Set output filemMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());// Step 5: Set the preview outputmMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());// Step 6: Prepare configured MediaRecordertry {mMediaRecorder.prepare();} catch (IllegalStateException e) {Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());releaseMediaRecorder();return false;} catch (IOException e) {Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());releaseMediaRecorder();return false;}return true;
}//如果是Android 2.2 以前,不能直接使用CamcorderProfile,需要手动设置如下参数:// Step 3: Set output format and encoding (for versions prior to API Level 8)mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);

2 Camera的lock和 unlock

录制视频之前需要调用unlock,释放MediaRecorder时需要调用lock。

/**源码中的unlock注释:

  • Unlocks the camera to allow another process to access it.
  • Normally, the camera is locked to the process with an active Camera
  • object until {@link #release()} is called. To allow rapid handoff
  • between processes, you can call this method to release the camera
  • temporarily for another process to use; once the other process is done
  • you can call {@link #reconnect()} to reclaim the camera.
  • This must be done before calling

  • {@link android.media.MediaRecorder#setCamera(Camera)}. This cannot be
  • called after recording starts.
  • If you are not recording video, you probably do not need this method.

  • @throws RuntimeException if the camera cannot be unlocked.
    public native final void unlock();

unlock的作用是Camera属于硬件设备,通常情况下Camera被一个使用Camera的进程锁定,是不允许其他进程使用的。调用unlock后Camera可以允许其他进行使用它,否则如果正被其他进程使用会发生错误,经过试验就是不被其它进程使用不调用这个函数也会发生莫名错误,其他进程使用完之后可以调用reconnect释放Camera。unLock必须在你调用MediaRecorder.setCamera之前调用。注意如果你不是要录制视频,只是简单地预览不需要调用这个函数。

/**源码lock函数注释

  • Re-locks the camera to prevent other processes from accessing it.
  • Camera objects are locked by default unless {@link #unlock()} is
  • called. Normally {@link #reconnect()} is used instead.
  • Since API level 14, camera is automatically locked for applications in

  • {@link android.media.MediaRecorder#start()}. Applications can use the
  • camera (ex: zoom) after recording starts. There is no need to call this
  • after recording starts or stops.
  • If you are not recording video, you probably do not need this method.

  • @throws RuntimeException if the camera cannot be re-locked (for
  • example, if the camera is still in use by another process).
    

*/
public native final void lock();

Lock函数再次禁止其他进程使用Camera,听起来有点像reconnect,的确它两个功能一样,通常使用reconnect函数。
从api14 开始,录制视频时MediaRecorder调用start函数时Camera 会自动的调用Lock,所以再开始录制视频之前或者录制视频结束之后不需要手动的调用lock函数。
注意:lock和unLock 都是只有在录制视频时才会使用,其他情况用不到这两个函数。

3 setAudioSource,setVideoSource

设置音频和视频的来源

设置音频来源:
在AudioSource类中包括:

  • MediaRecorder.AudioSource.DEFAULT: 默认音频源
  • MediaRecorder.AudioSource.MIC:主麦克风。
  • MediaRecorder.AudioSource.VOICE_CALL:来源为语音拨出的语音与对方说话的声音
  • MediaRecorder.AudioSource.VOICE_COMMUNICATION:摄像头旁边的麦克风
  • MediaRecorder.AudioSource.VOICE_DOWNLINK:下行声音
  • MediaRecorder.AudioSource.VOICE_RECOGNITION:语音识别
  • MediaRecorder.AudioSource.VOICE_UPLINK:上行声音
    一般使用默认或者主麦克风或者摄像头旁边的麦克风。
    设置视频来源:
  • VideoSource.DEFAULT:默认
  • VideoSource.CAMERA:摄像头
  • VideoSource.SURFACE:Surface作为来源
    在录制视频的步骤中有一步是调用setCamera设置Camera,这一步相当于设置来源是摄像头,下面就需要使用VideoSource.CAMERA作为视频源,还可以使用MediaRecorder的getSurface播放视频,代替setCamera,这时的视频来源就是Surface。

4 CamcorderProfile 和 参数设置

/**CamcorderProfile类源码中的注释
Retrieves the
predefined camcorder profile settings for camcorder applications.
These settings are read-only.
The compressed output from a recording session with a given
CamcorderProfile contains two tracks: one for audio and one for video.
Each profile specifies the following set of parameters:
The file output format
Video codec format
Video bit rate in bits per second
Video frame rate in frames per second
Video frame width and height,
Audio codec format
Audio bit rate in bits per second,
Audio sample rate
Number of audio channels for recording.
public class CamcorderProfile

CamcorderProfile获取方法:利用get方法

CamcorderProfile get(int cameraId, int quality)
CamcorderProfile get(int quality)
参数说明
**cameraId:**摄像头id,分为前置摄像头或者后置摄像头。
**quality:**质量情况

CamcorderProfile保存音视频配置信息,Android系统利用CamcorderProfile设定好的配置信息,可以通过选择不同质量等级,得到不同的配置信息。
CamcorderProfile 管理的配置信息包括:
fileFormat:文件输出格式
videoCodec:视频编解码格式
videoBitRate:视频比特率,以位/秒为单位
videoFrameRate:视频帧速率(以每秒帧数为单位)
videoFrameWidth,videoFrameHeight:视频帧宽和高度,
audioCodec:音频编解码格式
audioBitRate:音频比特率,以位/秒为单位,录制的音频通道数。
audioSampleRate:音频采样率
audioChannels:音频声道数

如何利用get函数获取不同质量的上面的参数呢?

get()函数中有一个quality的参数,这个参数的取值为:
QUALITY_LOW
QUALITY_HIGH
QUALITY_QCIF
QUALITY_CIF
QUALITY_480P
QUALITY_720P
QUALITY_1080P
QUALITY_2160P
QUALITY_TIME_LAPSE_LOW
QUALITY_TIME_LAPSE_HIGH
QUALITY_TIME_LAPSE_QCIF
QUALITY_TIME_LAPSE_CIF
QUALITY_TIME_LAPSE_480P
QUALITY_TIME_LAPSE_720P
QUALITY_TIME_LAPSE_1080P
QUALITY_TIME_LAPSE_2160P
QUALITY_HIGH_SPEED_LOW
QUALITY_HIGH_SPEED_HIGH
QUALITY_HIGH_SPEED_480P
QUALITY_HIGH_SPEED_720P
QUALITY_HIGH_SPEED_1080P
QUALITY_HIGH_SPEED_2160P

从命名可以看到主要分为三类:
第一类QUALITY_LOW,QUALITY_HIGH,QUALITY_QCIF,QUALITY_CIF,QUALITY_480P,QUALITY_720P,QUALITY_1080P,QUALITY_2160P,直接获取。
其中QUALITY_LOW,QUALITY_HIGH是一定存在的,不会出现错误,但是其他特性质量不一定存在,如果设置不存在的quality则无法获取CamcorderProfile,可以利用hasProfile(int, int)函数判断是否存在。QUALITY_LOW,QUALITY_HIGH在不同的手机上的值不一样要注意区分。

第二类是时间流逝质量(比特率)水平包括QUALITY_TIME_LAPSE_LOW,QUALITY_TIME_LAPSE_HIGH,
QUALITY_TIME_LAPSE_QCIF,QUALITY_TIME_LAPSE_CIF,QUALITY_TIME_LAPSE_480P,QUALITY_TIME_LAPSE_720P,QUALITY_TIME_LAPSE_1080P,QUALITY_TIME_LAPSE_2160P
QUALITY_TIME_LAPSE_LOW,QUALITY_TIME_LAPSE_HIGH是一定存在的其他需要进行判断使用。

第三类是高速(高帧率)质量包括QUALITY_HIGH_SPEED_LOW,QUALITY_HIGH_SPEED_HIGH,QUALITY_HIGH_SPEED_480P,QUALITY_HIGH_SPEED_720P,QUALITY_HIGH_SPEED_1080P,
QUALITY_HIGH_SPEED_2160P。
QUALITY_HIGH_SPEED_LOW and QUALITY_HIGH_SPEED_HIGH确定存在其他需要判断使用。

CamcorderProfile 设置参数方式

/**setProfile 源码注释

  • Uses the settings from a CamcorderProfile object for recording. This method should
  • be called after the video AND audio sources are set, and before setOutputFile().
  • If a time lapse CamcorderProfile is used, audio related source or recording
  • parameters are ignored.
  • @param profile the CamcorderProfile to use
  • @see android.media.CamcorderProfile
    */
    public void setProfile(CamcorderProfile profile)

利用setProfile设置参数必须在设置了video和audio Source之后调用,在setOutputFile之前设置。如果时间流逝的CamcorderProfile 被使用,audio相关的源或者参数设置将被忽略。
注意:如果调用了setProfile函数,setOutputFormat,setAudioEncoder,setVideoEncoder,不能再调用设置。

CamcorderProfile mCamcorderProfile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_BACK,CamcorderProfile.QUALITY_HIGH);mMediaRecorder.setProfile(mCamcorderProfile);

如果使用其他类型质量参数就需要进行判断:

CamcorderProfile profile = null;if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {profile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);} else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);} else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);}if (profile != null) {mMediaRecorder.setProfile(profile);}

手动设置参数

通过profile可以一步到位配置各个参数,但是缺点是没办法设置自己想要的视频清晰度,因为视频清晰度是根据码率和分辨率决定的,而每个profile已经固定了码率和分辨率,所以无法进行调整。
注意:
帧率通过camera获取支持的帧率,视频尺寸的大小,可以根据需要的质量先获取系统720p的profile,然后取profile.videoFrameWidth; profile.videoFrameHeight作为输出宽高。

setOutputFormat():设置输出格式。
系统包括:
DEFAULT:MP4
THREE_GPP:3gp
MPEG_4:.mp4,m4a
RAW_AMR:.arm
AAC_ADTS :aac

视频音频编码格式:
视频编码格式MediaRecorder.VideoEncoder
DEFAULT,H263,H264,MPEG_4_SP,VP8
音频编码格式MediaRecorder.AudioEncoder
default,AAC,HE_AAC,AAC_ELD,AMR_NB,AMR_WB,VORBIS

setVideoFrameRate:设置帧率,24fps已经很流畅,帧率不能太低,也不能太高。
setVideoEncodingBitRate:设置码率,配合视频大小可以调节视频的质量
setVideoSize:设置视频大小
setAudioEncodingBitRate:设置音频采样率
setAudioChannels:设置声道数

其他设置:
setOrientationHint()对输出的文件设置方向,一般和Camera的setDisplayOrientation角度相同。
特别注意:setOrientationHint并没有对视频的帧进行旋转操作,所以真实生成的视频时没有旋转的,它会增加一个Matrix举证,当output为OutputFormat.THREE_GPP 或者 OutputFormat.MPEG_4时视频播放时,播放器能够自动识别。
setMaxDuration:录制的最长时间
setOutputFile:设置输出的文件
setPreviewDisplay:设置Surface预览视频
setMaxFileSize:设置录制文件的最大值。

5 代码示例

public class Main20Activity extends AppCompatActivity implements SurfaceHolder.Callback {private static int mOrientation = 0;private static int mCameraID = Camera.CameraInfo.CAMERA_FACING_BACK;private SurfaceView mSurfaceView;private SurfaceHolder mSurfaceHolder;private Camera mCamera;private boolean havePermission = false;private Button mStart;private Button mStop;private MediaRecorder mMediaRecorder;private Camera.Size mSize;String srcPath = Environment.getExternalStorageDirectory().getPath() + "/mediarecorder/";String srcName = "video.mp4";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main20);mStart = findViewById(R.id.startrecord);mStop = findViewById(R.id.stoprecord);mStart.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {initMediaRecord();}});mStop.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mMediaRecorder != null ){mMediaRecorder.stop();mMediaRecorder.release();mCamera.lock();}}});// Android 6.0相机动态权限检查,省略了if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)== PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)== PackageManager.PERMISSION_GRANTED) {havePermission = true;init();} else {havePermission = false;ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.RECORD_AUDIO}, 100);}}public void initMediaRecord(){//创建MediaRecordermMediaRecorder = new MediaRecorder();mMediaRecorder.reset();mCamera.unlock();//创建录音文件File mRecorderFile = new File(srcPath+srcName);try {if (!mRecorderFile.getParentFile().exists()) mRecorderFile.getParentFile().mkdirs();mRecorderFile.createNewFile();} catch (IOException e) {e.printStackTrace();}mMediaRecorder.setCamera(mCamera);mMediaRecorder.setOrientationHint(mOrientation);//从麦克风采集mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);CamcorderProfile mCamcorderProfile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_BACK, CamcorderProfile.QUALITY_HIGH);System.out.println("============mCamcorderProfile============"+mCamcorderProfile.videoFrameWidth+"   "+mCamcorderProfile.videoFrameHeight);mMediaRecorder.setProfile(mCamcorderProfile);//使用CamcorderProfile做配置的话,输出格式,音频编码,视频编码 不要写,否则会报错(崩溃)/*mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);*///设置录制视频的大小,其实Camera也必须要和这个比例相同,此处为了简单不做处理mMediaRecorder.setVideoSize(mCamcorderProfile.videoFrameWidth,mCamcorderProfile.videoFrameHeight);//提高帧频率,录像模糊,花屏,绿屏可写上调试mMediaRecorder.setVideoEncodingBitRate(mCamcorderProfile.videoFrameWidth*mCamcorderProfile.videoFrameHeight*24*16);mMediaRecorder.setVideoFrameRate(24);//所有android系统都支持的适中采样的频率mMediaRecorder.setAudioSamplingRate(44100);//设置文件录音的位置mMediaRecorder.setOutputFile(mRecorderFile.getAbsolutePath());mMediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface());//开始录音try {mMediaRecorder.prepare();mMediaRecorder.start();} catch (IOException e) {e.printStackTrace();}}public void init(){if(mSurfaceView == null){mSurfaceView = findViewById(R.id.surfaceview);mSurfaceView.setCustomEvent(new CustomSurfaceView.ONTouchEvent() {@Overridepublic void onTouchEvent(MotionEvent event) {handleFocus(event, mCamera);}});mSurfaceHolder = mSurfaceView.getHolder();mSurfaceHolder.addCallback(this);WindowManager wm = (WindowManager) Main23Activity.this.getSystemService(Context.WINDOW_SERVICE);int width = wm.getDefaultDisplay().getWidth();LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mSurfaceView.getLayoutParams();layoutParams.width = width;layoutParams.height = width*4/3;useWidth = width;useHeight = width*4/3;mSurfaceView.setLayoutParams(layoutParams);}}private void initCamera() {if (mCamera != null){releaseCamera();System.out.println("===================releaseCamera=============");}mCamera = Camera.open(mCameraID);System.out.println("===================openCamera=============");if (mCamera != null){try {mCamera.setPreviewDisplay(mSurfaceHolder);} catch (IOException e) {e.printStackTrace();}Camera.Parameters parameters = mCamera.getParameters();parameters.setRecordingHint(true);{//设置获取数据parameters.setPreviewFormat(ImageFormat.NV21);//parameters.setPreviewFormat(ImageFormat.YUV_420_888);//通过setPreviewCallback方法监听预览的回调:mCamera.setPreviewCallback(new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] bytes, Camera camera) {//这里面的Bytes的数据就是NV21格式的数据,或者YUV_420_888的数据}});}if(mCameraID == Camera.CameraInfo.CAMERA_FACING_BACK){parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);}mCamera.setParameters(parameters);calculateCameraPreviewOrientation(this);Camera.Size tempSize = setPreviewSize(mCamera, useHeight,useWidth);{//此处可以处理,获取到tempSize,如果tempSize和设置的SurfaceView的宽高冲突,重新设置SurfaceView的宽高}setPictureSize(mCamera,  useHeight,useWidth);mCamera.setDisplayOrientation(mOrientation);int degree = calculateCameraPreviewOrientation(Main23Activity.this);mCamera.setDisplayOrientation(degree);mCamera.startPreview();}}@Overridepublic void surfaceCreated(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//当SurfaceView变化时也需要做相应操作,这里未做相应操作if (havePermission){initCamera();}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {mCamera.stopPreview();}private void setPictureSize(Camera camera ,int expectWidth,int expectHeight){Camera.Parameters parameters = camera.getParameters();Point point = new Point(expectWidth, expectHeight);Camera.Size size = findProperSize(point,parameters.getSupportedPreviewSizes());parameters.setPictureSize(size.width, size.height);camera.setParameters(parameters);}private Camera.Size setPreviewSize(Camera camera, int expectWidth, int expectHeight) {Camera.Parameters parameters = camera.getParameters();Point point = new Point(expectWidth, expectHeight);Camera.Size size = findProperSize(point,parameters.getSupportedPictureSizes());parameters.setPictureSize(size.width, size.height);camera.setParameters(parameters);return size;}/*** 找出最合适的尺寸,规则如下:* 1.将尺寸按比例分组,找出比例最接近屏幕比例的尺寸组* 2.在比例最接近的尺寸组中找出最接近屏幕尺寸且大于屏幕尺寸的尺寸* 3.如果没有找到,则忽略2中第二个条件再找一遍,应该是最合适的尺寸了*/private static Camera.Size findProperSize(Point surfaceSize, List<Camera.Size> sizeList) {if (surfaceSize.x <= 0 || surfaceSize.y <= 0 || sizeList == null) {return null;}int surfaceWidth = surfaceSize.x;int surfaceHeight = surfaceSize.y;List<List<Camera.Size>> ratioListList = new ArrayList<>();for (Camera.Size size : sizeList) {addRatioList(ratioListList, size);}final float surfaceRatio = (float) surfaceWidth / surfaceHeight;List<Camera.Size> bestRatioList = null;float ratioDiff = Float.MAX_VALUE;for (List<Camera.Size> ratioList : ratioListList) {float ratio = (float) ratioList.get(0).width / ratioList.get(0).height;float newRatioDiff = Math.abs(ratio - surfaceRatio);if (newRatioDiff < ratioDiff) {bestRatioList = ratioList;ratioDiff = newRatioDiff;}}Camera.Size bestSize = null;int diff = Integer.MAX_VALUE;assert bestRatioList != null;for (Camera.Size size : bestRatioList) {int newDiff = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);if (size.height >= surfaceHeight && newDiff < diff) {bestSize = size;diff = newDiff;}}if (bestSize != null) {return bestSize;}diff = Integer.MAX_VALUE;for (Camera.Size size : bestRatioList) {int newDiff = Math.abs(size.width - surfaceWidth) + Math.abs(size.height - surfaceHeight);if (newDiff < diff) {bestSize = size;diff = newDiff;}}return bestSize;}private static void addRatioList(List<List<Camera.Size>> ratioListList, Camera.Size size) {float ratio = (float) size.width / size.height;for (List<Camera.Size> ratioList : ratioListList) {float mine = (float) ratioList.get(0).width / ratioList.get(0).height;if (ratio == mine) {ratioList.add(size);return;}}List<Camera.Size> ratioList = new ArrayList<>();ratioList.add(size);ratioListList.add(ratioList);}/*** 排序* @param list*/private static void sortList(List<Camera.Size> list) {Collections.sort(list, new Comparator<Camera.Size>() {@Overridepublic int compare(Camera.Size pre, Camera.Size after) {if (pre.width > after.width) {return 1;} else if (pre.width < after.width) {return -1;}return 0;}});}/*** 设置预览角度,setDisplayOrientation本身只能改变预览的角度* previewFrameCallback以及拍摄出来的照片是不会发生改变的,拍摄出来的照片角度依旧不正常的* 拍摄的照片需要自行处理* 这里Nexus5X的相机简直没法吐槽,后置摄像头倒置了,切换摄像头之后就出现问题了。* @param activity*/public static int calculateCameraPreviewOrientation(Activity activity) {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(mCameraID, info);int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();int degrees = 0;switch (rotation) {case Surface.ROTATION_0:degrees = 0;break;case Surface.ROTATION_90:degrees = 90;break;case Surface.ROTATION_180:degrees = 180;break;case Surface.ROTATION_270:degrees = 270;break;}int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360;} else {result = (info.orientation - degrees + 360) % 360;}mOrientation = result;System.out.println("=========orienttaion============="+result);return result;}@Overrideprotected void onResume() {super.onResume();if (havePermission && mCamera != null)mCamera.startPreview();}@Overrideprotected void onPause() {super.onPause();if (havePermission && mCamera != null)mCamera.stopPreview();}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode) {// 相机权限case 100:havePermission = true;init();break;}}}

录制视频:

播放录制视频:

可以看到播放录制的视频时发生了旋转,如何让播放器知道制品旋转了,然后按正常的方向播放呢?根据前面讲解setOrientationHint的知识,只需要改变输出格式为OutputFormat.THREE_GPP 或者 OutputFormat.MPEG_4中的一种,DEFAULT就是默认的mp4格式也满足这个条件,所以修改代码。

 mMediaRecorder.setCamera(mCamera);mMediaRecorder.setOrientationHint(mOrientation);//从麦克风采集mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);CamcorderProfile mCamcorderProfile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_BACK, CamcorderProfile.QUALITY_HIGH);System.out.println("============mCamcorderProfile============"+mCamcorderProfile.videoFrameWidth+"   "+mCamcorderProfile.videoFrameHeight);
/* mMediaRecorder.setProfile(mCamcorderProfile);*///使用CamcorderProfile做配置的话,输出格式,音频编码,视频编码 不要写,否则会报错(崩溃)mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);//设置录制视频的大小,其实Camera也必须要和这个比例相同,此处为了简单不做处理mMediaRecorder.setVideoSize(mCamcorderProfile.videoFrameWidth,mCamcorderProfile.videoFrameHeight);//提高帧频率,录像模糊,花屏,绿屏可写上调试mMediaRecorder.setVideoEncodingBitRate(mCamcorderProfile.videoFrameWidth*mCamcorderProfile.videoFrameHeight*24*16);mMediaRecorder.setVideoFrameRate(24);


以上代码只是为了展示效果,想要使用需要优化,谢谢。

Android音视频之MediaRecorder录制视频相关推荐

  1. Android多媒体四:MediaRecorder录制视频短片

    Android多媒体四:MediaRecorder录制视频短片 MediaRecorder除了可用于录制音频之外,还可用于录制视频,使用MediaRecorder录制视频与录制音频的步骤基本相同. 只 ...

  2. MediaRecorder录制视频和录音

    一.MediaRecorder作用 1.MediaRecorder可以实现直接录制视频,不能对每一帧数据进行处理.如果对每一帧数据都能进行处理的话,有两种选择:1.MediaCodec 2.FFMpe ...

  3. android camera使用ISO值录制视频

    android camera使用ISO值录制视频 我的应用是使用自定义camera对着一个led灯箱录制视频,该led灯的频率是1000HZ,同时我使用高ISO值录像,最终想要的效果如下图,屏幕中会出 ...

  4. 使用MediaRecorder录制视频短片

    MediaRecorder除了可用于录制音频之外,还可用于录制视频,使用MediaRecorder录制视频与录制音频的步骤基本相同.只是录制视频时不仅需要采集声音,还需要采集图像.为了让MediaRe ...

  5. Android仿微信拍摄、录制视频,以及视频播放(基于JCameraView和GSYVideoPlayer)

    本项目使用Androidstudio开发工具 引入权限 <uses-permission android:name="android.permission.FLASHLIGHT&quo ...

  6. 前端 mediaRecorder 录制视频源代码实例,和本地播放器无法定位进度条问题分析和解决

    问题 mediaRecorder 本质上录制的是webm 当我们使用getUserMedia.MediaRecorder等API生成的webm视频时,会发现最终的webm是无法拖动进度条的 解决方案 ...

  7. MediaRecorder录制视频时计算文件大小

    MediaRecorder录制视频时通过音频编码率和视频编码率计算文件大小或时间 计算公式:(音频编码率(单位k)+视频编码率(单位k))/8*时间(秒) = 文件大小(kb) 例: mediaRec ...

  8. Android MediaRecorder录制视频音量小问题解决 (音视频转码合成)

    之前写得太乱,回头看看感觉自己都有点看不懂,重新写下. 在android上需要做一个录像留言功能,需要生成MP4或者waw格式视频.但是使用MediaRecorder录制出的MP4视频格式音量太小,几 ...

  9. Android音视频开发:MediaRecorder录制视频

    简介 安卓api提供了Camera类控制相机捕获图像,在api21以后,安卓也提供了Camera2,Camera变得过时了,但为了兼容性,这里还是使用Camera. 对于录制视频,可以使用MediaR ...

最新文章

  1. java dao层 service层_Java中DAO层、Service层和Controller层的区别
  2. Windows RDP协议 Fuzzing 漏洞挖掘研究
  3. Uncaught SecurityError: Failed to execute 'replaceState' on 'History': A history state object with
  4. Asp中一些FSO方面的函数
  5. try catch对异常进行输出到日志、_java安全编码指南之:异常处理
  6. android transfer error: Read-only file system
  7. ***ECharts图表入门和最佳实践
  8. 作为 IT 行业的过来人,你有什么话想对后辈说的?
  9. positional encoding位置编码详解:绝对位置与相对位置编码对比
  10. 一文理解设计模式之--策略模式(Strategy)
  11. 【动力学】基于matlab GUI汽车动力学分析系统【含Matlab源码 1050期】
  12. 全国地址SQL数据文件(精确到区县)
  13. Windows系统下载Android源码
  14. Axure制作倒计时
  15. 计算机中文件夹属性有哪些,文件和文件夹属性
  16. 地下城与勇士(DNF)异次元裂缝副本(哥布林王国、蠕动之城、兰蒂卢斯的鹰犬、黑色大地、虚无之境 、巴卡尔之城)(童年的回忆)
  17. 如何用iMazing软件将苹果手机数据备份及功能详解
  18. 全国首批城市级5G车联网应用项目落地!
  19. Spring的工作原理(一)简介
  20. 华为WLAN技术:AC/AP 实验

热门文章

  1. 手机号段一览(整理自百度百科)
  2. [ARX].NET开发AutoCAD2006指南
  3. Fortran编程——文件输入输出
  4. 人工智能AI系列 - 以图搜图,以文搜图,以图搜文的跨模态搜索
  5. tiptop debug 更改变量值
  6. Google Play成功上架30多款游戏,并且一直在运营 技术分享
  7. 计算机音频服务未运行怎么办,Windows7音频服务未运行怎么办?
  8. jsp社会保险信息(社保)管理系统
  9. Qunar 是如何解决 IDC 网络故障自愈问题的
  10. 浙江省主要农作物种植面积和总产量数据