hello,大家吼,我是那个爱猫的老司机,爱好是掀桌子的话唠程序猿。回想起刚开始码文章的时候,没想到内向的自己也可以撸出那么多文字,真是挖坑不止,且行且珍惜啊。有猜到今天聊的主角是谁吗?猜到是不是要送红包呢?

 
请捂着你的良心说话,对于贫穷的作者(我)不是应该打赏么 ̄へ ̄!,接下来工作又要忙起来了,更新应该是放缓了呢╮(╯_╰)╭,好伤心。

例牌飘过: github.com/CarGuo 请(bu yao)无视。

 想一想,我们聊过AudioReordAudioTrackMediaPlayer,那多媒体四大金刚,就剩下了MediaRecorder了(SoundPool?我这里信号不好···)。其实MediaRecorder个人用的也不多,很久前用它在拍摄视频上确实趟过无视次坑,那今天就聊它吧,把它聊到躺下(ノQ益Q)ノ彡┻━┻。

MediaRecorder

 一般用在多媒体录制上面,当然如果你只是简单的想录制音频,用它最合适不过,不过如果你想更多样化的录制这里推荐《Android MP3录制,波形显示,音频权限兼容与播放》。今天的主题是录制视频,用的还是老式通用的Camera,不是新的camera2(这就尴尬了.....((/- -)/),反正个人秉承能用是王道的做法(懒)。之前也尝试过FFMPEG的录制合成音频,大小和效果也不错,只是有时候的兼容性确实有些问题,最主要还是资料不多,不好改啊 ̄へ ̄(懒)。

 既然是录制视频,那么少不了Camera,这货也是让人又爱又恨(哪里有爱了┑( ̄Д  ̄)┍?),也许是因为Android碎片化的原因,所以用起来也是坑坑洼洼的,接下来就让我们结束废话吧:

  • 1、SurfaceView用于承载画面。
  • 2、初始化相机Camera
  • 3、初始化重力旋转用于横竖屏。
  • 4、配置闪光灯和旋转摄像头功能。
  • 5、配置MediaRecorder的录制参数后开始录制。
  • 6、结束录制预览视频。

1、SurfaceView显示画面

 
 旧项目用的都是SurfaceView,这次就就它吧。这里我们需要首先是implements SurfaceHolder.Callback,这样我们才能在surface创建的时候初始化相机渲染画面,在画面销毁的时候销毁相机(画面都没有要初始化相机何用)。

SurfaceHolder holder = cameraShowView.getHolder();
holder.addCallback(this);
// setType必须设置,要不出错.
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);···此处略过无数只草泥马@Override
public void surfaceCreated(SurfaceHolder holder) {surfaceHolder = holder;//更具当前的相机类型(前,后)初始化相机,闪光灯不启动initCamera(cameraType, false);
}@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {surfaceHolder = holder;
}@Override
public void surfaceDestroyed(SurfaceHolder holder) {//结束录制endRecord();//是否相机releaseCamera();
}复制代码

 

2、初始化Camera

 
 除了有点坑外,流程上还是比较简单的:

  • 释放已经初始化过的相机。
  • 根据当前摄像头类型打开相机。
  • 配置相机参数:预览大小,对焦,闪光灯,竖屏显示。
  • 设置显示画面的surface
  • 开始绘制
