实习工作记录
- Android下ffmpeg+x264+fdk-aac软解码demo
功能描述:Android下获取摄像头1080P的YV12格式视频编码为h264格式视频保存,同时获取PCM格式音频数据编码为AAC格式音频保存
重点:控制编码时间不超过33ms
编码时间优化过程:
首先在native层使用ffmpeg的编码器接口调用x264库
关键的参数设置为
pCodecCtx->thread_count = 4 ;//设置开启线程数(测试手机的cpu核数)
av_dict_set(¶m, "preset", "ultrafast", 0); //有ultrafast、superfast、veryfast、fast、、medium、slow、slower、veryslow的选项,影响编码速度和质量
av_dict_set(¶m, "tune", "zerolatency", 0); //有film 、animation、grain、stillimage、psnr、ssim、fastdecode、zerolatency的选项,从左到右编码复杂度依次递减,质量依次递减,实时编码必须设置zerolatency,表示零延迟输出当前编码帧
但是在这个参数设置下,编码时间为80ms一帧,并会逐渐增长,达不到要求
分析编码处理时间包括:
- 将YV21数据转换成YUV420P的时间
- ffmpeg与X264编码器之间的参数传递和函数调用
- x264编码器编码的时间
数据格式在1080P下的转换时间在10毫秒左右,为了排除ffmpeg的影响,我对x264库相同设置下的编码时间进行了测试,单独编译了x264库,配置了AS,在native层重写了一个调用264库的类,封装好给java层调用,编码速度得到提升,但还是逐渐变慢,分析应该是由于编码对于CPU的需求比较高,但编码线程并没有得到很好的调度,所以在java层采用android.os.Process类中设置优先级的函数来对编码线程进行调度优化,结果就是,直接使用x264工程加上调度优化可以满足需求,也就是说x264编码器在设置正确的情况下的速度是可以满足要求的。
接下来研究为什么在使用ffmpeg调用x264库时同样的设置起不了作用,对log和源码进行分析之后发现,虽然设置了帧并行编码线程数,但是并没有起作用,也就是说并行编码没起作用,这是由于ffmpeg的参数设置并不是与x264的参数完全一一对应,要加上以下设置(表明并行类型是帧并行),结果就与直接应用x264无差异了
pCodecCtx->thread_type = FF_THREAD_FRAME;
需要注意的问题:
1 编译x264和fdk-aac库的时候可能会出现找不到库的问题,这是由于路径设置可能存在问题
2 fdk-aac库在使用ffmpeg调用时,可能会报错,这是使用了根据id寻找编码器的函数造成的,由于fdk-aac的id与ffmpeg内置aac格式的音频编码器的id相同,所以应使用编码器name寻找(avcodec_find_encoder_by_name(“libfdk_aac”))
- 研究如何给ffmpeg添加自定义的编码器
- 编写Android下MediaCodec硬解码类
描述:实现三个接口,init、encode、release,encode实现YUV420P到h264同步编码,为了熟悉Android硬解码功能 - 优化首帧时间
描述:线上代码存在首帧硬编码时间过长问题(从开始获取图像至返回编码后的首帧时间400-500ms)
首帧过慢原因:
线上的硬编码代码逻辑是,每次获取到相机返回的图像后调用encodevideofrombuffer()
进行此帧的编码并返回编码后的帧
核心函数encodevideofrombuffer()
中的逻辑是获取编码器的的可用buffer将YUV数据写入,并调用dequeueOutputBuffer( bufferInfo, TIMEOUT_USEC)
取出编码后的帧
有两部分原因导致首帧时间长:
1 初始化硬解码器的时机不对,应该尽量将初始化代码提前
2 TIMEOUT_USEC设置过小,编码未完成导致超时返回,只能放弃此次取数据,等到下次相机返回图像时再取出此帧,也由于android的硬编码器规定要先输出一个配置帧,所以一般要等到第二、三帧时才能获取到首帧解码后的数据,并且帧率越小,帧间间隔越大,拿到首帧的时间越长
优化过程:
1、修改了init函数,将初始化时间提前到执行oncreate()
时
2、修改了TIMEOUT_USEC与逻辑,使得可在第一次调用encodevideofrombuffer()
时就返回解码后的首帧
优化结果:
从获取到相机图像到获取到解码后的首帧的时间缩短至40ms左右