X264简介-Android使用(二)

news2024/11/25 13:32:01

X264简介-Android使用(二)

4、Ubuntu上安装ffmpeg:

检查更新本地软件包(如果未更新,reboot Vmware):
sudo apt update 
sudo apt upgrade
官网下载的source文件安装: http://ffmpeg.org/

下载完成后,到根目录执行:

make  
make install
查看FFMpeg版本:
ffmpeg –version

在这里插入图片描述

Tips 如果查看ffmpeg出现如下错误:

在这里插入图片描述

解办法->配置ffmpeg的环境变量:

环境变量:

vi /etc/profile

在这里插入图片描述
在开启的配置文件末尾添加如下配置:

#set ffmpeg path environment
PATH=$PATH:/snap/ffmpeg/current/bin
export PATH
编译ffmpeg

sh脚本"build_ffmpeg.sh":

#!/bin/bash
   
export NDK=/home/bruceli/Work/android-ndk-r25d
export PREBUILD=$NDK/toolchains/llvm/prebuilt
export CROSS_PREFIX=${PREBUILD}/linux-x86_64/bin/arm-linux-androideabi-
export CC=$PREBUILD/linux-x86_64/bin/armv7a-linux-androideabi21-clang
export NM=$CROSS_PREFIXnm
export AR=$CROSS_PREFIXar
 
export PREFIX=./android/armeabi-v7a
 
function build_so
    {
	./configure \
	--prefix=$PREFIX \
	--cc=$CC \
	--nm=$NM \
	--ar=$AR \
	--enable-small \
	--disable-programs \
	--disable-avdevice \
	--disable-encoders \
	--disable-muxers \
	--disable-filters \
	--cross-prefix=$CROSS_PREFIX \
	--target-os=android \
	--arch=arm \
	--disable-shared \
	--enable-static \
	--enable-cross-compile
    }
 
make clean
build_so
make -j4
make install
}

将以上脚本放置与ffmpeg的根目录,并执行:
./build_ffmpeg.sh
Tips 出现以下错误
错误: make: *** [libavfilter/libavfilter.a] 错误 127
解决方案一->授权r25c文件夹权限(NG):
chmod +777 -R android-ndk-r25c/
解决方案二->install libavfilter(NG):
sudo apt-get install -y libavfilter-dev
解决方案三->配置 android-ndk-r21e 版本的NDK(SUCCESS):
...
export NDK=/home/bruceli/Work/android-ndk-r21e
...
文件生成路径:/home/bruceli/Work/ffmpeg-5.1.2/android/armeabi-v7a

运行结果:
在这里插入图片描述

下载NDK:

LINK1:

wget -c http://dl.google.com/android/ndk/android-ndk-r25c-linux-x86_64.bin

LINK2: https://developer.android.google.cn/ndk/downloads?hl=zh-cn
DOC: https://developer.android.google.cn/ndk/guides/other_build_systems?hl=zh-cn

配置NDK环境变量: 编辑
sudo gedit ~/.bashrc
文件末尾添加路径
export   NDK=/文件夹路径 
export   PATH=${PATH}:$NDK

保存文件
source  ~/.bashrc

build ndk:

ndk-build

在这里插入图片描述

5、编译X264:

官网连接: https://www.videolan.org/developers/x264.html
编译配置:
./configure --disable-asm --enable-shared --enable-pic
编译和安装:
make
make install

x264编译脚本

脚本(build_x264.sh):
新建以下文件:
vim build_x264.sh
文件内容:
#!/bin/bash
 
export NDK=/home/bruceli/Work/android-ndk-r21e
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export API=21
 
 
function build_one
{
./configure \
    --prefix=$PREFIX \
	--disable-cli \
    --enable-static \
    --enable-pic \
    --host=$my_host \
	--cross-prefix=$CROSS_PREFIX \
    --sysroot=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
 
make clean
make -j8
make install
}
 
#arm64-v8a
PREFIX=./android/arm64-v8a
my_host=aarch64-linux-android
export TARGET=aarch64-linux-android
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
build_one
 
#armeabi-v7a
PREFIX=./android/armeabi-v7a
my_host=armv7a-linux-android
export TARGET=armv7a-linux-androideabi
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
build_one

开启文件权限:
chmod +777 build_x264.sh
放置x264文件夹下并执行
./build_x264.sh

在这里插入图片描述
生成文件路径:

1> /home/bruceli/Work/x264/android/arm64-v8a 
2> /home/bruceli/Work/x264/android/armeabi-v7a

使用

Android Studio

CmakeList配置文件

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        queue.c
        native-lib.c)

set(distribution_DIR ../../../../libs)

add_library(libx264
        SHARED
        IMPORTED)
set_target_properties(libx264
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libx264.a)

add_library(librtmp
        SHARED
        IMPORTED)
set_target_properties(librtmp
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/librtmp.a)

add_library(libfaac
        SHARED
        IMPORTED)
set_target_properties(libfaac
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libfaac.a)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        native-lib
        libx264
        librtmp
        libfaac

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

交叉编译后生成文件放置的位置:

在这里插入图片描述

配置参数及说明

///setVideoOptions
JNIEXPORT void JNICALL
Java_com_example_smallwalnut_jni_PushNative_setVideoOptions(JNIEnv *env, jobject instance,
                                                            jint width, jint height, jint bitrate,
                                                            jint fps) {
    //x264流程:

    //x264_encoder_encode 编码
    //x264_encoder_close( h ) 关闭编码器,释放资源
    //x264_param_default_preset 设置
    x264_param_t param;
    //"ultrafast" - "medium" ... 与速度和画质有关 ,zerolatency ->可降低在线转码的编码延迟
    x264_param_default_preset(&param, "ultrafast", "zerolatency");
    //编码输入的像素格式
    //YUV 4:4:4采样,每一个Y对应一组UV分量。
    //YUV 4:2:2采样,每两个Y共用一组UV分量。
    //YUV 4:2:0采样,每四个Y共用一组UV分量。
    param.i_csp = X264_CSP_I420;
    param.i_width = width;
    param.i_height = height;

    y_len = width * height;
    u_len = y_len / 4;
    v_len = u_len;

    //参数i_rc_method表示码率控制,CQP(恒定质量),CRF(恒定码率),ABR(平均码率)
    //恒定码率,会尽量控制在固定码率
    param.rc.i_rc_method = X264_RC_CRF;
    param.rc.i_bitrate = bitrate / 1000; //* 码率(比特率,单位Kbps)
    param.rc.i_vbv_max_bitrate = bitrate / 1000 * 1.2; //瞬时最大码率

    //码率控制不通过timebase和timestamp,而是fps
    param.b_vfr_input = 0;
    param.i_fps_num = fps; //* 帧率分子
    param.i_fps_den = 1; //* 帧率分母
    param.i_timebase_den = param.i_fps_num;
    param.i_timebase_num = param.i_fps_den;
    param.i_threads = 1;//并行编码线程数量,0默认为多线程

    //码率控制不通过timebase和timestamp,而是fps
    //是否把sps和pts放入一个关键帧
    //SPS Sequence Parameter Set 参数序列集,PPs Picture Params Set图像参数集
    //为了提高图像的纠错能力
    param.b_repeat_headers = 1;
    //设置级别level
    param.i_level_idc = 51;
    //x264_param_apply_profile 设置档次
    x264_param_apply_profile(&param, "baseline");

    //x264_picture_alloc(x264_picture_t输入图像)初始化
    x264_picture_alloc(&pic_in, param.i_csp, param.i_width, param.i_height);
    pic_in.i_pts = 0;
    //x264_encoder_open 打开编码器
    video_encode_handle = x264_encoder_open(&param);

    if (video_encode_handle) {
        LOGI("%s", "打开编码器成功");
    } else {
        LOGI("%s", "打开编码器失败");
        throwNativeError(env,INIT_FAILED);

    }
}
YUV

YUV格式有两大类:planar和packed。
对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。
对于packed的YUV格式,每个像素点的Y,U,V是连续交叉存储的。

YUV分为三个分量,Y表示明亮度(Luminance或Luma),也就是灰度值;而U和V表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
与我们熟知的RGB类似,YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。

采样方式
YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,用三个图来直观地表示采集的方式,以黑点表示采样该像素点的Y分量,以空心圆圈表示采样该像素点的UV分量。

在这里插入图片描述

start

///startPush
JNIEXPORT void JNICALL
Java_com_example_smallwalnut_jni_PushNative_startPush(JNIEnv *env, jobject instance, jstring url_) {

    //
    jobj_push_native = (*env)->NewGlobalRef(env, instance);
    jclass j_cls_push_native_temp = (*env)->GetObjectClass(env, jobj_push_native);
    jcls_push_native = (*env)->NewGlobalRef(env,j_cls_push_native_temp);
    //PushNative.throwNativeError
    jmid_throw_native_error = (*env)->GetMethodID(env, jcls_push_native,
                                                           "throwNativeError",
                                                           "(I)V");
//    jmid_throw_native_error =  (*env)->NewGlobalRef(env,throw_native_error_mid_temp);//jmethodId 不能全局引用

    const char *url_cstr = (*env)->GetStringUTFChars(env, url_, 0);

    LOGI("播放地址:%s", url_cstr);
    //utl赋值
    rtmp_path = malloc(strlen(url_cstr) + 1);
    //数组初始化,临时变量清0
    memset(rtmp_path, 0, strlen(url_cstr) + 1);
    memcpy(rtmp_path, url_cstr, strlen(url_cstr));

    //初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    //创建
    create_queue();

    //init
    //启动消费者线程(从队列中能够不断拉取RTMPPacket发送给流媒体服务器)
    pthread_t push_thread_id;
    pthread_create(&push_thread_id, NULL, push_thread, NULL);
    (*env)->ReleaseStringUTFChars(env, url_, url_cstr);
}

视频编码

JNIEXPORT void JNICALL
Java_com_example_smallwalnut_jni_PushNative_fireVideo(JNIEnv *env, jobject instance,
                                                      jbyteArray data_) {
    jbyte *nv21_buffer = (*env)->GetByteArrayElements(env, data_, NULL);
    //视频数据转为yuv420p
    //nv21->yuv420p
    jbyte *u = (jbyte *) pic_in.img.plane[1];
    jbyte *v = (jbyte *) pic_in.img.plane[2];

    memcpy(pic_in.img.plane[0], nv21_buffer, y_len);
    int i = 0;
    for (; i < u_len; i++) {//notice
        *(u + i) = *(nv21_buffer + y_len + i * 2 + 1);
        *(v + i) = *(nv21_buffer + y_len + i * 2);
    }
    //x264编码得到NALU指针数组
    x264_nal_t *nal = NULL;
    int n_nal = -1;//nalu的个数
    //进行h264编码
    if (x264_encoder_encode(video_encode_handle, &nal, &n_nal, &pic_in, &pic_out) < 0) {
        LOGI("%s", "编码失败");
        throwNativeError(env,INIT_FAILED);
        return;
    }
    //使用rtmp协议将数据发送到流媒体服务器
    //帧分为关键帧和普通帧,为了提高画面的纠错率,关键帧必须包含sps和pps数据
    unsigned char sps[100];
    unsigned char pps[100];
    int sps_len, pps_len;
    memset(sps, 0, 100);
    memset(pps, 0, 100);
    pic_in.i_pts += 1;//顺序叠加

    i = 0;//遍历NALU数组,根据NALU的类型判断(SPS PPS )
    for (; i < n_nal; i++) {
        if (nal[i].i_type == NAL_SPS) {
            //复制sps数据
            sps_len = nal[i].i_payload - 4;
            memcpy(sps, nal[i].p_payload + 4, sps_len);//不复制4字节起始码
        } else if (nal[i].i_type == NAL_PPS) {
            //复制pps数据
            pps_len = nal[i].i_payload - 4;
            memcpy(pps, nal[i].p_payload + 4, pps_len);//不复制4字节起始码

            //发送序列信息
            //将sps和pps数据添加到h264关键帧发送
            add_264_key_header(pps, sps, pps_len, sps_len);
        } else {
            //发送普通帧信息
            add_264_body(nal[i].p_payload, nal[i].i_payload);
        }
    }

    // TODO
    (*env)->ReleaseByteArrayElements(env, data_, nv21_buffer, 0);
}

在H.264标准协议中规定了多种不同的NAL
Unit类型,其中类型7表示该NAL Unit内保存的数据为Sequence Paramater
Set。在H.264的各种语法元素中,SPS中的信息至关重要。如果其中的数据丢失或出现错误,那么解码过程很可能会失败。SPS及后续将要讲述的图像参数集PPS在某些平台的视频处理框架(比如iOS的VideoToolBox等)还通常作为解码器实例的初始化信息使用。

