音视频--AAC编码解析和示例

news2024/12/26 22:59:33

目录

1:AAC编码介绍

2:AAC格式介绍

3:AAC -ADTS帧组成

4:AAC-ADTS:(adts_fixed_header)格式介绍

5:AAC-ADTS:(adts_variable_header)格式介绍

6:示例代码:


1:AAC编码介绍

        AAC 代表 Advanced Audio Coding,是一种有损音频压缩格式。它由 MPEG-4 标准定义,由 Fraunhofer 公司开发,Dolby、Sony 和 AT&T 是主要的贡献者。AAC 提供了高质量的音频压缩,被广泛用于音乐存储和流媒体传输。

2:AAC格式介绍

AAC主要有下面两个格式:

  1. ADTS(Audio Data Transport Stream):一般用的都是这个格式。ADTS 是 AAC 音频的传输流格式。AAC 音频格式在 MPEG-2 标准(ISO-13318-7 2003)中有定义,并且后来被采用到 MPEG-4 标准中。ADTS 格式的特点是它是一个有同步字的比特流,也就是说每一帧都有头信息,这使得解码可以在流的任何位置开始。因此,ADTS 可以在任意帧解码,与之相反的是,ADIF 只有一个统一的头信息,也就是说它每⼀帧都有头信息。因此必须获取到所有的数据后才能解码。

  2. ADIF(Audio Data Interchange Format):ADIF 是一种音频数据交换格式。它的特点是可以确定地找到音频数据的开始,而不需要在音频数据流中间开始解码。这意味着解码可以从明确定义的起始点开始,通常用于存储在磁盘文件中的音频数据。这个基本用不到

当对音频进行编码时,有时直接编码得到的AAC文件(称为“裸流”)可能无法在个人电脑(PC)或手机上正常播放。这通常是因为这些AAC文件的每一帧缺少了必要的ADTS头信息

为了解决这个问题,需要在AAC原始数据块(即没有ADTS头的音频数据)前添加ADTS头。这样,每个AAC数据块前都会有一个包含必要信息的ADTS头,使得播放器可以正确地识别和播放音频。

添加ADTS头后,原始的AAC数据块就成为了一个完整的ADTS帧。ADTS帧由ADTS头和随后的AAC音频数据组成。ADTS头的长度通常是固定的7或9字节,具体取决于是否包含CRC(循环冗余校验)信息。

3:AAC -ADTS帧组成

        AAC⾳频⽂件的每⼀帧由ADTS Header和AAC Audio Data组成。结构体如 下:

ADTS(Audio Data Transport Stream)头部信息是AAC音频文件中非常重要的一部分,它包含了音频流的元数据,例如采样率,声道,帧⻓度等等,使得解码器能够正确地解析和播放音频。ADTS头部信息通常由7个字节组成,分为两个部分:其⼀为固定头信息,紧接着是可变头信息

  1. adts_fixed_header()(固定头信息:这部分是每个ADTS帧都相同的信息,不随音频内容变化。它包含以下内容:

    • syncword(同步头):总是0xFFF(十六进制),用于识别ADTS帧的开始。
    • ID:表示MPEG的版本,0表示MPEG-4,1表示MPEG-2。
    • Layer:对于AAC来说,这个值总是'00',因为AAC属于MPEG-4的Layer 1。
    • protection_absent:表示是否有CRC(循环冗余校验)存在,1表示没有CRC,0表示有CRC。
    • profile:表示使用的AAC编码配置,如Low Complexity(LC)等。
  2. adts_variable_header()(可变头信息:这部分信息在不同的ADTS帧之间可能会变化,它包含以下内容:

    • sampling_frequency_index:采样率索引,用于查找具体的采样率值。
    • channel_configuration:声道配置,表示音频的声道数,如立体声、5.1声道等。
    • frame_length:ADTS帧的长度,包括ADTS头和AAC原始数据的长度。
    • adts_buffer_fullness:码率信息,用于指示码率是否可变。
    • number_of_raw_data_blocks_in_frame:表示ADTS帧中AAC原始数据块的数量。

ADTS头部的这种结构设计允许解码器快速获取音频流的关键信息,并能够从流中的任意位置开始解码,这使得AAC文件非常适合用于流媒体传输。

在具体实现中,解码器会首先读取固定头信息以确认遇到了一个ADTS帧,然后读取可变头信息以获取必要的解码参数。这样,解码器就可以正确地解析出音频数据并进行播放。

4:AAC-ADTS:(adts_fixed_header)格式介绍

如下图:

  • syncword(同步头):这个字段总是设置为0xFFF,它是一个12位的字段,用于标识ADTS帧的开始。这个同步字是固定的,所有ADTS帧的开始都是这个值,便于解码器识别帧的起始位置。

  • ID:这个字段占用1位,用于区分MPEG的版本。0表示MPEG-4,1表示MPEG-2。由于AAC通常是MPEG-4的一部分,所以这个值通常是0。

  • Layer:这个字段占用2位。对于AAC来说,由于AAC是MPEG-4的一部分,并且是Layer 1的编码,所以这个值总是'00'。

  • protection_absent:这个字段占用1位。它指示了帧中是否存在CRC(循环冗余校验)。如果设置为1,表示没有CRC,如果设置为0,表示存在CRC。CRC用于检测传输过程中的错误。注意这个值是1表示没有校验,0表示有校验

  • profile:这个字段占用2位,用于表示使用的AAC编码配置。例如,如果设置为01,表示使用的是Low Complexity(LC)配置。不同的配置对应不同的编码复杂度和性能。有些芯⽚只⽀持AAC LC 。

其中:

AAC标准定义了不同的Profile,每个Profile支持不同的特性和工具,以适应不同的应用场景和需求。在ADTS头信息中,"Profile"字段用于指示使用的AAC编码配置。

对于MPEG-2 AAC,存在几种不同的Profile,每种都有特定的Audio Object Type(AOT),这些类型定义了编码音频的特定特性。在MPEG-2 AAC中,Profile的值确实是根据AOT的值来确定的,具体规则是:

  • Profile的值等于Audio Object Type的值减去1。

在MPEG-4 AAC中,Profile的概念略有不同,并且包含更多的Profile类型以支持更广泛的应用。但是,基本的思想是相同的:Profile标识了编码器支持的功能集。

MPEG-4 AAC定义类型如下:

接下来看:

sampling_frequency_index:表示使⽤的采样率下标,通过这个下标在 Sampling Frequencies[ ]数组中查找得知采样率的值。sampling_frequency_index是一个索引值,它用于在一个预定义的采样频率表中查找对应的采样率。在MPEG标准中,这个表是固定的,并且包含了常用的音频采样频率。以下是这个索引表的一个示例:

在ADTS头信息中,sampling_frequency_index通常占用一个4位的字段。这意味着它可以表示从0到15的16个不同的采样频率索引值。根据这个索引值,解码器就可以知道音频数据的采样率是多少,从而正确地进行解码处理。

举个例子,如果sampling_frequency_index的值为3,根据上面的表,我们知道对应的采样率是48000 Hz。这样,解码器就可以按照48000次/秒的频率来重构音频信号。

接下来看:

channel_configuration:用于描述音频流的声道布局或声道数。这个字段对于解码器来说是重要的,因为它决定了音频信号是如何组织的,以及如何通过扬声器或耳机进行播放。

channel_configuration字段通常占用4位,因此可以表示从0到15的16种不同的配置。每种配置对应一种特定的声道布局,以下是一些常见的声道布局及其对应的channel_configuration值:

5:AAC-ADTS:(adts_variable_header)格式介绍

adts_variable_header是AAC音频流中ADTS(Audio Data Transport Stream)头部的可变部分。它提供了关于每个ADTS帧的额外信息,这些信息对于解码器来说是必要的,因为它们描述了音频数据的具体属性。以下是adts_variable_header()中包含的关键字段及其作用:

格式如下:

  1. frame_length: 这个字段占用13位,它定义了一个ADTS帧的总长度,包括ADTS头部和随后的AAC原始数据流。长度的计算方式如下:

    因此,aac_frame_length的计算公式是:

    • 如果protection_absent字段为1(表示没有CRC校验),ADTS头部长度为7字节。
    • 如果protection_absent字段为0(表示有CRC校验),ADTS头部长度为9字节。                                                                                                                               𝑎𝑎𝑐𝑓𝑟𝑎𝑚𝑒𝑙𝑒𝑛𝑔𝑡ℎ=(𝑝𝑟𝑜𝑡𝑒𝑐𝑡𝑖𝑜𝑛𝑎𝑏𝑠𝑒𝑛𝑡==1?7:9)+𝑠𝑖𝑧𝑒(𝐴𝐴𝐶𝐹𝑟𝑎𝑚𝑒)                                      aacf​ramel​ength=(protectiona​bsent==1?7:9)+size(AACFrame)                                         其中size(AACFrame)是AAC原始数据的长度。
  2. adts_buffer_fullness: 这个字段占用11位,表示编码缓冲区的填充度,它用于流式传输中的码率控制。如果这个值是0x7FF,它表示码流是码率可变的。

  3. number_of_raw_data_blocks_in_frame: 这个字段占用2位,表示在ADTS帧中AAC原始数据块的数量。值得注意的是,这个数值是AAC数据块的数量,而不是数据块之后的任何填充字节。因此,如果number_of_raw_data_blocks_in_frame的值为0,它表示ADTS帧中实际上有一个AAC数据块

示例:

第⼀帧的帧头7个字节为:0xFF 0xF1 0x4C 0x40 0x20 0xFF 0xFC

  1. 0xFF: syncword的第一部分,表示ADTS帧的开始,固定为0xFFF。

  2. 0xF1: 包含ID(MPEG版本标识,这里为0表示MPEG-4)和Layer(这里为00,因为AAC是MPEG-4的一部分)。

  3. 0x4C: 包含protection_absent(这里为0,表示存在CRC校验)和profile(AAC编码配置,这里为01,表示Low Complexity,LC)。

  4. 0x40: 是sampling_frequency_index的一部分,表示采样率的索引。

  5. 0x20: 包含channel_configuration(声道配置)的一部分。

  6. 0xFF: frame_length的高字节。

  7. 0xFC: frame_length的低字节。

分开也就是:

111111111111
0
00
1
01
0011
0
001
0
0
0
0
0000100000111(帧长度)
11111111111
00

以下是对文档中二进制序列的简化解释:

  • 111111111111: 这是syncword的二进制表示,固定为0xFFF,用来标识ADTS帧的开始。
  • 0: 表示ID字段,这里为0,表示MPEG-4。
  • 00: 表示Layer字段,AAC属于MPEG-4 Layer 1,所以这里为00。
  • 1: 表示protection_absent字段,这里为1,表示没有CRC校验。
  • 01: 表示profile字段,这里可能表示AAC LC(Low Complexity)。
  • 0011: 表示sampling_frequency_index字段的一部分。
  • 0: 表示private_bit字段,通常为0。
  • 001: 表示channel_configuration字段的一部分。
  • 0000100000111: 表示frame_length字段,这是二进制形式的帧长度值。

我们可以计算frame_length

帧长度(二进制): 0000100000111

将这个二进制数转换为十进制:

帧长度(十进制): 2^9 + 2^6 + 2^3 + 2^2 + 2^0 = 512 + 64 + 8 + 4 + 1 = 263

所以,第一帧的ADTS帧长度是263个字节,包括7字节的ADTS头和256字节的AAC原始音频数据。这个帧长度包括了ADTS头部和随后的AAC原始数据流,不包括任何填充字节。

//帧长度为13位,使用unsignedint来存储帧长数值
unsigned int getAFrameLength(unsigned char* str) {
        // 函数开始,接收一个指向无符号字符的指针作为参数,用于计算帧的长度

        if (!str) {
            // 检查输入指针是否为空,如果为空则返回0
        return 0;
        }

        unsigned int tmplen = 0;
        // 声明一个无符号整数型变量tmplen,用于存储帧的长度

      int f_bit = str[3];
      int m_bit = str[4];
    int b_bit = str[5];
    // 声明三个整数型变量,分别用于存储输入数组中的第4、5、6个字节的值

    tmplen += (b_bit >> 5);
    // 将b_bit中的低3位取出,并将其加到tmplen中

    tmplen += (m_bit << 3);
    // 将m_bit中的所有位向左移动3位,并将其加到tmplen中

    tmplen += ((f_bit & 3) << 11);
    // 将f_bit中的低2位取出,然后将其向左移动11位,并将其加到tmplen中


    return tmplen;
    // 返回计算得到的帧长度
}

6:示例代码:

这段代码的主要功能是将一个媒体文件中的AAC音频流提取出来,并在每个AAC数据帧前添加ADTS头部,然后将这些帧写入到一个新的AAC文件中。代码使用了FFmpeg库中的函数来处理媒体文件和Packet。程序首先查找输入媒体文件中的最佳音频流,然后读取每个Packet,如果Packet属于音频流,则在前面添加一个ADTS头部,并将带有ADTS头部的Packet写入到输出文件中

#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>

#define ADTS_HEADER_LEN  7; // ADTS头部长度定义为7字节

// 采样频率数组,用于根据采样频率索引值查找具体的采样频率
const int sampling_frequencies[] = {
    96000,  // 0x0
    88200,  // 0x1
    64000,  // 0x2
    48000,  // 0x3
    44100,  // 0x4
    32000,  // 0x5
    24000,  // 0x6
    22050,  // 0x7
    16000,  // 0x8
    12000,  // 0x9
    11025,  // 0xa
    8000    // 0xb
    // 0xc d e f是保留的
};

// 构建ADTS头部的函数
int adts_header(char * const p_adts_header, const int data_length,
                const int profile, const int samplerate,
                const int channels) {
    int sampling_frequency_index = 3; // 默认使用48000hz采样频率索引
    int adtsLen = data_length + 7; // ADTS帧总长度,包括AAC数据和ADTS头部

    // 查找采样频率对应的索引值
    for(int i = 0; i < sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]); i++) {
        if(sampling_frequencies[i] == samplerate) {
            sampling_frequency_index = i;
            break;
        }
    }
    if(i >= sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0])) {
        printf("unsupport samplerate:%d\n", samplerate);
        return -1; // 如果没有找到对应的采样频率,返回错误
    }

    // 填充ADTS头部的固定字段
    p_adts_header[0] = 0xff; // syncword高8位
    p_adts_header[1] = 0xf0; // syncword低4位,MPEG版本(这里为MPEG-4 AAC),Layer(AAC是Layer 1)
    p_adts_header[1] |= (0 << 3);//1bit
    p_adts_header[1] |= (0 << 1);// 2bits
    p_adts_header[1] |= 1; // protection_absent: 表示没有CRC校验// 1bits

    // 填充ADTS头部的可变字段
    p_adts_header[2] = (profile)<<6; // profile字段 // 2bits
    p_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; // sampling_frequency_index字段  4bits
    p_adts_header[2] |= (0 << 1); // private_bit: 保留位 1bits
    p_adts_header[2] |= (channels & 0x04)>>2; // channel_configuration字段高1位

    p_adts_header[3] = (channels & 0x03)<<6; // channel_configuration字段低2位
    p_adts_header[3] |= (0 << 5); // original: 保留位 1bit
    p_adts_header[3] |= (0 << 4); // home: 保留位 1bit
    p_adts_header[3] |= (0 << 3); // copyright id bit: 保留位 1bit
    p_adts_header[3] |= (0 << 2); // copyright id start: 保留位 1bit
    p_adts_header[3] |= ((adtsLen & 0x1800) >> 11); // frame_length字段高2位

    p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3); // frame_length字段中间8位
    p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5); // frame_length字段低3位
    p_adts_header[5] |= 0x1f; // buffer fullness: 0x7ff,表示码率可变
    p_adts_header[6] = 0xfc; // buffer fullness: 0x7ff剩余的6位

    return 0; // 成功返回0
}

