"Speaking RTSP H.265 (HEVC) to RTMP, first of all, I have to know about the RTMP protocol. The RTMP protocol is originally defined without H.265, and the domestic CDN manufacturers jointly developed an RTMP / FLV extension h. .265 (HEVC) program, this is to adapt to the domestic video Internet Expressway, the current domestic developers are basically done according to this extension agreement.
The RTMP header information package does not define the HEVC. We use the CDN League's HEVC extension standard to define the HEVC's videotagheader as 12. See the figure below for details:
RTMP stream
The protocol layer has been determined that the key problem with the next step is to implement the RTMP H.265 (HEVC) push module. In the video push, we can choose FFMPEG or EasyRTMP, both can support the H.265 (HEVC) RTMP stream. But ffmpeg is a need to do some small modifications and recompile:
Step 1: We have improved on the basis of the H264 package to support the HEVC header package, and the HEVC head has SPS PPS VPS, and we refer to FFmpeg's HevcDecoderConfigurationRecord structure package Metadata, this structure code
Typedef struct hvcnalunitArray {
Uint8_t array_completeness;
UINT8_T NAL_Unit_type;
UINT16_T NUMNALUS;
UINT16_T * NALUNITLENGTH;
UINT8_T ** NALUNIT;
} HVCCNALUNITARRAY;
Typedef struct hevcdecoderconfigurationRecord {
uint8_t configurationVersion;
uint8_t general_profile_space;
UINT8_T General_tier_flag;
UINT8_T General_Profile_IDC;
UINT32_T General_Profile_compatibility_flags;
UINT64_T General_CONSTRAINT_INDICATOR_FLAGS;
UINT8_T General_level_idc;
UINT16_T min_spatial_segmentation_idc;
uint8_t parallex;
Uint8_t chromaformat;
Uint8_t BitDepthlumaminus8;
Uint8_t BitDepthromaminus8;
UINT16_T AVGFRAMERATE;
Uint8_t constantframerate;
UINT8_T NUMTEMPORALLAYERS;
Uint8_t temporalidnested;
Uint8_t lengthsizeminusone;
Uint8_t numofarrays;
HVCCNALUNITARRAY * Array;
HevcdecoderConfigurationRecord;
123456789101112131415161718192021222324252627282930
This structure is initialized with reference ffmeg as follows:
// Need to note is that other parameters of this structure we can actually care
// We only need to fill in the HEVC's VPS, SPS, and PPS information in the HVCCNALUNITARRAY array.
Static void HVCC_INIT (HevcdecoderConfigurationRecord * HVCC)
{
Memset (HVCC, 0, SIZEOF (HevcDecoderConfigurationRecord);
HVCC-> configurationVersion = 1;
HVCC-> Length = 3; // 4 Bytes
/ *
* The Following Fields Have All Their Valid Bits Set by Default,
* The Profiletierlevel Parsing Code Will Unset Them When Needed.
* /
HVCC-> general_profile_compatibility_flags = 0xffffff;
HVCC-> general_constraint_indicator_flags = 0xfffffffffff;
/ *
* Initialize this Field with an invalid value which can be used to detect
* WHETHER We Didn't See Any VUI (In Which Case It Should Be Reset To Zero).
* /
HVCC-> min_spatial_segmentation_idc = max_spatial_segmentation + 1;
}
12345678910111213141516171819202122
Finally, after filling the metadata information, we also need to modify it when sending frame data, change the tag head of I frame to 12, and the p frame tag is constant, set to 1, as shown below
INT i = 0;
IF (biskeyframe)
{
// body [i ++] = 0x17; // 2: Pframe 7: AVC
Body [i ++] = (m_metadata.nvideocodec == flv_codecid_hevc)? 0x1c: 0x17; // 1: iframe 7: AVC 12: HEVC
IF (m_bwaitingKeyframe)
{
m_bwaitingKeyFrame = false;
}
}
Else
{
// body [i ++] = 0x27; // 2: Pframe 7: AVC
Body [i ++] = (m_metadata.nvideocodec == flv_codecid_hevc)? 0x2c: 0x27; // 1: iframe 7: AVC 12: HEVC
}
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;
12345678910111213141516171819202122232425262728
EasyRTMP can directly support the H.265 (HEVC) stream, Easyrtmp is a set of simplicity, functional, efficient and stable RTMP function components, supporting most RTMP streaming media servers on the market, including Wowza, Red5, NGNIX_RTMP, CRTMPSERVER and other mainstream RTMP servers can be perfectly used in live demand, mobile phone live, live broadcast, live broadcast, live broadcast, classroom broadcast, etc.! GitHub Address: https://github.com/easydss/easyrtmp
RTSP draws
At present, the two sets of relatively good RTSP protocols in the market are the Live555, one is FFMPEG, two sets of frames have a thousand autumn, each has a series of applications:
Live555: Very old style RTSP framework, more than ten years, is still iterative and maintenance, the author Ross also uses this, open source + commercial operation, everyone is well known, the RTSP draw is Live555 Ffmpeg: ffmpeg is not more useful. At present, most of the domestic players are used in this, and the RTSP module is self-written, and the compatibility is also very good;
If the RTSP process protocol is not very familiar, and for FFMPEG's transformation or compiling is a very time-consuming event, and there is not high success rate, so you can use easyRTSPCLIENT: https: //github.com/tsingsee/ EasyRTSPClient, optimized and developed based on Live555, greatly simplifies the calling process of the RTSP protocol, especially if you don't have to care about RTSP DESCRIBE, SETUP, and PLAY process, direct callback, you can get the corresponding error message and data information. Includes data information such as SPS, PPS, VPS, such as H.264 / H.265;
EasyRTSPLIVE
The basic components described above are all components, and how can they connect the entire process to a complete set of RTSP pull-up RTMP streams, still need a lot of things, so we integrate an EasyRTSPLIVE project. It is very convenient to connect the entire process, supporting H.264, H.265 stream RTSP pull RTMP push: https: //github.com/easydarwin/easyrtsplive
#define _crtdbg_map_alloc
#include
#ifdef _win32
#include "" windows.h ""
#ELSE
#include
#include
#ENDIF
#include "getopt.h" "
#include
#include
#include
#include
// # include
#include
#include "EasyRTSPClientApi.h" "
#include "Easyrtmpapi.h" "
#include "INI.H" "
#include "TRACE.H" "
#ifdef _win32
#pragma Comment (Lib, "LibeasyrtSpClient.lib")
#pragma comment (Lib, "Libeasyrtmp.Lib")
#ENDIF
#define max_rtmp_URL_LEN 256
#define buffer_size 1024 * 1024
#define max_channel_index 1024
#define conf_file_path "" config.ini ""
Typedef struct _channel_cfg_struct_t
{
Int ChannelID;
Int option;
CHAR CHANELNAME [64];
Char srcrtspaddr [512];
Char DestMpAddr [512];
} _Channel_cfg;
Typedef struct _rtmp_pusher_struct_t
{
Easy_Handle Rtmphandle;
Unsigned int u32audiocodec;
Unsigned int u32audiosample;
Unsigned int U32audiochannel;
} _RTMP_PUSHER;
Typedef struct _channel_info_struct_t
{
_Channel_cfg fcfginfo;
_RTMP_PUSHER FPUSHERINFO;
Easy_handle fnvshandle;
File * floghandle;
Bool FhavePrintKeyInfo;
EASY_MEDIA_INFO_T fmediaInfo;
} _CHANNEL_INFO;
Static std :: list <_channel_info *> gchannelinfolist;
INT __EASYRTMP_CALLBACK (int _frametype, char * pbuf, easy_rtmp_state_t _state, void * _userptr)
{
_Channel_INFO * PCHANNEL = (_Channel_INFO *) _ Userptr;
Switch (_State)
{
Case Easy_rtmp_State_Connecting:
Trace_log (PChannel-> Floghandle, "Connecting ... \ n" ");
Break;
Case Easy_rtmp_State_Connected:
Trace_log (PChannel-> Floghandle, "Connected \ n");
Break;
Case Easy_rtmp_State_Connect_failed:
Trace_log (PChannel-> Floghandle, "Connect Failed \ N");
Break;
Case Easy_rtmp_State_Connect_Abort:
Trace_log (PChannel-> Floghandle, "" Connect Abort \ n "");
Break;
Case Easy_rtmp_State_disconnected:
Trace_log (PChannel-> FlogHandle, "Disconnect. \ N");
Break;
DEFAULT:
Break;
}
Return 0;
}
/ * EasyrtSpClient Callback * /
INT Easy_apicall __rtspsourceCallback (int _chid, void * _chptr, int _mediatype, char * pbuf, easy_frame_info * frameinfo)
{
IF (NULL! = frameInfo)
{
IF (frameInfo-> height == 1088) frameinfo-> height = 1080;
Else if (frameInfo-> height == 544) frameinfo-> height = 540;
}
Easy_bool Bret = 0;
INT IRET = 0;
Be
_Channel_INFO * PCHANNEL = (_Channel_Info *) _CHPTR;
IF (_mediatype == easy_SDK_VIDEO_FRAME_FLAG)
{
IF (FrameInfo && Frameinfo-> Length)
{
IF (frameInfo-> type == easy_sdk_video_frame_i)
{
IF (PChannel-> fpusherinfo.rtmphandle == 0)
{
PCHANNEL-> fpusherinfo.rtmphandle = easyrtmp_create ();
IF (PChannel-> fpusherinfo.rtmphandle == null)
{
Trace_log (PChannel-> Floghandle, "Fail to RTMP CREATE FAILED ... \ N" ");
Return -1;
}
EasyRTMP_SETCALLBACK (PChannel-> fpusherinfo.rtmphandle, __easyrtmp_callback, pchannel);
Bret = easyrtmp_connect (pchannel-> fpusherinfo.rtmphandle, pchannel-> fcfginfo.destrtmpaddr);
IF (! bret)
{
Trace_log (PChannel-> Floghandle, "Fail to RTMP Connect Failed ... \ n" ");
}
Easy_Media_Info_t MediaInfo;
MEMSET (& MediaInfo, 0, Sizeof (easy_media_info_t));
MediaInfo.u32video = pchannel-> fmediaInfo.u32videofps;
MediaInfo.u32audiosample = 8000;
Iret = easyrtmp_initmetadata (PChannel-> fpusherinfo.rtmphandle, & mediaInfo, 1024);
IF (IRET <0)
{
Trace_log (PChannel-> Floghandle, "Fail to Init Metadata ... \ n" ");
}
}
Easy_AV_Frame Avframe
MEMSET (& Avframe, 0, Sizeof (easy_av_frame);
Avframe.u32avframeflag = easy_SDK_VIDEO_FRAME_FLAG;
Avframe.u32avframelen = frameinfo-> length;
Avframe.pbuffer = (unsigned char *) PBUF;
Avframe.u32vframetype = easy_SDK_VIDEO_FRAME_I;
//AVFrame.u32timestampsec = frameinfo-> timestamp_sec;
//AVFrame.u32timestampusec = frameinfo-> timestamp_usec;
//
Iret = easyrtmp_sendpacket (pchannel-> fpusherinfo.rtmphandle, & avframe);
IF (IRET <0)
{
Trace_log (PChannel-> Floghandle, "" Fail to Send H264 Packet (I-Frame) ... \ n "");
}
Else
{
IF (! PChannel-> FhavePrintKeyInfo)
{
Trace_log (PChannel-> Floghandle, "I \ n" ");
PCHANNEL-> FHAVEPRINTKEYINFO = TRUE;
}
}
}
Else
{
IF (PChannel-> fpusherinfo.rtmphandle)
{
Easy_AV_Frame Avframe
MEMSET (& Avframe, 0, Sizeof (easy_av_frame);
Avframe.u32avframeflag = easy_SDK_VIDEO_FRAME_FLAG;
Avframel.u32avframelen = frameinfo-> length-4;
Avframe.pbuffer = (unsigned char *) PBUF + 4;
Avframe.u32vframetype = easy_SDK_VIDEO_FRAME_P;
//AVFrame.u32timestampsec = frameinfo-> timestamp_sec;
//AVFrame.u32timestampusec = frameinfo-> timestamp_usec;
Iret = easyrtmp_sendpacket (pchannel-> fpusherinfo.rtmphandle, & avframe);
IF (IRET <0)
{
Trace_log (pchannel-> floghandle, "" Fail to Send H264 Packet (P-FRAME) ... \ N "");
}
Else
{
IF (! PChannel-> FhavePrintKeyInfo)
{
Trace_log (PChannel-> Floghandle, "P \ n" ");
}
}
}
}
}
}
Else IF (_MEDiaType == easy_sdk_media_info_flag)
{
IF (PBUF! = NULL)
{
Easy_Media_Info_t MediaInfo;
Memset (PChannel-> fmediaInfo), 0x00, sizeof (easy_media_info_t);
Memcpy (& (PChannel-> fmediaInfo), PBUF, SIZEOF (EASY_MEDIA_INFO_T);
TRACE_LOG (PCHANNEL-> FlogHandle, "" RTSP Describe Get Media Info: Video:% U FPS:% U Audio:% U Channel:% u SampleRate:% U \ N ",
pChannel-> fMediainfo.u32VideoCodec, pChannel-> fMediainfo.u32VideoFps, pChannel-> fMediainfo.u32AudioCodec, pChannel-> fMediainfo.u32AudioChannel, pChannel-> fMediainfo.u32AudioSamplerate);
}
}
Return 0;
}
BOOL INITCFGINFO (VOID)
{
INT i = 0;
GChannelInfolist.clear ();
For (i = 0; i fcfginfo.channelid = i;
PCHANNELINFO-> FhavePrintKeyinfo = FALSE;
Sprintf (pchannelinfo-> fcfginfo.channelname, "" CHANNEL% D ", i);
STRCPY (PChannelInfo-> fcfginfo.srcrtspaddr, GetIniKeystring (pchannelinfo-> fcfginfo.channelname, "RTSP", conf_file_path);
STRCPY (PChannelInfo-> fcfginfo.destrtmpaddr, GetInikeystring
Our other product: