音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现
=================================================================
音视频入门基础:WAV专题系列文章:
音视频入门基础:WAV专题(1)——使用FFmpeg命令生成WAV音频文件
音视频入门基础:WAV专题(2)——WAV格式简介
音视频入门基础:WAV专题(3)——FFmpeg源码中,判断某文件是否为WAV音频文件的实现
音视频入门基础:WAV专题(4)——FFmpeg源码中获取WAV文件音频压缩编码格式、采样频率、声道数量、采样位数、码率的实现
音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现
=================================================================
一、引言
执行FFmpeg命令:
./ffmpeg -i XXX.wav
FFmpeg内部会调用wav_probe函数检测该文件是否为WAV格式的音频文件(具体可以参考:《音视频入门基础:WAV专题(3)——FFmpeg源码中,判断某文件是否为WAV音频文件的实现》)。然后如果检测出该文件为WAV格式的音频文件,会调用wav_read_header函数解码WAV Header。
二、wav_read_header函数的定义
wav_read_header函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3)的源文件libavformat/wavdec.c中:
/* wav input */
static int wav_read_header(AVFormatContext *s)
{int64_t size, av_uninit(data_size);int64_t sample_count = 0;int rf64 = 0, bw64 = 0;uint32_t tag;AVIOContext *pb = s->pb;AVStream *st = NULL;WAVDemuxContext *wav = s->priv_data;int ret, got_fmt = 0, got_xma2 = 0;int64_t next_tag_ofs, data_ofs = -1;wav->unaligned = avio_tell(s->pb) & 1;wav->smv_data_ofs = -1;/* read chunk ID */tag = avio_rl32(pb);switch (tag) {case MKTAG('R', 'I', 'F', 'F'):break;case MKTAG('R', 'I', 'F', 'X'):wav->rifx = 1;break;case MKTAG('R', 'F', '6', '4'):rf64 = 1;break;case MKTAG('B', 'W', '6', '4'):bw64 = 1;break;default:av_log(s, AV_LOG_ERROR, "invalid start code %s in RIFF header\n",av_fourcc2str(tag));return AVERROR_INVALIDDATA;}/* read chunk size */avio_rl32(pb);/* read format */if (avio_rl32(pb) != MKTAG('W', 'A', 'V', 'E')) {av_log(s, AV_LOG_ERROR, "invalid format in RIFF header\n");return AVERROR_INVALIDDATA;}if (rf64 || bw64) {if (avio_rl32(pb) != MKTAG('d', 's', '6', '4'))return AVERROR_INVALIDDATA;size = avio_rl32(pb);if (size < 24)return AVERROR_INVALIDDATA;avio_rl64(pb); /* RIFF size */data_size = avio_rl64(pb);sample_count = avio_rl64(pb);if (data_size < 0 || sample_count < 0) {av_log(s, AV_LOG_ERROR, "negative data_size and/or sample_count in ""ds64: data_size = %"PRId64", sample_count = %"PRId64"\n",data_size, sample_count);return AVERROR_INVALIDDATA;}avio_skip(pb, size - 24); /* skip rest of ds64 chunk */}/* Create the audio stream now so that its index is always zero */st = avformat_new_stream(s, NULL);if (!st)return AVERROR(ENOMEM);for (;;) {AVStream *vst;size = next_tag(pb, &tag, wav->rifx);next_tag_ofs = avio_tell(pb) + size;if (avio_feof(pb))break;switch (tag) {case MKTAG('f', 'm', 't', ' '):/* only parse the first 'fmt ' tag found */if (!got_xma2 && !got_fmt && (ret = wav_parse_fmt_tag(s, size, st)) < 0) {return ret;} else if (got_fmt)av_log(s, AV_LOG_WARNING, "found more than one 'fmt ' tag\n");got_fmt = 1;break;case MKTAG('X', 'M', 'A', '2'):/* only parse the first 'XMA2' tag found */if (!got_fmt && !got_xma2 && (ret = wav_parse_xma2_tag(s, size, st)) < 0) {return ret;} else if (got_xma2)av_log(s, AV_LOG_WARNING, "found more than one 'XMA2' tag\n");got_xma2 = 1;break;case MKTAG('d', 'a', 't', 'a'):if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) && !got_fmt && !got_xma2) {av_log(s, AV_LOG_ERROR,"found no 'fmt ' tag before the 'data' tag\n");return AVERROR_INVALIDDATA;}if (rf64 || bw64) {next_tag_ofs = wav->data_end = avio_tell(pb) + data_size;} else if (size != 0xFFFFFFFF) {data_size = size;next_tag_ofs = wav->data_end = size ? next_tag_ofs : INT64_MAX;} else {av_log(s, AV_LOG_WARNING, "Ignoring maximum wav data size, ""file may be invalid\n");data_size = 0;next_tag_ofs = wav->data_end = INT64_MAX;}data_ofs = avio_tell(pb);/* don't look for footer metadata if we can't seek or if we don't* know where the data tag ends*/if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || (!(rf64 && !bw64) && !size))goto break_loop;break;case MKTAG('f', 'a', 'c', 't'):if (!sample_count)sample_count = (!wav->rifx ? avio_rl32(pb) : avio_rb32(pb));break;case MKTAG('b', 'e', 'x', 't'):if ((ret = wav_parse_bext_tag(s, size)) < 0)return ret;break;case MKTAG('S','M','V','0'):if (!got_fmt) {av_log(s, AV_LOG_ERROR, "found no 'fmt ' tag before the 'SMV0' tag\n");return AVERROR_INVALIDDATA;}// SMV file, a wav file with video appended.if (size != MKTAG('0','2','0','0')) {av_log(s, AV_LOG_ERROR, "Unknown SMV version found\n");goto break_loop;}av_log(s, AV_LOG_DEBUG, "Found SMV data\n");wav->smv_given_first = 0;vst = avformat_new_stream(s, NULL);if (!vst)return AVERROR(ENOMEM);wav->vst = vst;avio_r8(pb);vst->id = 1;vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;vst->codecpar->codec_id = AV_CODEC_ID_SMVJPEG;vst->codecpar->width = avio_rl24(pb);vst->codecpar->height = avio_rl24(pb);if ((ret = ff_alloc_extradata(vst->codecpar, 4)) < 0) {av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n");return ret;}size = avio_rl24(pb);wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3;avio_rl24(pb);wav->smv_block_size = avio_rl24(pb);if (!wav->smv_block_size)return AVERROR_INVALIDDATA;avpriv_set_pts_info(vst, 32, 1, avio_rl24(pb));vst->duration = avio_rl24(pb);avio_rl24(pb);avio_rl24(pb);wav->smv_frames_per_jpeg = avio_rl24(pb);if (wav->smv_frames_per_jpeg > 65536) {av_log(s, AV_LOG_ERROR, "too many frames per jpeg\n");return AVERROR_INVALIDDATA;}AV_WL32(vst->codecpar->extradata, wav->smv_frames_per_jpeg);goto break_loop;case MKTAG('L', 'I', 'S', 'T'):case MKTAG('l', 'i', 's', 't'):if (size < 4) {av_log(s, AV_LOG_ERROR, "too short LIST tag\n");return AVERROR_INVALIDDATA;}switch (avio_rl32(pb)) {case MKTAG('I', 'N', 'F', 'O'):ff_read_riff_info(s, size - 4);break;case MKTAG('a', 'd', 't', 'l'):if (s->nb_chapters > 0) {while (avio_tell(pb) < next_tag_ofs &&!avio_feof(pb)) {char cue_label[512];unsigned id, sub_size;if (avio_rl32(pb) != MKTAG('l', 'a', 'b', 'l'))break;sub_size = avio_rl32(pb);if (sub_size < 5)break;id = avio_rl32(pb);avio_get_str(pb, sub_size - 4, cue_label, sizeof(cue_label));avio_skip(pb, avio_tell(pb) & 1);for (int i = 0; i < s->nb_chapters; i++) {if (s->chapters[i]->id == id) {av_dict_set(&s->chapters[i]->metadata, "title", cue_label, 0);break;}}}}break;}break;case MKTAG('I', 'D', '3', ' '):case MKTAG('i', 'd', '3', ' '): {ID3v2ExtraMeta *id3v2_extra_meta;ff_id3v2_read_dict(pb, &ffformatcontext(s)->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);if (id3v2_extra_meta) {ff_id3v2_parse_apic(s, id3v2_extra_meta);ff_id3v2_parse_chapters(s, id3v2_extra_meta);ff_id3v2_parse_priv(s, id3v2_extra_meta);}ff_id3v2_free_extra_meta(&id3v2_extra_meta);}break;case MKTAG('c', 'u', 'e', ' '):if (size >= 4 && got_fmt && st->codecpar->sample_rate > 0) {AVRational tb = {1, st->codecpar->sample_rate};unsigned nb_cues = avio_rl32(pb);if (size >= nb_cues * 24LL + 4LL) {for (int i = 0; i < nb_cues; i++) {unsigned offset, id = avio_rl32(pb);if (avio_feof(pb))return AVERROR_INVALIDDATA;avio_skip(pb, 16);offset = avio_rl32(pb);if (!avpriv_new_chapter(s, id, tb, offset, AV_NOPTS_VALUE, NULL))return AVERROR(ENOMEM);}}}break;}/* seek to next tag unless we know that we'll run into EOF */if ((avio_size(pb) > 0 && next_tag_ofs >= avio_size(pb)) ||wav_seek_tag(wav, pb, next_tag_ofs, SEEK_SET) < 0) {break;}}break_loop:if (!got_fmt && !got_xma2) {av_log(s, AV_LOG_ERROR, "no 'fmt ' or 'XMA2' tag found\n");return AVERROR_INVALIDDATA;}if (data_ofs < 0) {av_log(s, AV_LOG_ERROR, "no 'data' tag found\n");return AVERROR_INVALIDDATA;}avio_seek(pb, data_ofs, SEEK_SET);if (data_size > (INT64_MAX>>3)) {av_log(s, AV_LOG_WARNING, "Data size %"PRId64" is too large\n", data_size);data_size = 0;}if ( st->codecpar->bit_rate > 0 && data_size > 0&& st->codecpar->sample_rate > 0&& sample_count > 0 && st->codecpar->channels > 1&& sample_count % st->codecpar->channels == 0) {if (fabs(8.0 * data_size * st->codecpar->channels * st->codecpar->sample_rate /sample_count /st->codecpar->bit_rate - 1.0) < 0.3)sample_count /= st->codecpar->channels;}if ( data_size > 0 && sample_count && st->codecpar->channels&& (data_size << 3) / sample_count / st->codecpar->channels > st->codecpar->bits_per_coded_sample + 1) {av_log(s, AV_LOG_WARNING, "ignoring wrong sample_count %"PRId64"\n", sample_count);sample_count = 0;}/* G.729 hack (for Ticket4577)* FIXME: Come up with cleaner, more general solution */if (st->codecpar->codec_id == AV_CODEC_ID_G729 && sample_count && (data_size << 3) > sample_count) {av_log(s, AV_LOG_WARNING, "ignoring wrong sample_count %"PRId64"\n", sample_count);sample_count = 0;}if (!sample_count || av_get_exact_bits_per_sample(st->codecpar->codec_id) > 0)if ( st->codecpar->channels&& data_size&& av_get_bits_per_sample(st->codecpar->codec_id)&& wav->data_end <= avio_size(pb))sample_count = (data_size << 3)/(st->codecpar->channels * (uint64_t)av_get_bits_per_sample(st->codecpar->codec_id));if (sample_count)st->duration = sample_count;if (st->codecpar->codec_id == AV_CODEC_ID_PCM_S32LE &&st->codecpar->block_align == st->codecpar->channels * 4 &&st->codecpar->bits_per_coded_sample == 32 &&st->codecpar->extradata_size == 2 &&AV_RL16(st->codecpar->extradata) == 1) {st->codecpar->codec_id = AV_CODEC_ID_PCM_F16LE;st->codecpar->bits_per_coded_sample = 16;} else if (st->codecpar->codec_id == AV_CODEC_ID_PCM_S24LE &&st->codecpar->block_align == st->codecpar->channels * 4 &&st->codecpar->bits_per_coded_sample == 24) {st->codecpar->codec_id = AV_CODEC_ID_PCM_F24LE;} else if (st->codecpar->codec_id == AV_CODEC_ID_XMA1 ||st->codecpar->codec_id == AV_CODEC_ID_XMA2) {st->codecpar->block_align = 2048;} else if (st->codecpar->codec_id == AV_CODEC_ID_ADPCM_MS && st->codecpar->channels > 2 &&st->codecpar->block_align < INT_MAX / st->codecpar->channels) {st->codecpar->block_align *= st->codecpar->channels;}ff_metadata_conv_ctx(s, NULL, wav_metadata_conv);ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv);set_spdif(s, wav);return 0;
}
形参s:既是输入型参数也是输出型参数,指向AVFormatContext类型的变量。s->pb包含整个WAV Header的二进制数据。执行wav_read_header函数后,(WAVDemuxContext *)(s->priv_data)->vst->codecpar中的成员变量会被赋值为WAV Header中Format chunk中的信息(包括音频压缩编码格式、采样频率、声道数量、采样位数、码率)。
返回值:返回0表示解码WAV Header成功。返回一个负数表示解码WAV Header失败。
三、wav_read_header函数的内部实现分析
(一)读取WAV Header中的“区块编号”
WAV Header中的第一个区块为“区块编号”,通过下面语句读取“区块编号”的内容(关于avio_rXXX系列函数的用法可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》):
/* read chunk ID */tag = avio_rl32(pb);
有的同学看到这里可能会有疑问:根据文章《音视频入门基础:WAV专题(2)——WAV格式简介》的描述,“区块编号”不是大端字节序的吗?而avio_rl32函数是按照小端模式读取四个字节数据。所以为什么是用avio_rl32函数而不是avio_rb32函数读取呢?avio_rb32函数才是按照大端模式读取四个字节数据的吧?

