Enhanced-Rtmp支持H265

Enhanced-Rtmp支持H265

千呼万唤使出来,rtmp/flv算是有统一支持H265的国际版本。本文介绍一下:

  • 现存rtmp/flv支持H265的方式;
  • Enhanced-Rtmp协议如何支持H265;
  • ffmpeg/obs/srs/media-server各个开源的实现;
  • 国内方案与国外方案的兼容性问题;

1. rtmp/flv封装视频方式

准确的说,rtmp是传输协议,传输协议内部的封装是flv格式,其实我们所说的支持H265,是在flv封装格式里面支持H265编码数据。

flv对视频的封装格式, 原有VideoTagHeader定义如下:

------------------------------------------------------------------------------------
| FrameType(4bits) | CodecID(4bits) | AVCPacketType(8bits)| CompositionTime(24bits)|
------------------------------------------------------------------------------------

其中:

  • FrameType: 4个bits, 1: keyframe, 也就是I帧; 2: inter frame, 非I帧,B帧或P帧;
  • CodecID: 4个bits,
1: JPEG (currently unused); 
2: Sorenson H.263; 
3: Screen video; 
4: On2 VP6; 
5: On2 VP6 with alpha channel; 
6: Screen video version 2; 
7: AVC;

这里如果是H264,就是7。

  • AVCPacketType: 8个bits,也就是一个字节,0: AVC sequence header; 1: AVC NALU
  • CompositionTime: 3个字节(24bits),表示pts与dts的差值;

举例:

  • 如果视频数据是H264的sequence header(也就是包含sps/pps的Avcc Header),就应该是0x17 00;
  • 如果视频数据是H264的Iframe,就应该是0x17 01;
  • 如果视频数据是H264的非Iframe,就应该是0x27 01

flv的标准中,只设定了H264的codecId为7,之后的flv标准就没在针对video的codecId进行增加,这也就是导致后面rtmp/flv没有支持H265的标准。

1.1. 国内rtmp/flv对H265的支持

随着国内前10年移动互联网对直播需求的增加,对高清画质的需求与日俱增,支持H265直播的需求很早就在各家CDN和云厂商成为top需求。

因此,国内云厂商和CDN厂商对H265很早就支持,支持的方式比较简单,就是自定义H265的CodecID=0xC,也就是CodecID值为12。

1: JPEG (currently unused); 
2: Sorenson H.263; 
3: Screen video; 
4: On2 VP6; 
5: On2 VP6 with alpha channel; 
6: Screen video version 2; 
7: AVC;
12: H265(国内自定义H265的CodecID);

这样的自定义的好处:迅速解决了国内统一rtmp/flv支持H265的格式标准;国内的cdn厂家的服务都遵循CodecID=12来实现rtmp/flv直播服务。

举例:

  • 如果视频数据是H265的sequence header,就应该是0x1c 00;
  • 如果视频数据是H265的Iframe,就应该是0x1c 01;
  • 如果视频数据是H265的非Iframe,就应该是0x2c 01

国内的多个开源也都遵循国内的H265标准:

  • SRS
  • media-server
  • ffmpeg_rtmp_h265 自定义补丁

但是,同样有其局限性:CodecID是自定义的,并且对CodecID只有4个bits的局限性没能解决,后面对新增的编码方式无法适用,如新增VP8,VP9, AV1,或未来的H266,扩展会很难。

国外为准的流媒体开源,并未支持CodecId=12为H265,如:

  • ffmpeg: 未支持CodecId=12为H265;
  • obs: 未支持CodecId=12为H265;

2. Enhanced-Rtmp

Enhanced-Rtmp公布支持H265的标准,彻底解决在rtmp/flv支持H265的编码。

2.1 Enhanced-Rtmp规范

Enhanced-Rtmp规范: 原文

flv对视频的封装格式, 原有VideoTagHeader定义如下:

------------------------------------------------------------------------------------
| FrameType(4bits) | CodecID(4bits) | AVCPacketType(8bits)| CompositionTime(24bits)|
------------------------------------------------------------------------------------

而Enhanced-Rtmp对上面的格式进行修改:
首先FrameType第一个bit变为IsExHeader,如下

-------------------------------------
| IsExHeader(1bit)FrameType(3bits) |
-------------------------------------

也就是在原FrameType的最高位加了1bit的IsExHeader标志位,如果IsExHeader使能,表示Enhanced-Rtmp格式使能,后面的定义是Enhanced-Rtmp格式;否则还遵循之前的rtmp/flv传统规范。

格式的具体逻辑如下,UB代表bit的站位符(举例: UB[4]表示站位4bits)

IsExHeader = (UB[4] & 0b1000 != 0) ? true : false;
FrameType = UB[4] & 0b0111; //1 = key frame, 2 = inter frame

if (IsExHeader == 0)
{
    //如果IsExHeader未使能, 还遵循之前的rtmp/flv传统规范
    CodecId = UB[4];//4bits的codecId,H264的值为7.
    AVCPacketType = UB[8];//8bits的AVCPacketType, 0: sequence header; 1: NALU
    CompositionTime = UB[24];//24bits的CompositionTime,表示pts与dts的差值

    DATA = [H264 NALU]; //后续数据为常规的视频数据
}
else // IsExHeader使能,表示Enhanced-Rtmp格式使能
{
    PacketType = UB[4];// 0 = PacketTypeSequenceStart
                       // 1 = PacketTypeCodedFrames
                       // 2 = PacketTypeSequenceEnd
                       // 3 = PacketTypeCodedFramesX
                       // 4 = PacketTypeMetadata
                       // 5 = PacketTypeMPEG2TSSequenceStart
    FourCC = UB[32];// 4字节的FourCC,如下字符表示对应的视频CodecId
                    // AV1 = { 'a', 'v', '0', '1' }
                    // VP9 = { 'v', 'p', '0', '9' }
                    // HEVC = { 'h', 'v', 'c', '1' }, 也就是h265

    // 如果类型是HEVC, 也就是H265,后续规范如下
    if (FourCC == HEVC)
    {
        if (PacketType == PacketTypeSequenceStart)
        {
            //如果PacketType是PacketTypeSequenceStart,表示后续H265的数据内容是DecoderConfigurationRecord,也就是常说的sequence header;
            DATA = [HEVCDecoderConfigurationRecord]
        }
        else if (PacketType == PacketTypeCodedFrames || PacketType == PacketTypeCodedFramesX)
        {
            if (PacketType == PacketTypeCodedFrames)
            {
                //如果PacketType是PacketTypeCodedFrames,就是pts与dts有差值
                CompositionTime = UB[24];//24bits,表示pts与dts的差值
            }
            else //如果PacketType是PacketTypeCodedFramesX
            {
                //无CompositionTime,节省3字节的空间
            }
            //随后是正常的H265数据
            DATA = [HEVC NALU]
        }
    }
}

2.2 ffmpeg6.1实现Enhance Rtmp

ffmpeg在version6.1中正式支持enhance rtmp,以此支持H265 in rtmp/flv。这里介绍其对应的实现。

首先下载对应的ffmpeg6.1版本源码:

git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
cd ffmpeg
git fetch origin release/6.1
git checkout release/6.1

flv封装的实现,对应文件: libavformat/flvenc.c

