RTMP协议本身是不支持H265的。但现在的设备越来越追求更高的压缩比和更高的图形质量。H265相对其他的媒体格式更多受到厂家的重视。rtmp协议要支持H265首先要定义一个ID。按照大家的约定来看,基本使用12(0xc)作为ID. 同时相对H264对NALU的分析要进行改变。并对发送的Metadata数据进行修改。

先看下发送metadata:

int SendVideoSpsPpsVps(RTMP* r, unsigned char* pps, int pps_len, unsigned char* sps, int sps_len, unsigned char* vps,int vps_len, uint32_t dts)
{char        tBuffer[RTMP_HEAD_SIZE + 1024] = { 0 };RTMPPacket* packet = (RTMPPacket*)tBuffer;packet->m_body = (char*)packet + RTMP_HEAD_SIZE;unsigned char* body = (unsigned char*)packet->m_body;// http://ffmpeg.org/doxygen/trunk/hevc_8c_source.html#l00040  hvcc_write 函数// 在nginx-rtmp中会跳过48位不去处理 我们在表示后面补0// skip tag header and configurationVersion(1 byte)int i = 0;body[i++] = 0x1C;body[i++] = 0x0;body[i++] = 0x0;body[i++] = 0x0;body[i++] = 0x0;body[i++] = 0x1;// general_profile_idc 8bitbody[i++] = sps[1];// general_profile_compatibility_flags 32 bitbody[i++] = sps[2];body[i++] = sps[3];body[i++] = sps[4];body[i++] = sps[5];// 48 bit NUll nothing deal in rtmpbody[i++] = sps[6];body[i++] = sps[7];body[i++] = sps[8];body[i++] = sps[9];body[i++] = sps[10];body[i++] = sps[11];// general_level_idcbody[i++] = sps[12];// 48 bit NUll nothing deal in rtmpbody[i++] = 0;body[i++] = 0;body[i++] = 0;body[i++] = 0;body[i++] = 0;body[i++] = 0;body[i++] = 0;body[i++] = 0;// bit(16) avgFrameRate;/* bit(2) constantFrameRate; *//* bit(3) numTemporalLayers; *//* bit(1) temporalIdNested; */body[i++] = 0x83;/* unsigned int(8) numOfArrays; 03 */body[i++] = 0x03;// vps 32body[i++] = 0x20;body[i++] = (1 >> 8) & 0xff;body[i++] = 1 & 0xff;body[i++] = (vps_len >> 8) & 0xff;body[i++] = (vps_len) & 0xff;memcpy(&body[i], vps, vps_len);i += vps_len;// spsbody[i++] = 0x21;  // sps 33body[i++] = 0;body[i++] = 1;body[i++] = (sps_len >> 8) & 0xff;body[i++] = sps_len & 0xff;memcpy(&body[i], sps, sps_len);i += sps_len;// ppsbody[i++] = 0x22;  // pps 34body[i++] = (1 >> 8) & 0xff;body[i++] = 1 & 0xff;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 = dts;packet->m_hasAbsTimestamp = 0;packet->m_headerType = RTMP_PACKET_SIZE_LARGE;packet->m_nInfoField2 = r->m_stream_id;int nRet = 0;if (RTMP_IsConnected(r))nRet = RTMP_SendPacket(r, packet, 0);  // 1为放进发送队列,0是不放进发送队列,直接发送return nRet;
}

上面中需要注意// bit(16) avgFrameRate;
    /* bit(2) constantFrameRate; */
    /* bit(3) numTemporalLayers; */
    /* bit(1) temporalIdNested; */
    body[i++] = 0x83;

这个地方在一些网站上写的是0,或者不处理,可能造成一些服务器,不工作。

其他,在发送媒体信息的时候需要解释sps。对H265的解释跟H264不一样。