int main(int argc, char *argv[]) {
    // 检查参数数量
    if(argc < 3) {
        av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!\n");
        return -1;
    }

    char *in_filename = argv[1]; // 输入文件路径
    char *aac_filename = argv[2]; // 输出文件路径

    FILE *aac_fd = fopen(aac_filename, "wb"); // 打开输出文件
    if (!aac_fd) {
        av_log(NULL, AV_LOG_DEBUG, "Could not open destination file %s\n", aac_filename);
        return -1;
    }

    // 设置FFmpeg日志级别
    av_log_set_level(AV_LOG_DEBUG);

    // 打开输入媒体文件
    AVFormatContext *ifmt_ctx = NULL;
    int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
    if (ret < 0) {
        char errors[1024];
        av_strerror(ret, errors, 1024);
        av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n", in_filename, ret, errors);
        fclose(aac_fd);
        return -1;
    }

    // 获取媒体流信息
    if (avformat_find_stream_info(ifmt_ctx, NULL) < 0) {
        char errors[1024];
        av_log(NULL, AV_LOG_DEBUG, "failed to find stream information\n");
        fclose(aac_fd);
        return -1;
    }

    // 打印媒体文件格式信息
    av_dump_format(ifmt_ctx, 0, in_filename, 0);

    // 寻找音频流的索引
    int audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audio_index < 0) {
        av_log(NULL, AV_LOG_DEBUG, "Could not find audio stream in input file %s\n", in_filename);
        goto failed;
    }

    // 打印AAC配置信息
    printf("audio profile:%d, FF_PROFILE_AAC_LOW:%d\n", ifmt_ctx->streams[audio_index]->codecpar->profile, FF_PROFILE_AAC_LOW);

    // 检查是否为AAC编码的音频流
    if (ifmt_ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC) {
        printf("the media file no contain AAC stream, it's codec_id is %d\n", ifmt_ctx->streams[audio_index]->codecpar->codec_id);
        goto failed;
    }

    AVPacket pkt;
    av_init_packet(&pkt); // 初始化Packet

    // 读取输入文件的Packet并写入AAC文件
    while (av_read_frame(ifmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == audio_index) {
            char adts_header_buf[7] = {0};
            // 调用函数生成ADTS头部
            adts_header(adts_header_buf, pkt.size,
                        ifmt_ctx->streams[audio_index]->codecpar->profile,
                        ifmt_ctx->streams[audio_index]->codecpar->sample_rate,
                        ifmt_ctx->streams[audio_index]->codecpar->channels);
            // 写入ADTS头部
            fwrite(adts_header_buf, 1, 7, aac_fd);
            // 写入AAC数据
            int len = fwrite(pkt.data, 1, pkt.size, aac_fd);
            if (len != pkt.size) {
                av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)\n", len, pkt.size);
            }
        }
        av_packet_unref(&pkt); // 释放Packet资源
    }