SPS即Sequence Paramater Set,又称作序列参数集。SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。所谓的编码视频序列即原始视频的一帧一帧的像素数据经过编码之后的结构组成的序列。而每一帧的编码后数据所依赖的参数保存于图像参数集中。一般情况SPS和PPS的NAL Unit通常位于整个码流的起始位置。但在某些特殊情况下,在码流中间也可能出现这两种结构,主要原因可能为:
解码器需要在码流中间开始解码;
编码器在编码的过程中改变了码流的参数(如图像分辨率等);
在做视频播放器时,为了让后续的解码过程可以使用SPS中包含的参数,必须对其中的数据进行解析。

除了序列参数集SPS之外,H.264中另一重要的参数集合为图像参数集Picture Paramater
Set(PPS)。通常情况下,PPS类似于SPS,在H.264的裸码流中单独保存在一个NAL Unit中,只是PPS NAL
Unit的nal_unit_type值为8;而在封装格式中,PPS通常与SPS一起,保存在视频文件的文件头中。

SPS
在这里插入图片描述
PPS
在这里插入图片描述

发送frame信息

///发送帧信息
void add_264_body(unsigned char *buf, int len) {
//去掉起始码(界定符)
    if (buf[2] == 0x00) {  //00 00 00 01
        buf += 4;
        len -= 4;
    } else if (buf[2] == 0x01) { // 00 00 01
        buf += 3;
        len -= 3;
    }
    int body_size = len + 9;
    RTMPPacket *packet = malloc(sizeof(RTMPPacket));
    RTMPPacket_Alloc(packet, body_size);

    unsigned char *body = packet->m_body;
    //当NAL头信息中,type(5位)等于5,说明这是关键帧NAL单元
    //buf[0] NAL Header与运算,获取type,根据type判断关键帧和普通帧
    //00000101 & 00011111(0x1f) = 00000101
    int type = buf[0] & 0x1f;
    //Inter Frame 帧间压缩
    body[0] = 0x27;//VideoHeaderTag:FrameType(2=Inter Frame)+CodecID(7=AVC)
    //IDR I帧图像
    if (type == NAL_SLICE_IDR) {
        body[0] = 0x17;//VideoHeaderTag:FrameType(1=key frame)+CodecID(7=AVC)
    }
    //AVCPacketType = 1
    body[1] = 0x01; /*nal unit,NALUs(AVCPacketType == 1)*/
    body[2] = 0x00; //composition time 0x000000 24bit
    body[3] = 0x00;
    body[4] = 0x00;

    //写入NALU信息,右移8位,一个字节的读取?
    body[5] = (len >> 24) & 0xff;
    body[6] = (len >> 16) & 0xff;
    body[7] = (len >> 8) & 0xff;
    body[8] = (len) & 0xff;

    /*copy data*/
    memcpy(&body[9], buf, len);

    packet->m_hasAbsTimestamp = 0;
    packet->m_nBodySize = body_size;
    packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;//当前packet的类型:Video
    packet->m_nChannel = 0x04;
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
//	packet->m_nTimeStamp = -1;
    packet->m_nTimeStamp = RTMP_GetTime() - start_time;//记录了每一个tag相对于第一个tag(File Header)的相对时间
    add_rtmp_packet(packet);
}

Native class

public class PushNative {
    public static final int CONNECTION_FAILED=101;
    public static final int INIT_FAILED=102;
    static {
        System.loadLibrary("native-lib");
    }

    LiveStateChangeListener liveStateChangeListener;

    public void setLiveStateChangeListener(LiveStateChangeListener liveStateChangeListener) {
        this.liveStateChangeListener = liveStateChangeListener;
    }

    public void removeLiveStateChangeLitener(){
        if (liveStateChangeListener != null){
            liveStateChangeListener =null;
        }
    }

    /**
     * 接收native抛出的错误
     */
    public void throwNativeError(int code) {
        if (liveStateChangeListener != null) {
            liveStateChangeListener.onError(code);
        }
    }

    public native void startPush(String url);

    public native void releasePush();

    public native void stopPush();

