文章目录

  • 安卓直播推流专栏博客总结
  • 一、 Java 层传入的 RTMP 推流地址处理
  • 二、 RTMPDump 推流线程
  • 三、 创建 RTMP 对象
  • 四、 初始化 RTMP 对象
  • 五、 设置 RTMP 推流地址
  • 六、 启用 RTMP 写出功能
  • 七、 连接 RTMP 服务器
  • 八、 连接 RTMP 流
  • 九、 发送 RTMP 数据包
  • 十、 断开 RTMP 连接并释放资源
  • 十一、 RTMPDump 推流代码

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


Android RTMP 直播推流技术专栏 :

0 . 资源和源码地址 :

  • 资源下载地址 : 资源下载地址 , 服务器搭建 , x264 , faac , RTMPDump , 源码及交叉编译库 , 本专栏 Android 直播推流源码 ;
  • GitHub 源码地址 : han1202012 / RTMP_Pusher

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

  • 【Android RTMP】RTMP 直播推流服务器搭建 ( Ubuntu 18.04.4 虚拟机 )

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

  • 【Android RTMP】RTMPDumb 源码导入 Android Studio ( 交叉编译 | 配置 CMakeList.txt 构建脚本 )

  • 【Android RTMP】Android Studio 集成 x264 开源库 ( Ubuntu 交叉编译 | Android Studio 导入函数库 )

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

  • 【Android RTMP】RTMP 数据格式 ( FLV 视频格式分析 | 文件头 Header 分析 | 标签 Tag 分析 | 视频标签 Tag 数据分析 )

  • 【Android RTMP】RTMP 数据格式 ( FLV 视频格式分析 | AVC 序列头格式解析 )

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

  • 【Android RTMP】Android Camera 视频数据采集预览 ( 视频采集相关概念 | 摄像头预览参数设置 | 摄像头预览数据回调接口 )

  • 【Android RTMP】Android Camera 视频数据采集预览 ( NV21 图像格式 | I420 图像格式 | NV21 与 I420 格式对比 | NV21 转 I420 算法 )

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

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

  • 【Android RTMP】x264 编码器初始化及设置 ( 获取 x264 编码参数 | 编码规格 | 码率 | 帧率 | B帧个数 | 关键帧间隔 | 关键帧解码数据 SPS PPS )

  • 【Android RTMP】x264 图像数据编码 ( Camera 图像数据采集 | NV21 图像数据传到 Native 处理 | JNI 传输字节数组 | 局部引用变量处理 | 线程互斥 )

  • 【Android RTMP】x264 图像数据编码 ( NV21 格式中的 YUV 数据排列 | Y 灰度数据拷贝 | U 色彩值数据拷贝 | V 饱和度数据拷贝 | 图像编码操作 )

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

  • 【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 封装 SPS / PPS 数据包 )

  • 【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 关键帧数据格式 | 非关键帧数据格式 | x264 编码后的数据处理 | 封装 H.264 视频数据帧 )

  • 【Android RTMP】RTMPDump 推流过程 ( 独立线程推流 | 创建推流器 | 初始化操作 | 设置推流地址 | 启用写出 | 连接 RTMP 服务器 | 发送 RTMP 数据包 )

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

  • 【Android RTMP】RTMP 直播推流 ( 阿里云服务器购买 | 远程服务器控制 | 搭建 RTMP 服务器 | 服务器配置 | 推流软件配置 | 直播软件配置 | 推流直播效果展示 )

  • 【Android RTMP】RTMP 直播推流阶段总结 ( 服务器端搭建 | Android 手机端编码推流 | 电脑端观看直播 | 服务器状态查看 )

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

  • 【Android RTMP】NV21 图像旋转处理 ( 问题描述 | 图像顺时针旋转 90 度方案 | YUV 图像旋转细节 | 手机屏幕旋转方向 )

  • 【Android RTMP】NV21 图像旋转处理 ( 图像旋转算法 | 后置摄像头顺时针旋转 90 度 | 前置摄像头顺时针旋转 90 度 )

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

  • 【Android RTMP】NV21 图像旋转处理 ( 快速搭建 RTMP 服务器 Shell 脚本 | 创建 RTMP 服务器镜像 | 浏览器观看直播 | 前置 / 后置摄像头图像旋转效果展示 )

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

  • 【Android RTMP】音频数据采集编码 ( 音频数据采集编码 | AAC 高级音频编码 | FAAC 编码器 | Ubuntu 交叉编译 FAAC 编码器 )

  • 【Android RTMP】音频数据采集编码 ( FAAC 头文件与静态库拷贝到 AS | CMakeList.txt 配置 FAAC | AudioRecord 音频采样 PCM 格式 )

