librtmp_send264.h
/**
* Simplest Librtmp Send 264
*
*
*
* 本程序用于将内存中的H.264数据推送至RTMP流媒体服务器。
*
*/
/**
* 初始化并连接到服务器
*
* @param url 服务器上对应webapp的地址
*
* @成功则返回1 , 失败则返回0
*/
int RTMP264_Connect(const char* url);
/**
* 将内存中的一段H.264编码的视频数据利用RTMP协议发送到服务器
*
* @param read_buffer 回调函数,当数据不足的时候,系统会自动调用该函数获取输入数据。
* 2个参数功能:
* uint8_t *buf:外部数据送至该地址
* int buf_size:外部数据大小
* 返回值:成功读取的内存大小
* @成功则返回1 , 失败则返回0
*/
int RTMP264_Send(int (*read_buffer)(unsigned char *buf, int buf_size));
/**
* 断开连接,释放相关的资源。
*
*/
void RTMP264_Close();
librtmp_send264.cpp
/**
*
*
* 本程序用于将内存中的H.264数据推送至RTMP流媒体服务器。
*
*/
#include <stdio.h>
#include <stdlib.h>
#include "librtmp_send264.h"
#include "librtmp\rtmp.h"
#include "librtmp\rtmp_sys.h"
#include "librtmp\amf.h"
#include "sps_decode.h"
#include <windows.h>
#include <crtdbg.h>
#ifdef WIN32
#include <windows.h>
#pragma comment(lib,"WS2_32.lib")
#pragma comment(lib,"winmm.lib")
#endif
#include <iostream>
//定义包头长度,RTMP_MAX_HEADER_SIZE=18
#define RTMP_HEAD_SIZE (sizeof(RTMPPacket)+RTMP_MAX_HEADER_SIZE)
//存储Nal单元数据的buffer大小
//#define BUFFER_SIZE 65536
#define BUFFER_SIZE 95000
//搜寻Nal单元时的一些标志
#define GOT_A_NAL_CROSS_BUFFER BUFFER_SIZE+1
#define GOT_A_NAL_INCLUDE_A_BUFFER BUFFER_SIZE+2
#define NO_MORE_BUFFER_TO_READ BUFFER_SIZE+3
/**
* _NaluUnit
* 内部结构体。该结构体主要用于存储和传递Nal单元的类型、大小和数据
*/
typedef struct _NaluUnit
{
int type;
int size;
unsigned char *data;
}NaluUnit;
/**
* _RTMPMetadata
* 内部结构体。该结构体主要用于存储和传递元数据信息
*/
typedef struct _RTMPMetadata
{
// video, must be h264 type
unsigned int nWidth;
unsigned int nHeight;
unsigned int nFrameRate;
unsigned int nSpsLen;
unsigned char *Sps;
unsigned int nPpsLen;
unsigned char *Pps;
} RTMPMetadata,*LPRTMPMetadata;
enum
{
VIDEO_CODECID_H264 = 7,
};
/**
* 初始化winsock
*
* @成功则返回1 , 失败则返回相应错误代码
*/
int InitSockets()
{
#ifdef WIN32
WORD version;
WSADATA wsaData;
version = MAKEWORD(1, 1);
return (WSAStartup(version, &wsaData) == 0);
#else
return TRUE;
#endif
}
/**
* 释放winsock
*
* @成功则返回0 , 失败则返回相应错误代码
*/
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;
}
unsigned int m_nFileBufSize;
unsigned int nalhead_pos;
RTMP* m_pRtmp;
RTMPMetadata metaData;
unsigned char *m_pFileBuf;
unsigned char *m_pFileBuf_tmp;
unsigned char* m_pFileBuf_tmp_old; //used for realloc
/**
* 初始化并连接到服务器
*
* @param url 服务器上对应webapp的地址
*
* @成功则返回1 , 失败则返回0
*/
int RTMP264_Connect(const char* url)
{
nalhead_pos=0;
m_nFileBufSize=BUFFER_SIZE;
m_pFileBuf=(unsigned char*)malloc(BUFFER_SIZE);
//_CrtDumpMemoryLeaks();
m_pFileBuf_tmp=(unsigned char*)malloc(2*BUFFER_SIZE);
//_CrtDumpMemoryLeaks();
InitSockets();
m_pRtmp = RTMP_Alloc();
RTMP_Init(m_pRtmp);
/*设置URL*/
if (RTMP_SetupURL(m_pRtmp,(char*)url) == FALSE)
{
RTMP_Free(m_pRtmp);
return false;
}
/*设置可写,即发布流,这个函数必须在连接前使用,否则无效*/
RTMP_EnableWrite(m_pRtmp);
/*连接服务器*/
if (RTMP_Connect(m_pRtmp, NULL) == FALSE)
{
RTMP_Free(m_pRtmp);
return false;
}
/*连接流*/
if (RTMP_ConnectStream(m_pRtmp,0) == FALSE)
{
RTMP_Close(m_pRtmp);
RTMP_Free(m_pRtmp);
return false;
}
return true;
}
/**
* 断开连接,释放相关的资源。
*
*/
void RTMP264_Close()
{
if(m_pRtmp)
{
RTMP_Close(m_pRtmp);
RTMP_Free(m_pRtmp);
m_pRtmp = NULL;
}
CleanupSockets();
if (m_pFileBuf != NULL)
{
free(m_pFileBuf);
}
if (m_pFileBuf_tmp != NULL)
{
free(m_pFileBuf_tmp);
}
}
/**
* 发送RTMP数据包
*
* @param nPacketType 数据类型
* @param data 存储数据内容
* @param size 数据大小
* @param nTimestamp 当前包的时间戳
*
* @成功则返回 1 , 失败则返回一个小于0的数
*/
int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp)
{
RTMPPacket* packet;
/*分配包内存和初始化,len为包体长度*/
//packet = (RTMPPacket *)(PBYTE) ::HeapAlloc(GetProcessHeap(), 0, RTMP_HEAD_SIZE+1024);
//packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE+size);
packet = (RTMPPacket *)(PBYTE) ::HeapAlloc(GetProcessHeap(), 0, RTMP_HEAD_SIZE+102400);
//_CrtDumpMemoryLeaks();
memset(packet,0,RTMP_HEAD_SIZE);
/*包体内存*/
packet->m_body = (char *)packet + RTMP_HEAD_SIZE;
packet->m_nBodySize = size;
memcpy(packet->m_body,data,size);
packet->m_hasAbsTimestamp = 0;
packet->m_packetType = nPacketType; /*此处为类型有两种一种是音频,一种是视频*/
packet->m_nInfoField2 = m_pRtmp->m_stream_id;
packet->m_nChannel = 0x04;
packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
if (RTMP_PACKET_TYPE_AUDIO ==nPacketType && size !=4)
{
packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
}
packet->m_nTimeStamp = nTimestamp;
/*发送*/
int nRet =0;
if (RTMP_IsConnected(m_pRtmp))
{
nRet = RTMP_SendPacket(m_pRtmp,packet,FALSE); /*TRUE为放进发送队列,FALSE是不放进发送队列,直接发送*/
}
/*释放内存*/
::HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, packet);
//free(packet);
return nRet;
}
/**
* 发送视频的sps和pps信息
*
* @param pps 存储视频的pps信息
* @param pps_len 视频的pps信息长度
* @param sps 存储视频的pps信息
* @param sps_len 视频的sps信息长度
*
* @成功则返回 1 , 失败则返回0
*/
int SendVideoSpsPps(unsigned char *pps,int pps_len,unsigned char * sps,int sps_len)
{
RTMPPacket * packet=NULL;//rtmp包结构
unsigned char * body=NULL;
int i;
//packet = (RTMPPacket *)(PBYTE) ::HeapAlloc(GetProcessHeap(), 0, RTMP_HEAD_SIZE+1024);
//_CrtDumpMemoryLeaks();
packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE+1024);
//RTMPPacket_Reset(packet);//重置packet状态
memset(packet,0,RTMP_HEAD_SIZE+1024);
packet->m_body = (char *)packet + RTMP_HEAD_SIZE;
body = (unsigned char *)packet->m_body;
i = 0;
body[i++] = 0x17;
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
/*AVCDecoderConfigurationRecord*/
body[i++] = 0x01;
body[i++] = sps[1];
body[i++] = sps[2];
body[i++] = sps[3];
body[i++] = 0xff;
/*sps*/
body[i++] = 0xe1;
body[i++] = (sps_len >> 8) & 0xff;
body[i++] = sps_len & 0xff;
memcpy(&body[i],sps,sps_len);
i += sps_len;
/*pps*/
body[i++] = 0x01;
body[i++] = (pps_len >> 8) & 0xff;
body[i++] = (pps_len) & 0xff;
memcpy(&body[i],pps,pps_len);
i += pps_len;
packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
packet->m_nBodySize = i;
packet->m_nChannel = 0x04;
packet->m_nTimeStamp = 0;
packet->m_hasAbsTimestamp = 0;
packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
packet->m_nInfoField2 = m_pRtmp->m_stream_id;
/*调用发送接口*/
int nRet = RTMP_SendPacket(m_pRtmp,packet,TRUE);
free(packet); //释放内存
//::HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, packet);
return nRet;
}
/**
* 发送H264数据帧
*
* @param data 存储数据帧内容
* @param size 数据帧的大小
* @param bIsKeyFrame 记录该帧是否为关键帧
* @param nTimeStamp 当前帧的时间戳
*
* @成功则返回 1 , 失败则返回0
*/
int SendH264Packet(unsigned char *data,unsigned int size,int bIsKeyFrame,unsigned int nTimeStamp)
{
if(data == NULL && size<11){
return false;
}
unsigned char body[100000] = {0};
//char *x = (char*)malloc(100);
//const int nHeapSize = 1024;
//unsigned char* body = (unsigned char*)(PBYTE) ::HeapAlloc(GetProcessHeap(), 0, size +9);
//_CrtDumpMemoryLeaks();
//unsigned char* body = new unsigned char[size +9];
//unsigned char *body = (unsigned char*)malloc(size+9);
//memset(body,0,size+9);
int i = 0;
if(bIsKeyFrame){
body[i++] = 0x17;// 1:Iframe 7:AVC
body[i++] = 0x01;// AVC NALU
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
// NALU size
body[i++] = size>>24 &0xff;
body[i++] = size>>16 &0xff;
body[i++] = size>>8 &0xff;
body[i++] = size&0xff;
// NALU data
memcpy(&body[i],data,size);
SendVideoSpsPps(metaData.Pps,metaData.nPpsLen,metaData.Sps,metaData.nSpsLen);
}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 &0xff;
body[i++] = size>>16 &0xff;
body[i++] = size>>8 &0xff;
body[i++] = size&0xff;
// NALU data
memcpy(&body[i],data,size);
}
int bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp);
//free(body);
//::HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, body);
return bRet;
}
/**
* 从内存中读取出第一个Nal单元
*
* @param nalu 存储nalu数据
* @param read_buffer 回调函数,当数据不足的时候,系统会自动调用该函数获取输入数据。
* 2个参数功能:
* uint8_t *buf:外部数据送至该地址
* int buf_size:外部数据大小
* 返回值:成功读取的内存大小
* @成功则返回 1 , 失败则返回0
*/
int ReadFirstNaluFromBuf(NaluUnit &nalu,int (*read_buffer)(uint8_t *buf, int buf_size))
{
int naltail_pos=nalhead_pos;
memset(m_pFileBuf_tmp,0,BUFFER_SIZE);
while(nalhead_pos<m_nFileBufSize)
{
//search for nal header
if(m_pFileBuf[nalhead_pos++] == 0x00 &&
m_pFileBuf[nalhead_pos++] == 0x00)
{
if(m_pFileBuf[nalhead_pos++] == 0x01)
goto gotnal_head;
else
{
//cuz we have done an i++ before,so we need to roll back now
nalhead_pos--;
if(m_pFileBuf[nalhead_pos++] == 0x00 &&
m_pFileBuf[nalhead_pos++] == 0x01)
goto gotnal_head;
else
continue;
}
}
else
continue;
//search for nal tail which is also the head of next nal
gotnal_head:
//normal case:the whole nal is in this m_pFileBuf
naltail_pos = nalhead_pos;
while (naltail_pos<m_nFileBufSize)
{
if(m_pFileBuf[naltail_pos++] == 0x00 &&
m_pFileBuf[naltail_pos++] == 0x00 )
{
if(m_pFileBuf[naltail_pos++] == 0x01)
{
nalu.size = (naltail_pos-3)-nalhead_pos;
break;
}
else
{
naltail_pos--;
if(m_pFileBuf[naltail_pos++] == 0x00 &&
m_pFileBuf[naltail_pos++] == 0x01)
{
nalu.size = (naltail_pos-4)-nalhead_pos;
break;
}
}
}
}
nalu.type = m_pFileBuf[nalhead_pos]&0x1f;
memcpy(m_pFileBuf_tmp, m_pFileBuf + nalhead_pos, nalu.size);
nalu.data=m_pFileBuf_tmp;
nalhead_pos=naltail_pos;
if(nalu.size < 0)
{
std::cout << "break down!" << std::endl;
}
return TRUE;
}
if(nalu.size < 0)
{
std::cout << "break down!" << std::endl;
}
}
/**
* 从内存中读取出一个Nal单元
*
* @param nalu 存储nalu数据
* @param read_buffer 回调函数,当数据不足的时候,系统会自动调用该函数获取输入数据。
* 2个参数功能:
* uint8_t *buf:外部数据送至该地址
* int buf_size:外部数据大小
* 返回值:成功读取的内存大小
* @成功则返回 1 , 失败则返回0
*/
int ReadOneNaluFromBuf(NaluUnit &nalu,int (*read_buffer)(uint8_t *buf, int buf_size))
{
int naltail_pos=nalhead_pos;
int ret;
int nalustart;//nal的开始标识符是几个00
memset(m_pFileBuf_tmp,0,BUFFER_SIZE);
nalu.size=0;
while(1)
{
// 这里直接发送剩下的数据就可以了.
if(nalhead_pos==NO_MORE_BUFFER_TO_READ)
return FALSE;
while(naltail_pos<m_nFileBufSize)
{
//search for nal tail
if(m_pFileBuf[naltail_pos++] == 0x00 &&
m_pFileBuf[naltail_pos++] == 0x00)
{
if(m_pFileBuf[naltail_pos++] == 0x01)
{
nalustart=3;
goto gotnal ;
}
else
{
//cuz we have done an i++ before,so we need to roll back now
naltail_pos--;
if(m_pFileBuf[naltail_pos++] == 0x00 &&
m_pFileBuf[naltail_pos++] == 0x01)
{
nalustart=4;
goto gotnal;
}
else
continue;
}
}
else
continue;
gotnal:
/**
*special case1:parts of the nal lies in a m_pFileBuf and we have to read from buffer
*again to get the rest part of this nal
*/
// 跨包了
// 从头开始读取, tmp 已经有数据了.
if(nalhead_pos==GOT_A_NAL_CROSS_BUFFER || nalhead_pos==GOT_A_NAL_INCLUDE_A_BUFFER)
{
nalu.size = nalu.size+naltail_pos-nalustart;
if(nalu.size>BUFFER_SIZE)
{
m_pFileBuf_tmp_old=m_pFileBuf_tmp; //// save pointer in case realloc fails
if((m_pFileBuf_tmp = (unsigned char*)realloc(m_pFileBuf_tmp,nalu.size)) == NULL )
{
free( m_pFileBuf_tmp_old ); // free original block
return FALSE;
}
}
memcpy(m_pFileBuf_tmp+nalu.size+nalustart-naltail_pos,m_pFileBuf,naltail_pos-nalustart);
nalu.data=m_pFileBuf_tmp;
nalhead_pos=naltail_pos;
return TRUE;
}
//normal case:the whole nal is in this m_pFileBuf
else
{
nalu.type = m_pFileBuf[nalhead_pos]&0x1f;
nalu.size=naltail_pos-nalhead_pos-nalustart;
if(nalu.type==0x06)
{
nalhead_pos=naltail_pos;
continue;
}
memcpy(m_pFileBuf_tmp,m_pFileBuf+nalhead_pos,nalu.size);
nalu.data=m_pFileBuf_tmp;
nalhead_pos=naltail_pos;
return TRUE;
}
}
if(naltail_pos>=m_nFileBufSize && nalhead_pos!=GOT_A_NAL_CROSS_BUFFER && nalhead_pos != GOT_A_NAL_INCLUDE_A_BUFFER)
{
nalu.size = BUFFER_SIZE-nalhead_pos;
nalu.type = m_pFileBuf[nalhead_pos]&0x1f;
memcpy(m_pFileBuf_tmp,m_pFileBuf+nalhead_pos,nalu.size);
// ret 会小于0
//if((ret=read_buffer(m_pFileBuf,m_nFileBufSize))<(BUFFER_SIZE-nalu.size) && ret >= 0)
// 确保读取的内容大小等同于 BUFFER_SIZE;
// 如有BUG, 则可能是因为该线程 sleep 等待时间太短, BUFFERSIZE 太大, 应当等其写多点
// 如果小于 BUFFER_SIZE 则表示已经没东西可读了
if((ret=read_buffer(m_pFileBuf,m_nFileBufSize))<BUFFER_SIZE)
//if((ret=read_buffer(m_pFileBuf,m_nFileBufSize)) <= 0)
{
memcpy(m_pFileBuf_tmp+nalu.size,m_pFileBuf,ret);
nalu.size=nalu.size+ret;
nalu.data=m_pFileBuf_tmp;
nalhead_pos=NO_MORE_BUFFER_TO_READ;
return FALSE;
}
naltail_pos=0;
nalhead_pos=GOT_A_NAL_CROSS_BUFFER;
continue;
}
if(nalhead_pos==GOT_A_NAL_CROSS_BUFFER || nalhead_pos == GOT_A_NAL_INCLUDE_A_BUFFER)
{
nalu.size = BUFFER_SIZE+nalu.size;
m_pFileBuf_tmp_old=m_pFileBuf_tmp; //// save pointer in case realloc fails
if((m_pFileBuf_tmp = (unsigned char*)realloc(m_pFileBuf_tmp,nalu.size)) == NULL )
{
free( m_pFileBuf_tmp_old ); // free original block
return FALSE;
}
memcpy(m_pFileBuf_tmp+nalu.size-BUFFER_SIZE,m_pFileBuf,BUFFER_SIZE);
if((ret=read_buffer(m_pFileBuf,m_nFileBufSize))<BUFFER_SIZE)
{
memcpy(m_pFileBuf_tmp+nalu.size,m_pFileBuf,ret);
nalu.size=nalu.size+ret;
nalu.data=m_pFileBuf_tmp;
nalhead_pos=NO_MORE_BUFFER_TO_READ;
return FALSE;
}
naltail_pos=0;
nalhead_pos=GOT_A_NAL_INCLUDE_A_BUFFER;
continue;
}
}
return FALSE;
}
/**
* 将内存中的一段H.264编码的视频数据利用RTMP协议发送到服务器
*
* @param read_buffer 回调函数,当数据不足的时候,系统会自动调用该函数获取输入数据。
* 2个参数功能:
* uint8_t *buf:外部数据送至该地址
* int buf_size:外部数据大小
* 返回值:成功读取的内存大小
* @成功则返回1 , 失败则返回0
*/
int RTMP264_Send(int (*read_buffer)(unsigned char *buf, int buf_size))
{
int ret;
uint32_t now,last_update;
memset(&metaData,0,sizeof(RTMPMetadata));
memset(m_pFileBuf,0,BUFFER_SIZE);
if((ret=read_buffer(m_pFileBuf,m_nFileBufSize))<0)
{
return FALSE;
}
// 判断这里是否应当设置 nalhead_pos 为 0, 因为 m_pFileBuf 重新读取, 从头写
NaluUnit naluUnit;
// 读取SPS帧
ReadFirstNaluFromBuf(naluUnit,read_buffer);
metaData.nSpsLen = naluUnit.size;
metaData.Sps=NULL;
metaData.Sps=(unsigned char*)malloc(naluUnit.size);
//_CrtDumpMemoryLeaks();
memcpy(metaData.Sps,naluUnit.data,naluUnit.size);
// 读取PPS帧
ReadOneNaluFromBuf(naluUnit,read_buffer);
metaData.nPpsLen = naluUnit.size;
metaData.Pps=NULL;
metaData.Pps=(unsigned char*)malloc(naluUnit.size);
//_CrtDumpMemoryLeaks();
memcpy(metaData.Pps,naluUnit.data,naluUnit.size);
// 解码SPS,获取视频图像宽、高信息
int width = 0,height = 0, fps=0;
h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height,fps);
//metaData.nWidth = width;
//metaData.nHeight = height;
if(fps)
metaData.nFrameRate = fps;
else
metaData.nFrameRate = 30;
//发送PPS,SPS
//ret=SendVideoSpsPps(metaData.Pps,metaData.nPpsLen,metaData.Sps,metaData.nSpsLen);
//if(ret!=1)
// return FALSE;
unsigned int tick = 0;
unsigned int tick_gap = 1000/metaData.nFrameRate;
ReadOneNaluFromBuf(naluUnit,read_buffer);
int bKeyframe = (naluUnit.type == 0x05) ? TRUE : FALSE;
while(SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick))
{
got_sps_pps:
//if(naluUnit.size==8581)
printf("NALU size:%8d\n",naluUnit.size);
last_update=RTMP_GetTime();
if(!ReadOneNaluFromBuf(naluUnit,read_buffer))
goto end;
if(naluUnit.type == 0x07 || naluUnit.type == 0x08)
goto got_sps_pps;
bKeyframe = (naluUnit.type == 0x05) ? TRUE : FALSE;
tick +=tick_gap;
now=RTMP_GetTime();
int n = tick_gap-now+last_update;
if(n < 0)
n = 0;
if(n>tick_gap)
n = tick_gap;
//msleep(n);
//msleep(tick_gap-now+last_update);
//msleep(200);
}
end:
free(metaData.Sps);
free(metaData.Pps);
return TRUE;
}
浙公网安备 33010602011771号