文章目录






安卓直播推流专栏博客总结



​Android RTMP 直播推流技术专栏​​ :



0 . 资源和源码地址 :


1. 搭建 RTMP 服务器 :​ ​下面的博客中讲解了如何在 VMWare 虚拟机中搭建 RTMP 直播推流服务器 ;

2. 准备视频编码的 x264 编码器开源库 , 和 RTMP 数据包封装开源库 :

3. 讲解 RTMP 数据包封装格式 :

4. 图像数据采集 :​ ​从 Camera 摄像头中采集 NV21 格式的图像数据 , 并预览该数据 ;

5. NV21 格式的图像数据编码成 H.264 格式的视频数据 :

6. 将 H.264 格式的视频数据封装到 RTMP 数据包中 :

7. 阶段总结 : 阿里云服务器中搭建 RTMP 服务器 , 并使用电脑软件推流和观看直播内容 ;

8. 处理 Camera 图像传感器导致的 NV21 格式图像旋转问题 :

9. 下面这篇博客比较重要 , 里面有一个快速搭建 RTMP 服务器的脚本 , 强烈建议使用 ;

10. 编码 AAC 音频数据的开源库 FAAC 交叉编译与 Android Studio 环境搭建 :

11. 解析 AAC 音频格式 :

12 . 将麦克风采集的 PCM 音频采样编码成 AAC 格式音频 , 并封装到 RTMP 包中 , 推流到客户端 :






Android 直播推流流程 :​ 手机采集视频 / 音频数据 , 视频数据使用 H.264 编码 , 音频数据使用 AAC 编码 , 最后将音视频数据都打包到 RTMP 数据包中 , 使用 RTMP 协议上传到 RTMP 服务器中 ;



Android 端中主要完成手机端采集视频数据操作 , 并将视频数据传递给 JNI , 在 NDK 中使用 x264 将图像转为 H.264 格式的视频 , 最后将 H.264 格式的视频打包到 RTMP 数据包中 , 上传到 RTMP 服务器中 ;



本篇博客中主要讲解 Android 端数据采集 , Camera 摄像头获取 NV21 数据后 , 此时预览数据方向是错误的 , 因为 Camera 图像传感器采集数据时 , 始终以手机右上角为原点 , NV21 数据的图像信息也是倒着的 ;






一、 Camera 传感器方向简介



1 . Camera 采集 NV21 图像数据 :​ 手机 Camera 采集的图像数据完毕后 , 通过 PreviewCallback 接口的 onPreviewFrame 回调方法获取 NV21 图像数据 ;



2 . NV21 图像数据来源 :​ 该数据的最底层来源是手机 Camera 硬件的图像传感器 ;



3 . 图像传感器采集图像机制 :



① 图像传感器坐标原点 :​ 图像传感器取景时有一个坐标原点 , 就是手机的右上角 ;

② 图像传感器坐标方向 :​ 从图像传感器原点 / 手机右上角 ( 0 , 0 ) 向右下角方向是 X 增加方向 , 从图像传感器原点 / 手机右上角 ( 0 , 0 ) 向左上角方向是 Y 增加方向 ;

【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )_Camera






二、 Camera 图像传感器横向显示数据



1 . 向左横向 :​ 当手机向左横放时 , 图像传感器原点及方向 , 屏幕的原点及方向如下 ;



① 传感器原点和方向 :​ 图像传感器 ( 手机右上角 ) 原点 ( 0 , 0 ) ( 0 , 0 ) (0,0) , 向右 X 增加 , 向下 Y 增加 ;

② 屏幕原点和方向 :​ 手机屏幕当前左上角 ( 手机的右上角 ) 是屏幕原点 , 向右 X 增加 , 向下 Y 增加 ;

【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )_Android_02




2 . 图像显示 :​ 屏幕传感器的方向与屏幕方向一致 , 此时没有显示图像传感器 : 横向界面的 Camera 采集的图像数据是正常的 ;

【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )_NV21_03

注意 : 这是向左横向显示的数据 , 如果向右横向 , 数据整个都倒过来了 ;






三、 Camera 图像传感器纵向显示数据



1 . 正常竖屏 :​ 此时还是以右上角为原点 , 采集横向图像 ,



① 传感器原点和方向 :​ 图像传感器 ( 手机右上角 ) 原点 ( 0 , 0 ) ( 0 , 0 ) (0,0) , 向右 X 增加 , 向下 Y 增加 ;

