H264 live broadcast through RTMP

Source: Internet
Author: User

As mentioned in the previous article, the RTSP (Real Time Streaming Protocol) method is used to implement live video. However, one drawback of the RTSP method is that if the client needs to be accessed through a webpage, you need to embed an ActiveX control in the page, and ActiveX usually requires a signature for normal use. Otherwise, you need to change the browser settings when using ActiveX, and ActiveX only supports IE kernel browsers, chrome and FireFox require the IE Plug-in to run, which affects the user experience. RTMP (Real
Time Messaging Protocol) solves this problem. Because RTMP is a Streaming Media Protocol for FLASH, after a video is broadcast through RTMP, you only need to embed a WEB Player (such as javasplayer) on the Web to watch the video, and there are no restrictions on the platform, you can also conveniently watch the video on your phone.

An RTMP Server is required for video publishing through RTMP (common tools include FMS, Wowza Media Server, and open-source tools include CRtmpServer and Red5 ), as long as the original video is sent to the RTMP Server according to the RTMP protocol, the RTMP video stream can be published. To facilitate video packaging and publishing, an RTMPStream is encapsulated. Currently, only videos of H264 can be sent. You can directly send H264 data frames or H264 files. The interface provided by RTMPStream is as follows.

Class CRTMPStream {public: CRTMPStream (void );~ CRTMPStream (void); public: // Connect to RTMP Server bool Connect (const char * url); // disconnect void Close (); // send MetaData bool SendMetadata (LPRTMPMetadata lpMetaData); // send H264 data frame bool sendh1_packet (unsigned char * data, unsigned int size, bool bIsKeyFrame, unsigned int nTimeStamp ); // send the H264 file bool sendh1_file (const char * pFileName );//...}

Call example:

#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();  }  

The playback effect of player is as follows:


The complete RTMPStream code is attached:

/*************************************** * **************************** Filename: RTMPStream. hcreated: 2013-04-3author: firehood purpose: Send an H264 video to the RTMP Server, use the libRtmp library ************************************* * ******************************/# pragma once # include "rtmp. h "# include" rtmp_sys.h "# include" amf. h "# include <stdio. h> # define FILEBUFSIZE (1024*1024*10) // 10 M // typedef struct _ NaluUnit {Int type; int size; unsigned char * data;} NaluUnit; typedef struct _ RTMPMetadata {// video, must be h264 typeunsigned intnWidth; unsigned intnHeight; unsigned intnFrameRate; // fpsunsigned partial; // bpsunsigned intnSpsLen; unsigned charSps [1024]; unsigned intnPpsLen; unsigned charPps [1024]; // audio, must be aac typebool bHasAudio; unsigned listener; unsigned I NtnAudioChannels; char pAudioSpecCfg; unsigned intnAudioSpecCfgLen;} RTMPMetadata, * LPRTMPMetadata; class CRTMPStream {public: CRTMPStream (void );~ CRTMPStream (void); public: // Connect to RTMP Serverbool Connect (const char * url); // disconnect void Close (); // send MetaDatabool SendMetadata (LPRTMPMetadata lpMetaData ); // send the H264 data frame bool sendh1_packet (unsigned char * data, unsigned int size, bool bIsKeyFrame, unsigned int nTimeStamp); // send the H264 file bool sendh1_file (const char * pFileName ); private: // read a NALU package bool ReadOneNaluFromBuf (NaluUnit & nalu); // send data 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 ;};

/*************************************** * **************************** Filename: RTMPStream. cppcreated: 2013-04-3author: firehood purpose: Send an H264 video to the RTMP Server, use the libRtmp library ************************************* * ******************************/# include "RTMPStream. h "# include" SpsDecode. h "# ifdef WIN32 # include <windows. h> # endif # ifdef WIN32 # pragma comment (lib, "WS2_32.lib") # pragma comment (lib, "winmm. lib ") # Endifenum {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) {out Put [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, uint 64_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) {bytes = 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 CRTMPSt Ream: 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; pa. 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_stri Ng (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_s Tring (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: AVCbody [I ++] = 0x00; // AVC sequence headerbody [I ++] = 0x00; body [I ++] = 0x00; body [I ++] = 0x00; // fill in 0; // AVCDecoderConfigurationRecord. body [I ++] = 0x01; // configurationVersionbody [I ++] = lpMetaData-> Sps [1]; // AVCProfileIndicationbody [I ++] = lpMetaData-> Sps [2]; // profile_compatibilitybody [I ++] = lpMetaData-> Sps [3]; // AVCLevelIndication body [I ++] = 0xff; // lengthSizeMinusOne // sps numsbody [I ++] = 0xE1; // & 0x1f // sps data lengthbody [I ++] = lpMetaData-> nSpsLen> 8; body [I ++] = lpMetaData-> nSpsLen & 0xff; // sps datamemcpy (& body [I], lpMet AData-> Sps, lpMetaData-> nSpsLen); I = I + lpMetaData-> nSpsLen; // pps numsbody [I ++] = 0x01; // & 0x1f // pps data length body [I ++] = lpMetaData-> nPpsLen> 8; body [I ++] = lpMetaData-> nPpsLen & 0xff; // sps datamemcpy (& body [I], lpMetaData-> Pps, lpMetaData-> nPpsLen); I = I + lpMetaData-> nPpsLen; return SendPacket (RTMP_PACKET_TYPE_VIDEO, (unsigned char *) body, I, 0);} bool CRTMPStream: sendh1_packet (unsigned char * data, unsign Ed 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 NALUbody [I ++] = 0x00; body [I ++] = 0x00; body [I ++] = 0x00; // NALU sizebody [I ++] = size> 24; body [I ++] = size> 16; body [I ++] = size> 8; bo Dy [I ++] = size & 0xff; // NALU datamemcpy (& body [I], data, size); bool bRet = SendPacket (RTMP_PACKET_TYPE_VIDEO, body, I + size, nTimeStamp); delete [] body; return bRet;} bool CRTMPStream: sendh1_file (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 (Bytes> = FILEBUFSIZE) {printf ("warning: File size is larger than BUFSIZE \ n");} fclose (fp); RTMPMetadata metaData; memset (& metaData, 0, sizeof (RTMPMetadata )); naluUnit naluUnit; // read the SPS frame ReadOneNaluFromBuf (naluUnit); metaData. nSpsLen = naluUnit. size; memcpy (metaData. sps, naluUnit. dat A, naluUnit. size); // read the PPS frame ReadOneNaluFromBuf (naluUnit); metaData. nPpsLen = naluUnit. size; memcpy (metaData. pps, naluUnit. data, naluUnit. size); // decode the SPS to obtain the video image width and height. int width = 0, height = 0; h1__decode_sps (metaData. sps, metaData. nSpsLen, width, height); metaData. nWidth = width; metaData. nHeight = height; metaData. nFrameRate = 25; // send MetaData SendMetadata (& metaData); unsigned int tick = 0; while (ReadOneNaluFro MBuf (naluUnit) {bool bKeyframe = (naluUnit. type = 0x05 )? TRUE: FALSE; // send H264 data frame sendh1_packet (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) {if (m_pFileBuf [I ++] = 0x00 & m_pFileBuf [I ++] = 0x00 & m_pFileBuf [I ++] = 0x00 & m_pFileBuf [I ++] = 0x01) {int pos = I; while (pos <m_nFileBufSize) {if (m_pFileBuf [pos ++] = 0x00 & m_pFileBuf [pos ++] = 0x00 & m_pFileBuf [pos ++] = 0x00 & m_pFileBuf [pos ++] = 0x01) {break ;}} if (pos = nBufferSize) {nalu. size = pos-I;} else {nalu. size = (pos-4)-I;} nalu. type = m_pFileBuf [I] & 0x1f; nalu. data = & m_pFileBuf [I]; m_nCurPos = pos-4; return TRUE ;}} return FALSE ;}

Attached SpsDecode. h file:

# Include <stdio. h> # include <math. h> uint UE (byte * pbuff, uint nlen, uint & nstartbit) {// calculate the number of 0bit uint nzeronum = 0; while (nstartbit <nlen * 8) {If (pbuff [nstartbit/8] & (0x80> (nstartbit % 8) // &: returns the remainder by bit and % {break ;} nzeronum ++; nstartbit ++;} nstartbit ++; // calculation result DWORD dwret = 0; For (uint I = 0; I <nzeronum; I ++) {dwret <= 1; if (pbuff [nstartbit/8] & (0x80> (nstartbit % 8) {dwret + = 1 ;} nstartbit ++;} retu RN (1 <nzeronum)-1 + dwret;} int Se (byte * pbuff, uint nlen, uint & nstartbit) {int ueval = UE (pbuff, nlen, nstartbit ); double K = ueval; int nvalue = Ceil (K/2); // Ceil function: the ceil function is used to evaluate the smallest integer not less than the given real number. Ceil (2) = Ceil (1.2) = CEI (1.5) = 2.00if (ueval % 2 = 0) nvalue =-nvalue; return nvalue;} dword u (uint bitcount, byte * Buf, uint & nstartbit) {DWORD dwret = 0; For (uint I = 0; I <bitcount; I ++) {dwret <= 1; if (BUF [nstartbit/8] & (0x80> (nstartbit % 8) {dwret + = 1 ;}nstartbit ++;} return dwret ;} bool h1__decode_sps (byte * Buf, unsigned int nlen, Int & width, Int & height) {uint startbit = 0; int forbidden_zero_bit = u (1, Buf, startbit ); int nal_ref_idc = U (2, Buf, startbit); int nal_unit_type = U (5, Buf, startbit); If (nal_unit_type = 7) {int profile_idc = U (8, buf, startbit); int constraint_set0_flag = u (1, Buf, startbit); // (BUF [1] & 0x80)> 7; int constraint_set1_flag = u (1, buf, startbit); // (BUF [1] & 0x40)> 6; int constraint_set2_flag = u (1, Buf, startbit ); // (BUF [1] & 0x20)> 5; int constraint_set3_flag = u (1, Buf, startbit ); // (BUF [1] & 0x10)> 4; int reserved_zero_4bits = U (4, Buf, startbit); int level_idc = U (8, Buf, startbit ); int seq_parameter_set_id = UE (BUF, nlen, startbit); If (profile_idc = 100 | profile_idc = 110 | profile_idc = 122 | profile_idc = 144) {int chroma_format_idc = UE (BUF, nlen, startbit); If (chroma_format_idc = 3) int bytes = u (1, Buf, startbit); int bit_depth_luma_minus8 = UE (BUF, nlen, startbit); int bit_depth_chroma_minus8 = UE (BUF, nlen, startbit); int bytes = u (1, Buf, startbit); int bytes = u (1, Buf, startbit); int seq_scaling_list_present_flag [8]; If (latency) {for (INT I = 0; I <8; I ++) {seq_scaling_list_present_flag [I] = u (1, buf, startbit) ;}} int detail = UE (BUF, nlen, startbit); int pic_order_cnt_type = UE (BUF, nlen, startbit); If (pic_order_cnt_type = 0) int duration = UE (BUF, nlen, startbit); else if (pic_order_cnt_type = 1) {int duration = u (1, Buf, startbit); int offset_for_non_ref_pic = Se (BUF, nlen, startbit); int offset_for_top_to_bottom_field = Se (BUF, nlen, startbit); int cursor = UE (BUF, nlen, startbit); int * offset_for_ref_frame = new int [Break]; for (INT I = 0; I <num_ref_frames_in_pic_order_cnt_cycle; I ++) offset_for_ref_frame [I] = Se (BUF, nlen, startbit); Delete [] offset_for_ref_frame ;} int num_ref_frames = UE (BUF, nlen, startbit); int bytes = u (1, Buf, startbit); int pic_width_in_mbs_minus1 = UE (BUF, nlen, startbit ); int duration = UE (BUF, nlen, startbit); width = (pic_width_in_mbs_minus1 + 1) * 16; Height = (duration + 1) * 16; return true;} elsereturn false ;}


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.