static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) {
    //......
    // 如果编码格式是H265, 使用如下的实现
    if (par->codec_id == AV_CODEC_ID_HEVC) {
        // 如果报文pts和dts不一样,packettype为PacketTypeCodedFrames;
        // 否则packettype为PacketTypeCodedFramesX
        int pkttype = (pkt->pts != pkt->dts) ? PacketTypeCodedFrames : PacketTypeCodedFramesX;

        // 第一个字节,最高bit位写入FLV_IS_EX_HEADER;
        // 第一个字节,最高的第3, 4bits写入FLV_FRAME_KEY或FLV_FRAME_INTER
        // 第一个字节,最低2bits位,写入PacketTypeCodedFramesX或PacketTypeCodedFrames
        avio_w8(pb, FLV_IS_EX_HEADER | pkttype | frametype); // ExVideoTagHeader mode with PacketTypeCodedFrames(X)

        // 后4个字节,写入FourCC,写入4个字符: "hvc1"
        avio_write(pb, "hvc1", 4);

        // pkttype为PacketTypeCodedFrames,写入3个字节的pts与dts的差值
        if (pkttype == PacketTypeCodedFrames)
            avio_wb24(pb, pkt->pts - pkt->dts);
    }

    // 写入H264的数据
    avio_write(pb, pkt->data, pkt->size);

    // ......
}

2.3 OBS实现Enhance Rtmp

OBS在Release 29版本正式支持Enhance Rtmp,实现H265 in rtmp/flv的直播推流。
获取版本:

git clone git@github.com:obsproject/obs-studio.git
cd obs-studio
git fetch origin release/29.1
git checkout release/29.1

具体实现在: plugins/obs-outputs/rtmp-stream.c


enum packet_type_t {
	PACKETTYPE_SEQ_START = 0, //表示报文序列开始
	PACKETTYPE_FRAMES = 1,    //表示该帧dts与pts有差值
	PACKETTYPE_SEQ_END = 2,   //flv文件最后一帧
#ifdef ENABLE_HEVC
	PACKETTYPE_FRAMESX = 3,   //表示该帧dts == pts
#endif
	PACKETTYPE_METADATA = 4
};

//函数flv_packet_ex的最后一个参数type为packet_type_t的取值范围
void flv_packet_ex(struct encoder_packet *packet, enum video_id_t codec_id,
		   int32_t dts_offset, uint8_t **output, size_t *size, int type)
{
// ........
    
	// packet ext header
    // 第一个字节,最高bit位写入1, FRAME_HEADER_EX = 8 << 4;
    // 第一个字节,最高的第3, 4bits写入FLV_FRAME_KEY或FLV_FRAME_INTER
    // 第一个字节,最低2bits位,写入PacketTypeCodedFramesX或PacketTypeCodedFrames, dts与pts相等或不等;
    //                       或写入PACKETTYPE_SEQ_START,或PACKETTYPE_SEQ_END
	s_w8(&s,
	     FRAME_HEADER_EX | type | (packet->keyframe ? FT_KEY : FT_INTER));

    // 后4个字节,写入FourCC,写入4个字符: "hvc1"
	s_w4cc(&s, codec_id);

#ifdef ENABLE_HEVC
	// hevc composition time offset
	if (codec_id == CODEC_HEVC && type == PACKETTYPE_FRAMES) {
        // PacketType为PACKETTYPE_FRAMES,写入3个字节的pts与dts的差值
		s_wb24(&s, get_ms_time(packet, packet->pts - packet->dts));
	}
#endif

    // 写入h265的帧数据
	s_write(&s, packet->data, packet->size);
}

2.4 SRS实现Enhance Rtmp

SRS是国内最流行的流媒体服务器,支持多种直播协议rtmp, httpflv, hls,支持webrtc,也支持安放协议28181。SRS也率先支持Enhance Rtmp,服务端能够接受Enhance Rtmp的推流,同时也兼容国内CodecId=12的H265的rtmp方案。

但是在rtmp或httpflv拉流方向,是应用国内CodecId=12的H265的rtmp方案。

SRS的github地址: https://github.com/ossrs/srs.git, 当前支持分支develop。

实现主要在srs_kernel_codec.cpp这个文件中。

srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) {
    uint8_t frame_type = stream->read_1bytes();
    bool is_ext_header = frame_type & 0x80;

    //判断Rtmp Enhance是否支持,从而获取到编码类型
    if (!is_ext_header) {
        // 如果不是Rtmp Enhance,用传统的方式判断
        codec_id = (SrsVideoCodecId)(frame_type & 0x0f);
        frame_type = (frame_type >> 4) & 0x0f;
    } else {
        // 如果使能Rtmp Enhance,获取packet_type和frame_type
        packet_type = (SrsVideoAvcFrameTrait)(frame_type & 0x0f);
        frame_type = (frame_type >> 4) & 0x07;

        // 读取4个字节的FourCC,判断是否是HEVC.
        uint32_t four_cc = stream->read_4bytes();
        if (four_cc == 0x68766331) { // 'hvc1'=0x68766331
            codec_id = SrsVideoCodecIdHEVC;
        }
    }

    //判断Rtmp Enhance是否支持,从而获取到composition_time(dts与pts的差值)
    if (!is_ext_header) {
        // 如果是传统的Rtmp,读取1个字节的packet_type,和3个字节的composition_time
        packet_type = (SrsVideoAvcFrameTrait)stream->read_1bytes();
        composition_time = stream->read_3bytes();
    } else {
        // 如果使能Rtmp Enhance,当packet_type==1的时候,dts才与pts不一致,才读取3个字节的composition_time,
        // 否则没有composition_time字段;
        if (packet_type == 1) {
            composition_time = stream->read_3bytes();
        }
    }
}

2.5 media-server实现Enhance Rtmp

media-server是国内开源库中实现音视频格式和流媒体类型最全的开源之一,支持flv, mp4, mkv, hls, mpeg, rtmp, rtp, rtsp, sip等音视频格式和流媒体协议,并且在各种封装中支持的codec非常丰富,如H264, H265, AV1, H266都有支持。其采用C语言开发,兼容性好,同时适合服务器和嵌入式的开发。

media-server的github地址: https://github.com/ireader/media-server

media-server的Flv模块同时支持demuxer和muxer,对Enhance Rtmp支持比较全,同时支持AV1, H265, H266。(居然还支持H266,并且有H266 annexb to mp4和H266 mp4 to annexb的代码)

Enhance Rtmp的实现主要在flv-header.c这个文件中, 关键的对应代码如下:

// demuxer
int flv_video_tag_header_read(struct flv_video_tag_header_t* video, const uint8_t* buf, size_t len) {
    // 如果第一个bit是1,则Enhance Rtmp使能
    if (len >= 5 && 0 != (buf[0] & 0x80)) {
		video->keyframe = (buf[0] & 0x70) >> 4; //获取到是否keyframe
		video->avpacket = (buf[0] & 0x0F); //获取packettype
		video->cts = 0; // default

        switch(FLV_VIDEO_FOURCC(buf[1], buf[2], buf[3], buf[4]))
        {
		    case FLV_VIDEO_FOURCC_AV1:
		    	video->codecid = FLV_VIDEO_AV1;
		    	return 5;
		    case FLV_VIDEO_FOURCC_HEVC:
		    case FLV_VIDEO_FOURCC_VVC:
            {
                if (video->avpacket == 1) //如果packettype==1,则dts与pts的差值存在
                {
                    video->cts = ((uint32_t)buf[5] << 16) | ((uint32_t)buf[6] << 8) | buf[7];
                }
                return 5;
            }
        }
    }

    //否则走传统flv的解析流程
    //....
}

