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

音视频入门基础: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的实现

音视频入门基础&#xff1a;WAV专题系列文章&#xff1a; 音视频入门基础&#xff1a;WAV专题&#xff08;1&#xff09;——使用FFmpeg命令生成WAV音频文件 音视频入门基础&#xff1a;WAV专题&#xff08;2&#xff09;——WAV格式简介 音视频入门基础&#xff1a;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&#xff1a;需要绘制圆形的图像。 center&#xff1a;圆形的圆心位置坐标。 …...

ElasticSearch安装与集群部署

ElasticSearch安装与集群部署 很多小伙伴第一次接触ElasticSearch的时候是一脸愁容,这个东西他怎么用啊,不知道从哪里安装,那我们今天就着重从哪里下载?怎么下载?怎么安装?来研究一下吧! windows下载安装ElasticSearch 下载地址&#xff1a;https://www.elastic.co/cn/do…...

盘点12款企业常用源代码加密软件,源代码防泄密很重要!

在当今的商业环境中&#xff0c;源代码作为企业的核心资产之一&#xff0c;其安全性不容忽视。源代码的泄露可能导致企业丧失竞争优势、面临法律诉讼甚至经济损失。因此&#xff0c;选择合适的源代码加密软件成为企业保护知识产权和核心技术的关键步骤。 1. 安秉源代码加密软件…...

文件上传和下载

要想实现文件上传和下载&#xff0c;其实只需要下述代码即可&#xff1a; 文件上传和下载 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——泰勒公式理解深化)

核心思想&#xff1a;函数逼近 在泰勒的年代&#xff0c;如果想算出e的0.001次方&#xff0c;这是很难计算的。那为了能计算这样的数字&#xff0c;可以尝试逼近的思想。 但是函数又不能所有地方都相等&#xff0c;那退而求其次&#xff0c;只要在一个极小的范围&#xff0c;…...

Java | Leetcode Java题解之第318题最大单词长度乘积

题目&#xff1a; 题解&#xff1a; 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的使用和源码解读

一、概述 条件锁就是指在获取锁之后发现当前业务场景自己无法处理&#xff0c;而需要等待某个条件的出现才可以继续处理时使用的一种锁。 比如&#xff0c;在阻塞队列中&#xff0c;当队列中没有元素的时候是无法弹出一个元素的&#xff0c;这时候就需要阻塞在条件notEmpty上…...

Stable Diffusion绘画 | 图生图-基础使用介绍—提示词反推

按默认设置直接出图 拖入图片值图生图框中&#xff0c;保持默认设置&#xff0c;直接生成图片&#xff0c;出图效果如下&#xff1a; 因为重绘幅度0.7&#xff0c;所出图片与原图有差异&#xff0c;但整体的框架构图与颜色与原图类似。 输入关键词后出图 在正向提示词中输入…...

正点原子imx6ull-mini-Linux驱动之Linux SPI 驱动实验(22)

跟上一章一样&#xff0c;其实这些设备驱动&#xff0c;无非就是传感器对应寄存器的读写。而这个读写是建立在各种通信协议上的&#xff0c;比如上一章的i2c&#xff0c;我们做了什么呢&#xff0c;就是把设备注册成一个i2c平台驱动&#xff0c;这个i2c驱动怎么搞的呢&#xff…...

TypeScript 函数

函数是JavaScript应用程序的基础。 它帮助你实现抽象层&#xff0c;模拟类&#xff0c;信息隐藏和模块。 在TypeScript里&#xff0c;虽然已经支持类&#xff0c;命名空间和模块&#xff0c;但函数仍然是主要的定义 行为 的地方。 TypeScript为JavaScript函数添加了额外的功能&…...

C++ : namespace,输入与输出,函数重载,缺省参数

一&#xff0c;命名空间(namespace) 1.1命名空间的作用与定义 我们在学习c的过程中&#xff0c;经常会碰到命名冲突的情况。就拿我们在c语言中的一个string函数来说吧&#xff1a; int strncat 0; int main() {printf("%d", strncat);return 0; } 当我们运行之后&…...

目标检测 | yolov1 原理和介绍

1. 简介 论文链接&#xff1a;https://arxiv.org/abs/1506.02640 时间&#xff1a;2015年 作者&#xff1a;Joseph Redmon 代码参考&#xff1a;https://github.com/abeardear/pytorch-YOLO-v1 yolo属于one-stage算法&#xff0c;仅仅使用一个CNN网络直接预测不同目标的类别与…...

excel中有些以文本格式存储的数值如何批量转换为数字

一、背景 1.1 文本格式存储的数值特点 在平时工作中有时候会从别地方导出来表格&#xff0c;表格中有些数值是以文本格式存储的&#xff08;特点&#xff1a;单元格的左上角有个绿色的小标&#xff09;。 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)

