最简单的基于librtmp的示例:接收(RTMP保存为FLV)

来源:互联网 发布:松下plc软件序列号 编辑:程序博客网 时间:2024/05/17 22:43

=====================================================

最简单的基于libRTMP的示例系列文章列表:

最简单的基于librtmp的示例:接收(RTMP保存为FLV)

最简单的基于librtmp的示例:发布(FLV通过RTMP发布)

最简单的基于librtmp的示例:发布H.264(H.264通过RTMP发布)

=====================================================


本文记录一个基于libRTMP的发布流媒体的程序:Simplest libRTMP Send FLV。该程序可以将本地FLV文件发布到RTMP流媒体服务器。是最简单的基于libRTMP的流媒体发布示例。

 

流程图


使用librtmp发布RTMP流的可以使用两种API:RTMP_SendPacket()和RTMP_Write()。使用RTMP_SendPacket()发布流的时候的函数执行流程图如下图所示。使用RTMP_Write()发布流的时候的函数执行流程图相差不大。
 

流程图中关键函数的作用如下所列:

InitSockets():初始化Socket
RTMP_Alloc():为结构体“RTMP”分配内存。
RTMP_Init():初始化结构体“RTMP”中的成员变量。
RTMP_SetupURL():设置输入的RTMP连接的URL。
RTMP_EnableWrite():发布流的时候必须要使用。如果不使用则代表接收流。
RTMP_Connect():建立RTMP连接,创建一个RTMP协议规范中的NetConnection。
RTMP_ConnectStream():创建一个RTMP协议规范中的NetStream。
Delay:发布流过程中的延时,保证按正常播放速度发送数据。
RTMP_SendPacket():发送一个RTMP数据RTMPPacket。
RTMP_Close():关闭RTMP连接。
RTMP_Free():释放结构体“RTMP”。
CleanupSockets():关闭Socket。
 

源代码

