FFmpeg中位操作相关的源码:GetBitContext结构体,init_get_bits函数、get_bits1函数和get_bits函数分析

news2024/11/25 15:58:19

一、引言

由《音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB》可以知道,H.264 码流中的操作单位是位(bit),而不是字节。因为视频的传输和存贮是十分在乎体积的,对于每一个比特(bit)都要格外珍惜。用普通的指针是无法达到“位”的操作粒度的。FFmpeg源码中使用GetBitContext结构体来对“位”进行操作。

二、GetBitContext结构体定义

GetBitContext结构体定义在FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3,该ffmpeg在CentOS 7.5上通过10.2.1版本的gcc编译)的头文件libavcodec/get_bits.h中:

#ifndef CACHED_BITSTREAM_READER
#define CACHED_BITSTREAM_READER 0
#endif

typedef struct GetBitContext {
    const uint8_t *buffer, *buffer_end;
#if CACHED_BITSTREAM_READER
    uint64_t cache;
    unsigned bits_left;
#endif
    int index;
    int size_in_bits;
    int size_in_bits_plus8;
} GetBitContext;

三、init_get_bits函数定义

init_get_bits函数定义在libavcodec/get_bits.h 中:

/**
 * Initialize GetBitContext.
 * @param buffer bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes
 *        larger than the actual read bits because some optimized bitstream
 *        readers read 32 or 64 bit at once and could read over the end
 * @param bit_size the size of the buffer in bits
 * @return 0 on success, AVERROR_INVALIDDATA if the buffer_size would overflow.
 */
static inline int init_get_bits(GetBitContext *s, const uint8_t *buffer,
                                int bit_size)
{
#ifdef BITSTREAM_READER_LE
    return init_get_bits_xe(s, buffer, bit_size, 1);
#else
    return init_get_bits_xe(s, buffer, bit_size, 0);
#endif
}

其作用是初始化GetBitContext结构体。

形参s:输出型参数。指向要被初始化的GetBitContext类型的变量。

形参buffer:输入型参数。指向某个缓冲区,该缓冲区存放NALU Header + RBSP。

形参bit_size:输入型参数。NALU Header + SODB的位数,单位为bit。

返回值:返回0表示成功,返回AVERROR_INVALIDDATA表示失败。

执行init_get_bits函数初始化后,如果初始化成功:

s->buffer指向存放NALU Header + RBSP 的缓冲区。

s->buffer_end指向上述缓冲区的末尾,也就是RBSP的最后一个字节。

s->index的值等于0。

s->size_in_bit 的值等于NALU Header + SODB的位数,单位为bit。

s->size_in_bits_plus8的值等于 s->size_in_bit 的值 加 8。

四、get_bits1函数定义

get_bits1函数定义在 libavcodec/get_bits.h 中:

static inline unsigned int get_bits1(GetBitContext *s)
{
#if CACHED_BITSTREAM_READER
    if (!s->bits_left)
#ifdef BITSTREAM_READER_LE
        refill_64(s, 1);
#else
        refill_64(s, 0);
#endif

#ifdef BITSTREAM_READER_LE
    return get_val(s, 1, 1);
#else
    return get_val(s, 1, 0);
#endif
#else
    unsigned int index = s->index;
    uint8_t result     = s->buffer[index >> 3];
#ifdef BITSTREAM_READER_LE
    result >>= index & 7;
    result  &= 1;
#else
    result <<= index & 7;
    result >>= 8 - 1;
#endif
#if !UNCHECKED_BITSTREAM_READER
    if (s->index < s->size_in_bits_plus8)
#endif
        index++;
    s->index = index;

    return result;
#endif
}

该函数在使用init_get_bits函数初始化后,才能被调用。其作用是读取s->buffer指向的缓冲区(存放NALU Header + RBSP)中的1位(bit)数据。读取完后,s->index的值会加1(所以s->index实际上是用来标记当前读取到第几位了)。

形参s:既是输入型参数也是输出型参数。指向已经被初始化的GetBitContext类型的变量。

返回值:被读取到的1位(bit)的数据。

五、get_bits函数定义

get_bits函数定义在 libavcodec/get_bits.h 中:

/**
 * Read 1-25 bits.
 */
