【音视频】FFmpeg解封装
解封装
复用器,比如MP4/FLV
解复用器,MP4/FLV
封装格式相关函数
- avformat_alloc_context(); 负责申请一个AVFormatContext结构的内存,并进行简单初始化
- avformat_free_context(); 释放该结构里的所有东西以及该结构本身
- avformat_close_input();关闭解复用器。关闭后就不再需要使用avformat_free_context 进行释放。
- avformat_open_input(); 打开输入视频文件
- avformat_find_stream_info(): 获取视频文件信息
- av_read_frame(); 读取音视频包
- avformat_seek_file(); 定位文件
- av_seek_frame(): 定位文件
解封装流程
FFmpeg数据结构之间的关系
区分不同的码流
AVMEDIA_TYPE_VIDEO
视频流
video_index = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,-1,-1, NULL, 0)
AVMEDIA_TYPE_AUDIO
音频流
video_index = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,-1,-1, NULL, 0)
AVPacket
里面也有一个index
的字段,这个字段存储对应的一个流
重点
avformat_open_input
和avformat_find_stream_info
分别用于打开一个流和分析流信息。- 在初始信息不足的情况下(比如
FLV
和H264
文件),avformat_find_stream_info接口需要在内部调用read_frame_internal
接口读取流数据(音视频帧),然后再分析后,设置核心数据结构AVFormatContext
- 由于需要读取数据包,avformat_find_stream_info接口会带来很大的延迟
实现流程
添加视频文件
-
在
build
路径下添加相关mp4
,flv
,ts
文件
-
设置参数输入
avformat_open_input
int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
这个函数主要是用于打开输入的媒体文件或流,并对相关上下文进行初始化:
-
为
AVFormatContext
结构体分配内存,然后将其地址存储在*ps
中,后续对媒体文件的操作,如读取数据包、查找流信息等,都要基于这个上下文。 -
- 输入媒体文件的文件名或
URL
。可以是本地文件的路径,例如"/path/to/your/file.mp4"
;也可以是网络流的 URL,比如"http://example.com/stream.m3u8"
。
- 输入媒体文件的文件名或
-
第三个参数用于指定输入文件的格式。通常情况下设置为
NULL
,让 FFmpeg 自动探测文件格式。不过,在某些特殊情况下,如果你明确知道文件的格式,也可以指定具体的AVInputFormat
类型。 -
最后一个参数是指向
AVDictionary
指针的指针,用于传递额外的选项,例如编解码器选项、协议选项等。如果不需要传递额外选项,可以将其设置为NULL
。 -
输入为mp4文件的时候,这一步可以获取很多信息,如编解码
ID
、媒体持续时间等
![[Pasted image 20250330142428.png|200]]
- 输入文件为
flv
,则无法获取较多信息,因为flv
头部信息不足
比如这里的duration
没有被设置,是随机值
- 输入为
ts
,效果和flv
类似,但信息比flv
多一点
avformat_find_stream_info
这个函数主要是用于分析输入媒体的流信息,为上下文结构体补充信息,比如比特率等
- 在调用
avformat_open_input
打开媒体文件后,AVFormatContext
中仅包含了一些基本的文件信息,而各个流(如音频流、视频流、字幕流等)的详细信息,像编码格式、帧率、采样率、分辨率等,往往还未被解析出来。avformat_find_stream_info
函数的主要功能就是读取媒体文件的一定数量的数据包,对这些数据包进行分析,从而填充AVFormatContext
中各流的详细信息。
ret = avformat_find_stream_info(ifmt_ctx, NULL);
参考下图,执行后将bite_rate
、duration
等信息补充到了上下文中
如果一开始头部信息不足,调用这个函数比较耗费时间,因为需要在内部读入视频帧进行分析
-
ts
文件
-
flv
文件
mp4
文件
可以发现,是存在延迟的,如果文件容量更大的话,延迟可能会更大
av_dump_format
这个函数将上下文信息打印出来
av_dump_format(ifmt_ctx, 0, in_filename, 0);
- 使用
avformat_open_input
后打印上下文信息
- 使用
avformat_find_stream_info
后打印信息
这里还可以将上下文结构体的内容打印出来:
// url: 调用avformat_open_input读取到的媒体文件的路径/名字printf("media name:%s\n", ifmt_ctx->url);// nb_streams: nb_streams媒体流数量printf("stream number:%d\n", ifmt_ctx->nb_streams);// bit_rate: 媒体文件的码率,单位为bpsprintf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024));// 时间int total_seconds, hour, minute, second;// duration: 媒体文件时长,单位微妙total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE; // 1000us = 1ms, 1000ms = 1秒hour = total_seconds / 3600;minute = (total_seconds % 3600) / 60;second = (total_seconds % 60);//通过上述运算,可以得到媒体文件的总时长printf("total duration: %02d:%02d:%02d\n", hour, minute, second);printf("\n");
注意这里的duration
为媒体总时长,单位为微秒,因此转换为秒需要除以1e6
,AV_TIME_BASE
宏对应就是1e6
![[Pasted image 20250330145622.png|400]]
获取相应流
可以通过遍历上下文中对应的二维流数组来找到自己想要的流,比如音频流和视频流
AVStream
是 FFmpeg 库中一个关键的结构体,主要用于描述媒体文件中的一个流(例如视频流、音频流、字幕流等),结构体内存储了很多关于流的信息:
AVRational time_base
:AVRational
是一个表示分数的结构体,time_base
定义了流中时间戳的基本单位。时间戳(如 PTS
和 DTS
)是以 time_base
为单位的。例如,若 time_base
为 {1, 1000}
,则表示时间戳的单位是 1/1000 秒。在进行时间戳的转换和计算时,需要使用这个 time_base
。AVCodecParameters
结构体包含了流的编解码器相关的参数信息,如视频流的分辨率、帧率、像素格式,音频流的采样率、声道数、采样格式等。这些信息对于选择合适的解码器以及进行解码操作非常重要。
1. int index
此成员表示该流在 AVFormatContext
的 streams
数组中的索引。在处理多个流的媒体文件时,可以通过这个索引来区分不同的流。例如,在读取数据包时,AVPacket
结构体中的 stream_index
就会指向这个索引,以此确定该数据包属于哪个流。
2. AVRational time_base
AVRational
是一个表示分数的结构体,time_base
定义了流中时间戳的基本单位。时间戳(如 PTS
和 DTS
)是以 time_base
为单位的。例如,若 time_base
为 {1, 1000}
,则表示时间戳的单位是 1/1000 秒。在进行时间戳的转换和计算时,需要使用这个 time_base
。
3. AVCodecParameters *codecpar
AVCodecParameters
结构体包含了流的编解码器相关的参数信息,如视频流的分辨率、帧率、像素格式,音频流的采样率、声道数、采样格式等。这些信息对于选择合适的解码器以及进行解码操作非常重要。
4. int64_t duration
该成员表示流的总时长,单位是 time_base
。要将其转换为秒,可以使用以下公式:duration_in_seconds = (double)duration * av_q2d(time_base)
。
5. int64_t start_time
表示流的起始时间,单位同样是 time_base
。在某些情况下,流的起始时间可能不是从 0 开始的,通过这个成员可以获取到流实际的起始时间。
6. AVRational avg_frame_rate
和 AVRational r_frame_rate
avg_frame_rate
表示流的平均帧率,是根据流中所有帧的时间间隔计算得出的平均帧率。r_frame_rate
表示流的实际帧率,通常是固定的帧率,对于一些固定帧率的视频流,这个值会比较准确。
7. void *priv_data
这是一个指向私有数据的指针,用于存储特定格式的额外信息。不同的格式可能会使用这个指针来存储一些自定义的数据,一般情况下不需要直接操作这个指针。
在我们的示例中,通过遍历上下文所有的流,每个流都有唯一对应的流索引,因此可以通过流中的编解码参数信息,打印出相应的音视频格式:
- 获取当前索引的流结构体
AVStream *in_stream = ifmt_ctx->streams[i];// 音频流、视频流、字幕流
- 通过编解码器的参数获取编解码类型,返回相应的类型宏定义
- 音频
MEDIA_TYPE_AUDIO
if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type)
- 视频
MEDIA_TYPE_VIDEO
else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type)
- 如果是音频,可以打印出相关的音频信息,如采样率、采样格式(如
FLTP
、S16P
)、通道数、压缩格式(如AAC
、MP3
)、音频总时长等
printf("----- Audio info:\n");// index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标识printf("index:%d\n", in_stream->index);// sample_rate: 音频编解码器的采样率,单位为Hzprintf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);// codecpar->format: 音频采样格式if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");}else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_S16P\n");}// channels: 音频信道数目printf("channel number:%d\n", in_stream->codecpar->channels);// codec_id: 音频压缩编码格式if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id){printf("audio codec:AAC\n");}else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id){printf("audio codec:MP3\n");}else{printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);}// 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微秒,音频总时长跟视频总时长不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);//将音频总时长转换为时分秒的格式打印到控制台上printf("audio duration: %02d:%02d:%02d\n",duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));}else{printf("audio duration unknown");}printf("\n");
- 注意,在计算音频时长的时候,
AVStream
中的duration
和上下文AVFormatContext
中的单位不一样,这里的单位是时间基time_base
,不同的媒体文件可能时间基不同,比如可能是1/1000 s
作为一个时间基,那么我们转换为妙就需要如下操作
s = A V S t r e a m − > d u r a t i o n ∗ a v _ q 2 d ( A V S t r e a m − > t i m e _ b a s e ) s = AVStream->duration*av\_q2d(AVStream->time\_base) s=AVStream−>duration∗av_q2d(AVStream−>time_base)
这里的av_q2d
实际上就是将分数形式转换为double
类型的小数形式,因此转换实质上上就是:duration
* time_base
- 如果是视频,同样可以提取出视频编解码器的信息,比如视频帧率(
FPS
)、视频压缩编码格式(H264
、MPEG4
)、视频帧的宽高(1080x720
),转换视频的持续时间的方式与音频一样,注意,time_base
的值通常不同:
视频
- 典型值:
{1, 25}
(25
FPS
)、{1, 30}
(30
FPS
)、{1, 90000}
(精确时间基) - 含义:视频帧的时间间隔以帧率倒数为单位。
音频
- 典型值:
{1, 44100}
(44.1kHz
采样率)、{1, 48000}
(48kHz
采样率) - 含义:音频帧的时间间隔以采样周期为单位
printf("----- Video info:\n");printf("index:%d\n", in_stream->index);// avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频压缩编码格式{printf("video codec:MPEG4\n");}else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频压缩编码格式{printf("video codec:H264\n");}else{printf("video codec_id:%d\n", in_stream->codecpar->codec_id);}// 视频帧宽度和帧高度printf("width:%d height:%d\n", in_stream->codecpar->width,in_stream->codecpar->height);//视频总时长,单位为秒。注意如果把单位放大为毫秒或者微秒,音频总时长跟视频总时长不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);printf("video duration: %02d:%02d:%02d\n",duration_video / 3600,(duration_video % 3600) / 60,(duration_video % 60)); //将视频总时长转换为时分秒的格式打印到控制台上}else{printf("video duration unknown");}printf("\n");
获取相应包(Packet
)
上下文中还存储了压缩的数据包,比如对应的H264
、AAC
压缩包,我们可以读取这些压缩包
- 首先我们需要为
AVPacket
结构体分配内存
AVPacket *pkt = av_packet_alloc();
- 通过一个循环来依次读取每一帧的数据包到
AVPacket
中,每次读取一帧后,内部的指针都会向后移动
while (1){ret = av_read_frame(ifmt_ctx, pkt);}
- 判断数据包内的流索引(视频流、音频流),进行相应操作,如打印
pts
、dts
、包的大小size
、包对应文件的偏移量pos
,以及根据不同的索引在不同AVStream
中找到对应的当前帧的持续时间,如下
- 音频帧数据包持续时间
pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base)
- 视频帧数据包持续时间
pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base)
- 解码完当前帧数据包后,需要将这一帧数据包释放,否则会导致内存泄漏,直接调用
av_packet_unref
减少引用计数即可,引用计数为0
会自动释放帧数据包的buf
内存
av_packet_unref(pkt);
- 读取所有帧数据包之后,需要释放
AVPacket
结构体的内存
if(pkt)av_packet_free(&pkt);
释放内存
所有操作之后,需要释放上下文内存,并且关闭打开的文件或关闭对应网络流的连接
调用 avformat_close_input
函数即可实现上述功能
if(ifmt_ctx)avformat_close_input(&ifmt_ctx);
整体代码
main.c
#include <stdio.h>
#include <libavformat/avformat.h>
#include<time.h>int main(int argc, char **argv)
{//打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
// avformat_network_init();const char *default_filename = "believe.mp4";char *in_filename = NULL;if(argv[1] == NULL){in_filename = default_filename;}else{in_filename = argv[1];}printf("in_filename = %s\n", in_filename);//AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体AVFormatContext *ifmt_ctx = NULL; // 输入文件的demuxint videoindex = -1; // 视频索引int audioindex = -1; // 音频索引// 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);if (ret < 0) //如果打开媒体文件失败,打印失败原因{char buf[1024] = { 0 };av_strerror(ret, buf, sizeof(buf) - 1);printf("open %s failed:%s\n", in_filename, buf);goto failed;}printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);av_dump_format(ifmt_ctx, 0, in_filename, 0);printf_s("\n==== av_dump_format finish =======\n\n");clock_t started = clock();ret = avformat_find_stream_info(ifmt_ctx, NULL);clock_t ended = clock();double elapsed_time = (double)(ended - started) / CLOCKS_PER_SEC;printf("avformat_find_stream_info took %f seconds to execute.\n", elapsed_time);if (ret < 0) //如果打开媒体文件失败,打印失败原因{char buf[1024] = { 0 };av_strerror(ret, buf, sizeof(buf) - 1);printf("avformat_find_stream_info %s failed:%s\n", in_filename, buf);goto failed;}//打开媒体文件成功printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);av_dump_format(ifmt_ctx, 0, in_filename, 0);printf_s("\n==== av_dump_format finish =======\n\n");// url: 调用avformat_open_input读取到的媒体文件的路径/名字printf("media name:%s\n", ifmt_ctx->url);// nb_streams: nb_streams媒体流数量printf("stream number:%d\n", ifmt_ctx->nb_streams);// bit_rate: 媒体文件的码率,单位为bpsprintf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024));// 时间int total_seconds, hour, minute, second;// duration: 媒体文件时长,单位微妙total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE; // 1000us = 1ms, 1000ms = 1秒hour = total_seconds / 3600;minute = (total_seconds % 3600) / 60;second = (total_seconds % 60);//通过上述运算,可以得到媒体文件的总时长printf("total duration: %02d:%02d:%02d\n", hour, minute, second);printf("\n");/** 老版本通过遍历的方式读取媒体文件视频和音频的信息* 新版本的FFmpeg新增加了函数av_find_best_stream,也可以取得同样的效果*/for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i++){AVStream *in_stream = ifmt_ctx->streams[i];// 音频流、视频流、字幕流//如果是音频流,则打印音频的信息if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type){printf("----- Audio info:\n");// index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标识printf("index:%d\n", in_stream->index);// sample_rate: 音频编解码器的采样率,单位为Hzprintf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);// codecpar->format: 音频采样格式if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");}else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format){printf("sampleformat:AV_SAMPLE_FMT_S16P\n");}// channels: 音频信道数目printf("channel number:%d\n", in_stream->codecpar->channels);// codec_id: 音频压缩编码格式if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id){printf("audio codec:AAC\n");}else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id){printf("audio codec:MP3\n");}else{printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);}// 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微秒,音频总时长跟视频总时长不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);//将音频总时长转换为时分秒的格式打印到控制台上printf("audio duration: %02d:%02d:%02d\n",duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));}else{printf("audio duration unknown");}printf("\n");audioindex = i; // 获取音频的索引}else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type) //如果是视频流,则打印视频的信息{printf("----- Video info:\n");printf("index:%d\n", in_stream->index);// avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频压缩编码格式{printf("video codec:MPEG4\n");}else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频压缩编码格式{printf("video codec:H264\n");}else{printf("video codec_id:%d\n", in_stream->codecpar->codec_id);}// 视频帧宽度和帧高度printf("width:%d height:%d\n", in_stream->codecpar->width,in_stream->codecpar->height);//视频总时长,单位为秒。注意如果把单位放大为毫秒或者微秒,音频总时长跟视频总时长不一定相等的if(in_stream->duration != AV_NOPTS_VALUE){int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);printf("video duration: %02d:%02d:%02d\n",duration_video / 3600,(duration_video % 3600) / 60,(duration_video % 60)); //将视频总时长转换为时分秒的格式打印到控制台上}else{printf("video duration unknown");}printf("\n");videoindex = i;}}AVPacket *pkt = av_packet_alloc();int pkt_count = 0;int print_max_count = 10;printf("\n-----av_read_frame start\n");while (1){ret = av_read_frame(ifmt_ctx, pkt);if (ret < 0){printf("av_read_frame end\n");break;}if(pkt_count++ < print_max_count){if (pkt->stream_index == audioindex){printf("audio pts: %lld\n", pkt->pts);printf("audio dts: %lld\n", pkt->dts);printf("audio size: %d\n", pkt->size);printf("audio pos: %lld\n", pkt->pos);printf("audio duration: %lf\n\n",pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));}else if (pkt->stream_index == videoindex){printf("video pts: %lld\n", pkt->pts);printf("video dts: %lld\n", pkt->dts);printf("video size: %d\n", pkt->size);printf("video pos: %lld\n", pkt->pos);printf("video duration: %lf\n\n",pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));}else{printf("unknown stream_index:\n", pkt->stream_index);}}av_packet_unref(pkt);}if(pkt)av_packet_free(&pkt);
failed:if(ifmt_ctx)avformat_close_input(&ifmt_ctx);getchar(); //加上这一句,防止程序打印完信息马上退出return 0;
}
更多资料:https://github.com/0voice
相关文章:
【音视频】FFmpeg解封装
解封装 复用器,比如MP4/FLV 解复用器,MP4/FLV 封装格式相关函数 avformat_alloc_context(); 负责申请一个AVFormatContext结构的内存,并进行简单初始化avformat_free_context(); 释放该结构里的所有东西以及该结构本身avformat_close_input();关闭解复…...
autohue.js - 基于 JavaScript 开发的图片背景色提取开源库,能让图片和背景融为一体
图片提取主题色的工具库,可以实现一些酷炫的界面效果。 本文不是 AI 生成,大部分文字都是我自己敲键盘,部分文字摘自 autohue.js 作者主页,请各位放心舒适阅读。 autohue.js 是一个图片背景色提取库,基于提出来的颜色…...
bgp实验.包括联盟,隧道相关,以及一个低级错误
实验拓扑 低级错误 在配置隧道时,目标的单词是destination,我自动补全为description了,这个问题花了我40分钟 划分ip AS2内骨干网,一个网段需要两个地址,主机位2位,掩码30 需要6个 172.16.0.000000 00 172.16.0.0/30 172.16.0.000001 00 172.16.0.4/30 172.16.0.000010 00 1…...
科普动画短视频制作:角色塑造的魅力法则
宝子们,在科普动画短视频的世界里,角色塑造可是让作品出彩的关键!今天就来和大家唠唠那些超实用的角色塑造法则,还会给大家推荐一款超好用的工具哦~ 一、独特外形,吸睛第一步 在科普动画短视频制作中,角色…...
【飞渡科技数字孪生虚拟环境部署与集成教程 - CloudMaster实战指南】
飞渡科技数字孪生虚拟环境部署与集成教程 - CloudMaster实战指南 前言 本教程详细记录了飞渡科技的数字孪生平台CloudMaster的配置过程,以及如何将三维数字孪生场景集成到前端项目中。数字孪生技术能够在虚拟环境中精确复现物理实体的数据、特性和行为,…...
MongoDB 集合名称映射问题
项目场景 在使用 Spring Data MongoDB 进行开发时,定义了一个名为 CompetitionSignUpLog 的实体类,并创建了对应的 Repository 接口。需要明确该实体类在 MongoDB 中实际对应的集合名称是 CompetitionSignUpLog 还是 competitionSignUpLog。 问题描述 …...
禁止ubuntu自动更新
由于ubuntu server和desktop版本都默认 启动了,自动更新内核的操作。这对于生 产环境来说是不友好的。容易导致亿赛通 无法启动 默认开启了内核自动更新所以我们关闭自 动内核更新。 1.禁止更新执行 sudo apt-mark hold linux-image-generic linux-headers-generic…...
【C++】——入门基础(一)
前言 这是我C的第一篇文章,如果你想从事入门C行业,可以看看这幅漫画 当然,这只是一个玩笑,但如过你真的想学习C,和我一起学习吧 本人其他博客;恋风诗 本文出现的代码见gitte:mozhengy 这里写目录标题 前言1. C发展历史…...
[原创](现代Delphi 12指南):[macOS 64bit App开发]:在Mac App Store外创建、部署与公证
[作者] 常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共24年] 职业生涯: 22年 开发语言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、 开发工具: Visual Studio、Delphi、XCode、…...
美乐迪电玩客户端打包与资源替换实战教程
本篇为《美乐迪电玩全套系统搭建》系列的第二篇,聚焦客户端层的实战操作,主要面向前端开发者、美术资源替换人员及整体项目的打包部署人员。教程将涵盖安卓客户端的构建、资源目录说明、动画素材替换方式及常见适配问题处理等。 一、客户端项目结构说明&…...
多态以及多态底层的实现原理
本章目标 1.多态的概念 2.多态的定义实现 3.虚函数 4.多态的原理 1.多态的概念 多态作为面对三大特性之一,它所指代的和它的名字一样,多种形态.但是这个多种形态更多的指代是函数的多种形态. 多态分为静态多态和动态多态. 静态多态在前面已经学习过了,就是函数重载以及模板,…...
描述城市出行需求模式的复杂网络视角:大规模起点-目的地需求网络的图论分析
描述城市出行需求模式的复杂网络视角:大规模起点-目的地需求网络的图论分析 原文: A complex network perspective for characterizing urban travel demand patterns: graph theoretical analysis of large-scale origin–destination demand networks…...
文件操作函数
本文是小编巩固自身而作,如有错误,欢迎指出! 1.使用文件的原因 我们编写的程序都是有生命周期的,储存在电脑的内存中,如果程序退出,内存回收,数据就会丢失,等程序再次运行…...
Java高频面试之并发编程-05
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶 面试官:线程有哪些调度方法? 在Java中,线程的调用方法主要包括以下几种方式,每种方式适用于…...
LeetCode 1292 元素和小于等于阈值的正方形的最大边长
最大正方形边长问题详解 一、问题描述 给定一个大小为 mn 的矩阵 mat 和一个整数阈值 threshold,要求返回元素总和小于或等于阈值的正方形区域的最大边长;如果没有这样的正方形区域,则返回 0。 二、解题思路 前缀和矩阵的概念与构建 前缀…...
测试开发 - Java 自动化测试核心函数详解
目录 1. 元素定位 1.1 By.xpath 1.1.1 //* 1.1.2 //[指定节点] 1.1.3 / 1.1.4 /.. 1.1.5 [...] 1.1.6 指定索引获取对应元素 1.2 By.cssSelector 1.2.1 # 1.2.2 . 1.2.3 > 1.2.4 标签名:nth-child(n) 2. 获取元素 2.1 findElement 2.2 findElements 3. 操…...
【HarmonyOS】ArKUI框架
目录 概述 声明式开发范式 基于ArKUI的项目 • 1.创建资源文件 • 2.引用资源 • 3.引用系统资源: • 系统资源有哪些 • 4. 在配置和资源中引用资源 声明式语法 UI描述规范 UI组件概述 组件化 组件渲染控制语法 修改…...
【MQ篇】RabbitMQ之简单模式!
目录 引言一、 初识 RabbitMQ 与工作模式二、 简单模式 (Simple Queue) 详解:最直接的“点对点快递” 📮三、 Java (Spring Boot) 代码实战:让小兔子跑起来! 🐰🏃♂️四、 深入理解:简单模式的…...
K8S节点出现Evicted状态“被驱逐”
在Kubernetes集群中,Pod状态为“被驱逐(evicted)”表示Pod无法在当前节点上继续运行,已被集群从节点上移除。 问题分析: 节点磁盘空间不足 ,使用df -h查看磁盘使用情况 可以看到根目录 / 已100%满&#x…...
NumPyro:概率编程的现代Python框架深度解析
引言 概率编程作为统计学与机器学习的交叉领域,正在重塑我们构建不确定性模型的方式。在众多概率编程语言(PPL)中,NumPyro凭借其简洁的语法、强大的性能和与PyTorch生态系统的无缝集成,已经成为研究者和数据科学家的首…...
java进阶之git
git git介绍git常用命令代码回滚操作 git 介绍 工作区 改动(增删文件和内容)暂存区 输入命令:git add改动的文件名,此次改动就放到了"暂存区“本地仓库 输入命令:git commit 此次修改的描述,此次改动…...
负载阻尼效应及其作用解析
负载阻尼效应是指负载(如电路、机械系统或控制系统中连接的设备)对系统动态变化(如电压波动、机械振动等)产生的抑制或衰减作用。 其核心是通过消耗或吸收能量,减少系统中的振荡、波动或瞬态响应,从而提高…...
面向组织的网络安全措施
一、安全措施概述 在一个组织中,技术人员可以利用一系列强大的网络安全工具进行安全检测和防范,以保护组织的网络基础设施、数据和资产免受各种威胁。这些工具通常涵盖了从主动防御、威胁检测、漏洞管理到事件响应和安全分析的各个方面。 以下是一些关…...
Unity 跳转资源商店,并打开特定应用
需求: 打开资源商店,并定位到特定应用. 代码: #if UNITY_ANDROIDApplication.OpenURL("market://details?idcom.tencent.mm"); #elif UNITY_IPHONEApplication.OpenURL(“itms-apps://apps.apple.com/app/id333903271”); #end…...
2025年五大ETL数据集成工具推荐
ETL工具作为打通数据孤岛的核心引擎,直接影响着企业的决策效率与业务敏捷性。本文精选五款实战型ETL解决方案,从零门槛的国产免费工具到国际大厂企业级平台,助您找到最适合的数据集成利器。 一、谷云科技ETLCloud:国产数据集成工…...
基于 PaddleOCR对pdf文件中的文字提取
一、基于 PaddleOCR 提取 PDF 文件中的文字流程 1. 安装必要的依赖库:包括 PaddleOCR 和 PyMuPDF pip install paddlepaddle paddleocr pymupdf 2. 将 PDF 转换为图像:使用 PyMuPDF 将 PDF 的每一页转换为图像 3. 使用 PaddleOCR 进行文字识别&a…...
鸿蒙移动应用开发--渲染控制实验
任务:使用“对象数组”、“ForEach渲染”、“Badge角标组件”、“Grid布局”等相关知识,实现生效抽奖卡案例。如图1所示: 图1 生肖抽奖卡实例图 图1(a)中有6张生肖卡可以抽奖,每抽中一张,会通过弹层显示出来…...
【漫话机器学习系列】215.处理高度不平衡数据策略(Strategies For Highly Imbalanced Classes)
处理高度不平衡数据的四大策略详解 在机器学习与数据挖掘任务中,“类别不平衡”问题几乎无处不在。无论是信用卡欺诈检测、医疗异常诊断,还是网络攻击识别,正负样本的比例往往严重失衡。比如一个欺诈检测数据集中,可能只有不到 1…...
在离线 Ubuntu 环境下部署双 Neo4j 实例(Prod Dev)
在许多开发和生产场景中,我们可能需要在同一台服务器上运行多个独立的 Neo4j 数据库实例,例如一个用于生产环境 (Prod),一个用于开发测试环境 (Dev)。本文将详细介绍如何在 离线 的 Ubuntu 服务器上,使用 tar.gz 包部署两个 Neo4j…...
Windows下Golang与Nuxt项目宝塔部署指南
在Windows下将Golang后端和Nuxt前端项目打包,并使用宝塔面板部署的步骤如下 一、Golang后端打包 交叉编译为Linux可执行文件 在Windows PowerShell中执行: powershell复制下载 $env:GOOS "linux" $env:GOARCH "amd64" go build…...
基于贝叶斯优化的Transformer多输入单输出回归预测模型Bayes-Transformer【MATLAB】
Bayes-Transformer 在机器学习和深度学习领域,Transformer模型已经广泛应用于自然语言处理、图像识别、时间序列预测等多个领域。然而,在一些实际应用中,我们面临着如何高效地优化模型超参数的问题。贝叶斯优化(Bayesian Optimiz…...
ibus输入法微软词库分享
链接: https://pan.baidu.com/s/1aC-UvV-UDHEpxg5sZcAS2Q?pwddxpq 提取码: dxpq --来自百度网盘超级会员v8的分享 链接: https://pan.baidu.com/s/1aC-UvV-UDHEpxg5sZcAS2Q?pwddxpq 提取码: dxpq --来自百度网盘超级会员v8的分享 # 更改ibus输入法字体大小 sudo apt insta…...
Sharding-JDBC 系列专题 - 第五篇:分布式事务
Sharding-JDBC 系列专题 - 第五篇:分布式事务 本系列专题旨在帮助开发者全面掌握 Sharding-JDBC,一个轻量级的分布式数据库中间件。本篇作为系列的第五篇文章,将深入探讨 分布式事务(Distributed Transactions),包括其概念、支持的事务类型、配置方法、工作原理以及实战…...
力扣每日打卡17 49. 字母异位词分组 (中等)
力扣 49. 字母异位词分组 中等 前言一、题目内容二、解题方法1. 哈希函数2.官方题解2.1 前言2.2 方法一:排序2.2 方法二:计数 前言 这是刷算法题的第十七天,用到的语言是JS 题目:力扣 49. 字母异位词分组 (中等) 一、题目内容 给…...
深入解析C++ STL List:双向链表的特性与高级操作
一、引言 在C STL容器家族中,list作为双向链表容器,具有独特的性能特征。本文将通过完整代码示例,深入剖析链表的核心操作,揭示其底层实现机制,并对比其他容器的适用场景。文章包含4000余字详细解析,适合需…...
在 master 分支上进行了 commit 但还没有 push,怎么安全地切到新分支并保留这些更改
确保你的 commit 确实没有 push(否则会覆盖远程分支): git log --oneline # 查看本地 commit git log --oneline origin/master # 查看远程 master 的 commit 确保你的 commit 只存在于本地,远程 origin/master 没有…...
spark jar依赖顺序
1. 执行顺序 spark-submit --config "spark.{driver/executor}.extraClassPathsomeJar"提交的依赖包SystemClasspath – Spark安装时候提供的依赖包spark-submit --jars 提交的依赖包 2. 依赖解释 提交任务时指定的依赖 Spark-submit --config "spark.{drive…...
docker 国内源和常用命令
Ubuntu | Docker Docs 参考docker官方安装docker # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt…...
【目标检测】对YOLO系列发展的简单理解
目录 1.YOLOv12.YOLOv23.YOLOv34.YOLOv45.YOLOv66.YOLOv77.YOLOv9 YOLO系列文章汇总: 【论文#目标检测】You Only Look Once: Unified, Real-Time Object Detection 【论文#目标检测】YOLO9000: Better, Faster, Stronger 【论文#目标检测】YOLOv3: An Incremental …...
C# AppContext.BaseDirectory 应用程序的启动目录
Application.StartupPath定义与用途局限性示例 Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)定义与用途局限性示例 Directory.GetCurrentDirectory()定义与用途局限性示例 关键区别总结推荐使用场景需要应用程序安装目录需要动态工作目录插件或模块化应用…...
Sentinel数据S2_SR_HARMONIZED连续云掩膜+中位数合成
在GEE中实现时,发现简单的QA60是无法去云的,最近S2地表反射率数据集又进行了更新,原有的属性集也进行了变化,现在的SR数据集名称是“S2_SR_HARMONIZED”。那么: 要想得到研究区无云的图像,可以参考执行以下…...
探索Cangjie Magic:仓颉编程语言原生的LLM Agent开发新范式
引言:智能体开发的革命性突破 2025年3月,仓颉社区开源了Cangjie Magic——这是首个基于仓颉编程语言原生构建的LLM Agent开发平台,标志着智能体开发领域的一次重大突破。作为一名长期关注AI发展的技术爱好者,我有幸第一时间体验了…...
css三大特性
css三大特性:层叠性 继承性 优先性 一.层叠性 二.继承性 子标签会继承父标签的某些样式 恰当地使用继承性,减少代码复杂性子元素会继承父元素地某些样式(text-,font-,line-这些元素开头的可以继承,以及color属性) 2…...
Centos7安装Jenkins(图文教程)
本章教程,主要记录在centos7安装部署Jenkins 的详细过程。 [root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) 一、基础环境安装 内存大小要求:256 MB 内存以上 硬盘大小要求:10 GB 及以上 安装基础java环境:Java 17 ( JRE 或者 JDK 都可…...
Hyper-V 管理工具
什么是 Hyper-V Microsoft Hyper-V是一个虚拟化平台,可在Windows客户端和服务器上创建并运行虚拟计算机。操作系统(OS)被称为“监管程序”(supervisor),因为它负责为程序分配物理资源。在虚拟环境中&#…...
小雨滴的奇妙旅行
以下是基于原稿的优化版本,在保留童趣的基础上,进一步贴近5岁孩子的语言习惯和表演需求。修改处用(优化)标注,供参考: 《小雨滴的奇妙旅行》(优化标题,更易记忆) “滴答…...
极狐GitLab 权限和角色如何设置?
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 权限和角色 (BASIC ALL) 将用户添加到项目或群组时,您可以为他们分配角色。该角色决定他们在极狐GitLab 中可以执…...
NLP高频面试题(五十一)——LSTM详解
长短期记忆网络(LSTM)相较于传统循环神经网络(RNN)的核心改进在于通过引入记忆单元(cell state)和门机制(gating mechanism)来有效缓解梯度消失与梯度爆炸问题,从而更好地捕捉长距离依赖关系 。在其网络结构中,信息通过输入门(input gate)、遗忘门(forget gate)和…...
C++学习之游戏服务器开发十二nginx和http
目录 1.容器运行游戏需求分析 2.静态编译游戏服务 3.手动创建游戏镜像 4.编写游戏启动脚本 5.脚本创建游戏服务器镜像 6.登录服务器架构选择 7.http协议初识 8.http报文分析 9.nginx简介和安装 10.nginx配置静态页面 11.nginx配置反向代理 1.容器运行游戏需求分析 2.…...
Spark集群搭建-spark-local
(一)安装Spark 安装Spark的过程就是下载和解压的过程。接下来的操作,我们把它上传到集群中的节点,并解压运行。 1.启动虚拟机 2.通过finalshell连接虚拟机,并上传安装文件到 /opt/software下 3.解压spark安装文件到/op…...