音视频开发:音频编码原理+采集+编码实战

news2024/11/27 19:49:54

原理:

  1. 消除冗余信息,压缩量最大,也叫有损压缩
  • 剔除人耳听觉范围外的音频信号20Hz以下和20000Hz以上;
  • 去除被掩蔽的音频信号,信号的遮蔽可以分为频域遮蔽和时域遮蔽;
  • 频域遮蔽效应
    屏蔽70分贝以下,20HZ以下,20000HZ以上
    屏蔽分贝小,频率小的声音
    两个频率相近发出的声音,去除低强度的,也就是分贝高的会盖住分贝低的

  • 时域遮蔽效应:
    根根时间推移,相近频率且同时出现的声音,声音强度高的遮蔽强度低的声音,并且去除同一时间段前后杂音,前遮蔽50毫秒,后遮蔽200毫秒,在这段时间内的声音,强度越接近就越会被屏蔽。

  1. 去除冗余信息后,再进行无损压缩;
  • 无损压缩就是压缩后的数据能够解压缩进行还原,有损则不能;
  • 熵编码中有
    哈夫曼编码:用一个很小的二进制数代替一个长的字符串,频率越高,编码越小,频率越低,编码越长
    算术编码:利用小数进行编码,在香农编码的基础改进而来的
    香农编码

音频编码过程

数据先同时通过 时域转频域变换器和心理学模型处理数据,前者将数据转换成多种频段的数据,然后剔除不需要的频段数据,后者会去除非人耳听到的范围声音和一些复合声音,最后将两者合并经过量化编码,无损编码之类的,形成比特流数据,在此之前还会有一些辅助数据,此后数据就会变得非常小;

常见的音频编码器

opus、aac、Ogg、Speex、iLBC、AMR、G.711, 最常用的编码器是opus aac。
opus常用于直播,尤其是无延迟的直播,webrtc默认使用opus;
AAC是应用最广泛的编解码;
Ogg收费;
Speex支持回音消除;
G.711一般用于固定电话,声音损耗严重,通话会失真;

本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs),有需要的可以进企鹅裙927239107领取哦~

 

AAC比较适合有一定延迟的直播,AAC-LD属于低延迟编码器

  • AAC编码器:目前应用最广泛,如iOS、安卓和其他嵌入式设备都包含了AAC硬件编解码器,主要学习这个编码器;
    用来取代mp3,比mp3更高的压缩比和保真性更强;



常用的规格有AAC LC、AAC HE V1 、AAC HE V2三种;

AAC HE V1 = AAC + SBR;
AAV HE V2 = AAC + SBR + PS;
目前AAC HE V1 已经被取代 V2 取代了;

V2的码流跟V1的差别不是很大,根据声音的数据变化,如果两个声道的差别很大,码流差别就会越小;

AAC 中header有两种格式:

就相当于在aac数据前面加了个Header,header里面就会包含aac数据的一些信息,方便进行编解码

  1. ADIF(Audio data interchange format): 特点是只能从头开始解码,可以确定的找到音频数据的开始部分,不能从音频数据中间开始,这种格式常用于磁盘文件中;
  2. ADTS(Audio Data Transport Format):在每一帧的数据里面都会有一个同步字,也就是每帧都有一个header,所以他可以在任意的位置开始进行解码,就像流式数据;
  • ADTS结构: 由7-9个字节组成,通常情况下是7个字节,如果有CRC 就是9个字节,字节中的每一位都有独特的含义;
    • 1~12bit:全部是1也就是0xFFF,表示是同步字;
    • 13:编码规范 0 = MPEG-4 1 = MPEG-2;
    • 14~15:总是0;
    • 16:是否有保护 1 代表 没有 CRC 0 代表有CRC;
    • 17~18:表示的是MPEG-4的音频类型:AAC LC、 AAC HE V1 、AAC HE V2
    • 19~22:表示的是采样率
    • 24~26:通道数
    • 31~33:数据长度,也包括了header的长度
  • 剩余的之后补上



其中每一十进制数对应的含义:

Audio Object Type: 在代码中实际获取类型的时候需要进行+1,才是下面的类型
1 == AAC main
2 == AAC LC
5 == SBR == HE V1
29 == ps == HE V2

其中的采样率是通过十进制数表示的一个采样率,有一个表,比如:0 == 96000Hz 1 == 88200HZ 等

音频采集实战

