NDK开发汇总

文章目录

  • 一 初始化
  • 二 设置网络和推送数据
  • 三 转换和添加视频数据
  • 四 Demo

一 初始化

native-lib

void releasePackets(RTMPPacket *&packet) {if (packet) {RTMPPacket_Free(packet);delete packet;packet = 0;}
}void callback(RTMPPacket *packet) {if (packet) {packet->m_nTimeStamp = RTMP_GetTime() - start_time;packets.push(packet);}
}extern "C" JNIEXPORT void JNICALL
Java_com_cn_ray_rtmpdump_LivePusher_native_1init(JNIEnv *env, jobject instance) {//准备一个Video编码器的工具类 :进行编码videoChannel = new VideoChannel;//准备一个队列,打包好的数据 放入队列,在线程中统一的取出数据再发送给服务器videoChannel->setVideoCallback(callback);packets.setReleaseCallback(releasePackets);
}

二 设置网络和推送数据

native-lib


extern "C" JNIEXPORT void JNICALL
Java_com_cn_ray_rtmpdump_LivePusher_native_1setVideoEncInfo(JNIEnv *env, jobject instance,jint width, jint height, jint fps,jint bitrate) {if (videoChannel) {videoChannel->setVideoEncInfo(width, height, fps, bitrate);}
}void *start(void *args) {char *url = static_cast<char *>(args);RTMP *rtmp = 0;do {rtmp = RTMP_Alloc();if (!rtmp) {LOGE("rtmp创建失败");break;}RTMP_Init(rtmp);//设置超时时间 5srtmp->Link.timeout = 5;int ret = RTMP_SetupURL(rtmp, url);if (!ret) {LOGE("rtmp设置地址失败:%s", url);break;}//开启输出模式RTMP_EnableWrite(rtmp);ret = RTMP_Connect(rtmp, 0);if (!ret) {LOGE("rtmp连接地址失败:%s", url);break;}ret = RTMP_ConnectStream(rtmp, 0);if (!ret) {LOGE("rtmp连接流失败:%s", url);break;}//准备好了 可以开始推流了readyPushing = 1;//记录一个开始推流的时间start_time = RTMP_GetTime();packets.setWork(1);RTMPPacket *packet = 0;//循环从队列取包 然后发送while (isStart) {packets.pop(packet);if (!isStart) {break;}if (!packet) {continue;}// 给rtmp的流idpacket->m_nInfoField2 = rtmp->m_stream_id;//发送包 1:加入队列发送ret = RTMP_SendPacket(rtmp, packet, 1);releasePackets(packet);if (!ret) {LOGE("发送数据失败");break;}}releasePackets(packet);} while (0);if (rtmp) {RTMP_Close(rtmp);RTMP_Free(rtmp);}delete url;return 0;
}extern "C"
JNIEXPORT void JNICALL
Java_com_cn_ray_rtmpdump_LivePusher_native_1start(JNIEnv *env, jobject instance, jstring path_) {if (isStart) {return;}const char *path = env->GetStringUTFChars(path_, 0);char *url = new char[strlen(path) + 1];strcpy(url, path);isStart = 1;//启动线程pthread_create(&pid, 0, start, url);env->ReleaseStringUTFChars(path_, path);
}extern "C"
JNIEXPORT void JNICALL
Java_com_cn_ray_rtmpdump_LivePusher_native_1pushVideo(JNIEnv *env, jobject instance,jbyteArray data_) {if (!videoChannel || !readyPushing) {return;}jbyte *data = env->GetByteArrayElements(data_, NULL);videoChannel->encodeData(data);env->ReleaseByteArrayElements(data_, data, 0);
}

三 转换和添加视频数据

VideoChannel

//
// Created by PF0ZYBAJ on 2020-10-11.
//#include "VideoChannel.h"
#include "librtmp/rtmp.h"
#include "macro.h"VideoChannel::VideoChannel() {pthread_mutex_init(&mutex, 0);
}VideoChannel::~VideoChannel() {pthread_mutex_destroy(&mutex);if (videoCodec) {x264_encoder_close(videoCodec);videoCodec = 0;}if (pic_in) {x264_picture_clean(pic_in);delete pic_in;pic_in = 0;}
}void VideoChannel::setVideoEncInfo(int width, int height, int fps, int bitrate) {pthread_mutex_lock(&mutex);mWidth = width;mHeight = height;mFps = fps;mBitrate = bitrate;ySize = width * height;uvSize = ySize / 4;if (videoCodec) {x264_encoder_close(videoCodec);videoCodec = 0;}if (pic_in) {x264_picture_clean(pic_in);delete pic_in;pic_in = 0;}//打开x264编码器//x264编码器的属性x264_param_t param;//2: 最快//3:  无延迟编码x264_param_default_preset(&param, "ultrafast", "zerolatency");//base_line 3.2 编码规格param.i_level_idc = 32;//输入数据格式param.i_csp = X264_CSP_I420;param.i_width = width;param.i_height = height;//无b帧param.i_bframe = 0;//参数i_rc_method表示码率控制,CQP(恒定质量),CRF(恒定码率),ABR(平均码率)param.rc.i_rc_method = X264_RC_ABR;//码率(比特率,单位Kbps)param.rc.i_bitrate = bitrate / 1000;//瞬时最大码率param.rc.i_vbv_max_bitrate = bitrate / 1000 * 1.2;//设置了i_vbv_max_bitrate必须设置此参数,码率控制区大小,单位kbpsparam.rc.i_vbv_buffer_size = bitrate / 1000;//帧率param.i_fps_num = fps;param.i_fps_den = 1;param.i_timebase_den = param.i_fps_num;param.i_timebase_num = param.i_fps_den;
//    param.pf_log = x264_log_default2;//用fps而不是时间戳来计算帧间距离param.b_vfr_input = 0;//帧距离(关键帧)  2s一个关键帧param.i_keyint_max = fps * 2;// 是否复制sps和pps放在每个关键帧的前面 该参数设置是让每个关键帧(I帧)都附带sps/pps。param.b_repeat_headers = 1;//多线程param.i_threads = 1;x264_param_apply_profile(&param, "baseline");//打开编码器videoCodec = x264_encoder_open(&param);pic_in = new x264_picture_t;x264_picture_alloc(pic_in, X264_CSP_I420, width, height);pthread_mutex_unlock(&mutex);
}void VideoChannel::encodeData(int8_t *data) {//编码pthread_mutex_lock(&mutex);//将data 放入 pic_in//y数据memcpy(pic_in->img.plane[0], data, ySize);for (int i = 0; i < uvSize; ++i) {//间隔1个字节取一个数据//u数据*(pic_in->img.plane[1] + i) = *(data + ySize + i * 2 + 1);//v数据*(pic_in->img.plane[2] + i) = *(data + ySize + i * 2);}pic_in->i_pts = index++;//编码出的数据x264_nal_t *pp_nal;//编码出了几个 nalu (暂时理解为帧)int pi_nal;x264_picture_t pic_out;//编码int ret = x264_encoder_encode(videoCodec, &pp_nal, &pi_nal, pic_in, &pic_out);if (ret < 0) {pthread_mutex_unlock(&mutex);return;}int sps_len, pps_len;uint8_t sps[100];uint8_t pps[100];//for (int i = 0; i < pi_nal; ++i) {//数据类型if (pp_nal[i].i_type == NAL_SPS) {// 去掉 00 00 00 01sps_len = pp_nal[i].i_payload - 4;memcpy(sps, pp_nal[i].p_payload + 4, sps_len);} else if (pp_nal[i].i_type == NAL_PPS) {pps_len = pp_nal[i].i_payload - 4;memcpy(pps, pp_nal[i].p_payload + 4, pps_len);//拿到pps 就表示 sps已经拿到了sendSpsPps(sps, pps, sps_len, pps_len);} else {//关键帧、非关键帧sendFrame(pp_nal[i].i_type,pp_nal[i].i_payload,pp_nal[i].p_payload);}}pthread_mutex_unlock(&mutex);
}void VideoChannel::setVideoCallback(VideoCallback callback) {this->callback = callback;
}void VideoChannel::sendSpsPps(uint8_t *sps, uint8_t *pps, int sps_len, int pps_len) {RTMPPacket *packet = new RTMPPacket;int bodysize = 13 + sps_len + 3 + pps_len;RTMPPacket_Alloc(packet, bodysize);int i = 0;//固定头packet->m_body[i++] = 0x17;//类型packet->m_body[i++] = 0x00;//composition time 0x000000packet->m_body[i++] = 0x00;packet->m_body[i++] = 0x00;packet->m_body[i++] = 0x00;//版本packet->m_body[i++] = 0x01;//编码规格packet->m_body[i++] = sps[1];packet->m_body[i++] = sps[2];packet->m_body[i++] = sps[3];packet->m_body[i++] = 0xFF;//整个spspacket->m_body[i++] = 0xE1;//sps长度packet->m_body[i++] = (sps_len >> 8) & 0xff;packet->m_body[i++] = sps_len & 0xff;memcpy(&packet->m_body[i], sps, sps_len);i += sps_len;//ppspacket->m_body[i++] = 0x01;packet->m_body[i++] = (pps_len >> 8) & 0xff;packet->m_body[i++] = (pps_len) & 0xff;memcpy(&packet->m_body[i], pps, pps_len);//视频packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;packet->m_nBodySize = bodysize;//随意分配一个管道(尽量避开rtmp.c中使用的)packet->m_nChannel = 10;//sps pps没有时间戳packet->m_nTimeStamp = 0;//不使用绝对时间packet->m_hasAbsTimestamp = 0;packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;callback(packet);
}void VideoChannel::sendFrame(int type, int payload, uint8_t *p_payload) {//去掉 00 00 00 01 / 00 00 01if (p_payload[2] == 0x00){payload -= 4;p_payload += 4;} else if(p_payload[2] == 0x01){payload -= 3;p_payload += 3;}RTMPPacket *packet = new RTMPPacket;int bodysize = 9 + payload;RTMPPacket_Alloc(packet, bodysize);RTMPPacket_Reset(packet);
//    int type = payload[0] & 0x1f;packet->m_body[0] = 0x27;//关键帧if (type == NAL_SLICE_IDR) {LOGE("关键帧");packet->m_body[0] = 0x17;}//类型packet->m_body[1] = 0x01;//时间戳packet->m_body[2] = 0x00;packet->m_body[3] = 0x00;packet->m_body[4] = 0x00;//数据长度 int 4个字节 相当于把int转成4个字节的byte数组packet->m_body[5] = (payload >> 24) & 0xff;packet->m_body[6] = (payload >> 16) & 0xff;packet->m_body[7] = (payload >> 8) & 0xff;packet->m_body[8] = (payload) & 0xff;//图片数据memcpy(&packet->m_body[9],p_payload,  payload);packet->m_hasAbsTimestamp = 0;packet->m_nBodySize = bodysize;packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;packet->m_nChannel = 0x10;packet->m_headerType = RTMP_PACKET_SIZE_LARGE;callback(packet);
}