// muxer: 通过编译宏控制
int flv_video_tag_header_write(const struct flv_video_tag_header_t* video, uint8_t* buf, size_t len)
{
#ifdef FLV_ENHANCE_RTMP
buf[0] = 0x80 | (video->keyframe << 4) /*FrameType*/;
	buf[0] |= (0 == video->cts && FLV_AVPACKET == video->avpacket) ? FLV_PACKET_TYPE_CODED_FRAMES_X : video->avpacket;

	switch (video->codecid)
	{
	case FLV_VIDEO_AV1:
        SetFourCC(&buf[1], FLV_VIDEO_FOURCC_AV1);
		return 5;

	case FLV_VIDEO_H265:
        SetFourCC(&buf[1], FLV_VIDEO_FOURCC_HEVC);
		if (len >= 8 && FLV_AVPACKET == video->avpacket && video->cts != 0)
		{
            SetCTS(&buf[5], video->cts);
			return 8;
		}
		return 5;

	case FLV_VIDEO_H266:
        SetFourCC(&buf[1], FLV_VIDEO_FOURCC_VVC);
		if (len >= 8 && FLV_AVPACKET == video->avpacket && video->cts != 0)
		{
			SetCTS(&buf[5], video->cts);
			return 8;
		}
		return 5;

	default:
		break; // fallthrough
	}

#endif

    //否则走传统flv的muxer流程
}

3. 国内rtmp支持H265与Enhance rtmp的兼容性问题

因为国内之前支持H265的方案是CodecID=12,现在存在的兼容性其实有两个方向:

  • 推流方向(上行)
  • 拉流方向(下行)

3.1 推流方向

推流方向的兼容性,主要取决于服务端的兼容性,也就是说服务端必须同时能支持:

  • 国内CodecID=12的H265方案
  • Enhance Rtmp的H265方案

先说结论,上行推流方向的兼容性是没有问题,只需要在服务端做好兼容性,同时支持国内外的两种方案。

具体我们以SRS服务为例,只需要判断第一bit位是否使能,就能得知后面应该走Rtmp Enhance流程,还是走传统的Rtmp流程

    bool is_ext_header = frame_type & 0x80;

    //判断Rtmp Enhance是否支持,从而获取到编码类型
    if (!is_ext_header) {
        // 如果不是Rtmp Enhance,用传统的方式判断
    } else {
        // 如果使能Rtmp Enhance,获取packet_type和frame_type
        // 如果packet_type == 1,获取composition_time(3字节,dts与pts的差值)
    }

因为第一个bit位就能判断后续的代码处理流程,所以推流上行做兼容性是比较容易的;

上行解包后,形成数据结构的对象,将其传到下行拉流处,因为视频帧数据部分已经去掉flvTagHeader头,所以该对象传递到下行处理。

以下为伪码:

class SrsVideoFrame
{
public:
    SrsVideoAvcFrameType frame_type; // 帧类型: I/非I
    SrsVideoAvcFrameTrait avc_packet_type;// 是否sequenche header等类型
public:
    int nb_samples; // 帧个数,
    SrsSample samples[SrsMaxNbSamples];// 帧数组,每个SrsSample是一个视频帧数据
};

SrsVideoFrame中的SrsSample数据,是去掉flvTagHeader的视频帧数据,这样传递到下行后,可以根据需要再次打包flvTagHeader,下行可以再次打包成传统的CodecId=12的H265格式,也可以打包成Enhance Rtmp格式(因为下行的兼容性问题,不推荐下行打包成Enhance Rtmp格式,下一节会说明原因)

3.2 拉流方向

拉流方向的兼容性,主要取决于客户端的兼容性,也就是rtmp或http-flv拉流播放器端的兼容性。

因为之前国内的大多数已有的播放器,都支持CodecId=12的H265方案,现存市场很多播放器是没能支持Enhanche Rtmp,也就是说即使服务端下行方向支持Enhance Rtmp,把Enhance Rtmp流推给客户端,客户端可能存在不能识别的情况。对于大量存量的Rtmp/http-flv播放器(仅仅支持CodecId=12的H265方案),向下的流推送只能继续采用CodecId=12的H265方案。

所以,当前国内大多数云服务商和CDN在下行方向,都仅仅支持CodecID=12的国内H265方案。

总结

简单一句话总结兼容性:上行推流国内外两种推流都能兼容,但是下行仅仅提供CodecID=12的国内H265方案支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/596506.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

认识ansible 了解常用模块

ansible是什么&#xff1f; Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可以实现。是自动化运维工具&#xff0…

好惨啊!科研路上的经验教训…

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

知识图谱基础

三元组的定义 定义&#xff1a;在知识图谱中&#xff0c;三元组是由三个元素组成的有序集合&#xff0c;分别是主体&#xff08;subject&#xff09;、谓词&#xff08;predicate&#xff09;和客体&#xff08;object&#xff09;。例如&#xff0c;“苹果是水果”的三元组可…

深入了解C/C++的内存区域划分

&#x1f525;个人主页&#xff1a;北辰水墨 &#x1f525;专栏&#xff1a;C学习仓 本节我们来讲解C/C的内存区域划分&#xff0c;文末会附加一道题目来检验成果&#xff08;有参考答案&#xff09; 一、大体有哪些区域&#xff1f;分别存放什么变量开辟的空间&#xff1f; …

ROS 2边学边练(43)-- 利用GTest写一个基本测试(C++)

前言 在ROS&#xff08;Robot Operating System&#xff09;中&#xff0c;gtest&#xff08;Google Test&#xff09;是一个广泛使用的C测试框架&#xff0c;用于编写和执行单元测试。这些测试可以验证ROS节点、服务和消息等的正确性和性能。 如果我们需要在写的包中添加测试&…

红黑树

一、红黑树用在哪里 HashMap。Linux 进程调度 CFS。Epoll 事件块的管理。Nginx Timer 事件管理。&#xff08;key&#xff0c;value&#xff09;的形式&#xff0c;并且中序遍历是顺序的&#xff0c;红黑树是二叉排序树。 二、红黑树性质 每个节点是红色或者黑色。根节点是黑…

Mybatis进阶3--注解开发

先看&#xff1a; Mybatis进阶1-CSDN博客 Mybatis进阶2-CSDN博客 mybatis注解开发 前置&#xff1a;不需要xxxMapper..xml文件&#xff08;映射文件&#xff09; 在核心配置文件中&#xff1a;<mappers>标签只能使用&#xff1a;<package name"扫描的包&quo…

open-webui+ollama本地部署Llama3

前言 Meta Llama 3 是由 Meta 公司发布的下一代大型语言模型&#xff0c;拥有 80 亿和 700 亿参数两种版本&#xff0c;号称是最强大的开源语言模型。它在多个基准测试中超越了谷歌的 Gemma 7B 和 Mistral 7B Instruct 模型。 安装 1.gpt4all https://github.com/nomic-ai/…

记一次动态规划的采坑之旅, 741摘樱桃 https://leetcode.cn/problems/cherry-pickup/description/

首次看题目时&#xff0c;发现是困难。立马想到了&#xff0c;动态规划。 再看题目&#xff0c; 摘樱桃&#xff0c;还要返回摘两次&#xff0c;求摘最多的樱桃。 大脑第一反应就是&#xff1a; 先使用动态规划&#xff0c;找到 0 0 到 n-1 n-1处走过的最大樱桃&#xff0c; 并…

【码银送书第十九期】《图算法:行业应用与实践》

作者&#xff1a;嬴图团队 01 前言 在当今工业领域&#xff0c;图思维方式与图数据技术的应用日益广泛&#xff0c;成为图数据探索、挖掘与应用的坚实基础。本文旨在分享嬴图团队在算法实践应用中的宝贵经验与深刻思考&#xff0c;不仅促进业界爱好者之间的交流&#xff0c;…

AI不只是技术,更是一种思维方式

一、AI思维 1.个人&#xff1a;提升自己的综合能力&#xff0c;成为一名懂技术、懂设计、懂硬件、懂市场运营等知识的综合型人才 2.数据&#xff1a;从全局视角看数据流向&#xff0c;挖掘数据价值 3.产品&#xff1a;运用新技术&#xff0c;发掘新需求点&#xff0c;探索产…

