1. 程式人生 > >通過RTMP傳送264流(檔案或幀)

通過RTMP傳送264流(檔案或幀)

H264視訊通過RTMP傳送
       題外話,通過RTSP(Real Time Streaming Protocol)的方式來實現視訊的直播,但RTSP方式的一個弊端是如果需要支援客戶端通過網頁來訪問,就需要在在頁面中嵌入一個ActiveX控制元件,而ActiveX一般都需要簽名才能正常使用,否則使用者在使用時還需要更改瀏覽器設定,並且ActiveX還只支援IE核心的瀏覽器,Chrome、FireFox需要IE外掛才能執行,因此會特別影響使用者體驗。而RTMP(Real Time Messaging Protocol)很好的解決了這一個問題。由於RTMP是針對FLASH的流媒體協議,視訊通過RTMP直播後,只需要在WEB上嵌入一個Web Player(如Jwplayer)即可觀看,而且對平臺也沒什麼限制,還可以方便的通過手機觀看。

       視訊通過RTMP方式釋出需要一個RTMP Server(常見的有FMS、Wowza Media Server, 開源的有CRtmpServer、Red5等),原始視訊只要按照RTMP協議傳送給RTMP Server就可以RTMP視訊流的釋出了。為了便於視訊的打包釋出,封裝了一個RTMPStream,目前只支援傳送H264的視訊檔案。可以直接傳送H264資料幀或H264檔案,RTMPStream提供的介面如下。

<span style="font-family:KaiTi_GB2312;">class CRTMPStream  
{  
public:  
    CRTMPStream(void);  
    ~CRTMPStream(void);  
public:  
    // 連線到RTMP Server  
    bool Connect(const char* url);  
    // 斷開連線  
    void Close();  
    // 傳送MetaData  
    bool SendMetadata(LPRTMPMetadata lpMetaData);  
    // 傳送H264資料幀  
    bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);  
    // 傳送H264檔案  
    bool SendH264File(const char *pFileName);  
//...  
}  </span>
呼叫示例:
<span style="font-family:KaiTi_GB2312;">#include <stdio.h>  
#include "RTMPStream\RTMPStream.h"  
  
int main(int argc,char* argv[])  
{  
    CRTMPStream rtmpSender;  
  
    bool bRet = rtmpSender.Connect("rtmp://192.168.1.104/live/test");  
  
    rtmpSender.SendH264File("E:\\video\\test.264");  
  
    rtmpSender.Close();  
}  </span>

</pre><span style="font-family:KaiTi_GB2312;"><br style="margin: 0px; padding: 0px; font-size: 14px; line-height: 24px;" /><span style="margin: 0px; padding: 0px; list-style: none outside none; word-break: normal; word-wrap: break-word; font-size: 14px; line-height: 26px;">通過JwPlayer播放效果如下:</span><br style="margin: 0px; padding: 0px; font-size: 14px; line-height: 24px;" /></span><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-size: 14px; line-height: 24px;"></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-size: 14px; line-height: 26px;"><span style="margin: 0px; padding: 0px; list-style: none outside none; word-break: normal; word-wrap: break-word;"><span style="font-family:KaiTi_GB2312;"><img src="http://www.myexception.cn/img/2013/04/11/111410515.jpg" alt="" style="margin: 0px; padding: 0px; max-width: 579px; height: auto;" /><br style="margin: 0px; padding: 0px;" /></span></span></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-size: 14px; line-height: 26px;"><span style="margin: 0px; padding: 0px; list-style: none outside none; word-break: normal; word-wrap: break-word;"><span style="font-family:KaiTi_GB2312;"><span style="margin: 0px; padding: 0px; list-style: none outside none; word-break: normal; word-wrap: break-word;">最後附上RTMPStream完整的程式碼:</span><br style="margin: 0px; padding: 0px;" /></span></span></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-size: 14px; line-height: 26px;"><span style="margin: 0px; padding: 0px; list-style: none outside none; word-break: normal; word-wrap: break-word;"><span style="font-family:KaiTi_GB2312;"><span style="margin: 0px; padding: 0px; list-style: none outside none; word-break: normal; word-wrap: break-word;"></span></span></span></p><pre name="code" class="cpp" style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-size: 14px; line-height: 24px;"><span style="font-family:KaiTi_GB2312;"></span><pre name="code" class="cpp">/******************************************************************** 
filename:   RTMPStream.h
created:    2013-04-3
author:     firehood 
purpose:    傳送H264視訊到RTMP Server,使用libRtmp庫
*********************************************************************/ 
#pragma once
#include "rtmp.h"
#include "rtmp_sys.h"
#include "amf.h"
#include <stdio.h>

#define FILEBUFSIZE (1024 * 1024 * 10)       //  10M

// NALU單元
typedef struct _NaluUnit
{
	int type;
	int size;
	unsigned char *data;
}NaluUnit;

typedef struct _RTMPMetadata
{
	// video, must be h264 type
	unsigned int	nWidth;
	unsigned int	nHeight;
	unsigned int	nFrameRate;		// fps
	unsigned int	nVideoDataRate;	// bps
	unsigned int	nSpsLen;
	unsigned char	Sps[1024];
	unsigned int	nPpsLen;
	unsigned char	Pps[1024];

	// audio, must be aac type
	bool	        bHasAudio;
	unsigned int	nAudioSampleRate;
	unsigned int	nAudioSampleSize;
	unsigned int	nAudioChannels;
	char		    pAudioSpecCfg;
	unsigned int	nAudioSpecCfgLen;

} RTMPMetadata,*LPRTMPMetadata;


class CRTMPStream
{
public:
	CRTMPStream(void);
	~CRTMPStream(void);
public:
	// 連線到RTMP Server
	bool Connect(const char* url);
    // 斷開連線
	void Close();
    // 傳送MetaData
	bool SendMetadata(LPRTMPMetadata lpMetaData);
    // 傳送H264資料幀
	bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
	// 傳送H264檔案
	bool SendH264File(const char *pFileName);
private:
	// 送快取中讀取一個NALU包
	bool ReadOneNaluFromBuf(NaluUnit &nalu);
	// 傳送資料
	int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp);
private:
	RTMP* m_pRtmp;
	unsigned char* m_pFileBuf;
	unsigned int  m_nFileBufSize;
	unsigned int  m_nCurPos;
};

<pre name="code" class="cpp">/******************************************************************** 
filename:   RTMPStream.cpp
created:    2013-04-3
author:     firehood 
purpose:    傳送H264視訊到RTMP Server,使用libRtmp庫
*********************************************************************/ 
#include "RTMPStream.h"
#include "SpsDecode.h"
#ifdef WIN32  
#include <windows.h>
#endif

enum
{
	FLV_CODECID_H264 = 7,
};

int InitSockets()  
{  
#ifdef WIN32  
	WORD version;  
	WSADATA wsaData;  
	version = MAKEWORD(1, 1);  
	return (WSAStartup(version, &wsaData) == 0);  
#else  
	return TRUE;  
#endif  
}  

inline void CleanupSockets()  
{  
#ifdef WIN32  
	WSACleanup();  
#endif  
}  