每个端音频采集的底层和应用层的库是不一样的,所以使用ffmpeg中间层能够实现跨平台开发;

  • Android端的底层库是AudioRecorder,应用层是MediaRecorder;
  • iOS端的底层库是AudioUnit,应用层是AVFoundation;
  • Windows端的常用的是Directshow OpenAL 还有Windows7之上的AudioCore;

使用ffmpeg有两种采集方式:

  1. 使用命令方式,命令详情查看ffmpeg相关指令的那篇
  2. 使用代码调用api的方式
  • 在mac下的动态库需要对动态库进行签名

获取本地签名证书列表:/usr/bin/security find-identity -v -p codesigning
查看动态库是否签名: codesign -d -vv 动态库文件
签名命令:codesign -fs "iPhone Distribution: 你的签名证书." 动态库文件
xcode环境:13.2.1
签名了如果还是报错,关掉沙盒并且设置 Enable Hardened Runtime 为NO
在项目中设置user header search path的时候,要使用全路径方式,我使用$(PROJECT_NAME)方式,有的头文件在链接的时候会报错;

采集音频的步骤:

  1. 打开输入输出设备,涉及的包是avdevice avformat 注册设备 设置采集方式,根据平台选择,即设置输入 打开音频设备
  2. 获取数据包 包:avcodec 主要使用av_read_frame方法获取数据 将数据放入packet中 在读取的时候注意缓冲区无未准备好的情况
  3. 将数据输出到文件 创建文件--- fopen 将数据写入文件-- fwrite 关闭文件 -- fclose
  • 打开设备 ·
void startRecorder(void) {
    // 上下文
    AVFormatContext *av_context = NULL;
    AVDictionary *options = NULL;
    // 1. 注册设备
    avdevice_register_all();
    
    // 2. 设置采集方式
    //设置采集方式 mac os 下是AVfoundation Windows下是dshow  linux 下是alsa
    AVInputFormat *format = av_find_input_format("avfoundation");
    
    // 3. 打开设备
    //里面的识别格式为[[video device]:[audio device]]  这里写0 是获取第1个音频设备
    char *name = ":0";

    // url 是路径 可以是网络路径也可以是本地路径 本地路径mac下的格式是 video : audio 这里表示获取第一个音频设备
    int result = avformat_open_input(&av_context, name, format, &options);
    
    if (result != 0) {
        
        char errors[1024];
        // 根据返回值生成错误信息
        av_make_error_string(errors, 1024, result);
        printf("打开设备失败:%s\n", errors);
        return;
    }
    
    printf("打开设备成功!\n");
    

    get_audio_packet(av_context,&packet_callback);
    
    // 关闭输入 上下文
    avformat_close_input(&av_context);
    
}
  • 读取数据和存储到文件
void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 写  b == 二进制  + == 没有就创建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音视频学习/音视频文件/code_recorder.pcm", "wb+");
    
    
    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循环读取设备信息
    // result == -35 是Resource temporarily unavailable 因为获取太频繁 设备未准备好,还正在处理数据  
  // 因为输入设备准备好需要时间  睡一秒后再读取  
     sleep(1.0);
    while ((result = av_read_frame(·context, packet)) == 0  || result == -35) {
     
        if (packet->size > 0) {
            packet_callback(*packet);
            fwrite(packet->data, packet->size, 1, f);
            // 每读取一次 就清空数据包 不然数据包会一直增大
            av_packet_unref(packet);
          
        }
    }
    
    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);
        
        printf("get packet occured error is \"%s\" \n", errors);
    }
    
    // 将缓冲区剩余的数据 强制写入文件
    fflush(f);
    fclose(f);
    
    // 释放packet空间
    av_packet_free(&packet);
    
}
// 回调函数
void packet_callback(AVPacket packet) {
    
    printf("packet size is %d\n",packet.size);

}
  • 播放
  • ffplay 播放pcm数据: ffplay -ar(采样率) 44100 -ac(通道数) 2 -f(采样大小)f32le 文件名

音频编解码实战

音频重采样

就是将音频三元组(采样率 采样大小 通道数)的值转成另外一组值

1. 应用场景:

1、从设备采集的音频数据与编码器要求的不一致;
2、扬声器要求的音频数据与要播放的音频数据不一致;
3、方便运算:例如回音消除 将多声道变为单声道;

2. 如何判断是否需要重采样

  • 了解音频设备的参数
  • 查看ffmpeg源码

3. 重采样的步骤

