前面的文章中提到了通过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提供的接口如下。

  1. class CRTMPStream
  2. {
  3. public:
  4. CRTMPStream(void);
  5. ~CRTMPStream(void);
  6. public:
  7. // 连接到RTMP Server
  8. bool Connect(const char* url);
  9. // 断开连接
  10. void Close();
  11. // 发送MetaData
  12. bool SendMetadata(LPRTMPMetadata lpMetaData);
  13. // 发送H264数据帧
  14. bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
  15. // 发送H264文件
  16. bool SendH264File(const char *pFileName);
  17. //...
  18. }

调用示例:

  1. #include <stdio.h>
  2. #include "RTMPStream\RTMPStream.h"
  3. int main(int argc,char* argv[])
  4. {
  5. CRTMPStream rtmpSender;
  6. bool bRet = rtmpSender.Connect("rtmp://192.168.1.104/live/test");
  7. rtmpSender.SendH264File("E:\\video\\test.264");
  8. rtmpSender.Close();
  9. }

通过JwPlayer播放效果如下:

最后附上RTMPStream完整的代码:

  1. /********************************************************************
  2. filename:   RTMPStream.h
  3. created:    2013-04-3
  4. author:     firehood
  5. purpose:    发送H264视频到RTMP Server,使用libRtmp库
  6. *********************************************************************/
  7. #pragma once
  8. #include "rtmp.h"
  9. #include "rtmp_sys.h"
  10. #include "amf.h"
  11. #include <stdio.h>
  12. #define FILEBUFSIZE (1024 * 1024 * 10)       //  10M
  13. // NALU单元
  14. typedef struct _NaluUnit
  15. {
  16. int type;
  17. int size;
  18. unsigned char *data;
  19. }NaluUnit;
  20. typedef struct _RTMPMetadata
  21. {
  22. // video, must be h264 type
  23. unsigned int    nWidth;
  24. unsigned int    nHeight;
  25. unsigned int    nFrameRate;     // fps
  26. unsigned int    nVideoDataRate; // bps
  27. unsigned int    nSpsLen;
  28. unsigned char   Sps[1024];
  29. unsigned int    nPpsLen;
  30. unsigned char   Pps[1024];
  31. // audio, must be aac type
  32. bool            bHasAudio;
  33. unsigned int    nAudioSampleRate;
  34. unsigned int    nAudioSampleSize;
  35. unsigned int    nAudioChannels;
  36. char            pAudioSpecCfg;
  37. unsigned int    nAudioSpecCfgLen;
  38. } RTMPMetadata,*LPRTMPMetadata;
  39. class CRTMPStream
  40. {
  41. public:
  42. CRTMPStream(void);
  43. ~CRTMPStream(void);
  44. public:
  45. // 连接到RTMP Server
  46. bool Connect(const char* url);
  47. // 断开连接
  48. void Close();
  49. // 发送MetaData
  50. bool SendMetadata(LPRTMPMetadata lpMetaData);
  51. // 发送H264数据帧
  52. bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
  53. // 发送H264文件
  54. bool SendH264File(const char *pFileName);
  55. private:
  56. // 送缓存中读取一个NALU包
  57. bool ReadOneNaluFromBuf(NaluUnit &nalu);
  58. // 发送数据
  59. int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp);
  60. private:
  61. RTMP* m_pRtmp;
  62. unsigned char* m_pFileBuf;
  63. unsigned int  m_nFileBufSize;
  64. unsigned int  m_nCurPos;
  65. };
  1. /********************************************************************
  2. filename:   RTMPStream.cpp
  3. created:    2013-04-3
  4. author:     firehood
  5. purpose:    发送H264视频到RTMP Server,使用libRtmp库
  6. *********************************************************************/
  7. #include "RTMPStream.h"
  8. #include "SpsDecode.h"
  9. #ifdef WIN32
  10. #include <windows.h>
  11. #endif
  12. #ifdef WIN32
  13. #pragma comment(lib,"WS2_32.lib")
  14. #pragma comment(lib,"winmm.lib")
  15. #endif
  16. enum
  17. {
  18. FLV_CODECID_H264 = 7,
  19. };
  20. int InitSockets()
  21. {
  22. #ifdef WIN32
  23. WORD version;
  24. WSADATA wsaData;
  25. version = MAKEWORD(1, 1);
  26. return (WSAStartup(version, &wsaData) == 0);
  27. #else
  28. return TRUE;
  29. #endif
  30. }
  31. inline void CleanupSockets()
  32. {
  33. #ifdef WIN32
  34. WSACleanup();
  35. #endif
  36. }
  37. char * put_byte( char *output, uint8_t nVal )
  38. {
  39. output[0] = nVal;
  40. return output+1;
  41. }
  42. char * put_be16(char *output, uint16_t nVal )
  43. {
  44. output[1] = nVal & 0xff;
  45. output[0] = nVal >> 8;
  46. return output+2;
  47. }
  48. char * put_be24(char *output,uint32_t nVal )
  49. {
  50. output[2] = nVal & 0xff;
  51. output[1] = nVal >> 8;
  52. output[0] = nVal >> 16;
  53. return output+3;
  54. }
  55. char * put_be32(char *output, uint32_t nVal )
  56. {
  57. output[3] = nVal & 0xff;
  58. output[2] = nVal >> 8;
  59. output[1] = nVal >> 16;
  60. output[0] = nVal >> 24;
  61. return output+4;
  62. }
  63. char *  put_be64( char *output, uint64_t nVal )
  64. {
  65. output=put_be32( output, nVal >> 32 );
  66. output=put_be32( output, nVal );
  67. return output;
  68. }
  69. char * put_amf_string( char *c, const char *str )
  70. {
  71. uint16_t len = strlen( str );
  72. c=put_be16( c, len );
  73. memcpy(c,str,len);
  74. return c+len;
  75. }
  76. char * put_amf_double( char *c, double d )
  77. {
  78. *c++ = AMF_NUMBER;  /* type: Number */
  79. {
  80. unsigned char *ci, *co;
  81. ci = (unsigned char *)&d;
  82. co = (unsigned char *)c;
  83. co[0] = ci[7];
  84. co[1] = ci[6];
  85. co[2] = ci[5];
  86. co[3] = ci[4];
  87. co[4] = ci[3];
  88. co[5] = ci[2];
  89. co[6] = ci[1];
  90. co[7] = ci[0];
  91. }
  92. return c+8;
  93. }
  94. CRTMPStream::CRTMPStream(void):
  95. m_pRtmp(NULL),
  96. m_nFileBufSize(0),
  97. m_nCurPos(0)
  98. {
  99. m_pFileBuf = new unsigned char[FILEBUFSIZE];
  100. memset(m_pFileBuf,0,FILEBUFSIZE);
  101. InitSockets();
  102. m_pRtmp = RTMP_Alloc();
  103. RTMP_Init(m_pRtmp);
  104. }
  105. CRTMPStream::~CRTMPStream(void)
  106. {
  107. Close();
  108. WSACleanup();
  109. delete[] m_pFileBuf;
  110. }
  111. bool CRTMPStream::Connect(const char* url)
  112. {
  113. if(RTMP_SetupURL(m_pRtmp, (char*)url)<0)
  114. {
  115. return FALSE;
  116. }
  117. RTMP_EnableWrite(m_pRtmp);
  118. if(RTMP_Connect(m_pRtmp, NULL)<0)
  119. {
  120. return FALSE;
  121. }
  122. if(RTMP_ConnectStream(m_pRtmp,0)<0)
  123. {
  124. return FALSE;
  125. }
  126. return TRUE;
  127. }
  128. void CRTMPStream::Close()
  129. {
  130. if(m_pRtmp)
  131. {
  132. RTMP_Close(m_pRtmp);
  133. RTMP_Free(m_pRtmp);
  134. m_pRtmp = NULL;
  135. }
  136. }
  137. int CRTMPStream::SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp)
  138. {
  139. if(m_pRtmp == NULL)
  140. {
  141. return FALSE;
  142. }
  143. RTMPPacket packet;
  144. RTMPPacket_Reset(&packet);
  145. RTMPPacket_Alloc(&packet,size);
  146. packet.m_packetType = nPacketType;
  147. packet.m_nChannel = 0x04;
  148. packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
  149. packet.m_nTimeStamp = nTimestamp;
  150. packet.m_nInfoField2 = m_pRtmp->m_stream_id;
  151. packet.m_nBodySize = size;
  152. memcpy(packet.m_body,data,size);
  153. int nRet = RTMP_SendPacket(m_pRtmp,&packet,0);
  154. RTMPPacket_Free(&packet);
  155. return nRet;
  156. }
  157. bool CRTMPStream::SendMetadata(LPRTMPMetadata lpMetaData)
  158. {
  159. if(lpMetaData == NULL)
  160. {
  161. return false;
  162. }
  163. char body[1024] = {0};;
  164. char * p = (char *)body;
  165. p = put_byte(p, AMF_STRING );
  166. p = put_amf_string(p , "@setDataFrame" );
  167. p = put_byte( p, AMF_STRING );
  168. p = put_amf_string( p, "onMetaData" );
  169. p = put_byte(p, AMF_OBJECT );
  170. p = put_amf_string( p, "copyright" );
  171. p = put_byte(p, AMF_STRING );
  172. p = put_amf_string( p, "firehood" );
  173. p =put_amf_string( p, "width");
  174. p =put_amf_double( p, lpMetaData->nWidth);
  175. p =put_amf_string( p, "height");
  176. p =put_amf_double( p, lpMetaData->nHeight);
  177. p =put_amf_string( p, "framerate" );
  178. p =put_amf_double( p, lpMetaData->nFrameRate);
  179. p =put_amf_string( p, "videocodecid" );
  180. p =put_amf_double( p, FLV_CODECID_H264 );
  181. p =put_amf_string( p, "" );
  182. p =put_byte( p, AMF_OBJECT_END  );
  183. int index = p-body;
  184. SendPacket(RTMP_PACKET_TYPE_INFO,(unsigned char*)body,p-body,0);
  185. int i = 0;
  186. body[i++] = 0x17; // 1:keyframe  7:AVC
  187. body[i++] = 0x00; // AVC sequence header
  188. body[i++] = 0x00;
  189. body[i++] = 0x00;
  190. body[i++] = 0x00; // fill in 0;
  191. // AVCDecoderConfigurationRecord.
  192. body[i++] = 0x01; // configurationVersion
  193. body[i++] = lpMetaData->Sps[1]; // AVCProfileIndication
  194. body[i++] = lpMetaData->Sps[2]; // profile_compatibility
  195. body[i++] = lpMetaData->Sps[3]; // AVCLevelIndication
  196. body[i++] = 0xff; // lengthSizeMinusOne
  197. // sps nums
  198. body[i++] = 0xE1; //&0x1f
  199. // sps data length
  200. body[i++] = lpMetaData->nSpsLen>>8;
  201. body[i++] = lpMetaData->nSpsLen&0xff;
  202. // sps data
  203. memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);
  204. i= i+lpMetaData->nSpsLen;
  205. // pps nums
  206. body[i++] = 0x01; //&0x1f
  207. // pps data length
  208. body[i++] = lpMetaData->nPpsLen>>8;
  209. body[i++] = lpMetaData->nPpsLen&0xff;
  210. // sps data
  211. memcpy(&body[i],lpMetaData->Pps,lpMetaData->nPpsLen);
  212. i= i+lpMetaData->nPpsLen;
  213. return SendPacket(RTMP_PACKET_TYPE_VIDEO,(unsigned char*)body,i,0);
  214. }
  215. bool CRTMPStream::SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp)
  216. {
  217. if(data == NULL && size<11)
  218. {
  219. return false;
  220. }
  221. unsigned char *body = new unsigned char[size+9];
  222. int i = 0;
  223. if(bIsKeyFrame)
  224. {
  225. body[i++] = 0x17;// 1:Iframe  7:AVC
  226. }
  227. else
  228. {
  229. body[i++] = 0x27;// 2:Pframe  7:AVC
  230. }
  231. body[i++] = 0x01;// AVC NALU
  232. body[i++] = 0x00;
  233. body[i++] = 0x00;
  234. body[i++] = 0x00;
  235. // NALU size
  236. body[i++] = size>>24;
  237. body[i++] = size>>16;
  238. body[i++] = size>>8;
  239. body[i++] = size&0xff;;
  240. // NALU data
  241. memcpy(&body[i],data,size);
  242. bool bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp);
  243. delete[] body;
  244. return bRet;
  245. }
  246. bool CRTMPStream::SendH264File(const char *pFileName)
  247. {
  248. if(pFileName == NULL)
  249. {
  250. return FALSE;
  251. }
  252. FILE *fp = fopen(pFileName, "rb");
  253. if(!fp)
  254. {
  255. printf("ERROR:open file %s failed!",pFileName);
  256. }
  257. fseek(fp, 0, SEEK_SET);
  258. m_nFileBufSize = fread(m_pFileBuf, sizeof(unsigned char), FILEBUFSIZE, fp);
  259. if(m_nFileBufSize >= FILEBUFSIZE)
  260. {
  261. printf("warning : File size is larger than BUFSIZE\n");
  262. }
  263. fclose(fp);
  264. RTMPMetadata metaData;
  265. memset(&metaData,0,sizeof(RTMPMetadata));
  266. NaluUnit naluUnit;
  267. // 读取SPS帧
  268. ReadOneNaluFromBuf(naluUnit);
  269. metaData.nSpsLen = naluUnit.size;
  270. memcpy(metaData.Sps,naluUnit.data,naluUnit.size);
  271. // 读取PPS帧
  272. ReadOneNaluFromBuf(naluUnit);
  273. metaData.nPpsLen = naluUnit.size;
  274. memcpy(metaData.Pps,naluUnit.data,naluUnit.size);
  275. // 解码SPS,获取视频图像宽、高信息
  276. int width = 0,height = 0;
  277. h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height);
  278. metaData.nWidth = width;
  279. metaData.nHeight = height;
  280. metaData.nFrameRate = 25;
  281. // 发送MetaData
  282. SendMetadata(&metaData);
  283. unsigned int tick = 0;
  284. while(ReadOneNaluFromBuf(naluUnit))
  285. {
  286. bool bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;
  287. // 发送H264数据帧
  288. SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick);
  289. msleep(40);
  290. tick +=40;
  291. }
  292. return TRUE;
  293. }
  294. bool CRTMPStream::ReadOneNaluFromBuf(NaluUnit &nalu)
  295. {
  296. int i = m_nCurPos;
  297. while(i<m_nFileBufSize)
  298. {
  299. if(m_pFileBuf[i++] == 0x00 &&
  300. m_pFileBuf[i++] == 0x00 &&
  301. m_pFileBuf[i++] == 0x00 &&
  302. m_pFileBuf[i++] == 0x01
  303. )
  304. {
  305. int pos = i;
  306. while (pos<m_nFileBufSize)
  307. {
  308. if(m_pFileBuf[pos++] == 0x00 &&
  309. m_pFileBuf[pos++] == 0x00 &&
  310. m_pFileBuf[pos++] == 0x00 &&
  311. m_pFileBuf[pos++] == 0x01
  312. )
  313. {
  314. break;
  315. }
  316. }
  317. if(pos == nBufferSize)
  318. {
  319. nalu.size = pos-i;
  320. }
  321. else
  322. {
  323. nalu.size = (pos-4)-i;
  324. }
  325. nalu.type = m_pFileBuf[i]&0x1f;
  326. nalu.data = &m_pFileBuf[i];
  327. m_nCurPos = pos-4;
  328. return TRUE;
  329. }
  330. }
  331. return FALSE;
  332. }

