目录
架构设计
原设计架构:
调用系统Action属性打开手机相机,进行视频录制操作;
使用ProjectApplication.activity.startActivityForResult()回调方式获取录制视频文件;
利用callback机制回传获取录制视频文件到界面,进行数据上传和UI更新操作。
原设计架构存在问题:上传视频后存在颜色失真的问题,这个现象的原因是Camera录制的NV21图像预览数据,没有进行视频编码和封装,直接转为mp4格式文件存储,并不是手机支持的MediaCodec编解码颜色格式,则上传平台后存在颜色失真的现象。
新架构设计:
使用Camera框架采集视频像素数据;
使用MediaCodec将原生YUV数据转码为H264格式;
使用ffmpeg命令将H264文件封装为mp4文件。
利用callback机制将封装MP4文件回传到界面,进行数据上传和UI更新操作。
Camera
Android5.0之前使用android.hardware包下的Camera类进行拍照、录视频等功能。
Camera实现视频数据采集
public class DataCollectVideoDialog extends BaseDialog implements View.OnClickListener, Camera.PreviewCallback {
@BindView(R.id.record_video_sfv)
SurfaceView surfaceView;
@BindView(R.id.btn_start_video)
Button startBtn;
@BindView(R.id.btn_stop_video)
Button stopBtn;
private SurfaceHolder holder;
private android.hardware.Camera camera;
private DataCollectCallBack callBack;
private static int yuvqueuesize = 10;
private static ArrayBlockingQueue<byte[]> YUVQueue = new ArrayBlockingQueue<byte[]>(yuvqueuesize);
private MediaUtil mediaUtil;
public DataCollectVideoDialog(Context context, DataCollectCallBack callBack) {
super(context, R.style.FullScreenDialogTheme);
this.callBack = callBack;
}
@Override
protected int setContentView() {
return R.layout.dialog_data_collect_video;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void bindView() {
super.bindView();
supportAvcCodec();
init();
}
// 对View控件初始化
private void init() {
getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
holder = surfaceView.getHolder();
startBtn.setOnClickListener(this);
stopBtn.setOnClickListener(this);
stopBtn.setVisibility(View.GONE);
((Toolbar) view.findViewById(R.id.record_video_toolbar)).setNavigationOnClickListener(v -> cancelOn());
setOnKeyListener(((dialog, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK && keyCode == KeyEvent.ACTION_DOWN) {
cancelOn();
}
return false;
}));
show();
}
// 检查当前是否支持video编码
private boolean supportAvcCodec() {
if (Build.VERSION.SDK_INT >= 18) {
for (int j = MediaCodecList.getCodecCount() - 1; j >= 0; j--) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(j);
String[] types = codecInfo.getSupportedTypes();
for (int i = 0; i < types.length; i++) {
if (types[i].equalsIgnoreCase("video/avc"