​摘要 近年来&#xff0c;YOLO因其在计算成本与检测性能之间实现了有效平衡&#xff0c;已成为实时目标检测领域的主流范式。研究人员对YOLO的架构设计、优化目标、数据增强策略等方面进行了探索&#xff0c;并取得了显著进展。然而&#xff0c;YOLO对非极大值抑制&#xff0…...

第R1周:RNN-心脏病预测

本文为&#x1f517;365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊 要求&#xff1a; 1.本地读取并加载数据。 2.了解循环神经网络&#xff08;RNN&#xff09;的构建过程 3.测试集accuracy到达87% 拔高&#xff1a; 1.测试集accuracy到达89% 我的环境&a…...

Golang | Leetcode Golang题解之第321题拼接最大数

题目&#xff1a; 题解&#xff1a; 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 虚拟机是新装的&#xff0c;并且应该装的是比较纯净的版本&#xff08;纯净是指很多工具都尚未安装&#xff09;&#xff0c;然后在使用远程连接工具 XShell 连接时出现了很多问题&#xff0c;这些都是我之前没遇到过的&#xff08;因为之前主要使用云服…...

WebRTC 初探

前言 项目中有局域网投屏与文件传输的需求&#xff0c;所以研究了一下 webRTC&#xff0c;这里记录一下学习过程。 WebRTC 基本流程以及概念 下面以 1 对 1 音视频实时通话案例介绍 WebRTC 的基本流程以及概念 WebRTC 中的角色 WebRTC 终端,负责音视频采集、编解码、NAT 穿…...

Python:read,readline和readlines的区别

在Python中&#xff0c;read(), readline(), 和 readlines() 是文件操作中常用的三个方法&#xff0c;它们都用于从文件中读取数据&#xff0c;但各自的使用方式和适用场景有所不同。 read() 方法&#xff1a; read(size-1) 方法用于从文件中读取指定数量的字符。如果指定了si…...

重生之我学编程

编程小白如何成为大神&#xff1f;大学新生的最佳入门攻略 编程已成为当代大学生的必备技能&#xff0c;但面对众多编程语言和学习资源&#xff0c;新生们常常感到迷茫。如何选择适合自己的编程语言&#xff1f;如何制定有效的学习计划&#xff1f;如何避免常见的学习陷阱&…...

如何将PostgreSQL的数据实时迁移到SelectDB?

PostgreSQL 作为一个开源且功能强大的关系型数据库管理系统&#xff0c;在 OLTP 系统中得到了广泛应用。很多企业利用其卓越的性能和灵活的架构&#xff0c;应对高并发事务、快速响应等需求。 然而对于 OLAP 场景&#xff0c;PostgreSQL 可能并不是最佳选择。 为了实现庞大规…...

关于c语言的const 指针

const * type A 指向的数据是常量 如上所示&#xff0c;运行结果如下&#xff0c;通过解引用的方式&#xff0c;改变了data的值 const type * A 位置是常量&#xff0c;不能修改 运行结果如下 type const * A 指针是个常量&#xff0c;指向的值可以改变 如上所示&#xff0c…...

万能门店小程序开发平台功能源码系统 带完整的安装代码包以及安装搭建教程

互联网技术的迅猛发展和用户对于便捷性需求的不断提高&#xff0c;小程序以其轻量、快捷、无需安装的特点&#xff0c;成为了众多商家和开发者关注的焦点。为满足广大商家对于门店线上化、智能化管理的需求&#xff0c;小编给大家分享一款“万能门店小程序开发平台功能源码系统…...

C#初级——字典Dictionary

字典 字典是C#中的一种集合&#xff0c;它存储键值对&#xff0c;并且每个键与一个值相关联。 创建字典 Dictionary<键的类型, 值的类型> 字典名字 new Dictionary<键的类型, 值的类型>(); Dictionary<int, string> dicStudent new Dictionary<int, str…...

git版本控制的底层实现

目录 前言 核心概念串讲 底层存储形式探测 本地仓库的详细解析 提交与分支的深入解析 几个问题的深入探讨 前言 Git的重要性 Git是一个开源的版本控制工具&#xff0c;广泛用于编程开发领域。它极大地提高了研发团队的开发协作效率。对于开发者来说&#xff0c;Git是一个…...

深入解析数据处理的技术与实践

欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: 工💗重💗hao💗:野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、应用领域等内容。 ⭐…...

python-调用c#代码

环境&#xff1a; win10&#xff0c;net framework 4&#xff0c;python3.9 镜像&#xff1a; C#-使用IronPython调用python代码_ironpython wpf-CSDN博客 https://blog.csdn.net/pxy7896/article/details/119929434 目录 hello word不接收参数接收参数 其他例子 hello word 不…...