附上SpsDecode.h文件:

  1. #include <stdio.h>
  2. #include <math.h>
  3. UINT Ue(BYTE *pBuff, UINT nLen, UINT &nStartBit)
  4. {
  5. //计算0bit的个数
  6. UINT nZeroNum = 0;
  7. while (nStartBit < nLen * 8)
  8. {
  9. if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8))) //&:按位与,%取余
  10. {
  11. break;
  12. }
  13. nZeroNum++;
  14. nStartBit++;
  15. }
  16. nStartBit ++;
  17. //计算结果
  18. DWORD dwRet = 0;
  19. for (UINT i=0; i<nZeroNum; i++)
  20. {
  21. dwRet <<= 1;
  22. if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
  23. {
  24. dwRet += 1;
  25. }
  26. nStartBit++;
  27. }
  28. return (1 << nZeroNum) - 1 + dwRet;
  29. }
  30. int Se(BYTE *pBuff, UINT nLen, UINT &nStartBit)
  31. {
  32. int UeVal=Ue(pBuff,nLen,nStartBit);
  33. double k=UeVal;
  34. int nValue=ceil(k/2);//ceil函数:ceil函数的作用是求不小于给定实数的最小整数。ceil(2)=ceil(1.2)=cei(1.5)=2.00
  35. if (UeVal % 2==0)
  36. nValue=-nValue;
  37. return nValue;
  38. }
  39. DWORD u(UINT BitCount,BYTE * buf,UINT &nStartBit)
  40. {
  41. DWORD dwRet = 0;
  42. for (UINT i=0; i<BitCount; i++)
  43. {
  44. dwRet <<= 1;
  45. if (buf[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
  46. {
  47. dwRet += 1;
  48. }
  49. nStartBit++;
  50. }
  51. return dwRet;
  52. }
  53. bool h264_decode_sps(BYTE * buf,unsigned int nLen,int &width,int &height)
  54. {
  55. UINT StartBit=0;
  56. int forbidden_zero_bit=u(1,buf,StartBit);
  57. int nal_ref_idc=u(2,buf,StartBit);
  58. int nal_unit_type=u(5,buf,StartBit);
  59. if(nal_unit_type==7)
  60. {
  61. int profile_idc=u(8,buf,StartBit);
  62. int constraint_set0_flag=u(1,buf,StartBit);//(buf[1] & 0x80)>>7;
  63. int constraint_set1_flag=u(1,buf,StartBit);//(buf[1] & 0x40)>>6;
  64. int constraint_set2_flag=u(1,buf,StartBit);//(buf[1] & 0x20)>>5;
  65. int constraint_set3_flag=u(1,buf,StartBit);//(buf[1] & 0x10)>>4;
  66. int reserved_zero_4bits=u(4,buf,StartBit);
  67. int level_idc=u(8,buf,StartBit);
  68. int seq_parameter_set_id=Ue(buf,nLen,StartBit);
  69. if( profile_idc == 100 || profile_idc == 110 ||
  70. profile_idc == 122 || profile_idc == 144 )
  71. {
  72. int chroma_format_idc=Ue(buf,nLen,StartBit);
  73. if( chroma_format_idc == 3 )
  74. int residual_colour_transform_flag=u(1,buf,StartBit);
  75. int bit_depth_luma_minus8=Ue(buf,nLen,StartBit);
  76. int bit_depth_chroma_minus8=Ue(buf,nLen,StartBit);
  77. int qpprime_y_zero_transform_bypass_flag=u(1,buf,StartBit);
  78. int seq_scaling_matrix_present_flag=u(1,buf,StartBit);
  79. int seq_scaling_list_present_flag[8];
  80. if( seq_scaling_matrix_present_flag )
  81. {
  82. for( int i = 0; i < 8; i++ ) {
  83. seq_scaling_list_present_flag[i]=u(1,buf,StartBit);
  84. }
  85. }
  86. }
  87. int log2_max_frame_num_minus4=Ue(buf,nLen,StartBit);
  88. int pic_order_cnt_type=Ue(buf,nLen,StartBit);
  89. if( pic_order_cnt_type == 0 )
  90. int log2_max_pic_order_cnt_lsb_minus4=Ue(buf,nLen,StartBit);
  91. else if( pic_order_cnt_type == 1 )
  92. {
  93. int delta_pic_order_always_zero_flag=u(1,buf,StartBit);
  94. int offset_for_non_ref_pic=Se(buf,nLen,StartBit);
  95. int offset_for_top_to_bottom_field=Se(buf,nLen,StartBit);
  96. int num_ref_frames_in_pic_order_cnt_cycle=Ue(buf,nLen,StartBit);
  97. int *offset_for_ref_frame=new int[num_ref_frames_in_pic_order_cnt_cycle];
  98. for( int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
  99. offset_for_ref_frame[i]=Se(buf,nLen,StartBit);
  100. delete [] offset_for_ref_frame;
  101. }
  102. int num_ref_frames=Ue(buf,nLen,StartBit);
  103. int gaps_in_frame_num_value_allowed_flag=u(1,buf,StartBit);
  104. int pic_width_in_mbs_minus1=Ue(buf,nLen,StartBit);
  105. int pic_height_in_map_units_minus1=Ue(buf,nLen,StartBit);
  106. width=(pic_width_in_mbs_minus1+1)*16;
  107. height=(pic_height_in_map_units_minus1+1)*16;
  108. return true;
  109. }
  110. else
  111. return false;
  112. }
 
 

嵌入式 H264视频通过RTMP直播的更多相关文章

  1. H264视频通过RTMP直播

    http://blog.csdn.net/firehood_/article/details/8783589 前面的文章中提到了通过RTSP(Real Time Streaming Protocol) ...

  2. 公布一个软件,轻新视频录播程序,H264/AAC录制视音频,保存FLV,支持RTMP直播

    已经上传到CSDN,下载地址:http://download.csdn.net/detail/avsuper/7421647,不要钱滴,嘿嘿... 本程序能够把摄像头视频和麦克风音频,录制为FLV文件 ...

  3. 实时监控、直播流、流媒体、视频网站开发方案流媒体服务器搭建及配置详解:使用nginx搭建rtmp直播、rtmp点播、,hls直播服务配置详解

    注意:这里不会讲到nginx流媒体模块如何安装的问题,只研究rtmp,hls直播和录制相关的nginx服务器配置文件的详细用法和说明.可以对照这些命令详解配置nginx -rtmp服务 一.nginx ...

  4. Android流媒体开发之路二:NDK开发Android端RTMP直播推流程序

    NDK开发Android端RTMP直播推流程序 经过一番折腾,成功把RTMP直播推流代码,通过NDK交叉编译的方式,移植到了Android下,从而实现了Android端采集摄像头和麦克缝数据,然后进行 ...

  5. RTSP协议转换RTMP直播协议

    RTSP协议转换RTMP直播协议 RTSP协议也是广泛使用的直播/点播流媒体协议,最近实现了一个RTSP协议转换RTMP直播协议的程序,为的是可以接收远端设备或服务器的多路RTSP直播数据,实时转换为 ...

  6. 园 首页 新随笔 联系 管理 订阅 订阅 RTSP协议转换RTMP直播协议

    RTSP协议转换RTMP直播协议 RTSP协议也是广泛使用的直播/点播流媒体协议,最近实现了一个RTSP协议转换RTMP直播协议的程序,为的是可以接收远端设备或服务器的多路RTSP直播数据,实时转换为 ...

  7. 将EasyRTMP_RTSP移植到Android平台实现的RTSP拉流转推RTMP直播流功能

    本文转自EasyDarwin开源团队成员Kim的博客:http://blog.csdn.net/jinlong0603/article/details/73253044 前言 安防互联网化的需求已经越 ...

  8. 三、直播整体流程 五、搭建Nginx+Rtmp直播流服务

    HTML5实现视频直播功能思路详解_html5教程技巧_脚本之家 https://m.jb51.net/html5/587215.html 三.直播整体流程 直播整体流程大致可分为: 视频采集端:可以 ...

  9. 基于nginx的rtmp直播服务器(nginx-rtmp-module实现)

    首先,在搭建服务之前先了解下目前主流的几个直播协议: 1.RTMP: 实时消息传输协议,Real Time Messaging Protocol,是 Adobe Systems 公司为 Flash 播 ...

随机推荐

  1. 李洪强漫谈iOS开发[C语言-006]-程序的描述方式

  2. Linux下Boost交叉编译

    http://davidlwq.iteye.com/blog/1580752 运行环境:ubuntu 12.04, boost 1.50.0 由于要把boost移植到arm板上去,所以折腾了一下,后来 ...

  3. 10 signs you’re dating the wrong person

    10 signs you’re dating the wrong person10个迹象表明TA不是你的真心人       Do you have any exes who were so awful ...

  4. C#之使用AutoResetEvent实现线程的顺序执行

    前几天一朋友问我如何实现线程的顺序执行,说真的,虽然看过CLR这本书,也把线程部分拜读了两遍,但是这个问题出来之后还是没有一个思路.今天在搜索资料的时候无意中再次看到AutoResetEvent这个东 ...

  5. SpringBoot配置属性之Server

    SpringBoot配置属性系列 SpringBoot配置属性之MVC SpringBoot配置属性之Server SpringBoot配置属性之DataSource SpringBoot配置属性之N ...

  6. Android开发环境的安装 Eclipse

    Android开发环境的安装 1 IDE Android可以使用开发的IDE有Eclipse 或者 Android Studio.Android Studio还处于v 0.1.x版本,是early a ...

  7. java web每天定时执行任务

    第一步: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ...

  8. 使用 httpkit 来替代 jetty

    Compojure 是一个基于 ring 的上层web开发框架.在 lein new compojure my-app 生成的项目中,默认是启用 jetty 服务器的,最近用到了 http-kit 中 ...

  9. 四大开源协议:BSD、Apache、GPL、LGPL

    参考文献:http://www.fsf.org/licensing/licenses/ 现今存在的开源协议很多,而经过Open Source Initiative组织通过批准的开源协议目前有58种.我 ...

  10. non-overlapping-intervals

    https://leetcode.com/problems/non-overlapping-intervals/ 其中还用到了Java的Comparator接口和其中的compare方法. packa ...