char * put_byte( char *output, uint8_t nVal )  
{  
	output[0] = nVal;  
	return output+1;  
}  
char * put_be16(char *output, uint16_t nVal )  
{  
	output[1] = nVal & 0xff;  
	output[0] = nVal >> 8;  
	return output+2;  
}  
char * put_be24(char *output,uint32_t nVal )  
{  
	output[2] = nVal & 0xff;  
	output[1] = nVal >> 8;  
	output[0] = nVal >> 16;  
	return output+3;  
}  
char * put_be32(char *output, uint32_t nVal )  
{  
	output[3] = nVal & 0xff;  
	output[2] = nVal >> 8;  
	output[1] = nVal >> 16;  
	output[0] = nVal >> 24;  
	return output+4;  
}  
char *  put_be64( char *output, uint64_t nVal )  
{  
	output=put_be32( output, nVal >> 32 );  
	output=put_be32( output, nVal );  
	return output;  
}  
char * put_amf_string( char *c, const char *str )  
{  
	uint16_t len = strlen( str );  
	c=put_be16( c, len );  
	memcpy(c,str,len);  
	return c+len;  
}  
char * put_amf_double( char *c, double d )  
{  
	*c++ = AMF_NUMBER;  /* type: Number */  
	{  
		unsigned char *ci, *co;  
		ci = (unsigned char *)&d;  
		co = (unsigned char *)c;  
		co[0] = ci[7];  
		co[1] = ci[6];  
		co[2] = ci[5];  
		co[3] = ci[4];  
		co[4] = ci[3];  
		co[5] = ci[2];  
		co[6] = ci[1];  
		co[7] = ci[0];  
	}  
	return c+8;  
}

CRTMPStream::CRTMPStream(void):
m_pRtmp(NULL),
m_nFileBufSize(0),
m_nCurPos(0)
{
	m_pFileBuf = new unsigned char[FILEBUFSIZE];
	memset(m_pFileBuf,0,FILEBUFSIZE);
	InitSockets();
	m_pRtmp = RTMP_Alloc();  
	RTMP_Init(m_pRtmp);  
}

CRTMPStream::~CRTMPStream(void)
{
	Close();
	WSACleanup();  
	delete[] m_pFileBuf;
}

bool CRTMPStream::Connect(const char* url)
{
	if(RTMP_SetupURL(m_pRtmp, (char*)url)<0)
	{
		return FALSE;
	}
	RTMP_EnableWrite(m_pRtmp);
	if(RTMP_Connect(m_pRtmp, NULL)<0)
	{
		return FALSE;
	}
	if(RTMP_ConnectStream(m_pRtmp,0)<0)
	{
		return FALSE;
	}
	return TRUE;
}

void CRTMPStream::Close()
{
	if(m_pRtmp)
	{
		RTMP_Close(m_pRtmp);
		RTMP_Free(m_pRtmp);
		m_pRtmp = NULL;
	}
}

int CRTMPStream::SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp)
{
	if(m_pRtmp == NULL)
	{
		return FALSE;
	}

	RTMPPacket packet;
	RTMPPacket_Reset(&packet);
	RTMPPacket_Alloc(&packet,size);

	packet.m_packetType = nPacketType;
	packet.m_nChannel = 0x04;  
	packet.m_headerType = RTMP_PACKET_SIZE_LARGE;  
	packet.m_nTimeStamp = nTimestamp;  
	packet.m_nInfoField2 = m_pRtmp->m_stream_id;
	packet.m_nBodySize = size;
	memcpy(packet.m_body,data,size);

	int nRet = RTMP_SendPacket(m_pRtmp,&packet,0);

	RTMPPacket_Free(&packet);

	return nRet;
}

bool CRTMPStream::SendMetadata(LPRTMPMetadata lpMetaData)
{
	if(lpMetaData == NULL)
	{
		return false;
	}
	char body[1024] = {0};;
    
    char * p = (char *)body;  
	p = put_byte(p, AMF_STRING );
	p = put_amf_string(p , "@setDataFrame" );

	p = put_byte( p, AMF_STRING );
	p = put_amf_string( p, "onMetaData" );

	p = put_byte(p, AMF_OBJECT );  
	p = put_amf_string( p, "copyright" );  
	p = put_byte(p, AMF_STRING );  
	p = put_amf_string( p, "firehood" );  

	p =put_amf_string( p, "width");
	p =put_amf_double( p, lpMetaData->nWidth);

	p =put_amf_string( p, "height");
	p =put_amf_double( p, lpMetaData->nHeight);

	p =put_amf_string( p, "framerate" );
	p =put_amf_double( p, lpMetaData->nFrameRate); 

	p =put_amf_string( p, "videocodecid" );
	p =put_amf_double( p, FLV_CODECID_H264 );

	p =put_amf_string( p, "" );
	p =put_byte( p, AMF_OBJECT_END  );

	int index = p-body;

	SendPacket(RTMP_PACKET_TYPE_INFO,(unsigned char*)body,p-body,0);

	int i = 0;
	body[i++] = 0x17; // 1:keyframe  7:AVC
	body[i++] = 0x00; // AVC sequence header

	body[i++] = 0x00;
	body[i++] = 0x00;
	body[i++] = 0x00; // fill in 0;

	// AVCDecoderConfigurationRecord.
	body[i++] = 0x01; // configurationVersion
	body[i++] = lpMetaData->Sps[1]; // AVCProfileIndication
	body[i++] = lpMetaData->Sps[2]; // profile_compatibility
	body[i++] = lpMetaData->Sps[3]; // AVCLevelIndication 
    body[i++] = 0xff; // lengthSizeMinusOne  

    // sps nums
	body[i++] = 0xE1; //&0x1f
	// sps data length
	body[i++] = lpMetaData->nSpsLen>>8;
	body[i++] = lpMetaData->nSpsLen&0xff;
	// sps data
	memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);
	i= i+lpMetaData->nSpsLen;

	// pps nums
	body[i++] = 0x01; //&0x1f
	// pps data length 
	body[i++] = lpMetaData->nPpsLen>>8;
	body[i++] = lpMetaData->nPpsLen&0xff;
	// sps data
	memcpy(&body[i],lpMetaData->Pps,lpMetaData->nPpsLen);
	i= i+lpMetaData->nPpsLen;

	return SendPacket(RTMP_PACKET_TYPE_VIDEO,(unsigned char*)body,i,0);

}

bool CRTMPStream::SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp)
{
	if(data == NULL && size<11)
	{
		return false;
	}

	unsigned char *body = new unsigned char[size+9];

	int i = 0;
	if(bIsKeyFrame)
	{
		body[i++] = 0x17;// 1:Iframe  7:AVC
	}
	else
	{
		body[i++] = 0x27;// 2:Pframe  7:AVC
	}
	body[i++] = 0x01;// AVC NALU
	body[i++] = 0x00;
	body[i++] = 0x00;
	body[i++] = 0x00;

	// NALU size
	body[i++] = size>>24;
	body[i++] = size>>16;
	body[i++] = size>>8;
	body[i++] = size&0xff;;

	// NALU data
	memcpy(&body[i],data,size);

	bool bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp);

	delete[] body;

	return bRet;
}

bool CRTMPStream::SendH264File(const char *pFileName)
{
	if(pFileName == NULL)
	{
		return FALSE;
	}
	FILE *fp = fopen(pFileName, "rb");  
	if(!fp)  
	{  
		printf("ERROR:open file %s failed!",pFileName);
	}  
	fseek(fp, 0, SEEK_SET);
	m_nFileBufSize = fread(m_pFileBuf, sizeof(unsigned char), FILEBUFSIZE, fp);
	if(m_nFileBufSize >= FILEBUFSIZE)
	{
		printf("warning : File size is larger than BUFSIZE\n");
	}
	fclose(fp);  

	RTMPMetadata metaData;
	memset(&metaData,0,sizeof(RTMPMetadata));

    NaluUnit naluUnit;
	// 讀取SPS幀
    ReadOneNaluFromBuf(naluUnit);
	metaData.nSpsLen = naluUnit.size;
	memcpy(metaData.Sps,naluUnit.data,naluUnit.size);

	// 讀取PPS幀
	ReadOneNaluFromBuf(naluUnit);
	metaData.nPpsLen = naluUnit.size;
	memcpy(metaData.Pps,naluUnit.data,naluUnit.size);

	// 解碼SPS,獲取視訊影象寬、高資訊
	int width = 0,height = 0;
    h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height);
	metaData.nWidth = width;
    metaData.nHeight = height;
	metaData.nFrameRate = 25;
   
	// 傳送MetaData
    SendMetadata(&metaData);

	unsigned int tick = 0;
	while(ReadOneNaluFromBuf(naluUnit))
	{
		bool bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;
		// 傳送H264資料幀
		SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick);
		msleep(40);
		tick +=40;
	}

	return TRUE;
}