if (camera != null) {//如果已经初始化过,就先释放releaseCamera();
}try {//根据前后摄像头打开摄像头camera = Camera.open(type);if (camera == null) {//拿不到可能是没权限showCameraPermission();return;}camera.lock();//Point screen = new Point(getScreenWidth(this), getScreenHeight(this));//现在不用获取最高的显示效果//Point show = getBestCameraShow(camera.getParameters(), screen);Camera.Parameters parameters = camera.getParameters();if (type == 0) {//基本是都支持这个比例parameters.setPreviewSize(SIZE_1, SIZE_2);parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//1连续对焦camera.cancelAutoFocus();// 2如果要实现连续的自动对焦,这一句必须加上}camera.setParameters(parameters);FlashLogic(camera.getParameters(), flashType, flashDo);if (cameraType == 1) {frontCameraRotate();camera.setDisplayOrientation(frontRotate);} else {camera.setDisplayOrientation(90);}camera.setPreviewDisplay(surfaceHolder);camera.startPreview();camera.unlock();
} catch (Exception e) {e.printStackTrace();releaseCamera();
}复制代码

 这里需要注意坑(画面变形)问题,那就是你配置的相机分辨率画面,在录制的时候可能会因为和录制的分辨率画面不一致,导致开始录制的时候画面奇怪的突变,所以CameraMediaRecorder的分辨率最好一致。

 问题又来了CameraMediaRecorder不是什么分辨率都支持的,他们分别都有对应的接口:getSupportedPreviewSizesCamcorderProfile等来获取对应支持的分辨率的,路迢迢啊。
 
 经过轮番的尝试,还有上传对大小要求,所以最终选择写死,对,写死了640 * 480这样的大小,这个分辨率基本都支持(不支持那手机的尊严何在( ‵o′)凸),对于十来秒的视频,这个分辨率的尺寸还算可以(如果对画质有需要可以另外配置,如果FFMPEG压缩性能堪忧啊)。

 那么问题又来了(哪来那么多问题),但是手机屏幕大部分情况下是16:9,而这个分辨率明显是4:3(万恶的需求啊(ノQ益Q)ノ彡┻━┻)。这时候因为Surface的最外层是FrameLayout(搞不懂为什么超出屏幕的时候RelativeLayout有时候会有问题),个人的做法是调整surface的比例。如果是不充满屏幕高度的,就通过屏幕宽度比例算出surface的高度;如果充满屏幕高度,就算出surface的宽度。

 如此以来,不变形啦,在点击录制的瞬间也不跳动啦,唯一有点小问题的就是充满高度的时候,画面是超过了屏幕宽度的一点的,所以可能录到了什么不想录制的♂,但是刚好没看到︿( ̄︶ ̄)︿。

int screenWidth = getScreenWidth(this);
int screenHeight = getScreenHeight(this);
//根据比例设置surface的宽度
setViewSize(cameraShowView, screenWidth * SIZE_1 / SIZE_2, screenHeight);复制代码

3、重力感应旋转

 
 当时看到IOS微博的视频录制是可以支持横竖屏录制,觉得挺有意思的,这里用的是OrientationEventListener,具体的之前IJKPlayer视频文章里已经说过(懒),有兴趣的可以去看看。我们是在画面旋转的时候把对应的logo用属性动画也旋转了,然后得到当前的旋转角度,告诉MediaRecorder,拍摄出来的视频元信息里就带有了角度信息,播放的时候画面会就旋转为横屏或者竖屏啦。

