当前位置: 首页 > news >正文

T31开发笔记:librtmp拉流测试

若该文为原创文章,转载请注明原文出处。

T31使用librtmp拉流并保存成FLV文件或H264和AAC文件。

librtmp编译在前面有教程,自行编译。

实现的目的是想要获取获取rtmp的AAC流并播放,实时双向对讲功能。

一、硬件和开发环境
1、硬件:T31X+SC5235 

2、开发环境: ubuntu16.04-64bit

3、编译器:mips-gcc540-glibc222-32bit-r3.3.0.tar.gz

注:板子和和WIFI模块是某淘上淘的,使用的是RTL8188,使用的是USB接口,uboot和内核是自己裁剪移植的,内核默认自带WIFI驱动,所以不用移植可以直接使用。
 

二、使用librtmp拉流流程

  1. 初使化RTMP上下文
  2. 设置拉流地址
  3. 连接服务器
  4. 连接流地址
  5. 循环拉流,提取媒体数据,保存为文件或者交给解码模块
  6. 拉流完毕,释放资源

三、代码解析

编译代码需要用到的库,本人在T31上使用的是静态库,可以自行改成动态库,以减少编译文件的大小 。

编译所需的库有:librtmp.a、libssl.a、libcrypto.a、libz.a

下面这个例子演示了使用librtmp库从服务器拉流到本地保存为flv文件或是h264和aac文件,代码如下:

/*!*******************************************************************************  Copyright ? 2017-2018 yifeng. All Rights Reserved.** \file      main.c* \author    yifeng* \version   1.0* \date      2022年3月3日* \brief     rtmp测试代码**----------------------------------------------------------------------------* \attention********************************************************************************//*****************************************************************************change history: 1.date  : 2022年3月3日author: yifengchange: create file*****************************************************************************/#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>/* 环形队列头文件 */
#include "xiecc_rtmp.h"
#include "rtmp.h"typedef unsigned long   ULONG;
typedef unsigned int    UINT;
typedef unsigned char   BYTE;
typedef char            _TCHAR;uint8_t nalu_header[4] = { 0x00, 0x00, 0x00, 0x01 };/*!* \fn     main* \brief  主函数*          * \param  [in] int argc       #* \param  [in] char *argv[]   #* * \retval int*/
int main(int argc, char *argv[])
{ uint16_t object_type = 0;uint16_t sample_frequency_index = 0;uint8_t  channels = 0;uint8_t  frame_length_flag = 0;uint8_t  depend_on_core_coder = 0;uint8_t  extension_flag = 0;// 初使化RTMP上下文RTMP* pRTMP = RTMP_Alloc();RTMP_Init(pRTMP);// 设置拉流地址RTMP_SetupURL(pRTMP, (char*)"rtmp://192.168.0.109/live/stream");// 连接服务器pRTMP->Link.timeout = 10;pRTMP->Link.lFlags |= RTMP_LF_LIVE;bool b = RTMP_Connect(pRTMP, NULL);if (!b){printf("connect failed! \n");return -1;}// 连接流地址b = RTMP_ConnectStream(pRTMP, 0);if (!b){printf("connect stream failed! \n");return -1;}bool bSaveFlv = false;  // 保存成FLV格式FILE *pFile = fopen("testrtmp.flv", "wb");FILE *h264_file_ptr = fopen("testrtmp.h264", "wb");FILE *aac_file_ptr = fopen("testrtmp.aac", "wb");while (RTMP_IsConnected(pRTMP)){if (bSaveFlv == true){char sBuf[4096] = {0};int bytes = RTMP_Read(pRTMP, sBuf, sizeof(sBuf));printf("RTMP_Read() ret:[%d] \n", bytes);if (bytes <= 0)break;fwrite(sBuf, 1, bytes, pFile);}else{RTMPPacket packet;RTMPPacket_Reset(&packet);packet.m_body = NULL;packet.m_chunk = NULL;b = RTMP_ReadPacket(pRTMP, &packet);if (b < 0)break;if (0 == b )continue;if (RTMPPacket_IsReady(&packet)){RTMP_ClientPacket(pRTMP, &packet);if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO){bool keyframe = 0x17 == packet.m_body[0] ? true : false;bool sequence = 0x00 == packet.m_body[1];printf("keyframe=%s, sequence=%s\n", keyframe ? "true" : "false", sequence ? "true" : "false");// SPS/PPS sequenceif (sequence){uint32_t offset = 10;uint32_t sps_num = packet.m_body[offset++] & 0x1f;for (int i = 0; i < sps_num; i++) {uint8_t ch0 = packet.m_body[offset];uint8_t ch1 = packet.m_body[offset + 1];uint32_t sps_len = ((ch0 << 8) | ch1);offset += 2;// Write sps dataprintf("Write sps data len: %d\n", sps_len);fwrite(nalu_header, sizeof(uint8_t), 4, h264_file_ptr);fwrite(packet.m_body + offset, sizeof(uint8_t), sps_len, h264_file_ptr);offset += sps_len;}uint32_t pps_num = packet.m_body[offset++] & 0x1f;for (int i = 0; i < pps_num; i++) {uint8_t ch0 = packet.m_body[offset];uint8_t ch1 = packet.m_body[offset + 1];uint32_t pps_len = ((ch0 << 8) | ch1);offset += 2;// Write pps dataprintf("Write pps data len: %d\n", pps_len);fwrite(nalu_header, sizeof(uint8_t), 4, h264_file_ptr);fwrite(packet.m_body + offset, sizeof(uint8_t), pps_len, h264_file_ptr);offset += pps_len;}}// Nalu frameselse{uint32_t offset = 5;uint8_t ch0 = packet.m_body[offset];uint8_t ch1 = packet.m_body[offset + 1];uint8_t ch2 = packet.m_body[offset + 2];uint8_t ch3 = packet.m_body[offset + 3];uint32_t data_len = ((ch0 << 24) | (ch1 << 16) | (ch2 << 8) | ch3);offset += 4;// Write nalu data(already started with '0x00,0x00,0x00,0x01')printf("Write nalu data len: %d\n", data_len);fwrite(nalu_header, sizeof(uint8_t), 4, h264_file_ptr);fwrite(packet.m_body + offset, sizeof(uint8_t), data_len, h264_file_ptr);offset += data_len;}}else if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO){bool sequence = 0x00 == packet.m_body[1];printf("sequence=%s\n", sequence ? "true" : "false");// AAC sequenceif (sequence) {uint8_t format = (packet.m_body[0] & 0xf0) >> 4;uint8_t samplerate = (packet.m_body[0] & 0x0c) >> 2;uint8_t sampledepth = (packet.m_body[0] & 0x02) >> 1;uint8_t type = packet.m_body[0] & 0x01;// sequence = packet.m_body[1];// AAC(AudioSpecificConfig)if (format == 10){uint8_t ch0 = packet.m_body[2];uint8_t ch1 = packet.m_body[3];uint16_t config = ((ch0 << 8) | ch1);object_type = (config & 0xF800) >> 11;sample_frequency_index = (config & 0x0780) >> 7;channels = (config & 0x78) >> 3;frame_length_flag = (config & 0x04) >> 2;depend_on_core_coder = (config & 0x02) >> 1;extension_flag = config & 0x01;}// Speex(Fix data here, so no need to parse...)else if (format == 11) {// 16 KHz, mono, 16bit/sampletype = 0;sampledepth = 1;samplerate = 4;}}// Audio frameselse {// ADTS(7 bytes) + AAC datauint32_t data_len = packet.m_nBodySize - 2 + 7;uint8_t adts[7];adts[0] = 0xff;adts[1] = 0xf1;adts[2] = ((object_type - 1) << 6) | (sample_frequency_index << 2) | (channels >> 2);adts[3] = ((channels & 3) << 6) + (data_len >> 11);adts[4] = (data_len & 0x7FF) >> 3;adts[5] = ((data_len & 7) << 5) + 0x1F;adts[6] = 0xfc;// Write audio framesprintf("Write audio frames len: %d\n", packet.m_nBodySize - 2);fwrite(adts, sizeof(uint8_t), 7, aac_file_ptr);fwrite(packet.m_body + 2, sizeof(uint8_t), packet.m_nBodySize - 2, aac_file_ptr);}}else if (packet.m_packetType == RTMP_PACKET_TYPE_INFO) {// TODO:// ...printf("RTMP_PACKET_TYPE_INFO1\n");}else {// TODO:// ...printf("RTMP_PACKET_TYPE_INFO2\n");}}RTMPPacket_Free(&packet);}}fclose(pFile);RTMP_Close(pRTMP);RTMP_Free(pRTMP);return 0;
}

