当前位置: 首页 > news >正文

音视频开发13 FFmpeg 音频 相关格式分析 -- AAC ADTS格式分析

这一节,我们学习常用的音频的格式 AAC,重点是掌握 AAC的传输格式 ADTS 头部的信息,目的是 : 当音频数据有问题的时候,如果是AAC的编码,在分析 头部信息的时候能够根据头部信息 判断问题是否出现在 头部。

AAC ADTS格式分析

AAC⾳频格式:Advanced Audio Coding(⾼级⾳频解码),是⼀种由MPEG-4标准定义的有损⾳频压缩格式,由Fraunhofer发展,Dolby, Sony和AT&T是主要的贡献者。

AAC 的封装格式有两种 ADIF 和 ADTS

ADIF:已经基本弃用-ADIF只有⼀个统⼀的头,所以必须得到所有的数据后解码。

Audio Data Interchange Format ⾳频数据交换格式。这种格式的特征是可以确定的找到这个⾳频数据的开始,不需进⾏在⾳频数据流中间开始的解码,即它的解码必须在明确定义的开始处进⾏。故这种格式常⽤在磁盘⽂件中。

ADTS ADTS可以在任意帧解码,也就是说它每⼀帧都有头信息,这个是重点

Audio Data Transport Stream。是AAC⾳频的传输流格式。AAC⾳频格式在MPEG-2(ISO-13318-7 2003)中有定义。AAC后来⼜被采⽤到MPEG-4标准中。这种格式的特征是它是⼀个有同步字的⽐特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。

可能遇见的问题1:

有的时候当你编码AAC裸流的时候,会遇到写出来的AAC⽂件并不能在PC和⼿机上播放,很⼤的可能就是AAC⽂件的每⼀帧⾥缺少了ADTS头信息⽂件的包装拼接。

只需要加⼊头⽂件ADTS即可。⼀个AAC原始数据块⻓度是可变的,对原始帧加:上ADTS头进⾏ADTS的封装,就形成了ADTS帧。

ADTS 格式下 AAC⾳频⽂件格式

每⼀帧由ADTS Header和AAC Audio Data(在图中,是 AAC ES)组成。结构体如下:

也就是说,,一个AAC 帧,包含了一个ADTS header 和 一堆具体的数据。另外AAC的一帧一般包含了1024个采样点。

头文件 ADTS Header 的组成

是由 固定头信息  可变头信息 。组成
固定头信息中的数据每⼀帧都相同,⽽可变头信息则在帧与帧之间可变

每⼀帧的ADTS的头⽂件都包含了⾳频的采样率,声道,帧⻓度等信息,这样解码器才能解析读取。

⼀般情况下ADTS的头信息都是7个字节,分为2部分:

adts_fixed_header();

adts_variable_header();

其⼀为固定头信息,紧接着是可变头信息。固定头信息中的数据每⼀帧都相同,⽽可变头信息则在帧与帧之间可变。

头文件固定部分:adts_fixed_header()

syncword :同步头 总是0xFFF, all bits must be 1,代表着⼀个ADTS帧的开始 12bits

ID: MPEG标识符,0标识MPEG-4,1标识MPEG-2 1bits

Layer: always: '00' 2 bits

protection_absent:表示是否误码校验。1代表 header 有 7个字节,0代表 header有9个字节,一般情况下都是7个字节。Warning, set to 1 if there is no CRC and 0 if there is CRC 1bits

profile:表示使⽤哪个级别的AAC,如01 Low Complexity(LC)--- AAC LC。有些芯⽚只⽀持AAC LC 。 2bits。。 通过前面的ID,我们可以设置是 MPEG-4, 还是 MPEG-2

在MPEG-2 中,有明确的指出 profile这个值是多少。且只有三种 参见下表

在MPEG-4中,profile的计算要通过  MPEG-4 Audio Object Type - 1

profile = MPEG-4 Audio Object Type - 1

如下的MPEG-4中关于 aac audio Object Type的说明

对应的profile的值

sampling_frequency_index:表示使⽤的采样率下标,通过这个下标在 Sampling Frequencies[ ]数组中查找得知采样率的值。4bits

channel_configuration: 表示声道数,⽐如2表示⽴体声双声道 3bits,

MPEG-4 中规定的值如下:

0: Defined in AOT Specifc Config

1: 1 channel: front-center

2: 2 channels: front-left, front-right

3: 3 channels: front-center, front-left, front-right

4: 4 channels: front-center, front-left, front-right, back-center

5: 5 channels: front-center, front-left, front-right, back-left, back-right

6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel

7: 8 channels: front-center, front-left, front-right, side-left, side-right,back-left, back-right, LFE-channel

8-15: Reserved

还有3个没有介绍:都占1bite,

private_bits:

original:

home:

头文件adts_variable_header()

copyright_identification_bits: 未知, 占1bits

copyright_identification_start: 未知,占1bits

aac_frame_length : ⼀个ADTS帧的⻓度 包括ADTS头和AAC原始流. 单位是bytes

aac_frame_length = (protection_absent == 1 ? 7 : 9) + size(AACFrame) 13 bits

protection_absent=0时, header length=9bytes

protection_absent=1时, header length=7bytes

adts_buffer_fullness:0x7FF 说明是码率可变的码流。一般都是写的0x7FF这个值 11bits

number_of_raw_data_blocks_in_frame
表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。
如果number_of_raw_data_blocks_in_frame 的值是0, 表示说ADTS帧中有⼀个AAC原始帧。
如果 number_of_raw_data_blocks_in_frame 的值是1,表明ADTS帧中有2个原始帧。
例子:

下⾯是ADTS的AAC⽂件部分:⾼字节开始算

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

我们将这7个字节拿出来,转化成2进制

0xFF         0xF1          0x4C          0x40             0x20            0xFF          0xFC

11111111   11110001   01001100   0100 0000   0010 0000   1111 1111   1111 1100

分析各个关键数值:

0xFF 0xF1 表示如下的部分

111111111111 syncword :同步头 总是0xFFF, all bits must be 1,代表着?个ADTS帧的开始 12bits

0 ID: MPEG标识符,0标识MPEG-4,1标识MPEG-2 1bits

00 Layer: always: '00' 2 bits

1 protection_absent:表示是否误码校验。1代表 header 有 7个字节,0代表 header有9个字节,一般情况下都是7个字节。Warning, set to 1 if there is no CRC and 0 if there is CRC 1bits


0x4C 全部, 0x40中的4 表示部分如下

01 profile:表示使?哪个级别的AAC,如01 Low Complexity(LC)--- AAC LC。有些芯片只支持AAC LC 。 2bits

0011 ,sampling_frequency_index : 通过这个下标在 Sampling Frequencies[ ]数组中找采样率的值 4bits

0 private_bits: 1 bits

001 channel_configuration: 表示声道数,比如2表示立体声双声道 3bits

0 original: 1bits

0 home: 1bits


0x40中的4 ,,, 0x20 0xFF 0xFC 全部表示如下

0 copyright_identification_bits: 未知, 占1bits

0 copyright_identification_start: 未知,占1bits

0000100000111(帧⻓度) aac_frame_length 占 13 bits

11111111111 adts_buffer_fullness:0x7FF 说明是码率可变的码流。 11bits

00 number_of_raw_data_blocks_in_frame

表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。 占2bits

一般一个

计算帧⻓度:将⼆进制 0000100000111 转换成⼗进制为263。观察第⼀帧的⻓度确实为263个字节。红色部分的为帧头部的固定部分 0xFF 0xF1 0x4C 0x40 0x20 0xFF 0xFC , 该帧长度所属位置为蓝色部分,其中 第一个0的后两个0, 0000

得到帧长度的计算⽅法:(帧⻓度为13位,使⽤unsigned int来存储帧⻓数值)

unsigned int getFrameLength(unsigned char* str)
{if ( !str ){return 0;}unsigned int len = 0;int f_bit = str[3];int m_bit = str[4];int b_bit = str[5];len += (b_bit>>5);len += (m_bit<<3);len += ((f_bit&3)<<11);return len;
}

核心编码:注意的在生成 ADTS Header 的代码
 
    // 读取媒体文件,并把aac数据帧写入到本地文件,注意,从mp4文件中读取到的aac就只有 aac data 的部分,没有头的部分,头的部分我们需要自己添加,使用的方法为自定义的adts_header方法
