前言:因为camera 1 使用MediaRecorder录制视频时,相机的预览回调函数(onPreviewFrame(byte[] data, Camera camera)
)就不会执行。使用camera 2 我也遇到同样的问题,虽然有的文章说camera 2没有这样的问题,但是本人没有做到。但是项目需要处理相机预览回调数据,同时录制视频。所以为了让相机预览回调和录制视频同时进行,只能通过将相机预览回调数据保存为视频,以达目的。
首先,通过预览回调数据录制视频的代码参照:https://www.cnblogs.com/CoderTian/p/6224605.html,文章中有完整代码,运行起来没有问题,视频保存为H264格式的文件,同时本人测试时发现保存的视频是横屏的,在PC上播放时视频为逆时针旋转90度的画面。
为了将视频格式保存为mp4格式,同时将视频的画面调正,对以上文章中的代码稍作修改。
一、将视频调正。
先补充相机预览数据格式知识,相机预览回调函数onPreviewFrame(byte[] data, Camera camera)中的data返回的格式一般是NV21,是属于YUV420格式。以宽高分别为8和6为例,data的数组长度为8*6*3/2,存储方式如下图:
因为视频编码器MediaCodec使用的颜色格式是YUV420SP,需要将NV21格式转化为NV12(YUV420SP),以便视频编码。NV12格式和NV21类似,Y存储方式一样,不同就是V和U存储位置对调,比如上图的V0和U0对调,V1和U1对调,以此类推。以上文章中使用的方法是NV21ToNV12(),稍微简化成如下函数,
private void NV21ToNV12(byte[] nv21, byte[] nv12, int width, int height) {
if (nv21 == null || nv12 == null) return;
int framesize = width * height;
// for y nv21 and nv12 is equal
// reference to https://blog.csdn.net/baidu_31872269/article/details/70315193
System.arraycopy(nv21, 0, nv12, 0, framesize);
// remaining 1/2 uv data
for (int j = 0; j < framesize / 2; j += 2) {
nv12[framesize + j - 1] = nv21[j + framesize];
nv12[framesize + j] = nv21[j + framesize - 1];
}
}
重点来了,如何修改代码,让视频调正。
1、修改视频格式的宽和高,代码如下
// mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
mediaFormat = MediaFormat.createVideoFormat("video/avc", height, width);
参数中width > height,即视频的宽度是比高度大的。比如1280x720,640x480。
2、调用NV21转NV12(YUV420SP)格式转化函数并旋转90度。
网上YUV420SP顺时针旋转函数很多,直接用就行。但是为了节省转化时间,我写了个函数,直接将NV21转化成NV12并顺时针旋转90度。函数如下:
private void NV21ToNV12Rotate90(byte[] nv21, byte[] nv12, int width, int height) {
if (nv21 == null || nv12 == null) return;
int framesize = width * height;
//旋转Y, add by zzh, rotate 90
int k = 0;
int row = width;
for (int i = width; i > 0; i--) {
for (int j = height; j > 0; j--) {
nv12[k++] = nv21[width * j - row];
}
row--;
}
// uv 数据作为一个整体所在列,从最后一列开始赋值
int uvColIndex = height;
// uv data count
for (int i = framesize; i < framesize * 3 / 2;) {
// after rotate uv data rows
for (int j = 0; j < width / 2; j++) {
// 从最后一列开始赋值
nv12[framesize + uvColIndex - 2 + (j * height)] = nv21[i + 1];
nv12[framesize + uvColIndex - 1 + (j * height)] = nv21[i];
i +=2;
}
uvColIndex -= 2;
}
}
用图片展示8*6从NV21格式转化成NV12并顺时针旋转90度后的格式如下图:
二、将H264转成mp4格式。
1、先在Android studio app的build.gradle中加入mp4parser包的引用
dependencies {
// ...
// for h264 video to mp4 video
implementation 'com.googlecode.mp4parser:isoparser:1.1.21'
}
2、在AvcEncoder.StopThread()中调用h264转mp4函数,最好起一个线程执行,防止阻塞UI。
// add by zzh for h264 to mp4
private void h264ToMp4() {
try {
File file = new File(path);
if (!file.exists()) {
Log.e(TAG, "h264ToMp4 error, h264 file not exists");
return;
}
H264TrackImpl h264Track = new H264TrackImpl(new FileDataSourceImpl(path));
Movie movie = new Movie();
movie.addTrack(h264Track);
Container mp4file = new DefaultMp4Builder().build(movie);
FileChannel fc = new FileOutputStream(new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/test1.mp4")).getChannel();
mp4file.writeContainer(fc);
fc.close();
} catch (IOException e) {
e.printStackTrace();
}
}