定义了bSaveFlv标记,是否保存成flv文件,true保存成flv文件,否则保存成aac和h264,

rtmp的Url需要根据自己的服务器修改。

四、测试结果

执行后,在当前目录下生成264和aac文件

把文件复制到pc端用vlc播放

 使用librtmp拉流网友説会有问题,目前测试是正常,有遇到的网友麻烦告知一下。谢谢。

如有侵权,或需要完整代码,请及时联系博主。

相关文章:

T31开发笔记:librtmp拉流测试

若该文为原创文章&#xff0c;转载请注明原文出处。 T31使用librtmp拉流并保存成FLV文件或H264和AAC文件。 librtmp编译在前面有教程&#xff0c;自行编译。 实现的目的是想要获取获取rtmp的AAC流并播放&#xff0c;实时双向对讲功能。 一、硬件和开发环境 1、硬件&#xff1…...

2308C++概念化

原文 库 //概念化(需要C20) struct 可画 {void 画(小出流 &out) const {te::call([](auto const &s, auto &out)-> decltype(s.画(out)) { s.画(out); }, *this, out);} }; struct 方形 {void 画(小出流 &out) const { out << "方形"; } }…...

flutter开发实战-实现自定义按钮类似UIButton效果

flutter开发实战-实现自定义按钮类似UIButton效果 最近开发过程中需要实现一下UIButton效果的flutter按钮&#xff0c;这里使用的是监听手势点击事件。 一、GestureDetector GestureDetector属性定义 GestureDetector({super.key,this.child,this.onTapDown,this.onTapUp,t…...

深度优先搜索|1034, 1020, 1254

深度优先搜索|1034. 边界着色&#xff0c; 机器人的运动范围&#xff0c;529. 扫雷游戏 边界着色机器人的运动范围扫雷问题 边界着色 把这个题分段了&#xff0c;先找到包括 (row, col) 的连通分量&#xff0c;然后再去找符合条件的边界&#xff0c;找到以后涂上颜色就行。 c…...

都市信息供求网servlet+jsp新闻广告出售java源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 都市信息供求网servletjsp 系统1权限&#xff1a;管理…...

kubeadm init:failed to pull image registry.k8s.io/pause:3.6

错误信息&#xff1a; Unfortunately, an error has occurred: timed out waiting for the condition This error is likely caused by: - The kubelet is not running - The kubelet is unhealthy due to a misconfiguration of the node in some way…...

设计模式之简单工厂模式、工厂模式、抽象工厂模式

参考&#xff1a; 设计模式笔记 简单工厂模式 ● 将类的创建过程交给工厂类实现&#xff0c;如果需要一个类对象&#xff0c;则直接通过工厂创建一个类。 ● 简单工厂模式不符合开闭原则 ● 适用场景&#xff1a;工厂类负责创建的对象比较少&#xff1b;客户端只知道传入工厂…...

C# 控制台彩色深度打印 工具类

文章目录 前言Nuget 环境安装代码使用打印结果 总结 前言 有时候我们想要靠打印获得程序信息&#xff0c;因为Dubeg模式需要一点一点断点进入进出&#xff0c;但是我们觉得断点运行实在是太慢了&#xff0c;还是直接打印后找结果会好一点。 Nuget 环境安装 想自己写的话可以看…...

Pytorch Tensor维度变换方法

1.torch.reshape()、torch.view()可以调整Tensor的shape 2.torch.unsqueeze(index)可以为Tensor增加一个维度 3.squeeze()可以删减维度 4.expand&#xff08;&#xff09;扩展维度 5.repeat()维度重复&#xff0c;不常用 6.transpose(dim1, dim2)交换dim1与dim2&#xff0…...