//    av_read_frame方法的一些说明
//    对于音频,如果每个帧具有已知的固定大小(例如PCM或ADPCM数据),则它包含整数个帧。// 如果音频帧具有可变大小(例如MPEG音频),则它包含一个帧。//当前走到这里,读取的一定是AAC数据,那么av_read_frame读取到pkt中的一定是一帧的大小,因此adts_header方法中,传递的第二个参数就是一帧的大小。int ret1 =0;while((ret1 = av_read_frame(ifmt_ctx, &pkt)) >=0 ){if(pkt.stream_index == audio_index){char adts_header_buf[7] = {0};//这里我们自己写的时候,头部占7bytes,意味着 校验位的值是1,表示不用校验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);fwrite(adts_header_buf, 1, 7, aac_fd);  // 写adts header , ts流不适用,ts流分离出来的packet带了adts headerlen = fwrite( pkt.data, 1, pkt.size, aac_fd);   // 写adts dataif(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);}

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; // 默认使用48000hzint adtsLen = data_length + 7;int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);int i = 0;for(i = 0; i < frequencies_size; i++){if(sampling_frequencies[i] == samplerate){sampling_frequency_index = i;break;}}if(i >= frequencies_size){printf("unsupport samplerate:%d\n", samplerate);return -1;}p_adts_header[0] = 0xff;         //syncword:0xfff                          高8bitsp_adts_header[1] = 0xf0;         //syncword:0xfff                          低4bitsp_adts_header[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bitp_adts_header[1] |= (0 << 1);    //Layer:0                                 2bitsp_adts_header[1] |= 1;           //protection absent:1                     1bitp_adts_header[2] = (profile)<<6;            //profile:profile               2bitsp_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index  4bitsp_adts_header[2] |= (0 << 1);             //private bit:0                   1bitp_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels  高1bitp_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bitsp_adts_header[3] |= (0 << 5);               //original:0                1bitp_adts_header[3] |= (0 << 4);               //home:0                    1bitp_adts_header[3] |= (0 << 3);               //copyright id bit:0        1bitp_adts_header[3] |= (0 << 2);               //copyright id start:0      1bitp_adts_header[3] |= ((adtsLen & 0x1800) >> 11);           //frame length:value   高2bitsp_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length:value    中间8bitsp_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length:value    低3bitsp_adts_header[5] |= 0x1f;                                 //buffer fullness:0x7ff 高5bitsp_adts_header[6] = 0xfc;      //‭11111100‬       //buffer fullness:0x7ff 低6bits// number_of_raw_data_blocks_in_frame://    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。return 0;
}

全部代码:

#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>#define ADTS_HEADER_LEN  7;const int sampling_frequencies[] = {96000,  // 0x088200,  // 0x164000,  // 0x248000,  // 0x344100,  // 0x432000,  // 0x524000,  // 0x622050,  // 0x716000,  // 0x812000,  // 0x911025,  // 0xa8000   // 0xb// 0xc d e f是保留的
};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; // 默认使用48000hzint adtsLen = data_length + 7;int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);int i = 0;for(i = 0; i < frequencies_size; i++){if(sampling_frequencies[i] == samplerate){sampling_frequency_index = i;break;}}if(i >= frequencies_size){printf("unsupport samplerate:%d\n", samplerate);return -1;}p_adts_header[0] = 0xff;         //syncword:0xfff                          高8bitsp_adts_header[1] = 0xf0;         //syncword:0xfff                          低4bitsp_adts_header[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bitp_adts_header[1] |= (0 << 1);    //Layer:0                                 2bitsp_adts_header[1] |= 1;           //protection absent:1                     1bitp_adts_header[2] = (profile)<<6;            //profile:profile               2bitsp_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index  4bitsp_adts_header[2] |= (0 << 1);             //private bit:0                   1bitp_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels  高1bitp_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bitsp_adts_header[3] |= (0 << 5);               //original:0                1bitp_adts_header[3] |= (0 << 4);               //home:0                    1bitp_adts_header[3] |= (0 << 3);               //copyright id bit:0        1bitp_adts_header[3] |= (0 << 2);               //copyright id start:0      1bitp_adts_header[3] |= ((adtsLen & 0x1800) >> 11);           //frame length:value   高2bitsp_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length:value    中间8bitsp_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length:value    低3bitsp_adts_header[5] |= 0x1f;                                 //buffer fullness:0x7ff 高5bitsp_adts_header[6] = 0xfc;      //‭11111100‬       //buffer fullness:0x7ff 低6bits// number_of_raw_data_blocks_in_frame://    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。return 0;
}int main(int argc, char *argv[])
{int ret = -1;char errors[1024];char *in_filename = NULL;char *aac_filename = NULL;FILE *aac_fd = NULL;int audio_index = -1;int len = 0;AVFormatContext *ifmt_ctx = NULL;AVPacket pkt;// 设置打印级别av_log_set_level(AV_LOG_DEBUG);if(argc < 3){av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!\n");return -1;}in_filename = argv[1];      // 输入文件aac_filename = argv[2];     // 输出文件if(in_filename == NULL || aac_filename == NULL){av_log(NULL, AV_LOG_DEBUG, "src or dts file is null, plz check them!\n");return -1;}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;}// 打开输入文件if((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0){av_strerror(ret, errors, 1024);av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",in_filename,ret,errors);return -1;}// 获取解码器信息if((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0){av_strerror(ret, errors, 1024);av_log(NULL, AV_LOG_DEBUG, "failed to find stream information: %s, %d(%s)\n",in_filename,ret,errors);return -1;}// dump媒体信息
//    av_dump_format(ifmt_ctx, 0, in_filename, 0);// 初始化packetav_init_packet(&pkt);// 查找audio对应的steam indexaudio_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 %s stream in input file %s\n",av_get_media_type_string(AVMEDIA_TYPE_AUDIO),in_filename);return AVERROR(EINVAL);}// 打印AAC级别printf("audio profile:%d, FF_PROFILE_AAC_LOW:%d\n",ifmt_ctx->streams[audio_index]->codecpar->profile,FF_PROFILE_AAC_LOW);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;}// 读取媒体文件,并把aac数据帧写入到本地文件while(av_read_frame(ifmt_ctx, &pkt) >=0 ){if(pkt.stream_index == audio_index){char adts_header_buf[7] = {0};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->ch_layout.nb_channels);fwrite(adts_header_buf, 1, 7, aac_fd);  // 写adts header , ts流不适用,ts流分离出来的packet带了adts headerlen = fwrite( pkt.data, 1, pkt.size, aac_fd);   // 写adts dataif(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);}failed:// 关闭输入文件if(ifmt_ctx){avformat_close_input(&ifmt_ctx);}if(aac_fd){fclose(aac_fd);}return 0;
}