11. 解析 AAC 音频格式 :

  • 【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )

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

  • 【Android RTMP】音频数据采集编码 ( FAAC 音频编码参数设置 | FAAC 编码器创建 | 获取编码器参数 | 设置 AAC 编码规格 | 设置编码器输入输出参数 )

  • 【Android RTMP】音频数据采集编码 ( FAAC 编码器编码 AAC 音频解码信息 | 封装 RTMP 音频数据头 | 设置 AAC 音频数据类型 | 封装 RTMP 数据包 )

  • 【Android RTMP】音频数据采集编码 ( FAAC 编码器编码 AAC 音频采样数据 | 封装 RTMP 音频数据头 | 设置 AAC 音频数据类型 | 封装 RTMP 数据包 )


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

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

本篇博客中将介绍 , 使用 RTMPDump 开源库 , 将编码好的 RTMP 数据包 , 推送到远程 RTMP 服务器 ; 即 RTMPDump 推流过程 ;

一、 Java 层传入的 RTMP 推流地址处理


1 . Java 传递字符串数据到 JNI : 启动推流时 , Java 层会将 RTMP 推流地址传递给 JNI ;

2 . jstring 类型转为 char* 类型 : 将 Java 字符串转为 C 字符串 ;

// 获取 Rtmp 推流地址
// 该 pushPathFromJava 引用是局部引用, 超过作用域就无效了
// 局部引用不能跨方法 , 跨线程调用
const char* pushPathFromJava = env->GetStringUTFChars(path, 0);

3 . 局部引用变量处理 : 该转换后的 const char* pushPathFromJava 字符串是局部引用变量 , 不能跨进程 , 跨作用域使用 , 之后的推流操作在独立的线程中使用 , 因此需要将字符串数据在堆内存中存储 ;

// 获取地址的长度, 加上 '\0' 长度
char * pushPathNative = new char[strlen(pushPathFromJava) + 1];
// 拷贝 pushPathFromJava 到堆内存 pushPathNative 中
// 局部引用不能跨方法 , 跨线程调用, 这里需要在线程中使用该地址
// 因此需要将该局部引用拷贝到堆内存中, 然后传递到对应线程中
strcpy(pushPathNative, pushPathFromJava);

4 . 释放局部引用 : JNI 中的局部引用变量 , 使用完毕后及时释放 ;

// 释放从 Java 层获取的字符串
// 释放局部引用
env->ReleaseStringUTFChars(path, pushPathFromJava);

二、 RTMPDump 推流线程


1 . 独立线程推流 : RTMP 推流操作需要在一个独立的线程中完成 , 涉及到网络的操作都是耗时操作 , 在 Android 中都要在线程中执行 ;

2 . 线程 ID 声明 : 需要导入 #include <pthread.h> 包 , 之后才能使用线程 , 先声明线程 ID , pthread_t 类型 ;

/*** 开始推流工作线程的线程 ID*/
pthread_t startRtmpPushPid;

3 . 创建并执行线程 : 创建并执行线程 , 在线程中执行 startRtmpPush 方法 , 传入 pushPathNative 字符串参数 ;

// 创建线程
pthread_create(&startRtmpPushPid, 0, startRtmpPush, pushPathNative);

4 . 线程方法 : 定义线程方法 , 参数和返回值都是 void* 类型 , 在开始位置获取传入的参数 ;

void* startRtmpPush (void* args){// 0. 获取 Rtmp 推流地址char* pushPath = static_cast<char *>(args);// ...
}

三、 创建 RTMP 对象


创建 RTMP 对象 , 如果创建失败 , 直接停止整个推流方法 ;

