T31开发笔记:librtmp拉流测试

news2024/11/17 21:16:35

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

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: yifeng
      change: 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 sequence
                   if (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 data
                       printf("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 data
                       printf("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 frames
                   else
                   {
                     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 sequence
                   if (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/sample
                       type = 0;
                       sampledepth = 1;
                       samplerate = 4;
                     }
                   }
                   // Audio frames
                   else {
                    // ADTS(7 bytes) + AAC data
                    uint32_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 frames
                    printf("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拉流网友説会有问题,目前测试是正常,有遇到的网友麻烦告知一下。谢谢。

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

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

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

相关文章

Linux6.31 Kubernetes 二进制部署

文章目录 计算机系统5G云计算第二章 LINUX Kubernetes 部署一、二进制搭建 Kubernetes v1.201.操作系统初始化配置2.部署 etcd 集群3.Kubernetes 集群架构与组件4.部署 Master 组件5.部署 Worker Node 组件6.部署 CNI 网络组件——部署 flannel1&#xff09;K8S 中 Pod 网络通信…

Android 版本 对应的 API版本

Android 14&#xff08;开发者预览版&#xff09; 如需详细了解平台变更&#xff0c;请参阅 Android 14 文档。 Android 13&#xff08;API 级别 33&#xff09; 如需详细了解平台变更&#xff0c;请参阅 Android 13 文档。 Android 12&#xff08;API 级别 31、32&#xf…

《每天5分钟玩转kubernetes》读书笔记

笔记 概念 Pod是脆弱的&#xff0c;但应用是健壮的。 kubelet运行在Cluster所有节点上&#xff0c;负责启动Pod和容器。kubeadm用于初始化Cluster。kubectl是k8s命令行工具。通过kubectl可以部署和管理应用&#xff0c;查看各种资源&#xff0c;创建、删除和更新各种组件。 …

推荐一款非常简单实用的数据库连接工具Navicat Premium

Navicat Premium是一款非常实用的数据库连接工具&#xff0c;别再用HeidiSQL和idea自带的数据库连接了&#xff0c;看完这篇文章&#xff0c;赶紧把Navicat Premium用起来吧。 首先&#xff0c;需要获取Navicat Premium的安装包&#xff0c;可以通过以下网盘链接下载&#xff0…

谷歌联合CMU提出全新语义金字塔概念,无需额外训练使LLMs学会执行视觉任务

​ 论文链接&#xff1a;https://arxiv.org/abs/2306.17842 代码仓库&#xff1a;https://github.com/google-research/magvit/ 在目前的大模型社区中&#xff0c;发展较为成熟的当属以ChatGPT为代表的纯语言模型&#xff08;LLMs&#xff09;&#xff0c;以GPT-4为代表的多模态…

【大数据】ELK最简入门案例(带你进入ELK世界)

文章目录 1. 前言2. 安装3. 启动ELK启动Elasticsearch启动Kibana启动Logstash 4. 测试ELK环境 本文通过最简单纯正的案例带你入门ELK世界。 1. 前言 ELK是Elasticsearch、Logstash、Kibana的缩写&#xff0c;如果对Elasticsearch、Logstash、Kibana不是很了解&#xff0c;可以…

2023华数杯C题完整模型代码

华数杯C题完整论文模型代码已经完成&#xff0c;文末获取&#xff01; 母亲的心理健康状况对婴儿的成长和发展有重要的影响。本研究使用大数据分析方法&#xff0c;探索了母亲的心理健康状况、婴儿的行为特征以及婴儿的睡眠质量之间的相关性。我们采集了大量的数据&#xff0c;…

Python零基础入门(十一)——异常处理

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python入门专栏&#xff1a;《Python入门》欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; 码字不易&#xff0c;如果觉得文章不…

MS5182N/MS5189N——16bit、4/8 通道、200KSPS、 SAR 型 ADC

产品简述 MS5182N/MS5189N 是 4/8 通道、 16bit 、电荷再分配逐次 逼近型模数转换器。采用单电源供电。 MS5182N/MS5189N 内 部集成无失码的 16 位 SAR ADC 、低串扰多路复用器、内部低 漂移基准电压源 ( 可以选择 2.5 或 4.096 V) 、温度传感器、可选 择的单极…

Java 之LocalDateTime的介绍和使用

LocalDateTime是Java的日期和时间类之一&#xff0c;用于表示不带时区信息的日期时间。 LocalDateTime 没有时区&#xff0c; 所以也就不能用来直接获取时间戳LocalDateTime 是一个基于值得类&#xff0c; 所以该类的示例不是通过构造函数的方式进行创建 以下是一些关于Loca…

华为推出手机系统云翻新服务:什么是云翻新?如何使用?

华为手机系统云翻新是华为推出的一项功能&#xff0c;旨在通过云服务提供系统翻新的服务。它可以帮助用户对手机的系统进行优化和更新&#xff0c;以提高手机的性能和流畅度。具体而言&#xff0c;华为手机系统云翻新功能提供了免费的云空间&#xff0c;用户可以将手机中的系统…

【学习笔记】生成式AI(ChatGPT原理,大型语言模型)

ChatGPT原理剖析 语言模型 文字接龙 ChatGPT在测试阶段是不联网的。 ChatGPT背后的关键技术&#xff1a;预训练&#xff08;Pre-train&#xff09; 又叫自监督式学习&#xff08;Self-supervised Learning&#xff09;&#xff0c;得到的模型叫做基石模型&#xff08;Founda…

JavaScript【静态方法、实例方法/to类、实例方法/get类、实例方法/set类、Math与Date实操、 JS时间戳、日期互相转换】(九)

目录 Math对象_静态方法三 Date对象 Date对象_静态方法 Date对象_实例方法/to类 Date对象_实例方法/get类 Date对象_实例方法/set类 Math与Date实操 JS时间戳、日期互相转换 Math对象_静态方法三 Math.random() Math.random() 返回0到1之间的一个伪随机数&#xff0c;可…

python中几个有趣的函数和推导式

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 一、range()函数 1、range()通常用来做循环。 2、range()生成器的特性。 例子&#xff1a;假如range&#xff08;&#xff09;中使用的数值特别大&#xff0c;为100000000000000000000000000000&#xff1f; python解释…

同比增长50%!W/AR HUD赛道持续向好背后的变化

在智能座舱进入域控制器时代的同时&#xff0c;带来人机交互体验升级的HUD赛道&#xff0c;同样持续火热。 高工智能汽车研究院监测数据显示&#xff0c;2023年1-6月中国市场&#xff08;不含进出口&#xff09;乘用车前装标配W/AR HUD交付90.49万台&#xff0c;潜在选装规模6…

【深度学习_TensorFlow】梯度下降

写在前面 一直不太理解梯度下降算法是什么意思&#xff0c;今天我们就解开它神秘的面纱 写在中间 线性回归方程 如果要求出一条直线&#xff0c;我们只需知道直线上的两个不重合的点&#xff0c;就可以通过解方程组来求出直线 但是&#xff0c;如果我们选取的这两个点不在直…

使用 Amazon ECS Anywhere 在边缘部署 Amazon IoT Greengrass

1.概述 亚马逊云科技提供了完备的IoT服务能力&#xff0c;涵盖设备服务、连接和控制服务以及云端分析服务&#xff0c;是快速构建安全可靠、可扩展的 IoT 平台的常见选择。Amazon IoT Greengrass 边缘运行时和云服务&#xff0c;可帮助您在设备上构建、部署和管理 IoT 应用。A…

中小企业如何做好私域运营呢?

​通过在公域平台上进行引流到私域平台&#xff0c;流量一旦进来&#xff0c;后面再做活动就不需要进行推广的成本&#xff0c;从而进行多次复购。而在于公域平台&#xff0c;流量进来只是一次性&#xff0c;当它出去后可能就不会再记得你的这个产品或者这个店&#xff0c;即当…

NUEDC 2022 E - 声源定位跟踪系统

更好的阅读体验参考个人博客&#xff1a;NUEDC 2022 E | Framist’s Little House NUEDC 2022 E - 声源定位跟踪系统 省级大学生电子设计竞赛 一等奖作品 仓库地址&#xff1a;framist/NUEDC2022-E 求小星星♥(ˆ◡ˆԅ) fork from: framist/STemWinForHAL: 移植emWin与HAL库…

【React学习】—虚拟DOM两种创建方式(二)

【React学习】—虚拟DOM两种创建方式&#xff08;二&#xff09; 一、Hello React案例 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, init…