orientationEventListener = new OrientationEventListener(this) {@Overridepublic void onOrientationChanged(int rotation) {if (!flagRecord) {if (((rotation >= 0) && (rotation <= 30)) || (rotation >= 330)) {// 竖屏拍摄if (rotationFlag != 0) {//旋转logorotationAnimation(rotationFlag, 0);//这是竖屏视频需要的角度rotationRecord = 90;//这是记录当前角度的flagrotationFlag = 0;}} else if (((rotation >= 230) && (rotation <= 310))) {// 横屏拍摄if (rotationFlag != 90) {//旋转logorotationAnimation(rotationFlag, 90); //这是正横屏视频需要的角度rotationRecord = 0;//这是记录当前角度的flagrotationFlag = 90;}} else if (rotation > 30 && rotation < 95) {// 反横屏拍摄if (rotationFlag != 270) {//旋转logorotationAnimation(rotationFlag, 270);//这是反横屏视频需要的角度rotationRecord = 180;//这是记录当前角度的flagrotationFlag = 270;}}//倒过来就算了,你又不是小米MIX}}
};
orientationEventListener.enable();复制代码

前置摄像头

 此处有,还不止一个,如果你还需要支持前置摄像头(能说不吗?),直接使用上面的rotationRecord去配置MediaRecorder是会有问题的。

 首先说Camera,如果测试说你的前置Camera在某些手机上画面角度不对,这时候你可以偷偷把手机砸了,因为这是兼容问题。如果你没有勇气砸手机,看下面。

 传说中,只要拿下面的frontRotate去配置Camera就正常显示啦,伟人说的!而其中的frontOri,我们是用到配置后面MediaRecorder,具体看代码的,这是调出来的结果(。・・)ノ。

 /*** 旋转前置摄像头为正的*/
private void frontCameraRotate() {Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(1, info);int degrees = getDisplayRotation(this);int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360; // compensate the mirror} else { // back-facingresult = (info.orientation - degrees + 360) % 360;}frontOri = info.orientation;frontRotate = result;
}/*** 获取旋转角度*/
private int getDisplayRotation(Activity activity) {int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();switch (rotation) {case Surface.ROTATION_0:return 0;case Surface.ROTATION_90:return 90;case Surface.ROTATION_180:return 180;case Surface.ROTATION_270:return 270;}return 0;
}···此处无数字草泥马
//配置录制角度
int frontRotation;
if (rotationRecord == 180) {//反向横屏的前置角度frontRotation = 180;
} else {//竖屏和正向横屏的前置角度//录制下来的视屏选择角度,此处为前置frontRotation = (rotationRecord == 0) ? 270 - frontOri : frontOri;
}
//根据前后摄像头给角度
recorder.setOrientationHint((cameraType == 1) ? frontRotation : rotationRecord);复制代码

4、闪光灯和旋转摄像头

 闪光灯的打开关闭遇到过一个问题,就是有的手机还没有开启录制,一配置打开它就亮了。(砸手机)最后解决的是在配置的时候标志类型,设置好MediaRecorder之后拍摄才开始闪光灯。(其他的什么一闪一闪的模式就算了吧= =)

 至于旋转切换相机,主要还是针对前置camera需要做如上面所说的画面预览旋转。

/**
* 闪光灯逻辑
*
* @param p    相机参数
* @param type 打开还是关闭
* @param isOn 是否立即启动
*/
private void FlashLogic(Camera.Parameters p, int type, boolean isOn) {flashType = type;if (type == 0) {if (isOn) {p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);camera.setParameters(p);}videoFlashLight.setImageResource(R.drawable.flash_off);} else {if (isOn) {p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);camera.setParameters(p);}videoFlashLight.setImageResource(R.drawable.flash);}if (cameraFlag == 0) {videoFlashLight.setVisibility(View.GONE);} else {videoFlashLight.setVisibility(View.VISIBLE);}
}/*** 切换摄像头*/
public void switchCamera() {Camera.CameraInfo cameraInfo = new Camera.CameraInfo();int cameraCount = Camera.getNumberOfCameras();//得到摄像头的个数0或者1;try {for (int i = 0; i < cameraCount; i++) {Camera.getCameraInfo(i, cameraInfo);//得到每一个摄像头的信息if (cameraFlag == 1) {//后置到前置if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表摄像头的方位,CAMERA_FACING_FRONT前置      CAMERA_FACING_BACK后置frontCameraRotate();//前置旋转摄像头度数switchCameraLogic(i, 0, frontRotate);break;}} else {//前置到后置if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表摄像头的方位,CAMERA_FACING_FRONT前置      CAMERA_FACING_BACK后置switchCameraLogic(i, 1, 90);break;}}}} catch (Exception exception) {exception.printStackTrace();}
}复制代码

 

5、配置MediaRecorder的录制参数、生成视频。

 这里最坑的就是MediaRecorder的配置参数是有前后关系的,先生小孩后再洞房这种绿色模式是不行的,具体顺序参照下方代码,码率和帧数都是配置相对较小,适合拍摄上传。此处还需要注意,如果应用没有获取到录音权限,在录制的时候是会走catch里面的。

 停止录制相对就简单了,只要顺序正常即可,之后就可以把视频传到VideoView快速实现预览啦。作为谷歌亲儿子,VideoView自带对setOrientationHint的角度解析,只要根据视频大小配置好界面显示的效果即可。比起 之前本人撸的播放器 ,儿子还是自己的亲┑( ̄Д  ̄)┍,如果需求不高用起来还是可以闭着眼睛的用的。(之前还有小伙伴自己用MediaPlayer播放呢)


//开始
private boolean startRecord() {//懒人模式,根据闪光灯和摄像头前后重新初始化一遍,开期闪光灯工作模式initCamera(cameraType, true);if (recorder == null) {recorder = new MediaRecorder();}if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED|| camera == null || recorder == null) {camera = null;recorder = null;//还是没权限啊showCameraPermission();return false;}try {recorder.setCamera(camera);// 这两项需要放在setOutputFormat之前recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);// Set output file format,输出格式recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//必须在setEncoder之前recorder.setVideoFrameRate(15);  //帧数  一分钟帧,15帧就够了recorder.setVideoSize(SIZE_1, SIZE_2);//这个大小就够了// 这两项需要放在setOutputFormat之后recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);recorder.setVideoEncodingBitRate(3 * SIZE_1 * SIZE_2);//第一个数字越大,清晰度就越高,考虑文件大小的缘故,就调整为1int frontRotation;if (rotationRecord == 180) {//反向的前置frontRotation = 180;} else {//正向的前置frontRotation = (rotationRecord == 0) ? 270 - frontOri : frontOri; //录制下来的视屏选择角度,此处为前置}recorder.setOrientationHint((cameraType == 1) ? frontRotation : rotationRecord);//把摄像头的画面给它recorder.setPreviewDisplay(surfaceHolder.getSurface());//创建好视频文件用来保存videoDir();if (videoFile != null) {//设置创建好的输入路径recorder.setOutputFile(videoFile.getPath());recorder.prepare();recorder.start();//不能旋转啦orientationEventListener.disable();flagRecord = true;}} catch (Exception e) {//一般没有录制权限或者录制参数出现问题都走这里e.printStackTrace();//还是没权限啊recorder.reset();recorder.release();recorder = null;showCameraPermission();FileUtils.deleteFile(videoFile.getPath());return false;}return true;}//结束录制
private void endRecord() {//反正多次进入,比如surface的destroy和界面onPauseif (!flagRecord) {return;}flagRecord = false;try {if (recorder != null) {recorder.stop();recorder.reset();recorder.release();orientationEventListener.enable();recorder = null;}} catch (Exception e) {e.printStackTrace();}videoTime.stop();videoTime.setBase(SystemClock.elapsedRealtime());Intent intent = new Intent(this, PlayActivity.class);intent.putExtra(PlayActivity.DATA, videoFile.getAbsolutePath());startActivityForResult(intent, 2222);overridePendingTransition(R.anim.fab_in, R.anim.fab_out);
}复制代码
最后

 
 总的来说,录制视频还是蛮简单的,主要还是视频的角度问题需要考虑:

  • Camera的前置摄像头角度注意。
  • Android本身默认的是横屏录制效果,所以需要配置横屏和竖屏的录制角度。
  • MediaRecorder参数的配置顺序。
  • CameraMediaRecorder的分辨率和拉伸问题。
  • 闪光灯要在开始录制的时候才开启。
  • 初始化摄像头和释放摄像头需要在surface的surfaceCreatedsurfaceDestroyed
  • 注意锁屏、退到后台、onPuase的是会走surface的surfaceDestroyed
  • 如果是要一次性上传很长很长的拍摄视频,推荐还是找FFMPEG的录制方式吧,毕经录制好了再压缩的做法很费时。 
  • 告诉IOS,让他支持视频元信息的角度旋转播放。(不支持?网上那么多视频有角度信息,难道歪着看?)
  • 测试如果说前置画面拍摄出来的视频左右翻转,用本机拍一个前置视频或者照片给他看,不然你只能接FFMPEG了。  

<( ̄︶ ̄)↗知道你想说什么,DMEO在这里 : github.com/CarGuo/Vide…

有人支持么,好累啊

Android 拍摄(横 \ 竖屏)视频的懒人之路相关推荐

  1. Android 拍摄(横\竖屏)视频的懒人之路

    想一想,我们聊过AudioReord,AudioTrack,MediaPlayer,那多媒体四大金刚,就剩下了MediaRecorder了(SoundPool?我这里信号不好···).其实MediaR ...

  2. 今日头条竖屏视频没有收益吗,今日头条竖版视频没收益怎么回事

    今天给大家分享五点我们新手做自媒体最容易犯的错误,希望可以让你们少走些弯路. 第一:开通自己的头条号 我们一定要去开通自己的一个头条号,成为一个创作者.有的人,他只是下载了一个今日头条app,并没有去 ...

  3. iphone竖屏视频旋转_如何在iPhone上旋转视频

    iphone竖屏视频旋转 We all know that feeling: you record a video on your iPhone in portrait mode, and then ...

  4. iphone竖屏视频旋转_在Linux上从iPhone旋转视频

    iphone竖屏视频旋转 iPhone is nice to take videos. However, one headache is the video may be rotated by 90 ...

  5. 傻瓜式裂变—竖屏视频超级原创,呆头鹅批量剪辑软件上万人使用

    呆头鹅批量剪辑软件优势: 专业的技术开发团队,成熟的技术架构,完整的售后服务,我们为您解决所有的后顾之忧 .几乎涵盖市面上已知的所有剪辑功能.几乎涵盖市面上已知的所有剪辑功能.完成通知,运行间隔提醒, ...

  6. 如何调整竖屏视频为橫屏的最快捷的方法

    我们每天在刷视频时,都会看到有横版的视频.还有一些是竖版的视频,那当我们需要将竖屏的视频改成横屏的,如何用到批量剪辑工具,一键竖屏改横屏,这样操作会更省时间,更方便. 竖屏视频转成横屏后,播放的效果如 ...

  7. 同时为多个竖屏视频加背景图变成横屏的视频

    视频剪辑高手可以同时为多个尺寸相同的竖屏视频添加背景图,使之变成横屏播放的视频吗?小编的回答当然是可以的,下面一起来试试. 所需工具 视频剪辑高手 多段竖屏视频素材 一张横版图片 实例步骤 将需要剪辑 ...

  8. 批量剪辑教程,将竖屏视频剪辑为横屏的四种方法

    我们在刷抖音时,看到大部分视频都是竖屏展示的,但是有些视频平台,在上传视频时,需要将竖屏的视频改为横屏的,今天小编就用视频剪辑高手中的四种功能,批量将多个竖屏剪辑为横屏视频. 方法一:顺时旋转90度 ...

  9. html背景视频模糊效果,怎么给竖屏视频添加模糊背景效果?

    原标题:怎么给竖屏视频添加模糊背景效果? 下面是具体的步骤(本篇教程仅使用一个素材): 1.首先需要下载一个竖屏的视频或图片素材,双击打开视频剪辑软件,在左上角的"项目媒体"中,点 ...

最新文章

  1. leetcode 43. 字符串相乘(Multiply Strings)
  2. UA MATH571B 试验设计III 单因素试验设计1
  3. 全球编程语言薪资排行榜,Java竟然垫底!!!
  4. java图像处理,拷贝图像EXIF信息
  5. 2015-03-12---外观模式,建造者模式(附代码),观察者模式(附代码),boost库应用
  6. u盘 轻量linux,3种方法来创建轻量、持久化的Xubuntu Linux USB系统盘
  7. html在不同浏览器器下颜色不同,CSS在不同浏览器下实现颜色渐变效果
  8. 201521123060 《Java程序设计》第11周学习总结
  9. HoloLens 2开发: Vuforia图片识别
  10. Atitit.软件开发提升稳定性总结
  11. 三对角、五对角追赶法求解线性方程组
  12. java计算机毕业设计计算机组成原理教学网站MyBatis+系统+LW文档+源码+调试部署
  13. 雷蛇键盘灯光配置文件_三模连接一步到位,游戏宅女必备的雷蛇电竞外设套装...
  14. Mysql 超键 候选键 主键 外键之间关系
  15. android 魅族定位权限,魅族Flyme5.2的权限问题
  16. 程序员必备技能之英语学习(一)
  17. 重温与解析《最后生还者》的互动叙事精髓(下)
  18. 地下管线探测仪/路由探测仪 TFN T-6000管线探测的利器!!
  19. 一位15年资深HR直言:清退35岁以上基层员工,早已是公开秘密
  20. Android 分区

热门文章

  1. 读书笔记 摘自:《为什么精英都是时间控》
  2. python算库存管理_年终库存盘点怎么做?无代码库存管理系统高效率!
  3. Windows中texstudio的主题代码(持续更新)
  4. 王师傅玩的c语言小游戏,王师傅竟然背着老婆和男人做出这种事...最好玩的炉石冒险活动来了!...
  5. 【技术分享】Ubuntu20.04启动LGSVL-2021.03闪退解决方案
  6. java实现仓储选址_邮局选址问题 (Java代码)并不难
  7. 路由器wan口认证断开服务器无响应,路由器WAN口设置已断开(服务器无响应)的解决方法...
  8. 软件缺陷(定义+表现形式+优先级+信息+产生原因),看完这篇文章就懂了
  9. 打造超级IP,你真悟错了道!
  10. 图像处理——分水岭算法