微信小程序之点击文字文字自动转语音进行播放,微信小程序文字识别转语音播放

需求 一堆题目&#xff0c;题干需要在点击的时候进行语音朗读&#xff0c;不做音频上传&#xff0c;不然不便于维护 解决方案 点击查看微信官方文档&#xff1a;微信同声传译 使用流程 后台配置 mp.weixin.qq.com 设置 > 第三方设置 > 插件管理 小程序插件使用流…...

主动学习、半监督学习、它们之间的区别?

1、主动学习&#xff08;Active Learning&#xff09;&#xff1a; 含义&#xff1a; 有的时候&#xff0c;有类标的数据比较稀少而没有类标的数据是相当丰富的&#xff0c;但是对数据进行人工标注又非常昂贵&#xff0c;这时候&#xff0c;学习算法可以主动地提出一些标注请…...

linux快速安装Rabbitmq

linux快速安装Rabbitmq 准备yum仓库 # root执行rpm --import https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.ascrpm --import https://packagecloud.io/rabbitmq/erlang/gpgkeyrpm --import https://packagecloud.io/ra…...

spconv1.2.1库的编译与安装

SpConv是一个稀疏卷积库&#xff0c;在点云相关的深度学习算法中用的比较多。由于目前官方升级到了2.0&#xff0c;然而有些算法&#xff08;比如审稿人要我复现的Cylinder3D&#xff09;仍需要用到1.2.1版本&#xff0c;因此本人花了亿点点时间折腾了一下。。。 本机安装cuda…...

java+springboot+mysql企业邮件管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的企业邮件管理系统&#xff0c;系统包含超级管理员、管理员、员工角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理&#xff1b;员工管理&#xff1b;反馈管理&#xff1b;系统公告&#xff1b;个人…...

[CKA]考试之一个 Pod 封装多个容器

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 创建一个Pod&#xff0c;名字为kucc1&#xff0c;这个Pod包含4容器&#xff…...

iphone备份用什么软件?好用的苹果数据备份工具推荐!

众所周知&#xff0c;如果要将iPhone的数据跟电脑进行传输备份的话&#xff0c;我们需要用到iTunes这个pc工具。但是对于iTunes&#xff0c;不少人都反映这个软件比较难用&#xff0c;用不习惯。于是&#xff0c;顺应时代命运的iPhone备份同步工具就出现了。那iphone备份用什么…...

一语道破 python 迭代器和生成器

简而言之&#xff1a;迭代器是一个抽象化的概念&#xff0c;在python中表示访问数据集合中元素的一种方式&#xff1b;生成器也是一个抽象化的概念&#xff0c;在python 中&#xff0c;边循环边生成所需数据&#xff0c;是一种时间换空间的方法。从访问数据方式上来看&#xff…...

有哪些开源和非开源的项目管理工具?

开源和非开源项目管理工具各有其特点和优势。下面是一些常见的开源和非开源项目管理工具以及它们的简要介绍。 开源项目管理工具&#xff1a; OpenProject&#xff1a;OpenProject 是一个功能强大、易于使用的开源项目管理工具。它提供了项目计划、任务管理、团队协作、文档管…...

实战 01|「编写互动式界面」

前言 实践是最好的学习方式&#xff0c;技术也如此。 文章目录 前言一、功能需求&#xff08;一&#xff09;1、功能需求描述2、知识点3、布局与程序设计 二、功能需求&#xff08;二&#xff09;1、功能需求描述2、知识点1&#xff09;LinearLayout2&#xff09;RelativeLayou…...

开源社区寻找八月创作之星!你准备好了吗~

活动页面&#xff1a;https://openlab.cosmoplat.com/createStarCampaign-202308​​​​​​卡奥斯开源社区定位打造工业互联网行业顶级开源社区生态平台&#xff0c;为开发者、企业等用户提供代码托管、技术交流/共享、硬件认证/接入、培训认证、大赛活动等服务&#xff0c;目…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...