// 1. 创建 RTMP 对象, 申请内存
rtmp = RTMP_Alloc();
if (!rtmp) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "申请 RTMP 内存失败");break;
}

四、 初始化 RTMP 对象


初始化 RTMP 对象 , 并设置超时时间 ;

// 2. 初始化 RTMP
RTMP_Init(rtmp);
// 设置超时时间 5 秒
rtmp->Link.timeout = 5;

五、 设置 RTMP 推流地址


设置 RTMP 推流地址 , 如果设置失败 , 直接退出推流操作 ;

该地址就是 Java 层传给 JNI 的字符串 , 刚获取时是局部引用变量 , 将其拷贝到了堆内存中 , 才可以在推流线程中使用 ;

// 3. 设置 RTMP 推流服务器地址
int ret = RTMP_SetupURL(rtmp, pushPath);
if (!ret) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "设置 RTMP 推流服务器地址 %s 失败", pushPath);break;
}

六、 启用 RTMP 写出功能


启用 RTMP 写出功能 ;

// 4. 启用 RTMP 写出功能
RTMP_EnableWrite(rtmp);

七、 连接 RTMP 服务器


连接 RTMP 服务器 , 如果连接失败 , 直接退出该方法 ;

// 5. 连接 RTMP 服务器
ret = RTMP_Connect(rtmp, 0);
if (!ret) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "连接 RTMP 服务器 %s 失败", pushPath);break;
}

八、 连接 RTMP 流


连接 RTMP 流 , 如果连接失败 , 退出方法 ;

// 6. 连接 RTMP 流
ret = RTMP_ConnectStream(rtmp, 0);
if (!ret) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "连接 RTMP 流 %s 失败", pushPath);break;
}

九、 发送 RTMP 数据包


将 RTMP 数据包发送到服务器中 ;

// 7. 将 RTMP 数据包发送到服务器中
ret = RTMP_SendPacket(rtmp, packet, 1);

十、 断开 RTMP 连接并释放资源


推流结束后 , 关闭与 RTMP 服务器连接 , 释放资源 ;

// 8. 推流结束, 关闭与 RTMP 服务器连接, 释放资源
if(rtmp){RTMP_Close(rtmp);RTMP_Free(rtmp);
}

十一、 RTMPDump 推流代码


RTMPDump 推流代码 :

