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

一、引言

由《音视频入门基础: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
#endiftypedef struct GetBitContext {const uint8_t *buffer, *buffer_end;
#if CACHED_BITSTREAM_READERuint64_t cache;unsigned bits_left;
#endifint 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_LEreturn init_get_bits_xe(s, buffer, bit_size, 1);
#elsereturn 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_READERif (!s->bits_left)
#ifdef BITSTREAM_READER_LErefill_64(s, 1);
#elserefill_64(s, 0);
#endif#ifdef BITSTREAM_READER_LEreturn get_val(s, 1, 1);
#elsereturn get_val(s, 1, 0);
#endif
#elseunsigned int index = s->index;uint8_t result     = s->buffer[index >> 3];
#ifdef BITSTREAM_READER_LEresult >>= index & 7;result  &= 1;
#elseresult <<= index & 7;result >>= 8 - 1;
#endif
#if !UNCHECKED_BITSTREAM_READERif (s->index < s->size_in_bits_plus8)
#endifindex++;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_READERav_assert2(n>0 && n<=32);if (n > s->bits_left) {
#ifdef BITSTREAM_READER_LErefill_32(s, 1);
#elserefill_32(s, 0);
#endifif (s->bits_left < 32)s->bits_left = n;}#ifdef BITSTREAM_READER_LEtmp = get_val(s, n, 1);
#elsetmp = get_val(s, n, 0);
#endif
#elseOPEN_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);
#endifav_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);
}
#endifunion 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)#endiftypedef struct GetBitContext {const uint8_t *buffer, *buffer_end;
#if CACHED_BITSTREAM_READERuint64_t cache;unsigned bits_left;
#endifint 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_READERs->cache              = 0;s->bits_left          = 0;refill_64(s, is_le);
#endifreturn 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_LEreturn init_get_bits_xe(s, buffer, bit_size, 1);
#elsereturn init_get_bits_xe(s, buffer, bit_size, 0);
#endif
}static inline unsigned int get_bits1(GetBitContext *s)
{
#if CACHED_BITSTREAM_READERif (!s->bits_left)
#ifdef BITSTREAM_READER_LErefill_64(s, 1);
#elserefill_64(s, 0);
#endif#ifdef BITSTREAM_READER_LEreturn get_val(s, 1, 1);
#elsereturn get_val(s, 1, 0);
#endif
#elseunsigned int index = s->index;uint8_t result     = s->buffer[index >> 3];
#ifdef BITSTREAM_READER_LEresult >>= index & 7;result  &= 1;
#elseresult <<= index & 7;result >>= 8 - 1;
#endif
#if !UNCHECKED_BITSTREAM_READERif (s->index < s->size_in_bits_plus8)
#endifindex++;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_READERav_assert2(n>0 && n<=32);if (n > s->bits_left) {
#ifdef BITSTREAM_READER_LErefill_32(s, 1);
#elserefill_32(s, 0);
#endifif (s->bits_left < 32)s->bits_left = n;}#ifdef BITSTREAM_READER_LEtmp = get_val(s, n, 1);
#elsetmp = get_val(s, n, 0);
#endif
#elseOPEN_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);
#endifav_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.mzph.cn/bicheng/36751.shtml

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

相关文章

利用圆上两点和圆半径求解圆心坐标

已知圆上两点P1&#xff0c;P2&#xff0c;坐标依次为 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) (x1​,y1​),(x2​,y2​)&#xff0c;圆的半径为 r r r&#xff0c;求圆心的坐标。 假定P1&#xff0c;P2为任意两点&#xff0c;则两点连成线段的中点坐标是 x m i …

Git Flow 工作流学习要点

Git Flow 工作流学习要点 Git Flow — 流程图Git Flow — 操作指令优点&#xff1a;缺点&#xff1a;Git Flow 分支类型Git Flow 工作流程简述关于 feature 分支关于 Release 分支关于 hotfix 分支 总结 Git Flow — 流程图 图片来源&#xff1a;https://nvie.com/posts/a-succ…

vue全局方法plugins/utils

一、在src目录下创建一个plugins文件夹 test.ts文件存放创建的方法&#xff0c;index.ts用于接收所有自定义方法进行统一处理 二、编写自定义方法 // test.ts文件 export default {handleTest(val1: number, val2: number) {// 只是一个求和的方法return val1 val2;}, };三…

vue3 【提效】使用 CSS 框架 UnoCSS 实用教程

该换种更高效的方式写 CSS 啦&#xff0c;举个例&#xff1a; <div class"flex"> </div>相当于 <div class"flex"> </div> <style> .flex {display: flex; } </style>当然&#xff0c;还有超多强大的功能帮我们提升…

江科大笔记—FLASH闪存

FLASH闪存 程序现象&#xff1a; 1、读写内部FLASH 这个代码的目的&#xff0c;就是利用内部flash程序存储器的剩余空间&#xff0c;来存储一些掉电不丢失的参数。所以这里的程序是按下K1变换一下测试数据&#xff0c;然后存储到内部FLASH&#xff0c;按下K2把所有参数清0&…

检索增强生成RAG系列2--提高RAG准确度的关键点

上一章讲到了RAG的基本流程&#xff0c;但是如果只是完成一个基本流程&#xff0c;想要在商业上使用还是不行&#xff0c;因为正常商业上的使用其准确度至少有个90%甚至更高。那么如何提高RAG的准确度&#xff0c;那么需要看看RAG有哪些关键点。 目录 1 RAG结构图2 文档处理3 …

【PyQt5】一文向您详细介绍 QVBoxLayout() 的作用

【PyQt5】一文向您详细介绍 QVBoxLayout() 的作用 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&a…

Nest使用multer实现文件上传,并实现大文件分片上传(下)

上节我们学了在 Express 里用 multer 包处理 multipart/form-data 类型的请求中的 file。 单个、多个字段的单个、多个 file 都能轻松取出来。 接下来我们就来学习一下在Nest 里使用multer。 一,Nest如何使用multer实现文件上传 首先我们先创建一个Nest项目&#xff1a; nest…

性能测试4【搬代码】

性能测试4与性能测试3最后的 三、性能瓶颈分析和性能调优 (1)基准测试 (2)负载测试 (3)压力测试 (4)浪涌测试 (5)容量测试 有关&#xff0c;需要结合看 性能瓶颈分析和性能调优 (1)基准测试 一般是单接口&#xff08;单交易&#xff09;&#xff1a;使用一个用持续压测1min以…

【Linux系列】Fedora40安装VMware Workstation Pro报错

问题描述 由于Fedora 40使用的Linux内核是6.9,导致安装VMware Workstation Pro 时&#xff0c;安装依赖无法成功&#xff0c;具体报错如下 ..................CC [M] /tmp/modconfig-a8Fcf5/vmnet-only/smac.oCC [M] /tmp/modconfig-a8Fcf5/vmnet-only/vnetEvent.oCC [M] …

液体粒子计数器的原理及常见型号选择 lighthouse代理商北京中邦兴业

​液体颗粒计数用于测量液体样品中颗粒的大小和分布。通过用激光二极管照射液体样品并检测散射光来测量颗粒分布和尺寸。散射光的性质与粒子大小的大小有关。液体颗粒计数器可用于批量取样或在线&#xff08;连续监测&#xff09;应用&#xff0c;如水处理厂&#xff0c;或用于…

ADC位数、增益调制与参考电压

位数&#xff1a;12bit、10bit、8bit 一般就是对应的ADC值分别为&#xff1a;4095、1023、255&#xff0c;也就选用对应位数时ADC的最大值。 增益的作用 增益设置用于放大或缩小输入信号&#xff0c;使其适配到ADC的输入范围。增益设置可以通过配置SAADC的通道配置寄存器来实…

Vscode lanuch.json

Intro 使用launch.json 能够方便的运行需要传很多参数的代码文件 如下&#xff1a; import math import argparse # 1、导入argpase包def parse_args():parse argparse.ArgumentParser(descriptionCalculate cylinder volume) # 2、创建参数对象parse.add_argument(--rad…

怎么处理整合了shiro的应用的RPC接口鉴权问题

这篇文章分享一下&#xff1a;当一个服务提供者整合了shiro安全框架来实现权限访问控制时&#xff0c;服务消费者通过feign请求服务提供者的接口时的鉴权不通过问题。 问题描述 博主有一个项目pms&#xff08;权限管理系统&#xff09;&#xff0c;使用了shiro框架来实现鉴权功…

【免费可视化工具】智慧港口全景监测大屏引领行业变革

在传统的港口运营中&#xff0c;人们往往要面对繁琐的数据、复杂的流程和不确定的风险。但随着科技的发展&#xff0c;智慧港口全景监测大屏&#xff0c;集数据整合、实时监控、智能分析于一体&#xff0c;为港口运营提供了全新的解决方案。 今天要说的是山海鲸可视化搭建的智慧…

Android 通知组

一. 通知组简介 从 Android 7.0&#xff08;API 级别 24&#xff09;开始&#xff0c;您可以在一个组中显示相关通知。如下所示: 图 1. 收起&#xff08;顶部&#xff09;和展开&#xff08;底部&#xff09;的通知组。 注意 &#xff1a;如果应用发出 4 条或更多条通知且未…

Django(根据Models中模型类反向生成数据库表)—— python篇

一、数据库的配置 1、 django默认支持 sqlite&#xff0c;mysql, oracle,postgresql数据库。 sqlite&#xff1a;django默认使用sqlite的数据库&#xff0c;默认自带sqlite的数据库驱动 , 引擎名称&#xff1a;django.db.backends.sqlite3 mysql&#xff1a;引擎名称&#xff…

解决Transformer根本缺陷,所有大模型都能获得巨大改进

即使最强大的 LLM 也难以通过 token 索引来关注句子等概念&#xff0c;现在有办法了。 最近两天&#xff0c;马斯克和 LeCun 的口水战妥妥成为大家的看点。这两位 AI 圈的名人你来我往&#xff0c;在推特&#xff08;现为 X&#xff09;上相互拆对方台。 LeCun 在宣传自家最新论…

leetcode 动态规划(基础版)单词拆分

题目&#xff1a; 题解&#xff1a; 一种可行的dp做法是基于完全背包问题&#xff0c;将s看成是一个背包&#xff0c;wordDict看作是物品&#xff0c;然后往s中放入物品判断最终是否可以变为给定的s即可。这道题和上一题都用到了在dp如何枚举连续子串和状态表示&#xff1a;枚…

Golang 百题(实战快速掌握语法)_2

返回集合中满足指定条件的最后一个元素 本实验将实现判断给定集合中的元素是否符合&#xff0c;并返回符合的最后一个元素。 知识点 forfmt.Error 适合人群 本课程属于基础课程。需要用户掌握 Go 语言编程基础知识、计算机基础知识和 Linux 环境的基本用法。 许可证 内容…