#ifndef h265_decode_info_h__
#define h265_decode_info_h__#include <iostream>
#include <sstream>
//#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct vc_params_t
{LONG width, height;DWORD profile, level;DWORD nal_length_size;void clear(){memset(this, 0, sizeof(*this));}
};class NALBitstream
{
public:NALBitstream() : m_data(NULL), m_len(0), m_idx(0), m_bits(0), m_byte(0), m_zeros(0) {};NALBitstream(void * data, int len) { Init(data, len); };void Init(void * data, int len) { m_data = (LPBYTE)data; m_len = len; m_idx = 0; m_bits = 0; m_byte = 0; m_zeros = 0; };BYTE GetBYTE(){//printf("m_idx=%d,m_len=%d\n", m_idx, m_len);if (m_idx >= m_len)return 0;BYTE b = m_data[m_idx++];// to avoid start-code emulation, a byte 0x03 is inserted   // after any 00 00 pair. Discard that here.   if (b == 0){m_zeros++;if ((m_idx < m_len) && (m_zeros == 2) && (m_data[m_idx] == 0x03)){m_idx++;m_zeros = 0;}}else{m_zeros = 0;}return b;};UINT32 GetBit(){if (m_bits == 0){m_byte = GetBYTE();m_bits = 8;}m_bits--;return (m_byte >> m_bits) & 0x1;};UINT32 GetWord(int bits){UINT32 u = 0;while (bits > 0){u <<= 1;u |= GetBit();bits--;}return u;};UINT32 GetUE(){// Exp-Golomb entropy coding: leading zeros, then a one, then   // the data bits. The number of leading zeros is the number of   // data bits, counting up from that number of 1s as the base.   // That is, if you see   //      0001010   // You have three leading zeros, so there are three data bits (010)   // counting up from a base of 111: thus 111 + 010 = 1001 = 9   int zeros = 0;while (m_idx < m_len && GetBit() == 0) zeros++;return GetWord(zeros) + ((1 << zeros) - 1);};INT32 GetSE(){// same as UE but signed.   // basically the unsigned numbers are used as codes to indicate signed numbers in pairs   // in increasing value. Thus the encoded values   //      0, 1, 2, 3, 4   // mean   //      0, 1, -1, 2, -2 etc   UINT32 UE = GetUE();bool positive = UE & 1;INT32 SE = (UE + 1) >> 1;if (!positive){SE = -SE;}return SE;};private:LPBYTE m_data;int m_len;int m_idx;int m_bits;BYTE m_byte;int m_zeros;
};bool  ParseSequenceParameterSet(BYTE* data, int size, vc_params_t& params)
{if (size < 20){return false;}NALBitstream bs(data, size);// seq_parameter_set_rbsp()   bs.GetWord(4);// sps_video_parameter_set_id   int sps_max_sub_layers_minus1 = bs.GetWord(3); // "The value of sps_max_sub_layers_minus1 shall be in the range of 0 to 6, inclusive."   if (sps_max_sub_layers_minus1 > 6){return false;}bs.GetWord(1);// sps_temporal_id_nesting_flag   // profile_tier_level( sps_max_sub_layers_minus1 )   {bs.GetWord(2);// general_profile_space   bs.GetWord(1);// general_tier_flag   params.profile = bs.GetWord(5);// general_profile_idc   bs.GetWord(32);// general_profile_compatibility_flag[32]   bs.GetWord(1);// general_progressive_source_flag   bs.GetWord(1);// general_interlaced_source_flag   bs.GetWord(1);// general_non_packed_constraint_flag   bs.GetWord(1);// general_frame_only_constraint_flag   bs.GetWord(44);// general_reserved_zero_44bits   params.level = bs.GetWord(8);// general_level_idc   unsigned char sub_layer_profile_present_flag[6] = { 0 };unsigned char sub_layer_level_present_flag[6] = { 0 };for (int i = 0; i < sps_max_sub_layers_minus1; i++){sub_layer_profile_present_flag[i] = bs.GetWord(1);sub_layer_level_present_flag[i] = bs.GetWord(1);}if (sps_max_sub_layers_minus1 > 0){for (int i = sps_max_sub_layers_minus1; i < 8; i++){unsigned char reserved_zero_2bits = bs.GetWord(2);}}for (int i = 0; i < sps_max_sub_layers_minus1; i++){if (sub_layer_profile_present_flag[i]){bs.GetWord(2);// sub_layer_profile_space[i]   bs.GetWord(1);// sub_layer_tier_flag[i]   bs.GetWord(5);// sub_layer_profile_idc[i]   bs.GetWord(32);// sub_layer_profile_compatibility_flag[i][32]   bs.GetWord(1);// sub_layer_progressive_source_flag[i]   bs.GetWord(1);// sub_layer_interlaced_source_flag[i]   bs.GetWord(1);// sub_layer_non_packed_constraint_flag[i]   bs.GetWord(1);// sub_layer_frame_only_constraint_flag[i]   bs.GetWord(44);// sub_layer_reserved_zero_44bits[i]   }if (sub_layer_level_present_flag[i]){bs.GetWord(8);// sub_layer_level_idc[i]   }}}unsigned long sps_seq_parameter_set_id = bs.GetUE(); // "The  value  of sps_seq_parameter_set_id shall be in the range of 0 to 15, inclusive."   /*if (sps_seq_parameter_set_id > 15){printf("enter2\r\n");return false;}*/unsigned long chroma_format_idc = bs.GetUE(); // "The value of chroma_format_idc shall be in the range of 0 to 3, inclusive."   /*if (sps_seq_parameter_set_id > 3){printf("enter3\r\n");return false;}*/if (chroma_format_idc == 3){bs.GetWord(1);// separate_colour_plane_flag   }params.width = bs.GetUE(); // pic_width_in_luma_samples   params.height = bs.GetUE(); // pic_height_in_luma_samples   if (bs.GetWord(1)){// conformance_window_flag   bs.GetUE();  // conf_win_left_offset   bs.GetUE();  // conf_win_right_offset   bs.GetUE();  // conf_win_top_offset   bs.GetUE();  // conf_win_bottom_offset   }unsigned long bit_depth_luma_minus8 = bs.GetUE();unsigned long bit_depth_chroma_minus8 = bs.GetUE();/*if (bit_depth_luma_minus8 != bit_depth_chroma_minus8){printf("enter4\r\n");return false;}*///...   return true;
}#endif // h265_decode_info_h__

这样可以发送根据媒体格式进行头信息填写了。

if(lpMetaData == NULL){return -1;}char buffer[1024] = {0};char *body = buffer+RTMP_MAX_HEADER_SIZE;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, "CarEyeRTMP" );if (type == 1){p = put_amf_string(p, "width");p = put_amf_double(p, lpMetaData->Width);p = put_amf_string(p, "height");p = put_amf_double(p, lpMetaData->Height);p = put_amf_string(p, "framerate");p = put_amf_double(p, lpMetaData->FrameRate);p = put_amf_string(p, "videocodecid");if (lpMetaData->VCodec == CAREYE_VCODE_H264){p = put_amf_double(p, FLV_CODECID_H264);}else{p = put_amf_double(p, FLV_CODECID_H265);}}p =put_amf_string( p, "audiosamplerate");p =put_amf_double( p, lpMetaData->SampleRate);p =put_amf_string( p, "audiocodecid");p =put_amf_double( p, 10);p =put_amf_string( p, "" );p =put_byte( p, AMF_OBJECT_END  );

