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: yifeng
      change: create file

*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/* 环形队列头文件 */
#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文件

T31开发笔记:librtmp拉流测试_第1张图片

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

T31开发笔记:librtmp拉流测试_第2张图片

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

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

你可能感兴趣的:(君正T31,笔记,linux,运维)