failed:
    // 关闭输入文件和输出文件
    if (ifmt_ctx) {
        avformat_close_input(&ifmt_ctx);
    }
    if (aac_fd) {
        fclose(aac_fd);
    }

    return 0; // 程序正常退出
}

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

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

相关文章

Rust编程语言的特点及其适合做什么

Rust编程语言的特点 Rust是一门系统级编程语言&#xff0c;它有如下特点。 1. 类C的语言语法 Rust的具体语法和C/C类似&#xff0c;都是由花括号限定代码块&#xff0c;还有一样的控制流关键字&#xff0c;例如if、else、while和for。然而&#xff0c;也并非所有的C或者C关键…

初识sql注入--手工注入

目录 可能使用的sql函数 入侵网站方式 1、文件上传漏洞 2、rce 3、sql注入 SQL注入 什么是sql注入 进行SQL注入 实验环境 开始实验&#xff08;使用information_shema数据库&#xff09; 1、进入靶场 2、报列数 下面来解释一下为什么要照上面SQL语句写 url编码 单…

C#标签设计打印软件开发

1、新建自定义C#控件项目Custom using System; using System.Collections.Generic; using System.Text;namespace CustomControls {public class CommonSettings{/// <summary>/// 把像素换算成毫米/// </summary>/// <param name"Pixel">多少像素…

图论(洛谷刷题)

目录 前言&#xff1a; 题单&#xff1a; P3386 【模板】二分图最大匹配 P1525 [NOIP2010 提高组] 关押罪犯 P3385 【模板】负环 P3371 【模板】单源最短路径&#xff08;弱化版&#xff09; SPFA写法 Dij写法&#xff1a; P3385 【模板】负环 P5960 【模板】差分约束…

python的import导入规则

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pycharm只能看到当前工作路径父目录下所有文件和项目根目录下所有文件二、sys或者图形界面添加解释器路径&#xff08;搜寻路径&#xff09;三、import导入…

【ubuntu】ubuntu-18.04开机卡在Starting User Manager for UID 120....问题解决方案

错误截图 解决方案 启动系统&#xff0c;开机界面单击按键esc键&#xff0c;注意需要将鼠标定位到菜单界面&#xff0c;移动键盘上下键选择Advanced options for Ubuntu 进入如下菜单&#xff0c;选择recovery mode 回车之后会弹出如下界面&#xff0c;选择如下root&#xff0…

matlab使用教程(69)—创建包含多个 x 轴和 y 轴的图

此示例说明如何创建这样一张图&#xff0c;通过坐标区底部和左侧的轴放置第一个绘图&#xff0c;并通过坐标区顶部和右侧的轴放置第二个绘图。 使用 line 函数绘制一个红色线条。将 x 轴和 y 轴的轴线颜色设置为红色。 注意&#xff1a;从 R2014b 开始&#xff0c;您可以使用圆…

最大子序列的分数

题目链接 最大子序列的分数 题目描述 注意点 n nums1.length nums2.length从nums1和nums2中选一个长度为k的子序列对应的下标对nums1中下标对应元素求和&#xff0c;乘以nums2中下标对应元素的最小值得到子序列的分数0 < nums1[i], nums2[j] < 1000001 < k < …

精密机械设备运用弧形导轨中如何保持高精度?

