通过libx246 libfaac转换推送RTMP音视频直播流

一、RTMP简介及rtmplib库:

        RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题。

简介:

  1. RTMP是应用层协议,采用(通常)TCP来保证可靠传输;
  2. 在TCP完成握手链接建立后,还会进行RTMP的一些自己的握手动作,在建立RTMP Connection链接;
  3. 在Connection链接上会传输一些控制信息,例如SetChunkSize,SetACKWindowSize。CreateStream命令会创建一个Stream链接,用于传输具体的音视频数据和控制这些信息传输的命令信息;
  4. RTMP在发送的时候会将数据格式化为Message,然后再将一个个Messsage分为带有Message ID 的Chunk(块),每个Chuck可能是一个完整的Message,可能是一个Message的一部分。在接收端会根据Message ID,长度与data将chunk还原为Message。

        雷霄骅的关于rtmp及rtmplib源码解析的地址。

rtmplib:

        关于外网的RTMPDump文档介绍了rtmp。

RTMPLIB库中重要方法:

  • RTMP_Alloc ():创建会话句柄;
  • RTMP_Init ():初始化rtmp;
  • RTMP_SetupURL():设置URL;
  • RTMP_Connect():建立网络连接;
  • RTMP_ConnectStream():建立连接rtmp流会话;
  • RTMP_Read():读取rtmp流,当返回0字节时,流已经完成。
  • RTMP_Write():发布流,客户端可以在RTMP_Connect () 调用之前 调用RTMP_EnableWrite () 发布流,然后在建立会话后使用 RTMP_Write ();
  • RTMP_Pause():在播放流时,暂停和取消暂停;
  • RTMP_Seek():移动流播放位置;
  • RTMP_Close():关闭流;
  • RTMP_Free():释放会话句柄;
  • RTMPPacket_Alloc():初始化RTMPPacket的内存空间;
  • RTMPPacket:RTMP协议封包;
  • RTMPPacket_Free():释放RTMPPacket的内存空间;

二、X264库简介:

        x264库中重要方法:

  • x264_param_default():给各个参数设置默认值;
  • x264_param_default_preset():设置默认的preset,内部调用了x264_param_apply_preset()和x264_param_apply_tune(),在它们之中即可找到各个preset和tune的详细参数区别;
  • x264_param_apply_profile():给定的文件配置,流的速率;
  • x264_encoder_open():用于打开编码器,其中初始化了libx264编码所需要的各种变量,必须使用x264_encoder_close()进行释放;
  • x264_picture_alloc():为图片开辟数据空间,必须使用x264_picture_clean()进行释放;
  • x264_encoder_encode():编码一帧YUV为H.264码流

        雷霄骅关于X264的源码分析。

        

三、FAAC库简介:

        FAAC是一个MPEG-4和MPEG-2的AAC编码器,其特性是:可移植性好,快速,支持LC/Main/LTP,通过Dream支持DRM,代码小相对于FFMPEG的AAC转码。

        libFaac库中重要方法:

  •  faacEncOpen():打开编码器;
  • faacEncGetCurrentConfiguration():获取配置;
  • faacEncSetConfiguration() :设置配置;
  • faacEncGetDecoderSpecificInfo():得到解码信息;
  • faacEncEnccode():对帧进行编码,并返回编码后的长度。

四、代码框架图及总览:

        Android层获取对应的视频(YUV)及音频(PCM)数据,传入C++层分别通过libx264把YUV转成h264数据、通过libfaac把PCM转成AAC数据。然后包装h264/pcm成RTMPPacket封装包,回调push到PacketQueue队列中,调到子线程RTMP_SendPacket     

五、代码实现细节解析:

        1、LiveManger.java类中获取CamerX视频YUV数据:

private void updateVideoCodecInfo(int degree) {camera2Helper.updatePreviewDegree(degree);if (mRtmpLivePusher != null) {int width = previewSize.getWidth();int height = previewSize.getHeight();if (degree == 90 || degree == 270) {int temp = width;width = height;height = temp;}mRtmpLivePusher.setVideoCodecInfo(width, height, videoFrameRate, videoBitRate);}}@Overridepublic void onPreviewFrame(byte[] yuvData) {
//        Log.e(TAG, "onPreviewFrame:"+yuvData.length);if (yuvData != null && isLiving && mRtmpLivePusher != null) {mRtmpLivePusher.pushVideoData(yuvData);}}

        2、VideoStreamPacket.cpp中设置libx264的设置属性及把YUV封装成H264:

        

void VideoStreamPacket::encodeVideo(int8_t *data) {
//    callbackStatusMsg("VideoStreamPacket encodeVideo",0);lock_guard<mutex> lock(m_mutex);if (!pic_in)return;//YUV420解析分离int offset = 0;memcpy(pic_in->img.plane[0], data, (size_t) m_frameLen); // yoffset += m_frameLen;memcpy(pic_in->img.plane[1], data + offset, (size_t) m_frameLen / 4); // uoffset += m_frameLen / 4;memcpy(pic_in->img.plane[2], data + offset, (size_t) m_frameLen / 4);  //v//YUV封装成H264x264_nal_t *pp_nal;int pi_nal;x264_picture_t pic_out;x264_encoder_encode(videoCodec, &pp_nal, &pi_nal, pic_in, &pic_out);int pps_len, sps_len = 0;uint8_t sps[100] = {0};uint8_t pps[100] = {0};//H264包装成RTMP流格式for (int i = 0; i < pi_nal; ++i) {x264_nal_t nal = pp_nal[i];if (nal.i_type == NAL_SPS) {sps_len = nal.i_payload - 4;memcpy(sps, nal.p_payload + 4, static_cast<size_t>(sps_len));} else if (nal.i_type == NAL_PPS) {pps_len = nal.i_payload - 4;memcpy(pps, nal.p_payload + 4, static_cast<size_t>(pps_len));sendSpsPps(sps, pps, sps_len, pps_len);} else {sendFrame(nal.i_type, nal.p_payload, nal.i_payload);}}}void VideoStreamPacket::encodeVideo(int8_t *data) {
//    callbackStatusMsg("VideoStreamPacket encodeVideo",0);lock_guard<mutex> lock(m_mutex);if (!pic_in)return;//YUV420解析分离int offset = 0;memcpy(pic_in->img.plane[0], data, (size_t) m_frameLen); // yoffset += m_frameLen;memcpy(pic_in->img.plane[1], data + offset, (size_t) m_frameLen / 4); // uoffset += m_frameLen / 4;memcpy(pic_in->img.plane[2], data + offset, (size_t) m_frameLen / 4);  //v//YUV封装成H264x264_nal_t *pp_nal;int pi_nal;x264_picture_t pic_out;x264_encoder_encode(videoCodec, &pp_nal, &pi_nal, pic_in, &pic_out);int pps_len, sps_len = 0;uint8_t sps[100] = {0};uint8_t pps[100] = {0};//H264包装成RTMP流格式for (int i = 0; i < pi_nal; ++i) {x264_nal_t nal = pp_nal[i];if (nal.i_type == NAL_SPS) {sps_len = nal.i_payload - 4;memcpy(sps, nal.p_payload + 4, static_cast<size_t>(sps_len));} else if (nal.i_type == NAL_PPS) {pps_len = nal.i_payload - 4;memcpy(pps, nal.p_payload + 4, static_cast<size_t>(pps_len));sendSpsPps(sps, pps, sps_len, pps_len);} else {sendFrame(nal.i_type, nal.p_payload, nal.i_payload);}}}

        3、LiveManger.java中获取AudioRecord的PCM数据:

 private void initAudio() {int minBufferSize = AudioRecord.getMinBufferSize(sampleRate,channelConfig, audioFormat) * 2;int bufferSizeInBytes = Math.max(minBufferSize, mRtmpLivePusher.getInputSamplesFromNative());if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {// TODO: Consider calling//    ActivityCompat#requestPermissions// here to request the missing permissions, and then overriding//   public void onRequestPermissionsResult(int requestCode, String[] permissions,//                                          int[] grantResults)// to handle the case where the user grants the permission. See the documentation// for ActivityCompat#requestPermissions for more details.return;}mRtmpLivePusher.setAudioCodecInfo(sampleRate, channelConfig);inputSamples = mRtmpLivePusher.getInputSamplesFromNative();mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate,channelConfig, audioFormat, bufferSizeInBytes);}class AudioTask implements Runnable {@Overridepublic void run() {mAudioRecord.startRecording();byte[] bytes = new byte[inputSamples];while (isLiving) {int len = mAudioRecord.read(bytes, 0, bytes.length);if (len > 0) {mRtmpLivePusher.pushAudioData(bytes);}}mAudioRecord.stop();}}

        4、AudioStreamPacket.cpp中设置libfaac的属性及把PCM封装成AAC:

int AudioStreamPacket::setAudioEncInfo(int samplesInHZ, int channels) {callbackStatusMsg("AudioStreamPacket setAudioEncInfo", 0);m_channels = channels;//open faac encoderm_audioCodec = faacEncOpen(static_cast<unsigned long>(samplesInHZ),static_cast<unsigned int>(channels),&m_inputSamples,&m_maxOutputBytes);m_buffer = new u_char[m_maxOutputBytes];//set encoder paramsfaacEncConfigurationPtr config = faacEncGetCurrentConfiguration(m_audioCodec);config->mpegVersion = MPEG4;    //设置版本,录制MP4文件时要用MPEG4config->aacObjectType = LOW;     //编码类型config->inputFormat = FAAC_INPUT_16BIT;     //输入数据类型config->outputFormat = 0;return faacEncSetConfiguration(m_audioCodec, config);
}void AudioStreamPacket::encodeData(int8_t *data) {
//    callbackStatusMsg("AudioStreamPacket encodeData", 0);//encode a frame, and return encoded lenint byteLen = faacEncEncode(m_audioCodec, reinterpret_cast<int32_t *>(data),static_cast<unsigned int>(m_inputSamples),m_buffer,static_cast<unsigned int>(m_maxOutputBytes));if (byteLen > 0) {int bodySize = 2 + byteLen;auto *packet = new RTMPPacket();RTMPPacket_Alloc(packet, bodySize);//stereopacket->m_body[0] = 0xAF;if (m_channels == 1) {packet->m_body[0] = 0xAE;}packet->m_body[1] = 0x01;memcpy(&packet->m_body[2], m_buffer, static_cast<size_t>(byteLen));packet->m_hasAbsTimestamp = 0;packet->m_nBodySize = bodySize;packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;packet->m_nChannel = 0x11;packet->m_headerType = RTMP_PACKET_SIZE_LARGE;if (mContext != nullptr && mAudioCallback != nullptr) {mAudioCallback(mContext, packet);}}
}

        5、RtmpPusherManger.cpp分别把AudioStreamPacket.cpp和VideoStreamPacket.cpp回调的RTMPPacket包,然后传入到RtmpInit.cpp。包装的RTMPPacket包push到队列PacketQueue中,

void RtmpPusherManger::initVideoPacket() {RtmpStatusMessage(this, "initVideoPacket", 0);if (videoStreamPacket == nullptr) {videoStreamPacket = new VideoStreamPacket();}videoStreamPacket->setRtmpStatusCallback(this, RtmpStatusMessage);videoStreamPacket->setVideoCallback(this, callbackRtmpPacket);
}void RtmpPusherManger::initAudioPacket() {RtmpStatusMessage(this, "initAudioPacket", 0);if (audioStreamPacket == nullptr) {audioStreamPacket = new AudioStreamPacket();}audioStreamPacket->setRtmpStatusCallback(this, RtmpStatusMessage);audioStreamPacket->setAudioCallback(this, callbackRtmpPacket);
}void RtmpPusherManger::callbackRtmpPacket(void *context, RTMPPacket *packet) {if (context != nullptr && packet != nullptr) {RtmpPusherManger *pFmpegManger = static_cast<RtmpPusherManger *>(context);pFmpegManger->addRtmpPacket(packet);}}void RtmpPusherManger::addRtmpPacket(RTMPPacket *packet) {if (rtmpInit == nullptr) return;rtmpInit->addRtmpPacket(packet);
}

        6、RtmpInit.cpp中从RtmpPusherManger.cpp传入添加的RTMPPacket包,push到队列PacketQueue中。在子线程中初始化RTMP相关操作成功后不断从PacketQueue中pop出RTMPPacket,并最终把RTMPPacket在RTMP_SendPacket发送出去。

void RtmpInit::addRtmpPacket(RTMPPacket *packet) {
//    callbackStatusMsg("Rtmp addRtmpPacket", 0);packet->m_nTimeStamp = RTMP_GetTime() - start_time;packetQueue.push(packet);
}void RtmpInit::startThread() {callbackStatusMsg("Rtmp startThread", 0);LOGE("murl:%s", mUrl);char *url = const_cast<char *>(mUrl);RTMP *rtmp = 0;do {rtmp = RTMP_Alloc();if (!rtmp) {callbackStatusMsg("Rtmp create fail", -1);break;}RTMP_Init(rtmp);rtmp->Link.timeout = 5;int ret = RTMP_SetupURL(rtmp, url);if (!ret) {callbackStatusMsg("Rtmp SetupURL fail", ret);break;}//开启输出模式RTMP_EnableWrite(rtmp);ret = RTMP_Connect(rtmp, 0);if (!ret) {callbackStatusMsg("rtmp连接地址失败", ret);break;}ret = RTMP_ConnectStream(rtmp, 0);if (!ret) {callbackStatusMsg("rtmp连接流失败", ret);break;}//start pushingisPushing = true;packetQueue.setRunning(true);//获取音频的首帧值if (mContext != nullptr) {mGetAudioTagCallback(mContext);}RTMPPacket *packet = nullptr;while (isPushing) {packetQueue.pop(packet);if (!isPushing) {break;}if (!packet) {continue;}packet->m_nInfoField2 = rtmp->m_stream_id;ret = RTMP_SendPacket(rtmp, packet, 1);releasePackets(packet);if (!ret) {LOGE("RTMP_SendPacket fail...");callbackStatusMsg("RTMP_SendPacket fail...", -2);break;}}releasePackets(packet);} while (0);isPushing = false;packetQueue.setRunning(false);packetQueue.clear();//释放rtmpif (rtmp) {RTMP_Close(rtmp);RTMP_Free(rtmp);}delete url;
}

      7、 Github的项目地址:https://github.com/wangyongyao1989/WyFFmpeg/tree/main/rtmplive

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/48092.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【体外诊断】ARM/X86+FPGA嵌入式计算机在免疫分析设备中的应用

体外诊断 信迈提供基于Intel平台、AMD平台、NXP平台的核心板、2.5寸主板、Mini-ITX主板、4寸主板、PICO-ITX主板&#xff0c;以及嵌入式准系统等计算机硬件。产品支持GAHDMI等独立双显&#xff0c;提供丰富串口、USB、GPIO、PCIe扩展接口等I/O接口&#xff0c;扩展性强&#xf…

CH03_布局

第3章&#xff1a;布局 本章目标 理解布局的原则理解布局的过程理解布局的容器掌握各类布局容器的运用 理解 WPF 中的布局 WPF 布局原则 ​ WPF 窗口只能包含单个元素。为在WPF 窗口中放置多个元素并创建更贴近实用的用户男面&#xff0c;需要在窗口上放置一个容器&#x…

【性能优化】在大批量数据下使用 HTML+CSS实现走马灯,防止页面卡顿

切换效果 页面结构变化 1.需求背景 项目首页存有一个小的轮播模块,保密原因大概只能这么展示,左侧图片右侧文字,后端一次性返回几百条数据(开发环境下,生产环境只会更多).无法使用分页解决,前端需要懒加载防止页面卡顿 写个小demo演示,如下 2.解决思路 获取到数据后,取第一…

开源模型应用落地-FastAPI-助力模型交互-进阶篇(四)

一、前言 FastAPI 的高级用法可以为开发人员带来许多好处。它能帮助实现更复杂的路由逻辑和参数处理&#xff0c;使应用程序能够处理各种不同的请求场景&#xff0c;提高应用程序的灵活性和可扩展性。 在数据验证和转换方面&#xff0c;高级用法提供了更精细和准确的控制&#…

科技赋能,智慧粮仓视频综合管理方案助力粮食安全

一、背景需求 随着科技的快速发展&#xff0c;智慧化、智能化管理已成为各行各业的重要发展方向。粮食仓储作为国家粮食安全战略的重要组成部分&#xff0c;其管理的科学性和智能化水平直接关系到粮食的存储安全、品质保障和运营效率。 因此&#xff0c;TSINGSEE青犀提出一套…

Linux文件和目录常用命令

1.操作命令 查看目录内容 ls 切换目录 cd 创建和删除操作 touch rm mkdir 拷贝和移动文件 cp mv 查看文件内容 cat more grep 其他 echo 重定向 > 和 >> 管道 | 1.1 终端实用技巧 1>自动补全 在敲出 文件/目录/命令 的前几个字母之后&#xff0c;按下…

pdf怎么压缩的小一点?PDF压缩变小的6种方法(2024全新)

pdf怎么压缩的小一点&#xff1f;首先&#xff0c;PDF文件可以进行压缩。职场文档传阅还是比较建议PDF压缩&#xff0c;PDF文件可以无障碍访问&#xff0c;保持原始文本、图像和表格&#xff0c;无需担心展示效果差异等等优势&#xff0c;成为我们日常工作中不可或缺的一部分。…

阿里云盾占用资源的问题AliYunDun,AliYunDunUpdate

目录 1.关闭AliYunDunUpdate&#xff0c;AliYunDun&#xff0c;AliYunDunMonitor。 2.发现报错如下 3.打开阿里云安全中心控制台 4.成功解决 2.开启云盾命令 “如果您在解决类似问题时也遇到了困难&#xff0c;希望我的经验分享对您有所帮助。如果您有任何疑问或者想分享您…

哪种SSL证书可以快速签发保护http安全访问?

用户访问网站&#xff0c;经常会遇到访问http网页时&#xff0c;提示网站不安全或者不是私密连接的提示&#xff0c;因为http是使用明文传输&#xff0c;数据传输中可能被篡改&#xff0c;数据不被保护&#xff0c;通常需要SSL证书来给数据加密。 SSL证书的签发速度&#xff0…

使用JWT双令牌机制进行接口请求鉴权

在前后端分离的开发过程中&#xff0c;前端发起请求&#xff0c;调用后端接口&#xff0c;后端在接收请求时&#xff0c;首先需要对收到的请求鉴权&#xff0c;在这种情况先我们可以采用JWT机制来鉴权。 JWT有两种机制&#xff0c;单令牌机制和双令牌机制。 单令牌机制服务端…

环信IM x 亚马逊云科技,助力出海企业实现可靠通讯服务

随着全球化进程的加速&#xff0c;越来越多的企业选择出海&#xff0c;拓展国际市场。然而&#xff0c;面对不同国家和地区的用户&#xff0c;企业在即时通讯方面遇到了诸多挑战。为了帮助企业克服这些困难&#xff0c;环信IM与亚马逊云科技强强联手&#xff0c;共同推出了一套…

LDR6020:重塑iPad一体式有线键盘体验的创新力量

在移动办公与娱乐日益融合的时代&#xff0c;iPad凭借其强大的性能和便携性&#xff0c;成为了众多用户不可或缺的生产力工具。然而&#xff0c;为了进一步提升iPad的使用体验&#xff0c;一款高效、便捷的键盘成为了不可或缺的配件。今天&#xff0c;我们要介绍的&#xff0c;…

关于黑马商城微服务拆分

1.拆分流程 大差不差分为 创建module-依赖-启动类-配置yml文件-抽取代码-数据库-配置启动项-测试 2.微服务的好处 在测试的时候明显感觉到微服务的好处 不用启动所有的项目 只是单纯一个模块比如支付就可以自己调试 非常便捷而且易开发 抽取的公共模块api也不用启动就能测试 …

免费缺陷管理工具深度评测与使用心得

国内外主流的10款缺陷跟踪工具对比&#xff1a;PingCode、Worktile、滴答清单、CalendarTask、专注清单、Todo清单、Jira、Bugzilla、MantisBT、Redmine。 在寻找合适的缺陷管理工具时&#xff0c;很多团队面临一个共同的挑战&#xff1a;如何在有限的预算内找到既高效又易于使…

深入浅出WebRTC—ALR

ALR&#xff08;Application Limited Region&#xff09;指的是网络传输过程中&#xff0c;由于应用层的限制&#xff08;而非网络拥塞&#xff09;导致带宽未被充分利用的情况。在这种情况下&#xff0c;应用层可能因为处理能力、手动配置或其他因素无法充分利用可用带宽&…

Spring Authorization Server 自定义 OAuth2 密码模式返回数据结构优化

前言 对接了自定义密码模式&#xff0c;但是返回的数据结构不符合要求 我们需要改成下面格式 开始 我假设你已经对接好了自定义密码功能&#xff0c;不会的话看下面文章 Spring Authorization Server 1.1 扩展实现 OAuth2 密码模式与 Spring Cloud 的整合实战&#xff08;上…

像 MvvmLight 一样使用 CommunityToolkit.Mvvm 工具包

文章目录 简介一、安装工具包二、实现步骤1.按照MvvmLight 的结构创建对应文件夹和文件2.编辑 ViewModelLocator3.引用全局资源二、使用详情1.属性2.命令3. 消息通知4. 完整程序代码展示运行结果简介 CommunityToolkit.Mvvm 包(又名 MVVM 工具包,以前称为 Microsoft.Toolkit…

[数据集][目标检测]拐杖检测数据集VOC+YOLO格式2778张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2778 标注数量(xml文件个数)&#xff1a;2778 标注数量(txt文件个数)&#xff1a;2778 标注…

【iOS】—— isMemberOfClass isKindOfClass以及源码

【iOS】—— isMemberOfClass & isKindOfClass以及源码 isa指针示例源码解析&#xff1a;isKindOfClass&#xff1a;源码解析&#xff08;实例方法和类方法&#xff09;isMemberOfClass&#xff1a;源码解析&#xff08;实例方法和类方法&#xff09;源码分析总结&#xff…

【中项】系统集成项目管理工程师-第2章 信息技术发展-2.2新一代信息技术及应用-2.2.3大数据与2.2.4区块链

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…