在之前两篇关于SkeyeRTMPClient扩展支持HEVC(H.265)解决方案的文章中,我们已经完成了对H265的支持,本文主要阐述将H26和H265支持兼容起来,实现不同视频编码格式的自适应兼容适配。
根据视频编码ID判断视频编码类型,如果视频编码ID==FlvCodeId_Hevc(12),则判断视频编码格式为H265,反之则为H264(因为目前我们只支持这两种编码格式的视频推送),如下代码所示:
parser_VideoTag *video_tag = (parser_VideoTag*)(buf+parser_offset);
FlvCodeId video_code_id = (FlvCodeId)(video_tag->code_id&0x0f);
if (video_code_id == FlvCodeId_Hevc)
{
av_frame.u32AVFrameFlag = SKEYE_SDK_VIDEO_CODEC_H265;// HEVC;
}
else
{
av_frame.u32AVFrameFlag = SKEYE_SDK_VIDEO_CODEC_H264;// 默认h264, 其他类型是否需要判断?!;
}
根据FLV/RTMP扩展支持H265标准,支持HEVC的VideoTagHeader定义如下图所示:

即 当CodecID == 12时,AVCPacketType为HEVCPacketType:
如果HEVCPacketType为0,表示HEVCVIDEOPACKET中存放的是HEVC sequence header;
如果HEVCPacketType为1,表示HEVCVIDEOPACKET中存放的是HEVC NALU;
如果HEVCPacketType为2,表示HEVCVIDEPACKET中存放的是HEVC end of sequence,即HEVCDecoderConfigurationRecord;
而当CodecID == 7时,AVCPacketType为AVCPacketType:
如果AVCPacketType为0,表示HEVCVIDEOPACKET中存放的是AVC sequence header;
如果AVCPacketType为1,表示HEVCVIDEOPACKET中存放的是AVC NALU;
如果AVCPacketType为2,表示HEVCVIDEPACKET中存放的是AVC end of sequence,即AVCDecoderConfigurationRecord;
SkeyeRTMPClient对sequence header的解析函数如下代码段所示:
int ParserVideoSequencePacket(FlvCodeId video_code_id, char *buf,int len)
{
int parser_offset = 0;
char *parser_config = buf;
if (video_code_id == FlvCodeId_Hevc)
{
if(len <= sizeof(Parser_HEVCDecoderConfigurationRecord))
{
return -1001;
}
......’
//Parser HEVCDecoderConfigurationRecord
......
rtmpclient_h265_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_);
}
else
{
if(len <= sizeof(parser_AVCDecoderHeader))
{
return -1001;
}
......’
//Parser HEVCDecoderHeader
......
rtmpclient_h264_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_);
}
return 0;
}
根据FLV/RTMP扩展支持协议标准,支持H265的VideoTagBody定义如下, 扩展后的VideoTagBody如下图所示(红色字体为HEVC新增内容)::

当CodecID为12时,VideoTagBody中存放的就是HEVC视频帧内容。
SkeyeRTMPClient视频帧nalu解析如下代码所示:
int ParserOneVideoNalu(SkeyeRTMPClient_AV_Frame& av_frame,char *buf,int len,char* processbuf)
{
if(processbuf == NULL || buf == NULL || len == 0)
{
return -3001;
}
if(sps_len_ == 0 || pps_len_ == 0)
{
printf("do not get sequence head yet\n");
return -3002;
}
int parse_offset = 0;
int nalu_len = 0;
int nalu_type = 0;
int processlen = 0;
while(parse_offset < len - 4)
{
nalu_len = ntohl(*(int*)(buf + parse_offset));
parse_offset += 4;
//如果视频帧编码类型为H265
if(av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265)
{
nalu_type = (buf[parse_offset] >> 1) & 0x3F;
if(nalu_type == e_H265_NAL_UNIT_VPS)
{
ASSERT_PARSER(nalu_len,MAX_VPS_LEN);
memcpy(vps_buf_,buf + parse_offset,nalu_len);
vps_len_ = nalu_len;
parse_offset += nalu_len;
continue;
}
}else{
nalu_type = buf[parse_offset]&0x1F;
}
//H265 以及 H264的SPS头解析兼容
if((av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265&&nalu_type ==e_H265_NAL_UNIT_SPS) ||
av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H264&&nalu_type == e_H264_Frame_Type_Sps)
{
ASSERT_PARSER(nalu_len,MAX_PPS_LEN);
memcpy(sps_buf_,buf + parse_offset,nalu_len);
sps_len_ = nalu_len;
parse_offset += nalu_len;
if(width_ == 0 && sps_len_ > 0)
{
if(av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265)
rtmpclient_h265_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_);
else
rtmpclient_h264_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_);
}
continue;
}
//H265 以及 H264的PPS头解析兼容
else if((av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265&&nalu_type ==e_H265_NAL_UNIT_PPS) ||
av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H264&&nalu_type == e_H264_Frame_Type_Pps)
{
memcpy(pps_buf_,buf + parse_offset,nalu_len);
pps_len_ = nalu_len;
parse_offset += nalu_len;
continue;
}
//H265 以及 H264的I帧解析兼容
else if((av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265&&nalu_type >=e_H265_NAL_UNIT_SLICE_BLA&&nalu_type <=e_H265_NAL_UNIT_SLICE_CRA ) ||
av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H264&&nalu_type == e_H264_Frame_Type_Idr)
{
if(av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265)
{
memcpy(processbuf + processlen,nalu_head_,4);
processlen += 4;
memcpy(processbuf + processlen,vps_buf_,vps_len_);
processlen += vps_len_;
}
.......
//拷贝SPS和PPS以及Idr nalu
......
av_frame.u32VFrameType = SKEYE_SDK_VIDEO_FRAME_I;
continue ;
}
//H265 以及 H264的P帧解析兼容
else if((av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265&&nalu_type >=e_H265_NAL_UNIT_SLICE_TRAIL_R&&nalu_type <=e_H265_NAL_UNIT_SLICE_TFD ) ||
av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H264&&nalu_type == e_H264_Frame_Type_Slice)
{
memcpy(processbuf + processlen,nalu_head_,4);
processlen += 4;
memcpy(processbuf + processlen,buf + parse_offset,nalu_len);
processlen += nalu_len;
parse_offset += nalu_len;
av_frame.u32VFrameType = SKEYE_SDK_VIDEO_FRAME_P;
continue ;
}
else
{
parse_offset += nalu_len;
continue;
}
}
av_frame.pBuffer = (uint8_t *)processbuf;
av_frame.u32AVFrameLen = processlen;
return 0;
}
至此,SkeyeRTMPClient对H264和H265的兼容适配就完成了,我们可以通过SkeyeRTMPClient拉取任意编码格式为H264或者H265的RTMP进行拉流,均能取得完整的视频帧数据进行解码和播放。
欢迎大家下载SkeyePlayer测试播放支持H265的RTMP流:
https://gitee.com/visual-opening/skeyeplayer
有任何技术问题,欢迎大家和我进行技术交流:
295222688@qq.com
大家也可以加入SkeyePlayer流媒体播放器 QQ群进行讨论:
102644504
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion
我想编写一个ruby脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"
这个问题有两个部分。在RubyProgrammingLanguage一书中,有一个使用模块扩展字符串对象和类的示例(第8.1.1节)。第一个问题。为什么如果您使用新方法扩展类,然后创建该类的对象/实例,则无法访问该方法?irb(main):001:0>moduleGreeter;defciao;"Ciao!";end;end=>nilirb(main):002:0>String.extend(Greeter)=>Stringirb(main):003:0>String.ciao=>"Ciao!"irb(main):004:0>x="foobar"=>"foobar"irb(main):
为了在我的mac上为一个rails项目安装mysql,我遵循了安装Homebrew软件和删除mac端口的在线建议。这是问题开始的地方。rails项目不会构建,我得到这个:[rake--prereqs]rakeaborted!dlopen(/Users/Parker/.rvm/gems/ruby-1.9.3-p448/gems/nokogiri-1.6.0/lib/nokogiri/nokogiri.bundle,9):Librarynotloaded:/opt/local/lib/libiconv.2.dylibReferencedfrom:/Users/Parker/.rvm/gem
我已经按照https://github.com/wayneeseguin/rvm#installation上的说明通过RVM安装了Ruby.有关信息,我有所有文件(readline-5.2.tar.gz、readline-6.2.tar.gz、ruby-1.9.3-p327.tar.bz2、rubygems-1.8.24.tgz、wayneeseguin-rvm-stable.tgz和yaml-0.1.4.tar.gz)在~/.rvm/archives目录中,我不想在任何目录中重新下载它们方式。当我这样做时:sudo/usr/bin/apt-getinstallbuild-essent
我的Ruby-on-Rails项目中有以下文件结构,用于规范:/spec/msd/serviceservice_spec.rb/support/my_modulerequests_stubs.rb我的request_stubs.rb有:moduleMyModule::RequestsStubsmodule_functiondeflist_clientsurl="dummysite.com/clients"stub_request(:get,url).to_return(status:200,body:"clientsbody")endend在我的service_spec.rb我有:re
假设我们有A、B、C类。Adefself.inherited(sub)#metaprogramminggoeshere#takeclassthathasjustinheritedclassA#andforfooclassesinjectprepare_foo()as#firstlineofmethodthenrunrestofthecodeenddefprepare_foo#=>prepare_foo()neededhere#somecodeendendBprepare_foo()neededhere#somecodeendend如您所见,我正在尝试将foo_prepare()调用注入