api:需要使用libswresample库

1. 创建重采样上下文

 - swr_alloc_set_opts 通过设置采样参数获取上下文      

2. 设置参数

- 参数大体分为输出的采样率、采样大小、声道和输入的采样率、采样大小、声道;
- out_ch_layout:表示声道也可以是布局(扬声器的布局)AV_CH_LAYOUT_STEREO  立体声;
- out_sample_fmt:输出的采样格式 16 = AV_SAMPLE_FMT_S16 或者 32 =  AV_SAMPLE_FMT_FLT;
-  av_sample_fmt_s16 in_ch_layout:输入的声道布局  ;
-  in_sample_fmt 输入的采样格式 ;
-  in_sample_rate:  输入的采样率;
-  后两位是log相关 0,null

3. 初始化重采样

- swr_init 初始化上下文  

4. 进行重采样

- swr_convert 开始转换 ,目的就是将输入缓冲区的数据写入输出缓冲区
  out:输出结果缓冲区 out_count:每个通道的采样数 
  in:输入的缓冲区 in_count:输入的单个通道的采样数  
- 因为重采样的数据需要重新构造所以需要创建输入缓冲区和输出缓冲区  
  使用av_sample_array_and_samples audio_data创建
  其中的单通道采样数(单位是字节)`nb_samples = pkt.size / (32位 / 8) / 2(通道数)` 
  linessize:缓冲区大小  align:对齐 0 
- 在转换前需要将pkt的data按字节拷贝到输入缓冲区,调用memcpy需要引用string.h

- 将输出数据写入文件
  将输出缓冲区已经转换的数据写入文件

5. 释放资源

- 还有输入输出缓冲区的释放av_freep
- swr_free释放上下文

重采样上下文初始化代码

SwrContext * init_swr_context(void) {
    
    SwrContext *context = NULL;
    
    // 假设已经提前知道输入音频数据的三要素的值 AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_FLT, 44100
    context = swr_alloc_set_opts(NULL,
                                 AV_CH_LAYOUT_STEREO,
                                 AV_SAMPLE_FMT_S16,
                                 44100,
                                 AV_CH_LAYOUT_STEREO,
                                 AV_SAMPLE_FMT_FLT,
                                 44100,
                                 0, NULL);
    
    int result = swr_init(context);
    
    if (result != 0) {
        char error[1024];
        av_make_error_string(error, 1024, result);
        printf("初始化重采样上下文失败:%s", error);
    }
    
    return context;
}

将采集的数据重采样后 写入文件代码

void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 写  b == 二进制  + == 没有就创建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音视频学习/音视频文件/resample.pcm", "wb+");

    // 初始化重采样上下文
    SwrContext *swr_context = init_swr_context();

    // 初始化转换的输入输出缓冲区
    uint8_t **out_buffer = NULL;
    int linesize_out = 0;
    av_samples_alloc_array_and_samples(&out_buffer, &linesize_out, 2, 512, AV_SAMPLE_FMT_S16, 0);
    uint8_t **in_buffer = NULL;
    int linesize_in = 0;
    // nb_samples 单通道采样数 4096 / (32 / 8) / 2 = 1024
    av_samples_alloc_array_and_samples(&in_buffer, &linesize_in, 2, 512, AV_SAMPLE_FMT_FLT, 0);


    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循环读取设备信息
    // result == -35 是Resource temporarily unavailable 因为获取太频繁 设备未准备好,还正在处理数据
    sleep(1);
    while (((result = av_read_frame(context, packet)) == 0  || result == -35) && isRecording == 1) {

        if (packet->size > 0) {


            // 开始转换数据
            // 先将音频数据拷贝到输入缓冲区  只是重采样音频的话  只需要处理数组的第一个
            memcpy(in_buffer[0], packet->data, packet->size);
            // 再进行转换
            swr_convert(swr_context, out_buffer, 512, (const uint8_t **)in_buffer, 512);

            fwrite(out_buffer[0],linesize_out, 1, f);
            // 每读取一次 就清空数据包 不然数据包会一直增大
            av_packet_unref(packet);
        }

    }

    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);

        printf("get packet occured error is \"%s\" \n", errors);
    }

    // 释放重采样资源
    if (in_buffer) {
        av_freep(&in_buffer[0]);
    }
    if (out_buffer) {
        av_freep(&out_buffer[0]);
    }
    av_freep(&in_buffer);
    av_freep(&out_buffer);
    swr_free(&swr_context);

    // 将缓冲区剩余的数据 强制写入文件
    fflush(f);
    fclose(f);

    // 释放packet空间
    av_packet_free(&packet);
    
    
}