导轨精度标准是对导轨的精度统一规定&#xff0c;无论是滑移运动、滑块运动还是旋转运动&#xff0c;都有一定的精度规格。而导轨精度标准是为了保证导轨运动时的精确度而设定的精度标准&#xff0c;它是规定各种导轨的精度统一标准&#xff0c;是机械设备的运动精度基础和保障…

SpringAI 技术解析

1. 发展历史 SpringAI 的发展历史可以追溯到对 Spring 框架的扩展和改进&#xff0c;以支持人工智能相关的功能。随着人工智能技术的快速发展&#xff0c;SpringAI 逐渐成为 Spring 生态系统中的一个重要组成部分&#xff0c;为开发者提供了便捷、灵活的解决方案。 项目的灵感来…

声明变量的六种方法

ES6 声明变量的六种方法 varfunctionletconstclassimport 顶层对象的属性 1. ES6 声明变量的六种方法 ES5 只有两种声明变量的方法&#xff1a; var 命令和 function 命令。 ES6 除了添加 let 和 const 命令&#xff0c;还有另外两种声明变量的方法&#xff1a; import 命令和…

[AutoSar]BSW_Diagnostic_002 DCM模块介绍

目录 关键词平台说明背景一、DCM所处架构位置二、DCM 与其他模块的交互三、DCM 的功能四、DCM的内部子模块4.1 关键词 嵌入式、C语言、autosar、OS、BSW、UDS、diagnostic 平台说明 项目ValueOSautosar OSautosar厂商vector &#xff0c; EB芯片厂商TI 英飞凌编程语言C&…

Realsense-Realman手眼标定

硬件设备 Realsense D405 Realman 65b 软件环境搭建 软件环境依赖&#xff1a; librealsensehttps://github.com/IntelRealSense/librealsense.git ROS1.0ros-noetic-arucosudo apt-get install ros-noetic-aruco*realsense_roshttps://github.com/IntelRealSense/realsens…

萤火虫优化算法(Firefly Algorithm)

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 算法背景 萤火虫优化算法&#xff0c;是由剑桥大学的Xin-She Yang在2009年提出的一种基于群体智能的优化算法。它的灵感来源于萤火虫在夜晚闪烁…

Python | Leetcode Python题解之第83题删除排序链表中的重复元素

题目&#xff1a; 题解&#xff1a; class Solution:def deleteDuplicates(self, head: ListNode) -> ListNode:if not head:return headcur headwhile cur.next:if cur.val cur.next.val:cur.next cur.next.nextelse:cur cur.nextreturn head

PDF文件恢复:四种实用方法全解析

如何恢复已删除的PDF文件&#xff1f; PDF是Portable Document Format&#xff08;便携式文档格式&#xff09;的缩写&#xff0c;是一种由Adobe Systems开发的文件格式。PDF文件可以包含文本、图形、链接、多媒体以及其他各种元素&#xff0c;并且能够在各种操作系统和设备上…

XXE-lab靶场搭建

源码下载地址 https://github.com/c0ny1/xxe-lab1.php_xxe 直接放在php web页面下即可运行。 2.java_xxe java_xxe是serlvet项目&#xff0c;直接导入eclipse当中即可部署运行。 3.python_xxe: 安装好Flask模块python xxe.py 4.Csharp_xxe 直接导入VS中运行 phpstudy…

树莓派遇到ping的奇葩问题解决办法

首先&#xff0c;先 ping raspberrypi 一下。获得树莓派的ip 然后开始配置静态ip winR后输入命令ipconfig查询当前网关ip 输入命令sudo nano /etc/dhcpcd.conf 在最末尾输入以下信息 -----------------------------------------------------------------------------------…

波动性悖论:为何低风险股票长期跑赢高风险对手?

从去年开始&#xff0c;“红利低波”类的产品净值稳步向上&#xff0c;不断新高&#xff0c;让很多人关注到了A股“分红高”、“波动率低”这两类股票。分红高的公司更受投资者青睐&#xff0c;这从基本面的角度很容易理解&#xff0c;那么波动率低的股票明明波动更小&#xff…

8、QT——QLabel使用小记2

前言&#xff1a;记录开发过程中QLabel的使用&#xff0c;持续更新ing... 开发平台&#xff1a;Win10 64位 开发环境&#xff1a;Qt Creator 13.0.0 构建环境&#xff1a;Qt 5.15.2 MSVC2019 64位 一、基本属性 技巧&#xff1a;对于Qlabel这类控件的属性有一些共同的特点&am…