/*** 开始向远程 RTMP 服务器推送数据*/
extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_rtmp_LivePusher_native_1startRtmpPush(JNIEnv *env, jobject thiz,jstring path) {if(isStartRtmpPush){// 防止该方法多次调用, 如果之前调用过, 那么屏蔽本次调用return;}// 执行过一次后, 马上标记已执行状态, 下一次就不再执行该方法了isStartRtmpPush = TRUE;// 获取 Rtmp 推流地址// 该 pushPathFromJava 引用是局部引用, 超过作用域就无效了// 局部引用不能跨方法 , 跨线程调用const char* pushPathFromJava = env->GetStringUTFChars(path, 0);// 获取地址的长度, 加上 '\0' 长度char * pushPathNative = new char[strlen(pushPathFromJava) + 1];// 拷贝 pushPathFromJava 到堆内存 pushPathNative 中// 局部引用不能跨方法 , 跨线程调用, 这里需要在线程中使用该地址// 因此需要将该局部引用拷贝到堆内存中, 然后传递到对应线程中strcpy(pushPathNative, pushPathFromJava);// 创建线程pthread_create(&startRtmpPushPid, 0, startRtmpPush, pushPathNative);// 释放从 Java 层获取的字符串// 释放局部引用env->ReleaseStringUTFChars(path, pushPathFromJava);
}/*** 开始推流任务线程* 主要是调用 RTMPDump 进行推流* @param args* @return*/
void* startRtmpPush (void* args){// 0. 获取 Rtmp 推流地址char* pushPath = static_cast<char *>(args);// rtmp 推流器RTMP* rtmp = 0;// rtmp 推流数据包RTMPPacket *packet = 0;/*将推流核心执行内容放在 do while 循环中在出错后, 随时 break 退出循环, 执行后面的释放资源的代码可以保证, 在最后将资源释放掉, 避免内存泄漏避免执行失败, 直接 return, 导致资源没有释放*/do {// 1. 创建 RTMP 对象, 申请内存rtmp = RTMP_Alloc();if (!rtmp) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "申请 RTMP 内存失败");break;}// 2. 初始化 RTMPRTMP_Init(rtmp);// 设置超时时间 5 秒rtmp->Link.timeout = 5;// 3. 设置 RTMP 推流服务器地址int ret = RTMP_SetupURL(rtmp, pushPath);if (!ret) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "设置 RTMP 推流服务器地址 %s 失败", pushPath);break;}// 4. 启用 RTMP 写出功能RTMP_EnableWrite(rtmp);// 5. 连接 RTMP 服务器ret = RTMP_Connect(rtmp, 0);if (!ret) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "连接 RTMP 服务器 %s 失败", pushPath);break;}// 6. 连接 RTMP 流ret = RTMP_ConnectStream(rtmp, 0);if (!ret) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "连接 RTMP 流 %s 失败", pushPath);break;}// 准备推流相关的数据, 如线程安全队列readyForPush = TRUE;// 记录推流开始时间pushStartTime = RTMP_GetTime();// 线程安全队列开始工作packets.setWork(1);while (isStartRtmpPush) {// 从线程安全队列中// 取出一包已经打包好的 RTMP 数据包packets.pop(packet);// 确保当前处于推流状态if (!isStartRtmpPush) {break;}// 确保不会取出空的 RTMP 数据包if (!packet) {continue;}// 设置直播的流 IDpacket->m_nInfoField2 = rtmp->m_stream_id;// 7. 将 RTMP 数据包发送到服务器中ret = RTMP_SendPacket(rtmp, packet, 1);// RTMP 数据包使用完毕后, 释放该数据包if (packet) {RTMPPacket_Free(packet);delete packet;packet = 0;}if (!ret) {__android_log_print(ANDROID_LOG_INFO, "RTMP", "RTMP 数据包推流失败");break;}}}while (0);// 面的部分是收尾部分, 释放资源// 8. 推流结束, 关闭与 RTMP 服务器连接, 释放资源if(rtmp){RTMP_Close(rtmp);RTMP_Free(rtmp);}// 推流数据包 线程安全队列释放// 防止中途退出导致没有释放资源, 造成内存泄漏if (packet) {RTMPPacket_Free(packet);delete packet;packet = 0;}// 释放推流地址if(pushPath){delete pushPath;pushPath = 0;}return 0;
}

【Android RTMP】RTMPDump 推流过程 ( 独立线程推流 | 创建推流器 | 初始化操作 | 设置推流地址 | 启用写出 | 连接 RTMP 服务器 | 发送 RTMP 数据包 )相关推荐

  1. android 上传文件到 c 服务器,android 怎么样用socket给C服务器 发送二进制数据

    最近在写一个小APP,卡在android与服务器端通讯上了,大致需求是,android端发送二进制数据,文件头为0x00 0x08 0x0b,后面跟字符串的内容为abcd_efdgh, 结尾用0x00 ...

  2. 网络主机发送IP数据包过程

    主机发送IP数据包过程 当一个拥有公网IP的主机给其它主机发送IP数据包时,首先在IP首部封装自身IP地址(源IP)和目的主机的IP地址(目的IP),实现网络层数据的封装.接下来进行数据链路层封装,数 ...

  3. android四周阴影效果_帮助独立开发者轻松创建令人惊叹的产品视觉效果

    大家好,我是独立开发者Larry-作品发布.宣传交流过程中对作品进行样机包装或场景展示往往是不可或缺的,除了纯手工打造外,通常大家都会运用一些样机素材来完成对产品的快速包装,因此关于样机素材的资源网上 ...

  4. Android 向服务器发送XML数据及调用webservice

    首先讲一下webservice概念 可以看作是网络上的API,不过不是是通过new XXX().api()调用: 调用方式:客户端发送一段xml到服务器,在xml中指定要调用的方法的名称,以及各项参数 ...

  5. 【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 关键帧数据格式 | 非关键帧数据格式 | x264 编码后的数据处理 | 封装 H.264 视频数据帧 )

    文章目录 安卓直播推流专栏博客总结 一. x264 编码后的 H.264 数据帧 二. RTMP 协议中 关键帧 / 非关键帧 数据格式 说明 三. 判定 H.264 帧数据分隔符 四. 初始化 RT ...

  6. 【Android RTMP】音频数据采集编码 ( FAAC 编码器编码 AAC 音频采样数据 | 封装 RTMP 音频数据头 | 设置 AAC 音频数据类型 | 封装 RTMP 数据包 )

    文章目录 安卓直播推流专栏博客总结 一. FAAC 编码器编码 AAC 音频采样数据 二. 封装 RTMP 音频数据头 三. 封装 RTMP 音频数据类型 四. 拷贝 AAC 音频数据到 RTMPPa ...

  7. 【Android RTMP】音频数据采集编码 ( FAAC 编码器编码 AAC 音频解码信息 | 封装 RTMP 音频数据头 | 设置 AAC 音频数据类型 | 封装 RTMP 数据包 )

    文章目录 安卓直播推流专栏博客总结 一. FAAC 编码器编码 AAC 音频解码信息 二. 封装 RTMP 音频数据头 三. 封装 RTMP 音频数据类型 四. 拷贝 AAC 音频数据到 RTMPPa ...

  8. 【Android RTMP】x264 编码器初始化及设置 ( 获取 x264 编码参数 | 编码规格 | 码率 | 帧率 | B帧个数 | 关键帧间隔 | 关键帧解码数据 SPS PPS )

    文章目录 安卓直播推流专栏博客总结 一. x264 编码器参数设置引入 二. 获取 x264 编码器参数 三. 设置 x264 编码器编码规格 四. 设置 x264 编码器编码图像数据格式 五. 设置 ...

  9. 【Android RTMP】Android Camera 视频数据采集预览 ( 视频采集相关概念 | 摄像头预览参数设置 | 摄像头预览数据回调接口 )

    文章目录 安卓直播推流专栏博客总结 一. Android 端数据采集涉及到的相关概念 二. Camera 预览图像尺寸设置 三. 获取摄像头采集的数据格式 安卓直播推流专栏博客总结 Android R ...

最新文章

  1. tc写入txt成功却没有内容_挖洞经验 | 构造UserAgent请求头内容实现LFI到RCE提权
  2. nginx 开启ssi
  3. 做三年地方网站不如别人打一场麻将
  4. 腾讯面试题 linux下free命令详解
  5. mysql中char和text的区别_mysql中text与varchar与char的区别
  6. bool,_Bool和BOOL的区别
  7. twisted.internet.error.TimeoutError错误的解决方法
  8. [翻译] 使用 Serverless 和 .NET Core 构建飞速发展的架构
  9. c语言随机读写信息fetch,北京大学信息科学技术学院考试试卷-计算机系统导论-期中-2015(16页)-原创力文档...
  10. TensorFlow学习笔记03:单变量线性回归
  11. python装饰器怎么调用_Python学习22-外部程序的调用装饰器
  12. 小杜机器人线下店_阿里线下卖车已成事实,阿里造车还会远吗?
  13. hihocoder217周 树形DP
  14. Android应用自动更新功能代码实现
  15. 华为云的云计算比阿里云的云计算认证好吗?
  16. 鳄克斯系列服务器,英雄联盟:FPX冠军庆典活动进行中 一半宝石入手海科克斯系列皮肤...
  17. 怎么查二手小米手机价格
  18. 网易云音乐:从刷屏 IP 到音乐生活王国
  19. java 文件内容查找_java 查找一个TXT文件内容。
  20. Cloud IDEs For Web Developers – Best Of

热门文章

  1. 开启文件高级共享功能
  2. Android应用在不同版本间兼容性处理
  3. Android ListView 横向滑动删除 Item
  4. (收藏)Wp7开发中文网站
  5. TechEd China 2009 课程幻灯片和代码下载 - MBL311 SQL Server Compact 终极性能调校
  6. 【学习笔记】MHP-VOS: Multiple Hypotheses Propagation for Video Object Segmentation
  7. Kubernetes基本概念之Name和NameSpace
  8. MyEclipse中jsp编码设置
  9. Node版本管理nvm, npm
  10. 160613、MyBatis insert操作返回主键