ffmpeg 音频数据编码

本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs),有需要的可以进企鹅裙927239107领取哦~

在使用fdk_aac编码器的时候,由于默认的ffmpeg有自带的aac,所以通过avcodec_find_encoder_by_name("libfdk_aac")就获取不到。在编译的时候加上--enable-libfdk-aac。注意:重新编译安装ffmpeg之前最好先删掉之前的ffmpeg,然后更新项目中的动态库;
如果还不行,试试单独下载安装[fdk_aac](https://www.linuxfromscratch.org/blfs/view/svn/multimedia/fdk-aac.html),再重新编译ffmpeg

  1. 创建编码器 avcodec
    1. avcodec_find_encoder 一种通过名字查找 一种是通过id查找,id的查找方式只会找默认的编码器,比如aac,如果是fdkaac就需要通过名字查找;
  2. AV_CODEC_ID_AAC | opus 其他编码器
  3. "libfdk_aac", aac默认的规格是AAC LC
  4. 创建上下文 avcodexcontext
    设置音频三要素
  5. avcodec_alloc_context3
    3表示第三个版本
  6. sample_fmt = av_sample_FMT_S16 aac编码器不支持flt 32位
  7. chnnel_layout = AV_CH_LAYOUT_STEREO( 或者chanels = 2)
  8. sample_rate = 44100
  9. bit_rate = 64000; (KB 码率)可选设置
  10. profile = FF_PROFILE_AAC_HE_V2; (只有bit_rate=0 才有用) 可选设置,设置编码器规格
  1. 打开编码器
  2. avcodex_opne2
    2表示第二个版本
    送数据给编码器时,编码器内部有一个缓冲区,缓冲一部分数据后才进行编码
  1. 编码
  2. 用AVFrame包装未编码的数据,相当于是个输入,用AVPacket包装已编码的数据,相当于是个输出;
  3. 调用avcodec_send_frame 将avframe缓冲区的数据发送给编码器,如果返回值大于0,就表示数据成功发送到了编码器,接着就可以通过循环使用 avcodec_receive_packet读取编码好的数据到AVPacket,并写入文件中,如果读取的结果是AVERROR(EAGIN)或者是AVERROR_EOF,就停止读取,如果是其他的负数,就停止编码;
  4. av_frame_alloc 堆区初始化frame
  5. 设置frame的nb_samples 单通道一个数据帧采样数 512
  1. format 每个采样的大小 av_sample_fmt_s16
  2. channel_layout 声道 av_ch_layout_stereo
  3. av_frame_get_buffer 分配frame里面buffer的大小
  4. 还要判断frame的buffer是否分配成功
  5. 将重采样后的数据memcpy到frame->data中
  6. 再将frame中的数据塞到编码器上下文中 avcodec_send_frame,该函数会返回一个int , 当结果>=0的时候表明有数据已经在编码缓冲区了;
  7. avcodec_receive_packet 读取编码好的数据 avpacket
  8. av_packet_alloc 分配编码后的数据空间
  9. 因为编码器上下文中有一个缓冲区,其中会缓存多个frame,因此并不是每塞一个frame就会有一个packet出来,所以需要通过一个while循环判断编码器的数据是否>=0,再通过avcodec_receive_packet获取packet,该函数也会返回一个int,如果返回值>=0表明获取成功,如果失败直接退出编码,这个值返回值还有其他含义,需要判断eagain 表明编码器没有数据了或者是有数据但是不够编码 这个eagain需要用AVERROR包装成一个负数,表明数据还没准备好 averror_eof 表明一点数据都没有了;
  10. 最后将数据编码后的数据写入到文件pkt->data,数据格式就是aac了;
  11. 在停止录制的时候,由于编码的缓存区可能还有数据,在最后关闭之前,再去取一遍编码数据放入文件;
  1. 释放资源

在结束的时候释放frame(av_frame_free) 和packet(av_packet_frame);

编码实战代码:

1. 创建fdk_aac编码器及上下文

AVCodecContext* init_codec_context(void) {
    
    // 创建aac编码器
    AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");
    
    // 初始化上下文
    AVCodecContext *context = NULL;
    context = avcodec_alloc_context3(codec);
    context->sample_fmt = AV_SAMPLE_FMT_S16;
    context->sample_rate = 44100;
    context->channel_layout = AV_CH_LAYOUT_STEREO;
    context->bit_rate = 0;
    // bitrate == 0 才会生效
    context->profile = FF_PROFILE_AAC_HE_V2;
    
    int result = avcodec_open2(context, codec, NULL);
    if (result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, result);
        av_log(NULL, AV_LOG_DEBUG, "创建AAC编码器失败:%s",error);
    }
    
    return context;
    
}