② 屏幕原点和方向 :​ 手机屏幕当前左上角 ( 手机的右上角 ) 是屏幕原点 , 向右 X 增加 , 向下 Y 增加 ;

【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )_Camera_04




2 . 图像显示 :​ 屏幕传感器的方向与屏幕方向不一致 , 此时没有显示图像传感器 , 纵向数据是不正常的 , 此时垂直方向显示界面时 , 显示的拍照信息还是横向的 , 只是 Camera 采集的图像逆时针旋转了 90 度 ;

【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )_图像传感器_05

注意 : 这是向上纵向显示的数据 , 如果向下纵向 , 数据整个都倒过来了 ;






四、 设置 Camera 预览数据方向



1 . 纠正图像预览方向 :​ Google 官方提供了设置 Camera 预览方向的方式 , 以下代码定义在 Camera#setDisplayOrientation 文档注释中 , 为 Camera 设置了以下参数后 , 就不会有上述预览图像错误的问题产生 ;



2 . NV21 数据方向 :​ NV21 格式的图像数据的的实际方向还是错误的方向 , 需要用户自己使用时纠正 ;



/**
* 设置 Camera 预览方向
* 如果不设置, 视频是颠倒的
* 该方法内容拷贝自 {@link Camera#setDisplayOrientation} 注释, 这是 Google Docs 提供的
* @param parameters
*/
private void setCameraPreviewOrientation(Camera.Parameters parameters) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(mCameraFacing, info);
mRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (mRotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}






五、 Camera 使用流程



1 . 开启 Camera 摄像头 :

/**
* 开启 Camera 摄像头
*/
private void startCameraNV21DataPreview() {
try {
Log.i("octopus", "startCameraNV21DataPreview");
// 1. 打开指定方向的 Camera 摄像头
mCamera = Camera.open(mCameraFacing);
// 2. 获取 Camera 摄像头参数, 之后需要修改配置该参数
Camera.Parameters parameters = mCamera.getParameters();
// 3. 设置 Camera 采集后预览图像的数据格式 ImageFormat.NV21
parameters.setPreviewFormat(ImageFormat.NV21);
// 4. 设置摄像头预览尺寸
setPreviewSize(parameters);
// 5. 设置图像传感器参数
setCameraPreviewOrientation(parameters);
mCamera.setParameters(parameters);
// 6. 计算出 NV21 格式图像 mWidth * mHeight 像素数据大小
mNv21DataBuffer = new byte[mWidth * mHeight * 3 / 2];
// 7. 设置 Camera 预览数据缓存区
mCamera.addCallbackBuffer(mNv21DataBuffer);
// 8. 设置 Camera 数据采集回调函数, 采集完数据后
// 就会回调此 PreviewCallback 接口的
// void onPreviewFrame(byte[] data, Camera camera) 方法
mCamera.setPreviewCallbackWithBuffer(this);
// 9. 设置预览图像画面的 SurfaceView 画布
mCamera.setPreviewDisplay(mSurfaceHolder);

// 11. 开始预览
mCamera.startPreview();
} catch (Exception ex) {
ex.printStackTrace();
}
}



2 . 释放 Camera 摄像头 :

/**
* 释放 Camera 摄像头
*/
private void stopCameraNV21DataPreview() {
if (mCamera != null) {
// 下面的 API 都是 Android 提供的

// 1. 设置预览回调接口, 这里设置 null 即可
mCamera.setPreviewCallback(null);
// 2. 停止图像数据预览
mCamera.stopPreview();
// 3. 释放 Camera
mCamera.release();
mCamera = null;
}
}






六、 Camera 动态权限申请



1 . Android 6.0 以下静态设置权限 :​ AndroidManifest.xml 设置静态权限 ;

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

2 . Android 6.0 以上动态获取权限 :

/**
* 需要获取的权限列表
*/
private String[] permissions = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.INTERNET,
Manifest.permission.MODIFY_AUDIO_SETTINGS,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA
};

/**
* 动态申请权限的请求码
*/
private static final int PERMISSION_REQUEST_CODE = 888;

/**
* 动态申请权限
*/
@RequiresApi(api = Build.VERSION_CODES.M)
private void initPermissions() {
if (isLacksPermission()) {
//动态申请权限 , 第二参数是请求吗
requestPermissions(permissions, PERMISSION_REQUEST_CODE);
}
}

/**
* 判断是否有 permissions 中的权限
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public boolean isLacksPermission() {
for (String permission : permissions) {
if(checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED){
return true;
}
}
return false;
}