    //设置音频参数
    public native void setAudioOptions(int sampleRateInHz, int channel);

    //设置视频参数
    public native void setVideoOptions(int width, int height, int bitrate, int fps);

    //发送视频
    public native void fireVideo(byte[] data);

    //发送音频
    public native void fireAudio(byte[] bytes, int len);
}

小结

本次主要介绍X264,其中提到的rtmp,faac,ffmpeg不展开介绍

相关资料整理如下:

《x264_SPS&PPS_简介.docx》

《x264_command.docx》

《nginx.conf》

《ffmpeg的中文文档.pdf》

《build_ffmpeg.sh》

《build_x264.sh》


上一篇:X264简介-Android使用(一)

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

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

相关文章

基于JSP的网上书城

技术&#xff1a;Java、JSP等摘要&#xff1a;随着科技的迅速发展&#xff0c;计算机技术已应用到社会的各个领域。随着计算机技术和通信技术的迅速发展&#xff0c;网络的规模也逐渐增大&#xff0c;网络的元素也随之不断增加&#xff0c;有的利用其通信&#xff0c;有的利用其…

【C++】30h速成C++从入门到精通(IO流)

C语言的输入与输出C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取数据&#xff0c;并将值存放在变量中。printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。注意宽度输出和精度输出控制。C语言借助了相应的缓冲区来…

网络协议丨从物理层到MAC层

我们都知道TCP/IP协议其中一层&#xff0c;就是物理层。物理层其实很好理解&#xff0c;就是物理攻击的物理。我们使用电脑上网时的端口、网线这些都属于物理层&#xff0c;没有端口没有路由你没有办法上网。网线的头我们叫水晶头&#xff0c;也是物理层的一份子。如果你的面前…

认识CSS之基础选择器

&#x1f31f;所属专栏&#xff1a;前端只因变凤凰之路&#x1f414;作者简介&#xff1a;rchjr——五带信管菜只因一枚&#x1f62e;前言&#xff1a;该系列将持续更新前端的相关学习笔记&#xff0c;欢迎和我一样的小白订阅&#xff0c;一起学习共同进步~&#x1f449;文章简…

SpringCloud——Hystrix 从入门到辗转反侧

SpringCloud-Hystrix 服务故障的“雪崩”效应 微服务的“扇出” 多个微服务之间的关联调用称为服务"扇出"。例如微服务A调用微服务B和微服务C&#xff0c;微服务B和微服务C又调用其他的微服务。 由于网络原因或者自身的原因&#xff0c;服务并不能保证 100% 可用&…

fork的黑科技,它到底做了个啥,源码级分析linux内核的内存管理

最近一直在学习linux内核源码&#xff0c;总结一下 https://github.com/xiaozhang8tuo/linux-kernel-0.11 一份带注释的源码&#xff0c;学习用。 fork的黑科技&#xff0c;它到底做了个啥&#xff0c;源码级分析linux内核的内存管理 先导知识&#xff0c;操作系统&#xff1…

提高生活质量,增加学生对校园服务的需求,你知道有哪些?

随着电子商务平台利用移动互联网的趋势提高服务质量&#xff0c;越来越多的传统企业开始关注年轻大学生消费者的校园市场。 提高生活质量&#xff0c;增加学生对校园服务的需求 大学生越来越沉迷于用手机解决生活中的“吃、喝、玩、乐”等服务&#xff0c;如“吃、喝”——可…

gitlab中文社区

1、获取gitlab中文社区项目 中文社区版项目&#xff1a;https://gitlab.com/xhang/gitlab 2、克隆中文仓库 git clone https://gitlab.com/xhang/gitlab.git 3、查看gitlab版本 diff 获取对应版本的中文 head -1 /opt/gitlab/version-manifest.txt #安装的是gitlab-ce…

网络拓扑结构的简单介绍

网络拓扑(Network Topology)就是根据网络节点的业务量和所需的信息流特性&#xff0c;确定它们之间的位置关系和连接方式。网络拓扑结构是指用传输介质互连各种设备的物理布局&#xff0c;指构成网络的成员间特定的物理的(真实的)&#xff0c;或者逻辑的(虚拟的)排列方式。 为什…

Linux学习第十九节-NFS网络文件系统