bool CRTMPStream::ReadOneNaluFromBuf(NaluUnit &nalu)
{
	int i = m_nCurPos;
	while(i<m_nFileBufSize-4)
	{
		if(m_pFileBuf[i++] == 0x00 &&
			m_pFileBuf[i++] == 0x00 &&
			m_pFileBuf[i++] == 0x00 &&
			m_pFileBuf[i++] == 0x01
			)
		{
			int pos = i;
			while (pos<m_nFileBufSize-4)
			{
				if(m_pFileBuf[pos++] == 0x00 &&
					m_pFileBuf[pos++] == 0x00 &&
					m_pFileBuf[pos++] == 0x00 &&
					m_pFileBuf[pos++] == 0x01
					)
				{
					break;
				}
			}
			nalu.type = m_pFileBuf[i]&0x1f;
			nalu.size = (pos-4)-i;
			nalu.data = &m_pFileBuf[i];

			m_nCurPos = pos-4;
			return TRUE;
		}
	}
	return FALSE;
}


相關推薦

通過RTMP傳送264(檔案)

H264視訊通過RTMP傳送       題外話,通過RTSP(Real Time Streaming Protocol)的方式來實現視訊的直播,但RTSP方式的一個弊端是如果需要支援客戶端通過網頁來訪問,就需要在在頁面中嵌入一個ActiveX控制元件,而ActiveX一

Java 小例子:通過 Socket 傳送和接收檔案