static inline unsigned int get_bits(GetBitContext *s, int n)
{
    register unsigned int tmp;
#if CACHED_BITSTREAM_READER

    av_assert2(n>0 && n<=32);
    if (n > s->bits_left) {
#ifdef BITSTREAM_READER_LE
        refill_32(s, 1);
#else
        refill_32(s, 0);
#endif
        if (s->bits_left < 32)
            s->bits_left = n;
    }

#ifdef BITSTREAM_READER_LE
    tmp = get_val(s, n, 1);
#else
    tmp = get_val(s, n, 0);
#endif
#else
    OPEN_READER(re, s);
    av_assert2(n>0 && n<=25);
    UPDATE_CACHE(re, s);
    tmp = SHOW_UBITS(re, s, n);
    LAST_SKIP_BITS(re, s, n);
    CLOSE_READER(re, s);
#endif
    av_assert2(tmp < UINT64_C(1) << n);
    return tmp;
}

该函数在使用init_get_bits函数初始化后,才能被调用。其作用是读取s->buffer指向的缓冲区(存放NALU Header + RBSP)中的n位(bit)数据。读取完后,s->index的值会加n。

形参s:既是输入型参数也是输出型参数。指向已经被初始化的GetBitContext类型的变量。

返回值:被读取到的n位(bit)的数据。

六、编写测试例子,来理解get_bits1函数和get_bits函数的使用

编写测试例子main.c,在CentOS 7.5上通过10.2.1版本的gcc可以成功编译 :

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>


#define CONFIG_SAFE_BITSTREAM_READER 1

#ifndef UNCHECKED_BITSTREAM_READER
#define UNCHECKED_BITSTREAM_READER !CONFIG_SAFE_BITSTREAM_READER
#endif

#ifndef CACHED_BITSTREAM_READER
#define CACHED_BITSTREAM_READER 0
#endif


#if defined(__GNUC__) || defined(__clang__)
#    define av_unused __attribute__((unused))
#else
#    define av_unused
#endif


#ifdef __GNUC__
#    define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
#    define AV_GCC_VERSION_AT_MOST(x,y)  (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y))
#else
#    define AV_GCC_VERSION_AT_LEAST(x,y) 0
#    define AV_GCC_VERSION_AT_MOST(x,y)  0
#endif
 
 
#define av_alias __attribute__((may_alias))
 
#ifndef av_always_inline
#if AV_GCC_VERSION_AT_LEAST(3,1)
#    define av_always_inline __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
#    define av_always_inline __forceinline
#else
#    define av_always_inline inline
#endif
#endif
 
#if AV_GCC_VERSION_AT_LEAST(2,6) || defined(__clang__)
#    define av_const __attribute__((const))
#else
#    define av_const
#endif
 
 
#define AV_BSWAP16C(x) (((x) << 8 & 0xff00)  | ((x) >> 8 & 0x00ff))
#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16))
 
#ifndef av_bswap32
static av_always_inline av_const uint32_t av_bswap32(uint32_t x)
{
    return AV_BSWAP32C(x);
}
#endif
 
 
union unaligned_32 { uint32_t l; } __attribute__((packed)) av_alias;
 
#   define AV_RN(s, p) (((const union unaligned_##s *) (p))->l)
#   define AV_RB(s, p)    av_bswap##s(AV_RN##s(p))
 
#ifndef AV_RB32
#   define AV_RB32(p)    AV_RB(32, p)
#endif
 
#ifndef AV_RN32
#   define AV_RN32(p) AV_RN(32, p)
#endif


#ifndef NEG_USR32
#   define NEG_USR32(a,s) (((uint32_t)(a))>>(32-(s)))
#endif


/**
 * assert() equivalent, that does lie in speed critical code.
 */
#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
#define av_assert2(cond) av_assert0(cond)
#define av_assert2_fpu() av_assert0_fpu()
#else
#define av_assert2(cond) ((void)0)
#define av_assert2_fpu() ((void)0)
#endif


/**
 * @ingroup lavc_decoding
 * Required number of additionally allocated bytes at the end of the input bitstream for decoding.
 * This is mainly needed because some optimized bitstream readers read
 * 32 or 64 bit at once and could read over the end.<br>
 * Note: If the first 23 bits of the additional bytes are not 0, then damaged
 * MPEG bitstreams could cause overread and segfault.
 */
#define AV_INPUT_BUFFER_PADDING_SIZE 64

#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
#define MKTAG(a,b,c,d)   ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))
#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d))
#define AVERROR_INVALIDDATA        FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input


#if CACHED_BITSTREAM_READER
#   define MIN_CACHE_BITS 64
#elif defined LONG_BITSTREAM_READER
#   define MIN_CACHE_BITS 32
#else
#   define MIN_CACHE_BITS 25
#endif