相关文章:

音视频开发13 FFmpeg 音频 相关格式分析 -- AAC ADTS格式分析

这一节&#xff0c;我们学习常用的音频的格式 AAC&#xff0c;重点是掌握 AAC的传输格式 ADTS 头部的信息&#xff0c;目的是 &#xff1a; 当音频数据有问题的时候&#xff0c;如果是AAC的编码&#xff0c;在分析 头部信息的时候能够根据头部信息 判断问题是否出现在 头部。 A…...

周三晚19:00 | 数据资源入表案例拆解,他们如何应对入表难点?

数据资源入表的具体场景是怎样的&#xff1f;如何应对数据资源入表难点&#xff1f; 6月5日&#xff0c;即周三晚19:00&#xff0c;讲师-星光数智首席数据架构师 魏战松将继续带来关于《数据要素资产运营》的主题直播&#xff0c;为大家拆解数据资源入表的具体案例。 ​累计77…...

树的知识总结

一:树的基本术语(只写了查漏的部分 1 双亲:就是父节点 2 层序编号 3 有序无序树 4 森林 二:逻辑结构上与线性结构的比较 三:树的存储结构 ①双亲表示节点法:...

工业安全智勇较量,赛宁网安工业靶场决胜工业网络攻防对抗新战场

2024年1月30日&#xff0c;工信部发布《工业控制系统网络安全防护指南》&#xff08;工信部网安〔2024〕14号&#xff09;&#xff0c;围绕安全管理、技术防护、安全运营、责任落实四方面提出安全防护要求&#xff0c;强调聚焦安全薄弱关键环节&#xff0c;强化技术应对策略&am…...

一种一维时间序列信号的广义小波变换方法(MATLAB)

地震波在含油气介质中传播时&#xff0c;其高频分量往往比低频分量衰减更快。据此&#xff0c;地震波的高频分量和低频分量之间的差异值可以用于分析含油气衰减位置&#xff0c;从而间接指示出含油气储层。对于时频域中的地震波衰减分析&#xff0c;一般地&#xff0c;利用地震…...

【GIC400】——驱动代码实现

文章目录 驱动代码实现IRQ 中断处理GIC 驱动GIC 使用使用示例系列文章 【ARMv7-A】——异常与中断 【ARMv7-A】——异常中断处理概述 【ARMv7-A】——进入和退出异常中断的过程 【GIC400】——PLIC,NVIC 和 GIC 中断对比 【GIC400】——GIC 简介 【GIC400】——GIC 相关的 CP1…...

如何在 Vue 组件中正确地使用 data 函数?

在 Vue 组件中正确使用 data 函数有以下几点需要注意: 返回一个对象: data 函数必须返回一个对象,这个对象包含了组件实例需要用到的所有数据属性。export default {data() {return {message: Hello, Vue!,count: 0}} }不要使用箭头函数: data 函数不应该使用箭头函数 () >…...

.Net 基于MiniExcel的导入功能接口示例

/// <summary>/// 导入/// </summary>/// <param name"formFile"></param>/// <returns></returns>[HttpPost("Import")]public async Task<ExecResult> Import(IFormFile formFile){try{if (formFile null) t…...

流量焦虑?别担心,Xinstall一站式App推广解决方案来了!

在移动互联网时代&#xff0c;App已经成为人们日常生活中不可或缺的一部分。然而&#xff0c;对于众多开发者来说&#xff0c;如何有效地推广自己的App&#xff0c;吸引更多的用户&#xff0c;却是一个不小的挑战。今天&#xff0c;我们将为大家介绍一款强大的App推广工具——X…...

降薪潮要开始了么?

互联网要全面迎来降薪潮了么&#xff0c;最近这个观念一直冲击着我 起因就是&#xff0c;前一段一位朋友降薪40%拿到了offer&#xff1b;还有一位金融机构的人力资源负责人朋友告诉我&#xff0c;最近来的很多互联网人都是降薪来的&#xff0c;普遍降30-50%不等 我就在想&…...

网络服务DHCP的安装

DHCP的安装 检查并且安装dhcp有关软件包 rpm -qc dhcp #检查是否存在dhcp yum install -y dhcp #进行yum安装查看系统的配置文件 切换到对应目录查看相关文件配置&#xff0c;发现是空目录。 将官方提供的example复制到原配置文件中 cp /usr/share/doc/dhcp-4.2.5/dhcpd.…...

SELinux:安全增强型Linux

SELinux&#xff1a;安全增强型Linux 作用&#xff1a; 可以保护linux系统的安全为用户分配最小的权限 状态&#xff1a; Enforcing&#xff1a;强制保护Permissive&#xff1a;宽松状态Disabled&#xff1a;禁用 为了安全性考虑&#xff0c;希望SELinux设置为Enforcing状态…...

.NET Redis限制接口请求频率 滑动窗口算法

在.NET中使用Redis来限制接口请求频率&#xff08;每10秒只允许请求一次&#xff09; NuGet setup StackExchange.Redis 实现速率限制逻辑&#xff1a; 在控制器或服务层中&#xff0c;编写Redis速率限制计数器。 设置Redis键&#xff1a; 为每个用户或每个IP地址设置一个唯一…...

Java List数据结构与常用方法

1.1 数据结构概述 Java的集合框架其实就是对数据结构的封装&#xff0c;在学习集合框架之前&#xff0c;有必要先了解下数据结构。 1.1.1 什么是数据结构 所谓数据结构&#xff0c;其实就是计算机存储、组织数据的方式。 数据结构是用来分析研究数据存储操作的&#xff0c;其实…...

Docker搭建redis-cluster集群

1. 前期准备 1.1 拉redis镜像 docker search redis docker pull redis1. 2 创建网卡 docker network create myredis --subnet 172.28.0.0/16#查看创建的网卡 docker network inspect myredisdocker network rm myredis #删除网卡命令 多个中间 空格隔开 docker network --h…...

实验室类管理平台LIMS系统的ui设计实例

实验室类管理平台LIMS系统的ui设计实例...

<PLC><西门子><工控>西门子博图V18中使用SCL语言编写一个CRC16-modbus校验程序

前言 本系列是关于PLC相关的博文,包括PLC编程、PLC与上位机通讯、PLC与下位驱动、仪器仪表等通讯、PLC指令解析等相关内容。 PLC品牌包括但不限于西门子、三菱等国外品牌,汇川、信捷等国内品牌。 除了PLC为主要内容外,PLC相关元器件如触摸屏(HMI)、交换机等工控产品,如…...

Linux - 文件管理高级 find、grep

0.管道 | 将前面命令的标准输出传递给管道作为后面的标准输入 1.文件查找 find find 进行文件查找时&#xff0c;默认进行递归查找&#xff0c;会查找隐藏目录下的文件 1.1 用法 # find 查找路径 查找条件... -type // 文件类型 f 普通文件 b 设备 d …...

DOS编程入门:探索基础、深入技巧与实战应用

DOS编程入门&#xff1a;探索基础、深入技巧与实战应用 DOS编程&#xff0c;作为计算机编程的基石之一&#xff0c;对于初学者来说&#xff0c;既是一种挑战&#xff0c;也是一次深入了解计算机底层运作的绝佳机会。本文将从四个方面、五个方面、六个方面和七个方面&#xff0…...

创建线程的技术难点

在软件开发中&#xff0c;创建线程并正确地管理它们是一个复杂而关键的任务&#xff0c;涉及的技术难点主要有&#xff1a; 线程同步&#xff1a;当多个线程需要访问共享资源时&#xff0c;必须确保它们以某种方式同步&#xff0c;以避免数据不一致或其他并发问题。例如&#…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter

java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用&#xff08;Math::max&#xff09; 2 函数接口…...