car-eye RTMP推流是将GB28181或者GT1078协议的数据的音视频数据推送到RTMP拉流服务器。以实现客户端对RTMP,http,websocket,HLS等多种方式的拉取和播放。

car-eye流媒体服务器实现了对监控和车载移动设备多种场景的支持。相关的开源源码地址:https://github.com/Car-eye-team/

https://gitee.com/careye_open_source_platform_group

本文章参考:https://blog.csdn.net/qq_33795447/article/details/89457581

RTMP 推流增加对H265的支持相关推荐

  1. 高性能RTMP推流服务器软件EasyDSS如何支持推流摄像机推流直播进行云端录像存储及计划保存

    RTMP推流服务器 RTMP推流服务器EasyDSS: 提供设备接入: RTMP推流服务.RTMP分发.HLS分发.HTTP-FLV分发:云端录像.云端录像检索.云端录像点播.云端录像下载: RTMP ...

  2. 几款知名RTMP推流模块比较:OBS VS SmartPublisher VS Flash Media Live Encoder

    OBS 功能强大,几乎所有你想要的场景它都有,用起来很顺手.可以将桌面.摄像头.程序窗口通过rtmp推送到流媒体服务器上. 当然如果你是开发者,想基于OBS做二次开发,实现二次产品化的化,难度比较大, ...

  3. 大华的支持rtmp推流吗_RTSP安防摄像机(海康大华宇视等)如何推送到RTMP流媒体服务器进行直播...

    方案介绍 目前互联网直播的CDN和标准RTMP流媒体服务器通常只能接收RTMP格式的音视频推流.目前市场上有一些自带RTMP推流的摄像机和编码器,可以直接在其rtmp推流配置里面配置推送到RTMP流媒 ...

  4. rtmp断线重连_rtsp转rtmp rtsp2rtmp 同时16路视频 rtmp推流器 支持ipc dvr nvr

    本公司提供各种音视频方案定制,详情请咨询 283 邮件:zyy6569@163.com :  (咨询特价) 1.拉取网络摄像机RTSP的码流,通过rtmp推送到指定的rtmp服务器,支持h264视频, ...

  5. rtmp推流工具_小熊录屏:支持RTMP直播录屏的手机APP——墨涩网

    小熊录屏软件 小熊录屏是一款支持手机应用介绍小熊录屏(原安卓录屏大师)是一款稳定且优质的免费屏幕录制应用,能帮助你录制流畅清晰的屏幕视频.小熊录屏功能丰富,无需root即可使用,让你轻松录制游戏过程. ...

  6. QT软件开发-基于FFMPEG设计录屏与rtsp、rtmp推流软件(支持桌面与摄像头)(一)

    QT软件开发-基于FFMPEG设计录屏与rtsp.rtmp推流软件(支持桌面与摄像头)(一) https://xiaolong.blog.csdn.net/article/details/126954 ...

  7. QT软件开发-基于FFMPEG设计录屏与rtsp、rtmp推流软件(支持桌面与摄像头)(四)

    QT软件开发-基于FFMPEG设计录屏与rtsp.rtmp推流软件(支持桌面与摄像头)(一) https://xiaolong.blog.csdn.net/article/details/126954 ...

  8. RTMP推流直播流媒体平台LiveQing鉴权直播拉转直播开放直播支持推送总流量和播放总流量统计

    RTMP推流直播流媒体平台LiveQing鉴权直播拉转直播开放直播支持推送总流量和播放总流量统计 1.鉴权直播中的相关统计 2.拉转直播中的相关统计 3.RTMP推流视频直播和点播流媒体服务 1.鉴权 ...

  9. LiveQing视频点播RTMP推流直播服务支持H5无插件WebRTC超低延时视频直播

    LiveQing视频点播RTMP推流直播服务支持H5无插件WebRTC超低延时视频直播 1.WebRTC超低延时视频直播 2.WebRTC延时对比 3.LiveQing播放WebRTC流 4.分屏页面 ...

