1. 程式人生 > >最簡單的基於librtmp的示例:釋出(FLV通過RTMP釋出)

最簡單的基於librtmp的示例:釋出(FLV通過RTMP釋出)

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

最簡單的基於libRTMP的示例系列文章列表:

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


本文記錄一個基於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()釋出流媒體的原始碼,如下所示。
/**
 * Simplest Librtmp Send FLV
 *
 * 雷霄驊,張暉
 * [email protected]
 * [email protected]
 * 中國傳媒大學/數字電視技術
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程式用於將FLV格式的視音訊檔案使用RTMP推送至RTMP流媒體伺服器。
 * This program can send local flv file to net server as a rtmp live stream.
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#ifndef WIN32
#include <unistd.h>
#endif
 
 
#include "librtmp/rtmp_sys.h"
#include "librtmp/log.h"
 
#define HTON16(x)  ((x>>8&0xff)|(x<<8&0xff00))
#define HTON24(x)  ((x>>16&0xff)|(x<<16&0xff0000)|(x&0xff00))
#define HTON32(x)  ((x>>24&0xff)|(x>>8&0xff00)|\
         (x<<8&0xff0000)|(x<<24&0xff000000))
#define HTONTIME(x) ((x>>16&0xff)|(x<<16&0xff0000)|(x&0xff00)|(x&0xff000000))
 
/*read 1 byte*/
int ReadU8(uint32_t *u8,FILE*fp){
         if(fread(u8,1,1,fp)!=1)
                   return 0;
         return 1;
}
/*read 2 byte*/
int ReadU16(uint32_t *u16,FILE*fp){
         if(fread(u16,2,1,fp)!=1)
                   return 0;
         *u16=HTON16(*u16);
         return 1;
}
/*read 3 byte*/
int ReadU24(uint32_t *u24,FILE*fp){
         if(fread(u24,3,1,fp)!=1)
                   return 0;
         *u24=HTON24(*u24);
         return 1;
}
/*read 4 byte*/
int ReadU32(uint32_t *u32,FILE*fp){
         if(fread(u32,4,1,fp)!=1)
                   return 0;
         *u32=HTON32(*u32);
         return 1;
}
/*read 1 byte,and loopback 1 byte at once*/
int PeekU8(uint32_t *u8,FILE*fp){
         if(fread(u8,1,1,fp)!=1)
                   return 0;
         fseek(fp,-1,SEEK_CUR);
         return 1;
}
/*read 4 byte and convert to time format*/
int ReadTime(uint32_t *utime,FILE*fp){
         if(fread(utime,4,1,fp)!=1)
                   return 0;
         *utime=HTONTIME(*utime);
         return 1;
}
 
int InitSockets()
{
         WORD version;
         WSADATA wsaData;
         version=MAKEWORD(2,2);
         return (WSAStartup(version, &wsaData) == 0);
}
 
void CleanupSockets()
{
         WSACleanup();
}
 
