通过camera CameraCallbacks预览回调数据保存视频

前言:因为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,存储方式如下图:

通过camera CameraCallbacks预览回调数据保存视频_第1张图片

因为视频编码器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度后的格式如下图:

通过camera CameraCallbacks预览回调数据保存视频_第2张图片

二、将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();
        }
    }

 

你可能感兴趣的:(Android,camera)