最新文章

  1. freemarker自定义标签(二十一)
  2. js截屏 video_canvas与html5实现视频截图功能
  3. 怎么用python画简单的图-用python进行简单的画图操作
  4. 未转变者服务器床id,最新id欢迎补充
  5. stm32-串口接受不定长数据方法(3种)
  6. Gloomy对Windows内核的分析
  7. MSF(四):常用弱点扫描模块
  8. caffe 添加自定义层(custom layer)
  9. 一个交易平台源码,全源无接口
  10. Asp调用函数是否会影响性能?
  11. linux日志报multipath,linux device mapper multipath 驱动的路径聚合
  12. ECCV 2018丨YOLO遇上OpenPose,近200FPS的高帧数多人姿态检测
  13. .NET利用委托实现动态查询
  14. python开源代码app_十个基于Python的BBS论坛类开源web框架汇总(附源码地址)
  15. 13. 使用类 【连载 13】
  16. 微信小程序使用 wxparse 解析 iframe腾讯视频
  17. 【SQL注入05】延时盲注实例操作
  18. 苹果和亚马逊因疑似侵犯云端同步功能专利而被提起美国337调查
  19. Python for s60[1_pys60简介]
  20. 如何修改web浏览器title文字及icon

热门文章

  1. fzyzojP3618 -- [校内训练-互测20180412]士兵的游戏
  2. 赵小楼《天道》《遥远的救世主》深度解析(127)一解决生存,二让心有个安处
  3. C#中internal和protect internal的理解
  4. 很抱歉,office即点即用安装程序遇到问题,因为你的计算机安装基于window Installer的office程序
  5. 用一个Gmail邮箱注册多个任天堂游戏区,以日本区账号为例
  6. java需要圣魔才能安装_eclipse java怎么运行?如何使用Eclipse编写运行Java程序
  7. 《征信业务管理办法》实施一周年丨征信报告里的字母数字和符号怎么看?
  8. 教师网络计算机研修培训总结,教师远程培训研修总结范文(精选6篇)
  9. 用Java实现矩阵乘法
  10. unable to access ‘https://sys-gitlab.n.com.cn/IPSC/web/Bumblebee/platform.w/platform.git/‘: SSL cert