//Publish using RTMP_SendPacket()
int publish_using_packet(){
         RTMP *rtmp=NULL;                           
         RTMPPacket *packet=NULL;
         uint32_t start_time=0;
         uint32_t now_time=0;
         //the timestamp of the previous frame
         long pre_frame_time=0;
         long lasttime=0;
         int bNextIsKey=1;
         uint32_t preTagsize=0;
        
         //packet attributes
         uint32_t type=0;                        
         uint32_t datalength=0;           
         uint32_t timestamp=0;           
         uint32_t streamid=0;                        
 
         FILE*fp=NULL;
         fp=fopen("cuc_ieschool.flv","rb");
         if (!fp){
                   RTMP_LogPrintf("Open File Error.\n");
                   CleanupSockets();
                   return -1;
         }
 
         /* set log level */
         //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;
         //RTMP_LogSetLevel(loglvl);
                  
         if (!InitSockets()){
                   RTMP_LogPrintf("Init Socket Err\n");
                   return -1;
         }
 
         rtmp=RTMP_Alloc();
         RTMP_Init(rtmp);
         //set connection timeout,default 30s
         rtmp->Link.timeout=5;                      
         if(!RTMP_SetupURL(rtmp,"rtmp://localhost/publishlive/livestream"))
         {
                   RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
        
         //if unable,the AMF command would be 'play' instead of 'publish'
         RTMP_EnableWrite(rtmp);     
        
         if (!RTMP_Connect(rtmp,NULL)){
                   RTMP_Log(RTMP_LOGERROR,"Connect Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
        
         if (!RTMP_ConnectStream(rtmp,0)){
                   RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");
                   RTMP_Close(rtmp);
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
 
         packet=(RTMPPacket*)malloc(sizeof(RTMPPacket));
         RTMPPacket_Alloc(packet,1024*64);
         RTMPPacket_Reset(packet);
 
         packet->m_hasAbsTimestamp = 0;        
         packet->m_nChannel = 0x04;
         packet->m_nInfoField2 = rtmp->m_stream_id;
 
         RTMP_LogPrintf("Start to send data ...\n");
        
         //jump over FLV Header
         fseek(fp,9,SEEK_SET);     
         //jump over previousTagSizen
         fseek(fp,4,SEEK_CUR);   
         start_time=RTMP_GetTime();
         while(1)
         {
                   if((((now_time=RTMP_GetTime())-start_time)
                              <(pre_frame_time)) && bNextIsKey){        
                            //wait for 1 sec if the send process is too fast
                            //this mechanism is not very good,need some improvement
                            if(pre_frame_time>lasttime){
                                     RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time);
                                     lasttime=pre_frame_time;
                            }
                            Sleep(1000);
                            continue;
                   }
                  
                   //not quite the same as FLV spec
                   if(!ReadU8(&type,fp))     
                            break;
                   if(!ReadU24(&datalength,fp))
                            break;
                   if(!ReadTime(×tamp,fp))
                            break;
                   if(!ReadU24(&streamid,fp))
                            break;
 
                   if (type!=0x08&&type!=0x09){
                            //jump over non_audio and non_video frame,
                            //jump over next previousTagSizen at the same time
                            fseek(fp,datalength+4,SEEK_CUR);
                            continue;
                   }
                  
                   if(fread(packet->m_body,1,datalength,fp)!=datalength)
                            break;
 
                   packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
                   packet->m_nTimeStamp = timestamp;
                   packet->m_packetType = type;
                   packet->m_nBodySize  = datalength;
                   pre_frame_time=timestamp;
 
                   if (!RTMP_IsConnected(rtmp)){
                            RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n");
                            break;
                   }
                   if (!RTMP_SendPacket(rtmp,packet,0)){
                            RTMP_Log(RTMP_LOGERROR,"Send Error\n");
                            break;
                   }
 
                   if(!ReadU32(&preTagsize,fp))
                            break;
                           
                   if(!PeekU8(&type,fp))
                            break;
                   if(type==0x09){
                            if(fseek(fp,11,SEEK_CUR)!=0)
                                     break;
                            if(!PeekU8(&type,fp)){
                                     break;
                            }
                            if(type==0x17)
                                     bNextIsKey=1;
                            else
                                     bNextIsKey=0;
 
                            fseek(fp,-11,SEEK_CUR);
                   }
         }               
 
         RTMP_LogPrintf("\nSend Data Over\n");
        
         if(fp)
                   fclose(fp);
 
         if (rtmp!=NULL){
                   RTMP_Close(rtmp);        
                   RTMP_Free(rtmp); 
                   rtmp=NULL;
         }
         if (packet!=NULL){
                   RTMPPacket_Free(packet);    
                   free(packet);
                   packet=NULL;
         }
 
         CleanupSockets();
         return 0;
}
 
//Publish using RTMP_Write()
int publish_using_write(){
         uint32_t start_time=0;
         uint32_t now_time=0;
         uint32_t pre_frame_time=0;
         uint32_t lasttime=0;
         int bNextIsKey=0;
         char* pFileBuf=NULL;
 
         //read from tag header
         uint32_t type=0;
         uint32_t datalength=0;
         uint32_t timestamp=0;
 
         RTMP *rtmp=NULL;                           
        
         FILE*fp=NULL;
         fp=fopen("cuc_ieschool.flv","rb");
         if (!fp){
                   RTMP_LogPrintf("Open File Error.\n");
                   CleanupSockets();
                   return -1;
         }
 
         /* set log level */
         //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;
         //RTMP_LogSetLevel(loglvl);
                  
         if (!InitSockets()){
                  RTMP_LogPrintf("Init Socket Err\n");
                   return -1;
         }
 
         rtmp=RTMP_Alloc();
         RTMP_Init(rtmp);
         //set connection timeout,default 30s
         rtmp->Link.timeout=5;                      
         if(!RTMP_SetupURL(rtmp,"rtmp://localhost/publishlive/livestream"))
         {
                   RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
 
         RTMP_EnableWrite(rtmp);
         //1hour
         RTMP_SetBufferMS(rtmp, 3600*1000);         
         if (!RTMP_Connect(rtmp,NULL)){
                   RTMP_Log(RTMP_LOGERROR,"Connect Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
        
         if (!RTMP_ConnectStream(rtmp,0)){
                   RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");
                   RTMP_Close(rtmp);
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
 
         printf("Start to send data ...\n");
        
         //jump over FLV Header
         fseek(fp,9,SEEK_SET);     
         //jump over previousTagSizen
         fseek(fp,4,SEEK_CUR);   
         start_time=RTMP_GetTime();
         while(1)
         {
                   if((((now_time=RTMP_GetTime())-start_time)
                              <(pre_frame_time)) && bNextIsKey){        
                            //wait for 1 sec if the send process is too fast
                            //this mechanism is not very good,need some improvement
                            if(pre_frame_time>lasttime){
                                     RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time);
                                     lasttime=pre_frame_time;
                            }
                            Sleep(1000);
                            continue;
                   }
                  
                   //jump over type
                   fseek(fp,1,SEEK_CUR);   
                   if(!ReadU24(&datalength,fp))
                            break;
                   if(!ReadTime(×tamp,fp))
                            break;
                   //jump back
                   fseek(fp,-8,SEEK_CUR);  
                  
                   pFileBuf=(char*)malloc(11+datalength+4);
                   memset(pFileBuf,0,11+datalength+4);
                   if(fread(pFileBuf,1,11+datalength+4,fp)!=(11+datalength+4))
                            break;
                  
                   pre_frame_time=timestamp;
                  
                   if (!RTMP_IsConnected(rtmp)){
                            RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n");
                            break;
                   }
                   if (!RTMP_Write(rtmp,pFileBuf,11+datalength+4)){
                            RTMP_Log(RTMP_LOGERROR,"Rtmp Write Error\n");
                            break;
                   }
                  
                   free(pFileBuf);
                   pFileBuf=NULL;
 
                   if(!PeekU8(&type,fp))
                            break;
                   if(type==0x09){
                            if(fseek(fp,11,SEEK_CUR)!=0)
                                     break;
                            if(!PeekU8(&type,fp)){
                                     break;
                            }
                            if(type==0x17)
                                     bNextIsKey=1;
                            else
                                     bNextIsKey=0;
                            fseek(fp,-11,SEEK_CUR);
                   }
         }
 
         RTMP_LogPrintf("\nSend Data Over\n");
        
         if(fp)
                   fclose(fp);
 
         if (rtmp!=NULL){
                   RTMP_Close(rtmp);        
                   RTMP_Free(rtmp);
                   rtmp=NULL;
         }
 
         if(pFileBuf){
                   free(pFileBuf);
                   pFileBuf=NULL;
         }
 
         CleanupSockets();
         return 0;
}
 
int main(int argc, char* argv[]){
         //2 Methods:
         publish_using_packet();
         //publish_using_write();
         return 0;
}

執行結果

程式執行後,會將“cuc_ieschool.flv”檔案以直播流的形式釋出到“rtmp://localhost/publishlive/livestream”的URL。修改檔名稱和RTMP的URL可以實現將任意flv檔案釋出到任意RTMP的URL。

下載

Simplest LibRTMP Example
 

專案主頁

CSDN下載:http://download.csdn.net/detail/leixiaohua1020/8291757
 
本工程包含了LibRTMP的使用示例,包含如下子工程:
simplest_librtmp_receive: 接收RTMP流媒體並在本地儲存成FLV格式的檔案。
simplest_librtmp_send_flv: 將FLV格式的視音訊檔案使用RTMP推送至RTMP流媒體伺服器。
simplest_librtmp_send264: 將記憶體中的H.264資料推送至RTMP流媒體伺服器。
 
 
 
 
 

相關推薦

簡單基於librtmp示例釋出FLV通過RTMP釋出

=====================================================最簡單的基於libRTMP的示例系列文章列表:=====================================================本文記錄一個基於l

簡單的PHP介面連資料庫可用來做測試

<?php //獲取POST json 資料 $info = file_get_contents('php://input'); //解析資料 $info_array = json_decode(

簡單基於librtmp示例釋出H.264H.264通過RTMP釋出

=====================================================最簡單的基於libRTMP的示例系列文章列表:=====================================================本文記錄一個基於l

簡單基於librtmp示例釋出H.264H.264通過RTMP釋出——雷神經典

===================================================== 最簡單的基於libRTMP的示例系列文章列表: ===================================================== 本文記錄一個基於libRTMP

簡單基於librtmp示例 釋出H 264 H 264通過RTMP釋出

                =====================================================最簡單的基於libRTMP的示例系列文章列表:=====================================================本文記錄一個基於li

基於qml創建簡單的圖像處理程序1-基於qml創建界面

cep font mes quit vid www 習慣 image ble 為什麽使用QT,包括進一步使用QML?兩個主要原因,一是因為我是一個c++程序員,有語言使用慣性;二是我主要做圖像處理方面工作,使用什麽平臺對於我來說不重要,我只需要在不同平臺上面能

基於qml創建簡單的圖像處理程序2-使用c++&qml進行圖像處理

.cn turn isnull 按鈕 編寫 可能 finish height 通過 《基於qml創建最簡單的圖像處理程序》系列課程及配套代碼基於qml創建最簡單的圖像處理程序(1)-基於qml創建界面http://www.cnblogs.com/jsxyhelu/p/83

基於qml創建簡單的圖像處理程序3-使用opencv&qml進行圖像處理

結果 tar isempty reat features eabi qt quick resources 也會 《基於qml創建最簡單的圖像處理程序》系列課程及配套代碼基於qml創建最簡單的圖像處理程序(1)-基於qml創建界面http://www.cnblogs.com/

Web框架-龍捲風Tornado之世界上簡單的Tornado示例

原始碼 # _*_coding:utf-8_*_ import tornado.ioloop import tornado.web class MainHandler(tornado.web.R

1小時學會簡單的iOS直播推流yuv、pcm資料的介紹和獲取

最簡單的iOS 推流程式碼,視訊捕獲,軟編碼(faac,x264),硬編碼(aac,h264),美顏,flv編碼,rtmp協議,陸續更新程式碼解析,你想學的知識這裡都有,願意懂直播技術的同學快來看!! 前面介紹瞭如何通過相機實時獲取音視訊資

1小時學會簡單的iOS直播推流h264/aac 硬編碼

最簡單的iOS 推流程式碼,視訊捕獲,軟編碼(faac,x264),硬編碼(aac,h264),美顏,flv編碼,rtmp協議,陸續更新程式碼解析,你想學的知識這裡都有,願意懂直播技術的同學快來看!! 前面已經介紹瞭如何從硬體裝置獲取到音視

1小時學會簡單的iOS直播推流flv 編碼與音視訊時間戳同步

最簡單的iOS 推流程式碼,視訊捕獲,軟編碼(faac,x264),硬編碼(aac,h264),美顏,flv編碼,rtmp協議,陸續更新程式碼解析,你想學的知識這裡都有,願意懂直播技術的同學快來看!! 前文介紹瞭如何獲取音視訊的aac/h2

用Python實現簡單的文字識別基於百度雲文字識別API

Python版本:3.6.5 百度雲提供的文字識別技術,準確率還是非常高的,而且每天還有5w次免費的呼叫量,對於用來學習或者偶爾拿來用用,已經完全足夠了。文章提供一個模板,稍加修改就可以直接套用。註釋中提到必須輸入的地方,你都正確地輸入了的話,就可以完成一次簡單的文字識別了

簡單的例子說明設計模式之責任鏈、建造者、適配器、代理模式、享元模式

def dap CA 抽象 創建 tcl cte clas eth 責任鏈模式 一個請求有多個對象來處理,這些對象是一條鏈,但具體由哪個對象來處理,根據條件判斷來確定,如果不能處理會傳遞給該鏈中的下一個對象,直到有對象處理它為止 使用場景 1)有多個對象可以處理同

簡單的vue入門基礎語法學習

新建index.html,直接複製以下程式碼,雙擊瀏覽器執行即可。程式碼包含Vue的基礎語法,可對照練習。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">

基於Android簡單備忘錄的設計與實現附git原始碼連結

前言 課程作業需要,於是忙活兩天寫了一個簡單的備忘錄,使用了ListView,SQLite。 開發環境:Android Studio 原始碼連結:https://gitee.com/zg0212/Memoire 功能截圖 主頁面 新建頁面

JAVA 簡單獲取系統時間程式碼 LocalDateTime 以yyyy-MM-dd HH:mm:ss.SSS格式顯示

直接上程式碼,簡單粗暴:   import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * @Author : JCccc * @CreateTime : 2018-11-27

Android AIDL簡單易懂的使用與解析2

在上篇文章 Android AIDL最簡單易懂的使用與解析(1)中,我們學會了如何編寫一個簡單的 AIDL 來進行跨程序通訊,本著知其然更要知其所以然的道理,在這一篇中我們就來具體看看 AIDL幫我們生成了一套基於 Binder 的怎樣的介面吧~ 我們先來看看testAID

通過ASP.NET MVC框架 + 原生JavaScript + Ajax + SQL SERVER 實現一個簡單的有論壇功能的網站通過iis釋出的例子

  ASP.NET MVC. M 為Model模型層, V 為View檢視層, C 為Controller控制層。要想使用MVC框架來寫網站就需要了解M V C 的作用分別為哪些。給大家簡單的介紹一下:     1.當你的這個網站要與資料庫互動的時候,你可以使用EF建立一個數據庫模型,也可以用類存放你所需互動

簡單打增量包的方法已附上打包的java類

前言: 打增量包的目的是快捷打包出項目兩次更新版本之間的差異檔案(除了打包出新增檔案,還能打包出原有已經被改變的檔案)。 問題1: 打包出這些增量檔案有什麼作用? 答:快速部署這些增量檔案到tomcat的webapps資料夾對應的專案中。進行增量部署。 問題2: 打包的工具是什麼?