MediaPlus是基于FFmpeg从零开发的android多媒体组件,主要包括:采集,编码,同步,推流,滤镜及直播及短视频比较通用的功能等,后续功能的新增都会有相应文档更新,感谢关注。
-
android相机的视频采集格式比较多 ,如:NV21,NV12,YV12等。他们之间的区别就是U,V排列顺序不一致,具体YUV相关内容可以看看其他详细的文档,如:[总结]FFMPEG视音频编解码零基础学习方法。
需要了解的就是:YUV采样,数据分布及空间大小计算。 YUV采样:
YUV420P YUV排序如下图:
NV12,NV21,YV12,I420都属于YUV420,但是YUV420 又分为YUV420P,YUV420SP,P与SP区别就是,前者YUV420P UV顺序存储,而YUV420SP则是UV交错存储,这是最大的区别,具体的yuv排序就是这样的: I420: YYYYYYYY UU VV ->YUV420P YV12: YYYYYYYY VV UU ->YUV420P NV12: YYYYYYYY UVUV ->YUV420SP NV21: YYYYYYYY VUVU ->YUV420SP
那么H264编码,为什么需要把android 相机采集的NV21数据转换成YUV420P? 刚开始对这些颜色格式也很模糊,后来找到了真理:因为H264编码必须要用 I420, 所以这里必须要处理色彩格式转换。 MediaPlus采集视频数据为NV21格式,以下描述如何获取android camera采集的每一帧数据,并处理色彩格式转换,代码如下:
-
获取相机采集数据:
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
mParams = mCamera.getParameters();
setCameraDisplayOrientation(this, Camera.CameraInfo.CAMERA_FACING_BACK, mCamera);
mParams.setPreviewSize(SRC_FRAME_WIDTH, SRC_FRAME_HEIGHT);
mParams.setPreviewFormat(ImageFormat.NV21); //preview format:NV21
mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
m_camera.setDisplayOrientation(90);
mCamera.setParameters(mParams); // setting camera parameters
m_camera.addCallbackBuffer(m_nv21);
m_camera.setPreviewCallbackWithBuffer(this);
m_camera.startPreview();
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
// TODO Auto-generated method stub
//data这里就是获取到的NV21数据
m_camera.addCallbackBuffer(m_nv21);//这里要添加一次缓冲,否则onPreviewFrame可能不会再被回调
}
因为NV21数据的所需空间大小(字节)=宽 x 高 x 3 / 2 (y=WxH,u=WxH/4,v=WxH/4);所以我们需要建立一个byte数组,作为采集视频数据的缓冲区. MediaPlus>>app.mobile.nativeapp.com.libmedia.core.streamer.RtmpPushStreamer 类主要采集音视频数据,并交由底层处理;有两个线程分别用于处理音视频,AudioThread 、VideoThread.
-
首先看下VideoThread
/**
* 视频采集线程
*/
class VideoThread extends Thread {
public volatile boolean m_bExit = false;
byte[] m_nv21Data = new byte[mVideoSizeConfig.srcFrameWidth
* mVideoSizeConfig.srcFrameHeight * 3 / 2];
byte[] m_I420Data = new byte[mVideoSizeConfig.srcFrameWidth
* mVideoSizeConfig.srcFrameHeight * 3 / 2];
byte[] m_RotateData = new byte[mVideoSizeConfig.srcFrameWidth
* mVideoSizeConfig.srcFrameHeight * 3 / 2];
byte[] m_MirrorData = new byte[mVideoSizeConfig.srcFrameWidth
* mVideoSizeConfig.srcFrameHeight * 3 / 2];
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
VideoCaptureInterface.GetFrameDataReturn ret;
while (!m_bExit) {
try {
Thread.sleep(1, 10);
if (m_bExit) {
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
ret = mVideoCapture.GetFrameData(m_nv21Data,
m_nv21Data.length);
if (ret == VideoCaptureInterface.GetFrameDataReturn.RET_SUCCESS) {
frameCount++;
LibJniVideoProcess.NV21TOI420(mVideoSizeConfig.srcFrameWidth, mVideoSizeConfig.srcFrameHeight, m_nv21Data, m_I420Data);
if (curCameraType == VideoCaptureInterface.CameraDeviceType.CAMERA_FACING_FRONT) {
LibJniVideoProcess.MirrorI420(mVideoSizeConfig.srcFrameWidth, mVideoSizeConfig.srcFrameHeight, m_I420Data, m_MirrorData);
LibJniVideoProcess.RotateI420(mVideoSizeConfig.srcFrameWidth, mVideoSizeConfig.srcFrameHeight, m_MirrorData, m_RotateData, 90);
} else if (curCameraType == VideoCaptureInterface.CameraDeviceType.CAMERA_FACING_BACK) {
LibJniVideoProcess.RotateI420(mVideoSizeConfig.srcFrameWidth, mVideoSizeConf