四 Demo

RTMPDump

NDK36_RTMP:视频编码器与推送视频数据相关推荐

  1. 服务器主动推送消息数据给客户端

    1 引言 这个问题第一次是我在实现一个导师的方案的时候所发现的,一开始我需要实现服务器与客户端的密钥协商和数据传递,服务器需要主动分发(推送)密钥给客户端,因为以前没有做过相关编码,后来只能想到用反向 ...

  2. windows ffmpeg 推送摄像头数据到rtmp服务

    文本主要讲述windows系统下如何利用ffmpeg获取摄像机流并推送到rtmp服务,命令的用法前文 中有讲到过,这次是通过代码来实现.实现该项功能的基本流程如下: 图1 ffmpeg推流流程图 较前 ...

  3. Gstreamer推送摄像头数据到RTMP服务器的方法(SRS成功示例)

    最近在研究如何将Linux中的视频数据推送到流媒体服务器,看到gstreamer还比较不错,于是试了一下 服务端用的SRS,RTMP推流,搭建非常简单,教程可参考:https://blog.csdn. ...

  4. 服务器之间数据文件推送,数据库数据推送到另外服务器

    数据库数据推送到另外服务器 内容精选 换一换 该方案优点是简单,容易上手,缺点是停机时间较长.因此它适用于数据量不大,或者允许停机的时间较长,并且在这个时间范围内能够完成的数据.由于云数据库RDS服务 ...

  5. 夺命雷公狗---微信开发26----客服消息接口基础和推送视频

    我们按照上一课的套路继续改写下index.php即可实现,代码如下所示: <?php /*** wechat php test*///define your token require_once ...

  6. 服务器微信推送,开启服务器消息推送后收不到微信推送的数据

    appId :wx221563f29cd20a25 在小程序的后台开启了消息推送按文档说的客服消息接入指引操作的 验证成功后未能收到微信服务器推送的post数据. 自己已经使用postman去测试了服 ...

  7. SpringCloud工作笔记067---消息推送_推送视频_推送图片_在通知栏里显示图片视频_自定义点击消息后的动作

    JAVA技术交流QQ群:170933152 http://docs.getui.com/getui/server/java/template/ 个推 ,这里有文档,看看就能搞,另外: 在hbuilde ...

  8. SSE实现后端向前页面推送实时数据,是长链接不是连接一次就断开

    新接触sse,从网上找了好多资料都是在后端retuen数据给页面,这样导致了页面的长链接就断开了,然后我就改了下它,如果有其他更简便的方式,请大佬们告诉我 直接上代码 后端接口: 后端我是集成了swa ...

  9. 以B站推流为例,运用ffmpeg推流的各种操作-3_# 安装ffmpeg Ubuntu云服务器用ffmpeg推送视频篇

    第一步 升级服务器安装工具 sudo apt-get update 第二步 安装ffmpeg sudo apt-get install ffmpeg 第三步:在winscp上登录云后将想要直播的文件传 ...