2. 创建输入缓冲区

AVFrame* create_audio_input_frame(void) {
    
    AVFrame *codec_frame = NULL;
    codec_frame = av_frame_alloc();
    
    codec_frame->nb_samples = 512;
    codec_frame->channel_layout = AV_CH_LAYOUT_STEREO;
    codec_frame->format = AV_SAMPLE_FMT_S16;
    int buffer_result = av_frame_get_buffer(codec_frame, 0);
    if (buffer_result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, buffer_result);
        printf("frame 缓冲区分配失败:%s", error);
    }
    
    return codec_frame;
}

3. 开始编码并写入文件

void audio_encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *packet, FILE *fl) {
    
    
    // 将数据送入编码器
    int codec_result = avcodec_send_frame(ctx, frame);
    while (codec_result >= 0) {
        // 从packet中循环读取编码好的数据
        codec_result = avcodec_receive_packet(ctx, packet);
        if (codec_result == AVERROR(EAGAIN) || codec_result == AVERROR_EOF) {
        
            break;
        } else if (codec_result < 0) {
            char error[1024];
            av_make_error_string(error, 1024, codec_result);
            printf("编码器出错:%s     停止编码", error);
        } else {
            fwrite(packet->data, 1,packet->size, fl);
        }
    }
    if (codec_result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, codec_result);
        printf("将数据送入编码器错误: %s\n",error);
    }
}

4. 调用

  • 先将重采样的数据放入avframe的缓冲区中
memcpy(codec_frame->data[0], out_buffer[0], linesize_out);
  • 再开始编码
audio_encode(codec_context, codec_frame, codec_packet, f);
  • 总览
void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 写  b == 二进制  + == 没有就创建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音视频学习/音视频文件/encoder.aac", "wb+");
    
    // 创建编码器上下文
    AVCodecContext *codec_context = init_codec_context();
    // 初始化输入缓冲区  AVframe
    AVFrame *codec_frame = create_audio_input_frame();
    // 初始化编码输出缓冲区
    AVPacket *codec_packet = av_packet_alloc();
    
    // 初始化重采样上下文
    SwrContext *swr_context = init_swr_context();
    // 初始化重采样的缓冲区
    uint8_t **out_buffer = NULL;
    int linesize_out = 0;
    uint8_t **in_buffer = NULL;
    int linesize_in = 0;
    init_resammple_buffer(&in_buffer, &linesize_in, &out_buffer, &linesize_out);
    

    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循环读取设备信息
    
    while (isRecording == 1) {
        
        result = av_read_frame(context, packet);
        if (packet->size > 0 && result == 0) {

            packet_callback(*packet);
            // 开始转换数据
            // 先将音频数据拷贝到输入缓冲区  只是重采样音频的话  只需要处理数组的第一个
            memcpy(in_buffer[0], packet->data, packet->size);
            // 再进行转换
            swr_convert(swr_context, out_buffer, 512, (const uint8_t **)in_buffer, 512);
            // 将重采样好的数据按字节拷贝到frame缓冲区
            memcpy(codec_frame->data[0], out_buffer[0], linesize_out);
            audio_encode(codec_context, codec_frame, codec_packet, f);
            // 每读取一次 就清空数据包 不然数据包会一直增大
            av_packet_unref(packet);
        } else if (result == -EAGAIN) {
            // result == -35 是Resource temporarily unavailable 因为设备未准备好,还正在处理数据
            av_usleep(1);
        }
    }
    // 把缓冲区剩余的数据拿出来编码
    audio_encode(codec_context, NULL, codec_packet, f);

    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);
        printf("get packet occured error is \"%s\" \n", errors);
    }

    // 释放重采样资源
    if (in_buffer) {
        av_freep(&in_buffer[0]);
    }
    if (out_buffer) {
        av_freep(&out_buffer[0]);
    }
    av_freep(&in_buffer);
    av_freep(&out_buffer);
    swr_free(&swr_context);
    
    av_frame_free(&codec_frame);
    av_packet_free(&codec_packet);

    // 将缓冲区剩余的数据 强制写入文件
    fflush(f);
    fclose(f);

    // 释放packet空间
    av_packet_free(&packet);
    
    
}

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

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

相关文章

【QT】仿函数

函数调用运算符 () 也可以重载由于重载后使用的方式非常像函数的调用&#xff0c;因此称为仿函数仿函数没有固定写法&#xff0c;非常灵活 示例&#xff1a; #include <iostream> #include <string> using namespace std;class MyPrint { public://重载的运算符是…

MFC网络编程-Udp客户端

目录 1、UI的设计&#xff1a; 2、代码的实现&#xff1a; &#xff08;1&#xff09;、重写CSocket虚函数OnReceive&#xff0c;并且传入对话框的指针 &#xff08;2&#xff09;、初始化SOCKET &#xff08;3&#xff09;、绑定本地IP和端口 &#xff08;4&#xff09;、…

Microsoft SQL Server 缓冲区错误漏洞(CVE-2018-8273)解决方法

前言&#xff1a; 在一次漏洞扫描中&#xff0c;扫描出紧急漏洞Microsoft SQL Server 缓冲区错误漏洞(CVE-2018-8273) 根据修复建议找补丁。 一、漏洞详情 二、寻找补丁 根据漏洞修复建议去下载补丁 目前厂商已发布升级补丁以修复漏洞&#xff0c;补丁获取链接&#xff1a;h…

龙迅视频转换IC LT6711GX适用于HDMI2.1转TPYE-C/DP1.4/EDP功能应用

1.描述 应用功能&#xff1a;LT6711GX适用于HDMI2.1转TPYE-C/DP1.4/EDP 分辨率&#xff1a;最高支持8K30HZ或8K60Hz压缩数据 工作温度范围&#xff1a;−40C to 85C 产品封装&#xff1a;QFN88 &#xff08;10*10&#xff09; 最小包装量&#xff1a;1680PCS 2.产品应用市场 •…

【JMeter】线程组分类以及功能介绍

1.概念介绍 进程&#xff1a; 正在运行的程序&#xff0c;比如Jmeter 线程组&#xff1a;进程中有多个线程 2线程组分类 JMeter自带的线程组&#xff1a; 普通线程组&#xff08;Thread Group&#xff09;开始线程组&#xff08;setUp Thread Group&#xff09;结束线程…

绝对好用!一个浏览器插件解决跨设备同步问题,吊打文件传输助手!

在数字化的时代&#xff0c;我们所接触的信息呈现指数级的增长。无论是办公资料、学习资源&#xff0c;还是各种生活中的点滴&#xff0c;所有这些信息以各种形式——文本、图片、视频、音乐等——出现在我们面前&#xff0c;如何有效地同步和管理这些内容成为一个挑战。 就跨…

PhotoFoundry for Mac:图片滤镜编辑软件的终极进化!

如果你是一个摄影师&#xff0c;或者是一个热衷于使用图片编辑软件的人&#xff0c;那么PhotoFoundry for Mac将会是你的新最佳伙伴&#xff01;这款软件不仅具备了强大的图片滤镜编辑功能&#xff0c;更在操作简易度、用户体验以及创意控制方面做到了极致。 PhotoFoundry for…

大华智慧园区综合管理平台文件上传漏洞(CVE-2023-3836)

大华智慧园区综合管理平台文件上传漏洞 漏洞描述漏洞危害Fofa检索漏洞复现1. 构造poc2. 复现3. 访问shell地址 漏洞描述 大华智慧园区综合管理平台是一个基于智能物联技术的园区安防、办公、运营的数字化解决方案。 大华智慧园区综合管理平台(截至 20230713)版本中存在文件上传…

vim三种模式,文本操作(操作字符/光标,列出行号可视化块模式/多文件查看)

目录 vim--文本编辑器 功能 基本概念 命令/默认模式 插入模式 底行模式 文本操作 引入 移动光标位置 删除字符 -- x/dd 复制/粘贴字符 -- yw/yyp 替换文本 -- r / %s 底行模式 全局替换 -- /g 撤销操作 -- u / ctrlr 修改字符 -- cw 示例 跳行 -- ctrlg 底行…

uniapp原生插件之无预览静默拍照