AI智体的分级:从基于规则到基于LLM

摘要&#xff1a; AI智体被定义为感知环境、做出决策和采取行动的人工实体。受SAE&#xff08;汽车工程师学会&#xff09;自动驾驶6个级别的启发&#xff0c;AI智体也根据效用和强度进行分类&#xff0c;分为以下几个级别&#xff1a;L0——无AI&#xff0c;有工具&#xff0…

马常旭新歌《如愿》:音乐界的“旭日”再现

在这个春暖花开的季节&#xff0c;音乐界又迎来了一股清新的“旭日”气息。是的&#xff0c;就在2024年4月17日&#xff0c;马常旭的新歌《如愿》&#xff08;旭日版&#xff09;在网易云音乐上线了&#xff01;一年的等待&#xff0c;终于迎来了他的音乐回归&#xff0c;给我们…

C语言知识点补充——ASCLL码表

1、ASCLL码表 ASCII码表&#xff08;American Standard Code for Information Interchange&#xff09;是一种用于将字符编码为数字的标准。它定义了128个字符的编码方式&#xff0c;包括数字、字母、标点符号和控制字符等。每个字符都对应一个唯一的7位或8位二进制数 2、Ascl…

贪吃蛇项目(小白保姆级教程)

游戏介绍 游戏背景&#xff1a; 贪吃蛇游戏是经典的游戏项目之一&#xff0c;也是很简单的小游戏 实现背景&#xff1a; 这里我们是基于32位的Win32_API进行实现的 需要的知识点&#xff1a; C语言函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32_API等 适合人群&a…

java中的字符串(String)常量池理解

下面创建String对象的方式一样吗&#xff1f; 上述程序创建对象类似&#xff0c;为什么s1和s2引用对象一样&#xff0c;但是s3和s4不一样呢&#xff1f; 在java程序中&#xff0c;许多基本类型的字面常量会经常用到&#xff0c;例如2,3.11&#xff0c;“hyy”等。为了提升程序…

C语言动态内存管理malloc、calloc、realloc、free函数、内存泄漏、动态内存开辟的位置等的介绍

文章目录 前言一、为什么存在动态内存管理二、动态内存函数的介绍1. malloc函数2. 内存泄漏3. 动态内存开辟位置4. free函数5. calloc 函数6. realloc 函数7. realloc 传空指针 总结 前言 C语言动态内存管理malloc、calloc、realloc、free函数、内存泄漏、动态内存开辟的位置等…

25.哀家要长脑子了---哈希表

1.525. 连续数组 - 力扣&#xff08;LeetCode&#xff09; 在我对通义千问的一番折磨下&#xff0c;终于弄清楚一点点了。哈希表存储前缀和数组值 用一个counter来记录nums中0、1数量差值的变化。 哈希表map存储某个特定的counter值首次出现的位置。counter的计算&#xff1a;…

【LeetCode 121】买卖股票的最佳时机

思路 思路&#xff1a; 所谓代码的复杂性来源于业务的复杂性&#xff0c;如果能够想清楚业务实现逻辑&#xff0c;就能够轻松写出代码&#xff1b; 假设当前是第i天&#xff0c;如何在第i天赚到最多的钱&#xff1f;需要在第i天之前以最低价买入股票&#xff1b; 所以需要求…

13 【PS作图】人物绘画理论-脸型

三庭五眼 三庭&#xff1a;脸的长度比例 &#xff08;1&#xff09;发际线到眉毛 &#xff08;2&#xff09;眉毛到鼻底 &#xff08;3&#xff09;鼻底到下巴 三个部分大致为三等分 五眼&#xff1a;脸的宽度比例 以眼睛长度为单位&#xff0c;把脸的宽度分成五等分&#x…
最新文章