最新文章

  1. android功能网格布局,Visual Studio 开发安卓之布局-网格布局(GridLayout)
  2. Attribute的一个列子
  3. 将php源码修改成存在注入的源码,天融信关于ucms系统存在代码注入漏洞的分析...
  4. 【Java】Object基类
  5. 【Sentry】为什么Cloudera要创建Hadoop安全组件Sentry?
  6. 技术圈的女性工程师都去哪呢?
  7. BAT-局域网内在线电脑IP
  8. 3804. 构造字符串-AcWing题库
  9. java将学生的成绩按不同的分数段_Java练习题
  10. vue引用阿里云iconfont使用icon图标(elementUI图标太少)
  11. 基于微信小程序的比赛报名系统
  12. Windows下载执行命令大全
  13. 营业收费系统 建立报表库服务器,浅谈计算机在自来水收费系统的重要应用
  14. 修改登录密码html代码,html登录界面设置账号密码
  15. MATLAB最小矩形法边界法,最小外接矩形法.PPT
  16. java 小技巧_Java中有哪些好用的小技巧?
  17. 如何停止keepalived_systemctl无法停掉keepalived
  18. Django的多应用分布式路由
  19. PDF转Word方法大盘点:看了这一篇,就不用再找转换技巧了
  20. MeVisLab模块化医学图像处理可视化软件

热门文章

  1. 流浪者柯南自定义服务器,流放者柯南个人服务器修改怎么配置 个人服务器修改配置路径介绍...
  2. 两个JSON文件比较差异 (O_o)
  3. 中文/英文]VC6 sp6补丁下载|VS6 sp6补丁下载 [防VC6卡死]
  4. 2020-08-09周末实验-王鹏程
  5. 无人超市的核心是科技
  6. 虚拟机下安装anaconda3+python3.6+TensorFlow
  7. 20191021 练习:模拟+搜索+二分+AC自动机
  8. git:error: open(“.vs/xxb/v17/Browse.VC.opendb“): Permission denied
  9. 鸿蒙开源oppo,华为鸿蒙开源,OPPO公关黄宏涛被推上风口浪尖,网上一片哗然
  10. ZOJ 3527 树形DP(章鱼图DP)