这个日志拖了很久了,是我的毕业设计中碰到的一个需求。
首先视频硬编码MediaRecorder类并不适合做实时发送(不知道为什么博主的机器测试出来的mdat后面紧接着并不是网上所说的一个Int表示的场长度,而是连续8个3F预留字节位,努力很久后彻底放弃了MediaRecorder,改为实现Camera的onPreviewFrame方法)
Camera的onPreviewFrame会在每一帧取回一个字节数组,格式为YUV422(ImageFormat.NV21)
这种无压缩的图片格式是相当占用空间的(480x320分辨率可以达到200KB每张图的体积,10FPS的话就有2MB每秒的传输速率,如果要网络发送的话压力是非常大的),因此需要压缩。
在网上找到的方法基本是Yuv到RGB的方式,把Yuv的数据通过数学运算得到每个像素点的RGB编码,存入Bitmap对象,再调用Bitmap对象自带的压缩方法压为jpg图片。这种方法效率极低,一张480x320分辨率的图片有20万个字节,因此运算需要经过20万次循环,在博主的小米1上测试的结果是一张图需要400毫秒的处理时间,帧速被强行降低到了2.5FPS(onPreviewFrame在执行完成之前得到的数据会自动丢弃),单张图片还好说,如果是连续图片构成的视频流是不可能完成这种需求的。
在我一筹莫展之际发现了android.graphics包下面已经存在一个YuvImage类,可以将数据直接导入:
YuvImage image = new YuvImage(data,ImageFormat.NV21,IMG_WIDTH,IMG_HEIGHT,null);
这个构造方法有5个参数,第一个就是字节数组,第二个是格式信息,和相机配置的时候设置的previewFormat一致即可。第三、第四个不需要解释,就是图片的长宽信息(YUV格式没有储存图片的尺寸信息,最后一个博主也不知道是什么,总之给个null就可以了)。
然后很碰巧的是YuvImage有一个compressToJPEG()方法。
需要传入的参数有3个:一个Rect对象,包含图片的显示范围,一般是(0,0)到(width,height),也可以利用这个参数对图片进行切割;
第二个是质量因数,1-100内的整数;
第三个是一个OutputStream对象,会把结果输出到这个Stream。
实际测试80质量的480x320尺寸图片压缩只用了50ms,效率大大提高。
不过这种jpg图片似乎和传统jpg编码格式有差异,打开是可以正常打开的,而用FFMpeg压缩的时候显示是jpg-yuv的格式,也就是说直接将yuv以jpg格式压缩的。如果需要进一步转换可能依然需要转为RGB格式。