#if !CACHED_BITSTREAM_READER

#define OPEN_READER_NOSIZE(name, gb)            \
    unsigned int name ## _index = (gb)->index;  \
    unsigned int av_unused name ## _cache

#if UNCHECKED_BITSTREAM_READER
#define OPEN_READER(name, gb) OPEN_READER_NOSIZE(name, gb)

#define BITS_AVAILABLE(name, gb) 1
#else
#define OPEN_READER(name, gb)                   \
    OPEN_READER_NOSIZE(name, gb);               \
    unsigned int name ## _size_plus8 = (gb)->size_in_bits_plus8

#define BITS_AVAILABLE(name, gb) name ## _index < name ## _size_plus8
#endif

#define CLOSE_READER(name, gb) (gb)->index = name ## _index

# ifdef LONG_BITSTREAM_READER

# define UPDATE_CACHE_LE(name, gb) name ## _cache = \
      AV_RL64((gb)->buffer + (name ## _index >> 3)) >> (name ## _index & 7)

# define UPDATE_CACHE_BE(name, gb) name ## _cache = \
      AV_RB64((gb)->buffer + (name ## _index >> 3)) >> (32 - (name ## _index & 7))

#else

# define UPDATE_CACHE_LE(name, gb) name ## _cache = \
      AV_RL32((gb)->buffer + (name ## _index >> 3)) >> (name ## _index & 7)

# define UPDATE_CACHE_BE(name, gb) name ## _cache = \
      AV_RB32((gb)->buffer + (name ## _index >> 3)) << (name ## _index & 7)

#endif


#ifdef BITSTREAM_READER_LE

# define UPDATE_CACHE(name, gb) UPDATE_CACHE_LE(name, gb)

# define SKIP_CACHE(name, gb, num) name ## _cache >>= (num)

#else

# define UPDATE_CACHE(name, gb) UPDATE_CACHE_BE(name, gb)

# define SKIP_CACHE(name, gb, num) name ## _cache <<= (num)

#endif

#if UNCHECKED_BITSTREAM_READER
#   define SKIP_COUNTER(name, gb, num) name ## _index += (num)
#else
#   define SKIP_COUNTER(name, gb, num) \
    name ## _index = FFMIN(name ## _size_plus8, name ## _index + (num))
#endif

#define BITS_LEFT(name, gb) ((int)((gb)->size_in_bits - name ## _index))

#define SKIP_BITS(name, gb, num)                \
    do {                                        \
        SKIP_CACHE(name, gb, num);              \
        SKIP_COUNTER(name, gb, num);            \
    } while (0)

#define LAST_SKIP_BITS(name, gb, num) SKIP_COUNTER(name, gb, num)

#define SHOW_UBITS_LE(name, gb, num) zero_extend(name ## _cache, num)
#define SHOW_SBITS_LE(name, gb, num) sign_extend(name ## _cache, num)

#define SHOW_UBITS_BE(name, gb, num) NEG_USR32(name ## _cache, num)
#define SHOW_SBITS_BE(name, gb, num) NEG_SSR32(name ## _cache, num)

#ifdef BITSTREAM_READER_LE
#   define SHOW_UBITS(name, gb, num) SHOW_UBITS_LE(name, gb, num)
#   define SHOW_SBITS(name, gb, num) SHOW_SBITS_LE(name, gb, num)
#else
#   define SHOW_UBITS(name, gb, num) SHOW_UBITS_BE(name, gb, num)
#   define SHOW_SBITS(name, gb, num) SHOW_SBITS_BE(name, gb, num)
#endif

#define GET_CACHE(name, gb) ((uint32_t) name ## _cache)

#endif


typedef struct GetBitContext {
    const uint8_t *buffer, *buffer_end;
#if CACHED_BITSTREAM_READER
    uint64_t cache;
    unsigned bits_left;
#endif
    int index;
    int size_in_bits;
    int size_in_bits_plus8;
} GetBitContext;


static inline int init_get_bits_xe(GetBitContext *s, const uint8_t *buffer,
                                   int bit_size, int is_le)
{
    int buffer_size;
    int ret = 0;

    if (bit_size >= INT_MAX - FFMAX(7, AV_INPUT_BUFFER_PADDING_SIZE*8) || bit_size < 0 || !buffer) {
        bit_size    = 0;
        buffer      = NULL;
        ret         = AVERROR_INVALIDDATA;
    }

    buffer_size = (bit_size + 7) >> 3;

    s->buffer             = buffer;
    s->size_in_bits       = bit_size;
    s->size_in_bits_plus8 = bit_size + 8;
    s->buffer_end         = buffer + buffer_size;
    s->index              = 0;

#if CACHED_BITSTREAM_READER
    s->cache              = 0;
    s->bits_left          = 0;
    refill_64(s, is_le);
#endif

    return ret;
}


/**
 * Initialize GetBitContext.
 * @param buffer bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes
 *        larger than the actual read bits because some optimized bitstream
 *        readers read 32 or 64 bit at once and could read over the end
 * @param bit_size the size of the buffer in bits
 * @return 0 on success, AVERROR_INVALIDDATA if the buffer_size would overflow.
 */
static inline int init_get_bits(GetBitContext *s, const uint8_t *buffer,
                                int bit_size)
{
#ifdef BITSTREAM_READER_LE
    return init_get_bits_xe(s, buffer, bit_size, 1);
#else
    return init_get_bits_xe(s, buffer, bit_size, 0);
#endif
}


static inline unsigned int get_bits1(GetBitContext *s)
{
#if CACHED_BITSTREAM_READER
    if (!s->bits_left)
#ifdef BITSTREAM_READER_LE
        refill_64(s, 1);
#else
        refill_64(s, 0);
#endif

#ifdef BITSTREAM_READER_LE
    return get_val(s, 1, 1);
#else
    return get_val(s, 1, 0);
#endif
#else
    unsigned int index = s->index;
    uint8_t result     = s->buffer[index >> 3];
#ifdef BITSTREAM_READER_LE
    result >>= index & 7;
    result  &= 1;
#else
    result <<= index & 7;
    result >>= 8 - 1;
#endif
#if !UNCHECKED_BITSTREAM_READER
    if (s->index < s->size_in_bits_plus8)
#endif
        index++;
    s->index = index;

    return result;
#endif
}



/**
 * Read 1-25 bits.
 */
static inline unsigned int get_bits(GetBitContext *s, int n)
{
    register unsigned int tmp;
#if CACHED_BITSTREAM_READER

    av_assert2(n>0 && n<=32);
    if (n > s->bits_left) {
#ifdef BITSTREAM_READER_LE
        refill_32(s, 1);
#else
        refill_32(s, 0);
#endif
        if (s->bits_left < 32)
            s->bits_left = n;
    }

#ifdef BITSTREAM_READER_LE
    tmp = get_val(s, n, 1);
#else
    tmp = get_val(s, n, 0);
#endif
#else
    OPEN_READER(re, s);
    av_assert2(n>0 && n<=25);
    UPDATE_CACHE(re, s);
    tmp = SHOW_UBITS(re, s, n);
    LAST_SKIP_BITS(re, s, n);
    CLOSE_READER(re, s);
#endif
    av_assert2(tmp < UINT64_C(1) << n);
    return tmp;
}


int main()
{
    GetBitContext gb;
    uint8_t *data = (uint8_t *)malloc(sizeof(uint8_t) * 3);
    if(data)
    {
        data[0] = 0x12;
        data[1] = 0x34;
        data[2] = 0x80;
 
        int ret = init_get_bits(&gb, data, 16);
        for(int i=0; i<gb.size_in_bits; i++)
        {
            printf("value:%u, index:%d\n", get_bits1(&gb), gb.index);
        }

        printf("\n______________________________________________\n\n");
        
        ret = init_get_bits(&gb, data, 16);
        printf("value:%u, index:%d\n", get_bits1(&gb), gb.index);
        printf("value:%u, index:%d\n", get_bits(&gb, 2), gb.index);
        printf("value:%u, index:%d\n", get_bits(&gb, 5), gb.index);
        
        free(data);
	    data = NULL;
    }

    return 0;
}

使用gcc编译,运行,输出如下:

本测试例子中,首先通过uint8_t *data = (uint8_t *)malloc(sizeof(uint8_t) * 3); 分配3个字节的内存。然后通过data[0] = 0x12;data[1] = 0x34;data[2] = 0x80; 对该缓冲区进行赋值,使该缓冲区的第一个字节为0x12,第二个字节为0x34,第三个字节为0x80。使用该缓冲区来模拟存放的是H.264中的“NALU Header + RBSP”。

由于第三个字节为0x80,转换为2进制为10000000。所以可以认为第三个字节的0x80是RBSP中的stop bit + rbsp_alignment_zero_bit。所以NALU Header + SODB的位数应该是去掉第三个字节的长度,也就是2个字节,等于16位。


所以:int ret = init_get_bits(&gb, data, 16);中的第三个形参为16。使用init_get_bits初始化GetBitContext结构体类型的变量gb。

然后在for(int i=0; i<gb.size_in_bits; i++)循环中不断通过get_bits1函数,打印gb->buffer指向的缓冲区中的每个位的值。由于0x12转换成2进制是00010010,0x34转换成2进制是00110100。

所以打印为:

打印完后初始化变量gb,重新通过get_bits1和get_bits函数打印。2进制00010010的第0位是0,所以printf("value:%u, index:%d\n", get_bits1(&gb), gb.index);的输出为“value:0, index:0”。此时由于打印了1位,所以index的值变为1。再执行printf("value:%u, index:%d\n", get_bits(&gb, 2), gb.index);由于2进制00010010的第1、第2位都是0,也就是0b00,所以输出是:value:0, index:1,此时由于又打印了2位,所以index的值变为3。再执行printf("value:%u, index:%d\n", get_bits(&gb, 5), gb.index);由于2进制00010010的第3到第7 位是0b10010,转成10进制是18,所以输出是:value:18, index:3。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS安装Docker教程(包含踩坑的经验)

目录 一.基础安装 ▐ 安装Docker 二.启动Docker服务 三.配置Docker镜像加速 一.基础安装 在安装Docker之前可能需要先做以下准备 首先如果系统中已经存在旧的Docker&#xff0c;则先卸载&#xff1a; yum remove docker \docker-client \docker-client-latest \docker-…

Modbus转Profibus网关在汽车行业的应用

一、前言 在当前汽车工业的快速发展中&#xff0c;汽车制造商正通过自动化技术实现生产的自动化&#xff0c;目的是提高生产效率和减少成本。Modbus转Profibus网关&#xff08;XD-MDPB100&#xff09;应用于汽车行业&#xff0c;主要体现在提升自动化水平、优化数据传输以及实…

Java8新特性stream的原理和使用

这是一种流式惰性计算&#xff0c;整体过程是&#xff1a; stream的使用也异常方便&#xff0c;可以对比如List、Set之类的对象进行流式计算&#xff0c;挑出最终想要的结果&#xff1a; List<Timestamp> laterTimes allRecords.stream().map(Record::getTime).filter…

第11章 规划过程组(收集需求)

第11章 规划过程组&#xff08;一&#xff09;11.3收集需求&#xff0c;在第三版教材第375~378页&#xff1b; 文字图片音频方式 第一个知识点&#xff1a;工具与技术 1、决策 投票 用于生成、归类和排序产品需求 独裁型决策制定 一个人负责为整个集体制定决策 多标准决策分析…

软件需求管理规程(DOC原件)

软件需求管理规程是确保软件开发过程中需求清晰、一致、可追踪的关键环节&#xff1a; 明确需求&#xff1a;项目初期&#xff0c;与利益相关者明确项目目标和需求&#xff0c;确保需求完整、无歧义。需求评审&#xff1a;组织专家团队对需求进行评审&#xff0c;识别潜在风险和…

DM达梦数据库存储过程

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

Arduino - OLED

Arduino - OLED Arduino - OLED Arduino通过u8g2库驱动OLEDU8g2 驱动oled自定义中文字库 The OLED (Organic Light-Emitting Diode) display is an alternative for LCD display. The OLED is super-light, almost paper-thin, flexible, and produce a brighter and crisper…

【乐吾乐2D可视化组态编辑器】图形库、组件库

15 图形库 15.1 图纸 新建文件夹、新建图纸、删除文件夹、删除图纸 15.2 系统组件 乐吾乐图形库一共分为三大类&#xff1a;基础图形库、电力图形库、物联网图形库、2.5D图形库&#xff0c;总共约4000个图元&#xff0c;能满足大部分行业的基本需求。 格式有三种&#xff1a…

智慧法务引领:构筑数字化法治核心,塑造未来企业竞争力

在全球化及信息化时代背景下&#xff0c;企业面临的法律环境越来越复杂&#xff0c;法治数字化成为企业维护合法权益、提升市场竞争力的必然选择。智慧法务管理系统作为推动企业法治数字化转型的重要工具&#xff0c;不仅提高了法律服务效率&#xff0c;而且加强了企业的法律风…

百问网全志D1h开发板投屏功能实现

投屏功能实现 D1系列号称点屏神器&#xff0c;不仅能点屏&#xff0c;还能用于投屏。 源码准备 百问网为 【百问网D1h开发板】提供了投屏功能需要使用的源码&#xff0c;直接git下载即可&#xff1a; git clone https://github.com/DongshanPI/DongshannezhaSTU_DLNA_Scree…

MoneyPrinterPlus:AI自动短视频生成工具-微软云配置详解

MoneyPrinterPlus可以使用大模型自动生成短视频&#xff0c;我们可以借助Azure提供的语音服务来实现语音合成和语音识别的功能。 Azure的语音服务应该是我用过的效果最好的服务了&#xff0c;微软还得是微软。 很多小伙伴可能不知道应该如何配置&#xff0c;这里给大家提供一…

沉淀强化镍基合金660大螺丝的物理性能

沉淀强化镍基合金660大螺丝&#xff0c;是一种高性能的工程材料&#xff0c;其在极端环境中展现了优异的稳定性和耐用性。以下&#xff0c;我们将深入解析其主要的物理性能。 首先&#xff0c;该合金螺丝的密度为7.99g/cm&#xff0c;这意味着它具有较高的质量密度&#xff0c;…

力扣随机一题 6/28 数组/矩阵

&#x1f4dd;个人主页&#x1f339;&#xff1a;誓则盟约⏩收录专栏⏪&#xff1a;IT 竞赛&#x1f921;往期回顾&#x1f921;&#xff1a;6/27 每日一题关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d…

GEOS学习笔记(一)

下载编译GEOS 从Download and Build | GEOS (libgeos.org)下载geos-3.10.6.tar.bz2 使用cmake-3.14.0版本配置VS2015编译 按默认配置生成VS工程文件 编译后生成geos.dll&#xff0c;geos_c.dll 后面学习使用C接口进行编程

MySQL中的常用逻辑操作符

逻辑运算符在MySQL查询中扮演着重要角色&#xff0c;通过AND、OR、NOT等运算符的组合使用&#xff0c;可以提高查询的准确性和灵活性&#xff0c;确保查询结果满足业务需求。合理使用这些运算符还能优化查询性能&#xff0c;减少不必要的数据检索&#xff0c;并提高SQL语句的可…

maven 根据不同环境,走不同的实现(多种环境组合)

​ 原因&#xff1a; 线上程序同时支持人大金仓和mysql&#xff0c;且支持根据环境动态选择 java JCE 的实现方式前期已完成 springboot 从mysql 迁移人大金仓 -kingbase &#xff1a;https://blog.csdn.net/qq_26408545/article/details/137777602?spm1001.2014.3001.5502 …

检测SD NAND文件系统异常和修复的方法

目录 1、打开命令提示符&#xff1a; 2、运行chkdsk命令&#xff1a; 3、命令参数说明&#xff1a; chkdsk是Windows中的一个命令行工具&#xff0c;用于检查磁盘上的文件系统错误和修复坏块。MK米客方德为您提供指导&#xff0c;以下是使用chkdsk的步骤&#xff1a; 1、打开…

CAN收发器

1、收发器的主要功能 &#xff08;1&#xff09;CAN通讯&#xff08;即报文收发&#xff09; MCU要CAN通讯&#xff1a;收发器模式切换至正常通讯模式&#xff08;Normal&#xff09;&#xff0c;正常通讯模式收发器能收能发。 MCU不要CAN通讯&#xff1a;把收发器切换至其它…

EHS环境健康安全管理:制造业ESG尖子生的“绿色通行证”

嘿&#xff0c;亲爱的制造业老铁们&#xff01;你们是不是经常听到“EHS环安卫管理”这个词&#xff0c;但又觉得它有些神秘和高大上呢&#xff1f;别担心&#xff0c;今天我就带你们轻松愉快地了解这个让制造业更加绿色、健康、安全的“神器”&#xff01; 一、EHS环安卫管理&…

大模型学习笔记-汇总篇

本文记录一下最近一个月学习的大模型相关的技术知识点&#xff0c;为拥抱AI浪潮做些技术储备。 大模型术语相关 参数规模 GPT 3.5 千亿级别 GPT4 1.8W亿级别 国内一般都是十亿或百亿级别 ChatGLM2_2K_6B BAICHUAN_4K_13B 淘宝星辰_4K_13B 【一一AGI大模型学习 所有资源获…