源代码中包含了使用两种API函数RTMP_SendPacket()和RTMP_Write()发布流媒体的源代码,如下所示。
[cpp] view plain copy
  1. /** 
  2.  * Simplest Librtmp Send FLV 
  3.  * 
  4.  * 雷霄骅,张晖 
  5.  * leixiaohua1020@126.com 
  6.  * zhanghuicuc@gmail.com 
  7.  * 中国传媒大学/数字电视技术 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  * 
  11.  * 本程序用于将FLV格式的视音频文件使用RTMP推送至RTMP流媒体服务器。 
  12.  * This program can send local flv file to net server as a rtmp live stream. 
  13.  */  
  14.    
  15. #include <stdio.h>  
  16. #include <stdlib.h>  
  17. #include <string.h>  
  18. #include <stdint.h>  
  19. #ifndef WIN32  
  20. #include <unistd.h>  
  21. #endif  
  22.    
  23.    
  24. #include "librtmp/rtmp_sys.h"  
  25. #include "librtmp/log.h"  
  26.    
  27. #define HTON16(x)  ((x>>8&0xff)|(x<<8&0xff00))  
  28. #define HTON24(x)  ((x>>16&0xff)|(x<<16&0xff0000)|(x&0xff00))  
  29. #define HTON32(x)  ((x>>24&0xff)|(x>>8&0xff00)|\  
  30.          (x<<8&0xff0000)|(x<<24&0xff000000))  
  31. #define HTONTIME(x) ((x>>16&0xff)|(x<<16&0xff0000)|(x&0xff00)|(x&0xff000000))  
  32.    
  33. /*read 1 byte*/  
  34. int ReadU8(uint32_t *u8,FILE*fp){  
  35.          if(fread(u8,1,1,fp)!=1)  
  36.                    return 0;  
  37.          return 1;  
  38. }  
  39. /*read 2 byte*/  
  40. int ReadU16(uint32_t *u16,FILE*fp){  
  41.          if(fread(u16,2,1,fp)!=1)  
  42.                    return 0;  
  43.          *u16=HTON16(*u16);  
  44.          return 1;  
  45. }  
  46. /*read 3 byte*/  
  47. int ReadU24(uint32_t *u24,FILE*fp){  
  48.          if(fread(u24,3,1,fp)!=1)  
  49.                    return 0;  
  50.          *u24=HTON24(*u24);  
  51.          return 1;  
  52. }  
  53. /*read 4 byte*/  
  54. int ReadU32(uint32_t *u32,FILE*fp){  
  55.          if(fread(u32,4,1,fp)!=1)  
  56.                    return 0;  
  57.          *u32=HTON32(*u32);  
  58.          return 1;  
  59. }  
  60. /*read 1 byte,and loopback 1 byte at once*/  
  61. int PeekU8(uint32_t *u8,FILE*fp){  
  62.          if(fread(u8,1,1,fp)!=1)  
  63.                    return 0;  
  64.          fseek(fp,-1,SEEK_CUR);  
  65.          return 1;  
  66. }  
  67. /*read 4 byte and convert to time format*/  
  68. int ReadTime(uint32_t *utime,FILE*fp){  
  69.          if(fread(utime,4,1,fp)!=1)  
  70.                    return 0;  
  71.          *utime=HTONTIME(*utime);  
  72.          return 1;  
  73. }  
  74.    
  75. int InitSockets()  
  76. {  
  77.          WORD version;  
  78.          WSADATA wsaData;  
  79.          version=MAKEWORD(2,2);  
  80.          return (WSAStartup(version, &wsaData) == 0);  
  81. }  
  82.    
  83. void CleanupSockets()  
  84. {  
  85.          WSACleanup();  
  86. }  
  87.    
  88. //Publish using RTMP_SendPacket()  
  89. int publish_using_packet(){  
  90.          RTMP *rtmp=NULL;                             
  91.          RTMPPacket *packet=NULL;  
  92.          uint32_t start_time=0;  
  93.          uint32_t now_time=0;  
  94.          //the timestamp of the previous frame  
  95.          long pre_frame_time=0;  
  96.          long lasttime=0;  
  97.          int bNextIsKey=1;  
  98.          uint32_t preTagsize=0;  
  99.           
  100.          //packet attributes  
  101.          uint32_t type=0;                          
  102.          uint32_t datalength=0;             
  103.          uint32_t timestamp=0;             
  104.          uint32_t streamid=0;                          
  105.    
  106.          FILE*fp=NULL;  
  107.          fp=fopen("cuc_ieschool.flv","rb");  
  108.          if (!fp){  
  109.                    RTMP_LogPrintf("Open File Error.\n");  
  110.                    CleanupSockets();  
  111.                    return -1;  
  112.          }  
  113.    
  114.          /* set log level */  
  115.          //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;  
  116.          //RTMP_LogSetLevel(loglvl);  
  117.                     
  118.          if (!InitSockets()){  
  119.                    RTMP_LogPrintf("Init Socket Err\n");  
  120.                    return -1;  
  121.          }  
  122.    
  123.          rtmp=RTMP_Alloc();  
  124.          RTMP_Init(rtmp);  
  125.          //set connection timeout,default 30s  
  126.          rtmp->Link.timeout=5;                        
  127.          if(!RTMP_SetupURL(rtmp,"rtmp://localhost/live/livestream"))  //该地址要对应自己的rtmp服务器的配置
  128.          {  
  129.                    RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");  
  130.                    RTMP_Free(rtmp);  
  131.                    CleanupSockets();  
  132.                    return -1;  
  133.          }  
  134.           
  135.          //if unable,the AMF command would be 'play' instead of 'publish'  
  136.          RTMP_EnableWrite(rtmp);       
  137.           
  138.          if (!RTMP_Connect(rtmp,NULL)){  
  139.                    RTMP_Log(RTMP_LOGERROR,"Connect Err\n");  
  140.                    RTMP_Free(rtmp);  
  141.                    CleanupSockets();  
  142.                    return -1;  
  143.          }  
  144.           
  145.          if (!RTMP_ConnectStream(rtmp,0)){  
  146.                    RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");  
  147.                    RTMP_Close(rtmp);  
  148.                    RTMP_Free(rtmp);  
  149.                    CleanupSockets();  
  150.                    return -1;  
  151.          }  
  152.    
  153.          packet=(RTMPPacket*)malloc(sizeof(RTMPPacket));  
  154.          RTMPPacket_Alloc(packet,1024*64);  //为RTMPPacket的数据分配空间
  155.          RTMPPacket_Reset(packet);  
  156.    
  157.          packet->m_hasAbsTimestamp = 0; //是否有绝对时间戳         
  158.          packet->m_nChannel = 0x04; //channel id
  159.          packet->m_nInfoField2 = rtmp->m_stream_id;  //??
  160.    
  161.          RTMP_LogPrintf("Start to send data ...\n");  
  162.           
  163.          //jump over FLV Header,FLV格式头部占9个字节
  164.          fseek(fp,9,SEEK_SET);       
  165.          //jump over previousTagSizen  
  166.          fseek(fp,4,SEEK_CUR);     
  167.          start_time=RTMP_GetTime();  
  168.          while(1) // ???????????
  169.          {  
  170.                    if((((now_time=RTMP_GetTime())-start_time)  
  171.                               <(pre_frame_time)) && bNextIsKey){          
  172.                             //wait for 1 sec if the send process is too fast  
  173.                             //this mechanism is not very good,need some improvement  
  174.                             if(pre_frame_time>lasttime){  
  175.                                      RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time);  
  176.                                      lasttime=pre_frame_time;  
  177.                             }  
  178.                             Sleep(1000);  
  179.                             continue;  
  180.                    }  
  181.                     
  182.                    //not quite the same as FLV spec  
  183.                    if(!ReadU8(&type,fp)) //报头类型1个字节     
  184.                             break;  
  185.                    if(!ReadU24(&datalength,fp))  //数据部分的长度3个字节
  186.                             break;  
  187.                    if(!ReadTime(×tamp,fp))  //时间戳是占3个字节的,但是这里居然是4字节,我也困惑
  188.                             break;  
  189.                    if(!ReadU24(&streamid,fp)) //流ID占3字节 ,但这里居然是3字节,感觉这两个写反了,所以才不至于造成重大错误。
  190.                             break;  
  191.    
  192.                    if (type!=0x08&&type!=0x09){  //8表示音频包,9表示视频,都不是的情况表明该包是另一包的剩余数据,按道理这种包的包头为字节,这里却偏移了4字节,实在不解。
  193.                             //jump over non_audio and non_video frame,  
  194.                             //jump over next previousTagSizen at the same time  
  195.                             fseek(fp,datalength+4,SEEK_CUR);  
  196.                             continue;  
  197.                    }  
  198.                     
  199.                    if(fread(packet->m_body,1,datalength,fp)!=datalength)  
  200.                             break;  
  201.    //packet是rtmp的数据包,相当于协议数据单元PDU,下面是给该数据包的包头各个字段赋值,关于rtmp数据包的结构可参看我的其他文章:
  202. //http://blog.csdn.net/handsomehong/article/details/73743077
  203.                    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;  
  204.                    packet->m_nTimeStamp = timestamp;  
  205.                    packet->m_packetType = type;  
  206.                    packet->m_nBodySize  = datalength;  
  207.                    pre_frame_time=timestamp;  //将本帧的时间戳赋给记录上一帧时间戳的变量,因为本帧在下次就变成了上一帧了。
  208.    
  209.                    if (!RTMP_IsConnected(rtmp)){  //判断rtmp连接是否还在
  210.                             RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n");  
  211.                             break;  
  212.                    }  
  213.                    if (!RTMP_SendPacket(rtmp,packet,0)){  //发送一个packet到服务器
  214.                             RTMP_Log(RTMP_LOGERROR,"Send Error\n");  
  215.                             break;  
  216.                    }  
  217.    
  218.                    if(!ReadU32(&preTagsize,fp))  
  219.                             break;  
  220.                              
  221.                    if(!PeekU8(&type,fp))  
  222.                             break;  
  223.                    if(type==0x09){  
  224.                             if(fseek(fp,11,SEEK_CUR)!=0)  
  225.                                      break;  
  226.                             if(!PeekU8(&type,fp)){  
  227.                                      break;  
  228.                             }  
  229.                             if(type==0x17)  
  230.                                      bNextIsKey=1;  
  231.                             else  
  232.                                      bNextIsKey=0;  
  233.    
  234.                             fseek(fp,-11,SEEK_CUR);  
  235.                    }  
  236.          }                 
  237.    
  238.          RTMP_LogPrintf("\nSend Data Over\n");  
  239.           
  240.          if(fp)  
  241.                    fclose(fp);  
  242.    
  243.          if (rtmp!=NULL){  
  244.                    RTMP_Close(rtmp);          
  245.                    RTMP_Free(rtmp);   
  246.                    rtmp=NULL;  
  247.          }  
  248.          if (packet!=NULL){  
  249.                    RTMPPacket_Free(packet);      
  250.                    free(packet);  
  251.                    packet=NULL;  
  252.          }  
  253.    
  254.          CleanupSockets();  
  255.          return 0;  
  256. }  
  257.    
  258. //Publish using RTMP_Write()  
  259. int publish_using_write(){  
  260.          uint32_t start_time=0;  
  261.          uint32_t now_time=0;  
  262.          uint32_t pre_frame_time=0;  
  263.          uint32_t lasttime=0;  
  264.          int bNextIsKey=0;  
  265.          char* pFileBuf=NULL;  
  266.    
  267.          //read from tag header  
  268.          uint32_t type=0;  
  269.          uint32_t datalength=0;  
  270.          uint32_t timestamp=0;  
  271.    
  272.          RTMP *rtmp=NULL;                             
  273.           
  274.          FILE*fp=NULL;  
  275.          fp=fopen("cuc_ieschool.flv","rb");  
  276.          if (!fp){  
  277.                    RTMP_LogPrintf("Open File Error.\n");  
  278.                    CleanupSockets();  
  279.                    return -1;  
  280.          }  
  281.    
  282.          /* set log level */  
  283.          //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;  
  284.          //RTMP_LogSetLevel(loglvl);  
  285.                     
  286.          if (!InitSockets()){  
  287.                   RTMP_LogPrintf("Init Socket Err\n");  
  288.                    return -1;  
  289.          }  
  290.    
  291.          rtmp=RTMP_Alloc();  
  292.          RTMP_Init(rtmp);  
  293.          //set connection timeout,default 30s  
  294.          rtmp->Link.timeout=5;                        
  295.          if(!RTMP_SetupURL(rtmp,"rtmp://localhost/publishlive/livestream"))  
  296.          {  
  297.                    RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");  
  298.                    RTMP_Free(rtmp);  
  299.                    CleanupSockets();  
  300.                    return -1;  
  301.          }  
  302.    
  303.          RTMP_EnableWrite(rtmp);  
  304.          //1hour  
  305.          RTMP_SetBufferMS(rtmp, 3600*1000);           
  306.          if (!RTMP_Connect(rtmp,NULL)){  
  307.                    RTMP_Log(RTMP_LOGERROR,"Connect Err\n");  
  308.                    RTMP_Free(rtmp);  
  309.                    CleanupSockets();  
  310.                    return -1;  
  311.          }  
  312.           
  313.          if (!RTMP_ConnectStream(rtmp,0)){  
  314.                    RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");  
  315.                    RTMP_Close(rtmp);  
  316.                    RTMP_Free(rtmp);  
  317.                    CleanupSockets();  
  318.                    return -1;  
  319.          }  
  320.    
  321.          printf("Start to send data ...\n");  
  322.           
  323.          //jump over FLV Header  
  324.          fseek(fp,9,SEEK_SET);       
  325.          //jump over previousTagSizen  
  326.          fseek(fp,4,SEEK_CUR);     
  327.          start_time=RTMP_GetTime();  
  328.          while(1)  
  329.          {  
  330.                    if((((now_time=RTMP_GetTime())-start_time)  
  331.                               <(pre_frame_time)) && bNextIsKey){          
  332.                             //wait for 1 sec if the send process is too fast  
  333.                             //this mechanism is not very good,need some improvement  
  334.                             if(pre_frame_time>lasttime){  
  335.                                      RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time);  
  336.                                      lasttime=pre_frame_time;  
  337.                             }  
  338.                             Sleep(1000);  
  339.                             continue;  
  340.                    }  
  341.                     
  342.                    //jump over type  
  343.                    fseek(fp,1,SEEK_CUR);     
  344.                    if(!ReadU24(&datalength,fp))  
  345.                             break;  
  346.                    if(!ReadTime(×tamp,fp))  
  347.                             break;  
  348.                    //jump back  
  349.                    fseek(fp,-8,SEEK_CUR);    
  350.                     
  351.                    pFileBuf=(char*)malloc(11+datalength+4);  
  352.                    memset(pFileBuf,0,11+datalength+4);  
  353.                    if(fread(pFileBuf,1,11+datalength+4,fp)!=(11+datalength+4))  
  354.                             break;  
  355.                     
  356.                    pre_frame_time=timestamp;  
  357.                     
  358.                    if (!RTMP_IsConnected(rtmp)){  
  359.                             RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n");  
  360.                             break;  
  361.                    }  
  362.                    if (!RTMP_Write(rtmp,pFileBuf,11+datalength+4)){  
  363.                             RTMP_Log(RTMP_LOGERROR,"Rtmp Write Error\n");  
  364.                             break;  
  365.                    }  
  366.                     
  367.                    free(pFileBuf);  
  368.                    pFileBuf=NULL;  
  369.    
  370.                    if(!PeekU8(&type,fp))  
  371.                             break;  
  372.                    if(type==0x09){  
  373.                             if(fseek(fp,11,SEEK_CUR)!=0)  
  374.                                      break;  
  375.                             if(!PeekU8(&type,fp)){  
  376.                                      break;  
  377.                             }  
  378.                             if(type==0x17)  
  379.                                      bNextIsKey=1;  
  380.                             else  
  381.                                      bNextIsKey=0;  
  382.                             fseek(fp,-11,SEEK_CUR);  
  383.                    }  
  384.          }  
  385.    
  386.          RTMP_LogPrintf("\nSend Data Over\n");  
  387.           
  388.          if(fp)  
  389.                    fclose(fp);  
  390.    
  391.          if (rtmp!=NULL){  
  392.                    RTMP_Close(rtmp);          
  393.                    RTMP_Free(rtmp);  
  394.                    rtmp=NULL;  
  395.          }  
  396.    
  397.          if(pFileBuf){  
  398.                    free(pFileBuf);  
  399.                    pFileBuf=NULL;  
  400.          }  
  401.    
  402.          CleanupSockets();  
  403.          return 0;  
  404. }  
  405.    
  406. int main(int argc, char* argv[]){  
  407.          //2 Methods:  
  408.          publish_using_packet();  
  409.          //publish_using_write();  
  410.          return 0;  
  411. }  


运行结果

程序运行后,会将“cuc_ieschool.flv”文件以直播流的形式发布到“rtmp://localhost/publishlive/livestream”的URL。修改文件名称和RTMP的URL可以实现将任意flv文件发布到任意RTMP的URL。

阅读全文
0 0
原创粉丝点击