1.NFS概念 NFS&#xff08;Network File System&#xff09;网络文件系统 &#xff0c; 是一种基于TCP/UDP传输协议的文件共享服务。 NFS基于C/S架构&#xff0c;服务端启用协议将文件共享到网络上&#xff0c;然后允许本地NFS客户端通过网络挂载服务端共享的文件。 NFS基于…

idea downloading spring initializer下不来

图中url输入:https://plugins.jetbrains.com/ 原先是https://start.spring.io

Rust Web入门(六):服务器端web应用

本教程笔记来自 杨旭老师的 rust web 全栈教程&#xff0c;链接如下&#xff1a; https://www.bilibili.com/video/BV1RP4y1G7KF?p1&vd_source8595fbbf160cc11a0cc07cadacf22951 学习 Rust Web 需要学习 rust 的前置知识可以学习杨旭老师的另一门教程 https://www.bili…

使用免费负载生成器swingbench对oracle数据库进行压力测试(测试Oracle的功能或评估性能)

1.Swingbench 简介 Swingbench 是一个免费负载生成器&#xff08;和基准测试&#xff09;&#xff0c;旨在对 Oracle 数据库 进行压力测试。目前最新版本 Swingbench 2.6。 SwingBench 由负载生成器&#xff0c;协调器和集群概述组成。该软件可以生成负载 并绘制交易/响应时间…

Vector - CAPL - Write窗口常用函数

在CAPL自动化开发中,特别是通过Vector CAPL Browser开发中,最终都是通过仿真节点来进行最终的测试,然而这种测试方式没有自动化执行过程报告,只能通过最后的html报告来查看执行过程,那我们是否有更好的调试方法呢?当然是有的,那就是write窗口,通过该窗口我们不但可以打…

【SpringBoot高级篇】SpringBoot集成jasypt 配置脱敏和数据脱敏

【SpringBoot高级篇】SpringBoot集成jasypt数据脱敏配置脱敏使用场景配置脱敏实践数据脱敏pomymlEncryptMethodEncryptFieldEncryptConstantEncryptHandlerPersonJasyptApplication配置脱敏 使用场景 数据库密码直接明文写在application.yml配置中&#xff0c;对安全来说&…

神垕古镇景区5A级十年都没有实现的三大主因

钧 瓷 内 参 第40期&#xff08;总第371期&#xff09; 2023年3月5日 神垕古镇景区5A级十年都没有实现的三大主因 这是2013年&#xff0c;禹州市市政府第一次提出创建5A级景区到今年三月份整整十年啊&#xff01; 目前神垕古镇景区是4A级景区&#xff0c;5A级一直进行中&a…

使用去中心化存储构建网站

今天的大多数网站都遵循后端服务器到前端代码的架构。但在 Web3 应用程序中&#xff0c;前端代码不具有与受智能合约保护的后端代码相同的去中心化性和弹性。那么如何使网站像智能合约一样具有弹性呢&#xff1f; 该体系结构似乎很简单&#xff1a; 创建一个没有服务器的静态…

CorelDRAWX4的VBA插件开发(四十四)建立类(1)汇总相似功能简化重复代码:一键建立设计外框加出血线和等分折页线

这次主要来浅讲一下"类"这个功能,先上一下要实现的功能,建立设计外框加出血线和等分折页线,先上图 那什么是类呢?类其实就是CLASS,用来封装成员参数和函数的,拆开来里面就是这些东西,那写起来其实也没有什么区别,那既然都是参数和函数,那类的出现有什么意义呢.那我…

聚观早报 | 京东百亿补贴今日上线;微软推出全能型人工智能模型

今日要闻&#xff1a;京东“百亿补贴”今日全面上线&#xff1b;小鹏回应人脸识别需对车头半跪&#xff1b;微软推出全能型人工智能模型&#xff1b;雷军建议构建完善汽车数据安全管理体系&#xff1b;苹果、Meta已向国内Micro LED企业下单京东“百亿补贴”今日全面上线 3 月 6…

创建springboot项目文件报红

目录 一、遇到问题 二、出现这个问题的原因 三、解决办法 三种方法 四、操作步骤 一、遇到问题 创建springboot项目的时候&#xff0c;会发现一些重要文件都变成红色了&#xff0c;但是不影响程序的运行。只是看起来会有点不舒服。 二、出现这个问题的原因 因为这个spr…