插件介绍 无预览静默拍照&#xff0c;在用户无感觉情况下调用摄像头拍照 插件地址 无预览静默拍照 - DCloud 插件市场 超级福利 uniapp 插件购买超级福利 插件申请权限 存储卡读写权限摄像头权限 manifest.json权限列表 /* android打包配置 */"android" : {…

ps提示vcruntime140.dll无法继续执行此代码的多种解决方法分享

我在安装Photoshop软件时遇到了一个问题&#xff0c;即在运行过程中弹出了一个错误提示框&#xff0c;显示“由于找不到vcruntime140.dll&#xff0c;无法继续执行此代码”&#xff0c;我通过查找资料了解到vcruntime140.dll是一个动态链接库文件&#xff0c;它是Visual C Redi…

MySQL主从复制---一主一从配置过程

1、mysql版本一致且后台以服务运行 2、主从都配置在[mysqld]结点下&#xff0c;都是小写 3、主机修改my.ini配置文件 配置信息说明&#xff1a; 1、主服务器唯一ID server-id1 2、启用二进制日志 log-bin自己本地的路径/data/mysqlbin log-binD:/devSoft/MySQLServer5.5…

自动化混沌工程 ChaosMeta V0.6 版本发布

混沌工程 ChaosMeta 的全新版本 V0.6.0 现已正式发布&#xff01;该版本包含了许多新特性和增强功能&#xff0c;在编排界面提供了包括流量注入、度量等各类节点的支持&#xff0c;可视化支撑演练全流程。解决混沌工程原则中“持续自动化运行实验”的最后一公里问题。 简介 Ch…

Spring Security认证:获得认证、持久化认证模块详解

参考&#xff1a;认证 :: Spring Security Reference (springdoc.cn) 本文将讲述Spring Security中与认证相关的各个基本模块&#xff0c;力求对整个认证框架提供完善的认知。 众所周知&#xff0c;认证Authentication实际上就是一个代表身份的类&#xff0c;实例就是具体的身…

第一章 01Java入门-常见的CMD命令

前言 学习常见的CMD命令 一、常见的CMD命令 常见的CMD命令主要包括切换盘符、查看当前路径下的内容、进入单级目录、回到上一级目录、进入多级目录、回退到盘符目录、清屏以及退出命令提示符窗口等。 二、代码演示 先从默认的C盘符切换到E盘符(E:),然后再查看E盘符下面的…

Redis的四种部署方案

这篇文章介绍Reids最为常见的四种部署模式&#xff0c;其实Reids和数据库的集群模式差不多&#xff0c;可以分为 Redis单机模式部署、Redis主从模式部署、Redis哨兵模式部署、Cluster集群模式部署&#xff0c;其他的部署方式基本都是围绕以下几种方式在进行调整到适应的生产环境…

【C++】多态 ⑩ ( 不建议将所有函数都声明为 virtual 虚函数 | 多态的理解层次 | 父类指针和子类指针步长 )

文章目录 一、不建议将所有函数都声明为 virtual 虚函数二、多态的理解层次三、父类指针和子类指针步长 对象可以直接获取到自身封装的 普通函数 , 如果要访问虚函数 , 需要增加一次寻址操作 , 因此 这里建议不需要将有 多态 需求的函数声明为 虚函数 ; C 中 指向某类型对象的…

有什么好用的仓库配件管理软件?如何实现企业配件管理智能化?

在当今高度信息化的商业环境中&#xff0c;企业运营的效率和管理的重要性日益凸显。对于许多企业来说&#xff0c;仓库配件管理是一个关键的环节&#xff0c;它不仅涉及到物品的存储和分发&#xff0c;还与企业的成本控制和运营流程紧密相关。然而&#xff0c;管理仓库配件是一…

C++快餐——C++11(3)

应该在脖子上长一个自己的脑袋 文章目录 function包装器bind绑定普通函数绑定成员函数std::bind优点 线程库thread类创建线程线程管理线程标识 原子性操作库(atomic)lock_guard与unique_lockstd::lock_guardstd::unique_lock mutex的种类总结 function包装器 C11引入了一个名为…

ubuntu启动报错error: proc_thermal_add, will cont

如题&#xff0c;ubuntu启动报错error: proc_thermal_add, will cont 截图如下&#xff1a; 困扰了我很久&#xff0c;差点就打算重装系统&#xff0c;准备放弃了&#xff0c;但是感谢国外的老哥&#xff0c;写了一篇非常详细的解决方案&#xff0c;我搬过来。 解决方案&#…