原因是这样的,在语句tag = avio_rl32(pb)之后,又通过MKTAG函数(关于MKTAG和MKBETAG宏定义的用法可以参考:《FFmpeg源码:MKTAG和MKBETAG宏定义分析》)将字符(比如'R', 'I', 'F', 'F')转换为整形,按小端模式存贮,让其跟tag进行比较。所以这就是所谓的“负负得正”,“用avio_rl32函数读取然后跟MKTAG转换出来的整数进行比较”,这个跟“用avio_rb32函数读取然后用MKBETAG转换出来的整数进行比较”,效果是一样的:
switch (tag) {case MKTAG('R', 'I', 'F', 'F'):break;case MKTAG('R', 'I', 'F', 'X'):wav->rifx = 1;break;case MKTAG('R', 'F', '6', '4'):rf64 = 1;break;case MKTAG('B', 'W', '6', '4'):bw64 = 1;break;default:av_log(s, AV_LOG_ERROR, "invalid start code %s in RIFF header\n",av_fourcc2str(tag));return AVERROR_INVALIDDATA;}
所以下面代码块:
/* read chunk ID */tag = avio_rl32(pb);switch (tag) {case MKTAG('R', 'I', 'F', 'F'):break;case MKTAG('R', 'I', 'F', 'X'):wav->rifx = 1;break;case MKTAG('R', 'F', '6', '4'):rf64 = 1;break;case MKTAG('B', 'W', '6', '4'):bw64 = 1;break;default:av_log(s, AV_LOG_ERROR, "invalid start code %s in RIFF header\n",av_fourcc2str(tag));return AVERROR_INVALIDDATA;}
等价于:
/* read chunk ID */tag = avio_rb32(pb);switch (tag) {case MKBETAG('R', 'I', 'F', 'F'):break;case MKBETAG('R', 'I', 'F', 'X'):wav->rifx = 1;break;case MKBETAG('R', 'F', '6', '4'):rf64 = 1;break;case MKBETAG('B', 'W', '6', '4'):bw64 = 1;break;default:av_log(s, AV_LOG_ERROR, "invalid start code %s in RIFF header\n",av_fourcc2str(tag));return AVERROR_INVALIDDATA;}
读取到“区块编号”后,如果值等于“RIFF”,表示该文件遵守RIFF格式的规则,按默认处理;如果值等于“RIFX”,让wav->rifx赋值为1,表示该文件遵守RIFX格式的规则;如果值等于“RF64”,让变量rf64赋值为1,表示这是WAVE 64位扩展格式中的一种:WAV RF64;如果值等于“BW64”,让变量bw64赋值为1,表示这是WAVE 64位扩展格式中的一种:WAV BW64;如果值不为上述,打印日志:"invalid start code XXX in RIFF header"并返回AVERROR_INVALIDDATA表示WAV Header中的数据不合法。
(二)读取WAV Header中的“总区块大小”
WAV Header中的第二个区块为“总区块大小”,通过下面语句读取“总区块大小”。由于该值可以由WAV Header中的其它值推导出来,所以对于FFmpeg来讲它没有意义,故FFmpeg没有将其存贮到内部的成员变量中:
/* read chunk size */avio_rl32(pb);
(三)读取WAV Header中的“档案格式”
WAV Header中的第三个区块为“档案格式”。通过下面语句读取该值并进行比较,如果值不为“WAVE”,日志打印"invalid format in RIFF header"表示WAV Header中的数据不合法:
/* read format */if (avio_rl32(pb) != MKTAG('W', 'A', 'V', 'E')) {av_log(s, AV_LOG_ERROR, "invalid format in RIFF header\n");return AVERROR_INVALIDDATA;}
(四)WAVE 64位扩展格式处理
FFmpeg源码内部将WAV和WAVE 64位扩展格式都放到一起处理,下面代码处理文件格式为WAVE 64位时的情况:
if (rf64 || bw64) {if (avio_rl32(pb) != MKTAG('d', 's', '6', '4'))return AVERROR_INVALIDDATA;size = avio_rl32(pb);if (size < 24)return AVERROR_INVALIDDATA;avio_rl64(pb); /* RIFF size */data_size = avio_rl64(pb);sample_count = avio_rl64(pb);if (data_size < 0 || sample_count < 0) {av_log(s, AV_LOG_ERROR, "negative data_size and/or sample_count in ""ds64: data_size = %"PRId64", sample_count = %"PRId64"\n",data_size, sample_count);return AVERROR_INVALIDDATA;}avio_skip(pb, size - 24); /* skip rest of ds64 chunk */}
(五)读取WAV Header中的子区块
WAV Header中包含“Format chunk”、“Data chunk”这种必须存在的子区块,也包含Fact chunk、Cue points chunk、Playlist chunk、Associated data list chunk等可选区块。通过下面语句中的for循环和switch case语句来判断是哪种子区块,然后循环处理:
for (;;) {AVStream *vst;size = next_tag(pb, &tag, wav->rifx);next_tag_ofs = avio_tell(pb) + size;if (avio_feof(pb))break;switch (tag) {case MKTAG('f', 'm', 't', ' ')://...break;case MKTAG('X', 'M', 'A', '2')://...break;case MKTAG('d', 'a', 't', 'a')://...break;case MKTAG('f', 'a', 'c', 't')://...break;case MKTAG('b', 'e', 'x', 't')://...break;case MKTAG('S','M','V','0')://...goto break_loop;case MKTAG('L', 'I', 'S', 'T'):case MKTAG('l', 'i', 's', 't')://...break;case MKTAG('I', 'D', '3', ' '):case MKTAG('i', 'd', '3', ' '): {//...break;case MKTAG('c', 'u', 'e', ' ')://...break;}}
}
(六)读取WAV Header中“Format chunk”子区块的信息
下面以读取“Format chunk”子区块中的信息为例子进行讲解如何解码子区块。ff_get_wav_header函数中通过wav_parse_fmt_tag函数解析“Format chunk”子区块:
int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb,AVCodecParameters *par, int size, int big_endian)
{
//...for (;;) {switch (tag) {case MKTAG('f', 'm', 't', ' '):/* only parse the first 'fmt ' tag found */if (!got_xma2 && !got_fmt && (ret = wav_parse_fmt_tag(s, size, st)) < 0) {return ret;} else if (got_fmt)av_log(s, AV_LOG_WARNING, "found more than one 'fmt ' tag\n");got_fmt = 1;break;}
//...}
}
而wav_parse_fmt_tag函数内部又调用了ff_get_wav_header函数来获取获取音频压缩编码格式、采样频率、声道数量、采样位数、码率信息(具体可以参考:《音视频入门基础:WAV专题(4)——FFmpeg源码中获取WAV文件音频压缩编码格式、采样频率、声道数量、采样位数、码率的实现》):
static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream *st)
{AVIOContext *pb = s->pb;WAVDemuxContext *wav = s->priv_data;int ret;/* parse fmt header */ret = ff_get_wav_header(s, pb, st->codecpar, size, wav->rifx);if (ret < 0)return ret;handle_stream_probing(st);ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW;avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);return 0;
}
相关文章:
音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现
音视频入门基础:WAV专题系列文章: 音视频入门基础:WAV专题(1)——使用FFmpeg命令生成WAV音频文件 音视频入门基础:WAV专题(2)——WAV格式简介 音视频入门基础:WAV专题…...
爬虫:csv存储:写入和读取
目录 csv写入 csv读取 csv写入 import csv# data [ # (tf, 20, 180), # (dl, 20, 170), # (hc, 18, 190) # ] # header (姓名,年龄,身高) # # # csv写入数据会默认写一行隔一行 newline就是让它不要有空行 # with open(text.csv,w,encodingutf8,newline) as f:…...
Opencv-绘制几何图形
1. 绘制圆形 1.1 circle()函数原型 void cv::circle(InputOutputArray img, Point center, int radius, const Scalar & color, int thickness 1, int lineType LINE_8, int shift 0 ) img:需要绘制圆形的图像。 center:圆形的圆心位置坐标。 …...
ElasticSearch安装与集群部署
ElasticSearch安装与集群部署 很多小伙伴第一次接触ElasticSearch的时候是一脸愁容,这个东西他怎么用啊,不知道从哪里安装,那我们今天就着重从哪里下载?怎么下载?怎么安装?来研究一下吧! windows下载安装ElasticSearch 下载地址:https://www.elastic.co/cn/do…...
盘点12款企业常用源代码加密软件,源代码防泄密很重要!
在当今的商业环境中,源代码作为企业的核心资产之一,其安全性不容忽视。源代码的泄露可能导致企业丧失竞争优势、面临法律诉讼甚至经济损失。因此,选择合适的源代码加密软件成为企业保护知识产权和核心技术的关键步骤。 1. 安秉源代码加密软件…...
文件上传和下载
要想实现文件上传和下载,其实只需要下述代码即可: 文件上传和下载 import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import com.example.common.Result; import org.springframework.web.bind.annotation.*; import org.sprin…...
机械学习—零基础学习日志(高数22——泰勒公式理解深化)
核心思想:函数逼近 在泰勒的年代,如果想算出e的0.001次方,这是很难计算的。那为了能计算这样的数字,可以尝试逼近的思想。 但是函数又不能所有地方都相等,那退而求其次,只要在一个极小的范围,…...
Java | Leetcode Java题解之第318题最大单词长度乘积
题目: 题解: class Solution {public int maxProduct(String[] words) {Map<Integer, Integer> map new HashMap<Integer, Integer>();int length words.length;for (int i 0; i < length; i) {int mask 0;String word words[i];in…...
科普文:JUC系列之多线程门闩同步器Condition的使用和源码解读
一、概述 条件锁就是指在获取锁之后发现当前业务场景自己无法处理,而需要等待某个条件的出现才可以继续处理时使用的一种锁。 比如,在阻塞队列中,当队列中没有元素的时候是无法弹出一个元素的,这时候就需要阻塞在条件notEmpty上…...
Stable Diffusion绘画 | 图生图-基础使用介绍—提示词反推
按默认设置直接出图 拖入图片值图生图框中,保持默认设置,直接生成图片,出图效果如下: 因为重绘幅度0.7,所出图片与原图有差异,但整体的框架构图与颜色与原图类似。 输入关键词后出图 在正向提示词中输入…...
正点原子imx6ull-mini-Linux驱动之Linux SPI 驱动实验(22)
跟上一章一样,其实这些设备驱动,无非就是传感器对应寄存器的读写。而这个读写是建立在各种通信协议上的,比如上一章的i2c,我们做了什么呢,就是把设备注册成一个i2c平台驱动,这个i2c驱动怎么搞的呢ÿ…...
TypeScript 函数
函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义 行为 的地方。 TypeScript为JavaScript函数添加了额外的功能&…...
C++ : namespace,输入与输出,函数重载,缺省参数
一,命名空间(namespace) 1.1命名空间的作用与定义 我们在学习c的过程中,经常会碰到命名冲突的情况。就拿我们在c语言中的一个string函数来说吧: int strncat 0; int main() {printf("%d", strncat);return 0; } 当我们运行之后&…...
目标检测 | yolov1 原理和介绍
1. 简介 论文链接:https://arxiv.org/abs/1506.02640 时间:2015年 作者:Joseph Redmon 代码参考:https://github.com/abeardear/pytorch-YOLO-v1 yolo属于one-stage算法,仅仅使用一个CNN网络直接预测不同目标的类别与…...
excel中有些以文本格式存储的数值如何批量转换为数字
一、背景 1.1 文本格式存储的数值特点 在平时工作中有时候会从别地方导出来表格,表格中有些数值是以文本格式存储的(特点:单元格的左上角有个绿色的小标)。 1.2 文本格式存储的数值在排序时不符合预期 当我们需要进行排序的时候…...
原神升级计划数据表:4个倒计时可以修改提示信息和时间,可以点击等级、命座、天赋、备注进行修改。
<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>原神倒计时</title><style>* {margin: 0;padding: 0;box-sizing: border-box;body {background: #0b1b2c;}}header {width: 100vw;heigh…...
YoloV10 论文翻译(Real-Time End-to-End Object Detection)
摘要 近年来,YOLO因其在计算成本与检测性能之间实现了有效平衡,已成为实时目标检测领域的主流范式。研究人员对YOLO的架构设计、优化目标、数据增强策略等方面进行了探索,并取得了显著进展。然而,YOLO对非极大值抑制࿰…...
第R1周:RNN-心脏病预测
本文为🔗365天深度学习训练营 中的学习记录博客 原作者:K同学啊 要求: 1.本地读取并加载数据。 2.了解循环神经网络(RNN)的构建过程 3.测试集accuracy到达87% 拔高: 1.测试集accuracy到达89% 我的环境&a…...
Golang | Leetcode Golang题解之第321题拼接最大数
题目: 题解: func maxSubsequence(a []int, k int) (s []int) {for i, v : range a {for len(s) > 0 && len(s)len(a)-1-i > k && v > s[len(s)-1] {s s[:len(s)-1]}if len(s) < k {s append(s, v)}}return }func lexico…...
远程连接本地虚拟机失败问题汇总
前言 因为我的 Ubuntu 虚拟机是新装的,并且应该装的是比较纯净的版本(纯净是指很多工具都尚未安装),然后在使用远程连接工具 XShell 连接时出现了很多问题,这些都是我之前没遇到过的(因为之前主要使用云服…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
pycharm 设置环境出错
pycharm 设置环境出错 pycharm 新建项目,设置虚拟环境,出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...