這是一個簡單的包含傳送端和接收端的例子。傳送端向接收端傳送檔名和檔案內容,接收端將收到的檔案儲存在磁碟上。接收端可以同時接收多個傳送端傳來的檔案,但沒有處理檔案同名的情況。 這個例子中設計了一個簡單的協議。傳送的內容是這樣的: 檔名長度(4位元組)—檔名—檔案內容長度(4位

通過XMLHttpRequest載入外部圖片檔案資料

var remoteImage, container = document.querySelector('.imageContainer'), toLoad = { 'images': [ 'http://myserver.com/im

H264視訊通過RTMP傳送

       前面的文章中提到了通過RTSP(Real Time Streaming Protocol)的方式來實現視訊的直播,但RTSP方式的一個弊端是如果需要支援客戶端通過網頁來訪問,就需要在在頁面中嵌入一個ActiveX控制元件,而ActiveX一般都需要簽名才能正常

scp ssh-copy-id 無法通過通過root 使用者傳送檔案公鑰解決辦法

廢話不說直接解決問題 問題原因: 無法通過root使用者傳送檔案或公鑰至遠端伺服器 如下圖: 解決辦法: sudo vim /etc/ssh/sshd_config 找到  LoginGraceTime PermitRootLogin StrictModes 將以上三項改為:

H.264檔案中獲取每一資料

    測試解碼器效能時,最常用的無非是向解碼器中推送碼流。     之前封裝了一個avc的解碼器,想做一個測試,讀取H.264裸流檔案將碼流定期定時推送到解碼器。     測試其實很簡單:     1.瞭解H.264裸流檔案的構成     2.解析H.264裸流檔案

Android通過Bluetooth藍芽傳送手機照片檔案到Windows PC:Java實現

Android通過Bluetooth藍芽傳送手機照片檔案到Windows PC:Java實現 本文在《Android通過藍芽傳送資料到Windows PC電腦:Java實現(連結地址:https://blog.csdn.net/zhangphil/article/details/831467

vuejs解析檔案excel表格js通過外掛解析表格讀取檔案

 安裝外掛 cnpm i xlsx --save-dev cnpm i jquery --save-dev 引入外掛    html引入檔案  <input type="file" id="excel-file" value="fil

JAVA中建立HTTP通訊,從伺服器上獲取HTML程式碼,通過HTTP請求來下載圖片其他二進位制檔案的程式,下載結果要按下載到的檔案型別進行存檔中。

通過HTTP請求來下載圖片或其他二進位制檔案的程式,下載結果要按下載到的檔案型別進行存檔 將程式碼從伺服器的中獲取下來的程式碼,在我之前已經講過了這裡寫連結內容 這裡我們就直接將原始碼稍加改動,加入一個檔案並請將builder 寫入即可。 import

Ubuntu系統下通過命令查詢檔案資料夾

1. whereis+檔名 用於程式名的搜尋,搜尋結果只限於二進位制檔案(引數-b)、man說明檔案(引數-m)和原始碼檔案(引數-s),如果省略引數,則返回所有資訊。 2. find / -name +檔名 find是在指定的目錄下遍歷查詢,如果目錄使用 /

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

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

通過nginx,nginx-rtmp-module實現媒體直播

1、 下載nginx http://nginx.org/en/download.html 下載nginx-rtmp-module: nginx-rtmp-module的官方github地址:https://github.com/arut/nginx-rtmp-module

如何檢測判斷一個檔案位元組(無BOM)是什麼編碼型別

前言: 不小心看到一條留言: 然後就去該地址看了一下,這一看,順帶折騰了一天。 今天,就和大夥分享下折騰的感覺。 在該開源地址中,程式碼有C++和C#兩個版本,編碼的整體風格傾向與於C++。 主要的時間,花了在對於檢測無BOM的部分,順帶重溫了各種編碼的基礎。 建議在看此文之前,先了解下編

java通過Http請求訪問網路圖片檔案返回byte陣列的兩種方式

第一種方式,使用HttpURLConnection 使用HttpURlConnection傳送一個get請求,開啟一個連線,從連接獲取到流,將流轉成byte陣列 /** * 發起Get請求 * * @param urlStr * @

httpclient通過POST來上傳檔案,而不是通過的形式,並在服務端進行解析(通過httpmime.jar來操作)

1. 首先需要對應的JAR包 匯入 httpmime-4.1.1.jar。 package url; import io.IoStreamUtil; import java.io.File; import java.io.IOException; import jav

Wireshark Lua: 一個從RTP抓包裡匯出H.264 Payload,變成264裸碼檔案(xxx.264)的Wireshark外掛

     抓取一個包含H.264 Payload RTP包的SIP會話或RTSP會話後,用Wireshark的Play功能只能播放聲音,不能播放視訊。把RTP payload直接匯出成檔案後也是不能直接播放的,因為H.264 over RTP封包是符合RFC3984規範的,

通過FFmpeg將rtsp攝像頭視訊轉碼為rtmp播放

注意:用這種方式可以顯示攝像頭視訊,但是存在諸多問題,無法正常投入使用,包括卡頓跟延遲,請想要借鑑的朋友慎重使用。        前幾天經理給我安排了一個新任務,將網路攝像頭用rtsp協議在頁面播放,因為我以前做的是http協議的,根據攝像頭的API來實現的,

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

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

linux下使用nginx+nginx-rtmp-module搭建媒體伺服器,傳送即時影像

1:前言 公司最近想做利用網路來傳輸視訊流的影響系統,然後我又開始鼓搗不太熟悉的linux系統,好在網上關於這方面的資源挺多,按照相關的設定以及方法能大部分完成,其他附上我搭建時遇到的一些小問題的解決辦法。 2:搭建的環境: (1):linux系統:Ubuntu 16.

使用FFmpeg的SDK庫實現將H.264封裝進MP4檔案時全域性SPS、PPS與中SPS、PPS衝突的問題

一、問題1. 使用FFmpeg的SDK庫實現將H.264流封裝進MP4檔案的原始碼大致如下:char* filename = "./test.mp4"AVOutputFormat *fmt;AVStream* video_st;AVFormatContext *av_cont