ffmpeg音视频开发从入门到精通——ffmpeg实现音频抽取

news/2024/9/19 4:57:02/ 标签: ffmpeg, 音视频

文章目录

    • FFmpeg 实现音频流抽取
      • 1. 包含FFmpeg头文件与命名空间声明
      • 2. 主函数与参数处理
      • 3. 打开输入文件
      • 4. 获取文件信息
      • 5. 查找音频流
      • 6. 分配输出文件上下文
      • 7. 猜测输出文件格式
      • 8. 创建新的音频流
      • 9. 打开输出文件
      • 10. 写入文件头信息
      • 11. 读取并写入音频数据
      • 12. 写入文件尾部信息并释放资源
    • 运行程序
    • 注意事项
    • 抽取音频完整代码

FFmpeg 实现音频流抽取

1. 包含FFmpeg头文件与命名空间声明

使用FFmpeg库前需要包含相应的头文件,并在C++中声明外部C函数的命名空间。

#ifdef __cplusplus
extern "C" {
#endif
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#ifdef __cplusplus
}
#endif

2. 主函数与参数处理

程序入口点,处理命令行参数。

int main(int argc, char *argv[]) {// 参数检查if (argc < 3) {av_log(nullptr, AV_LOG_INFO, "参数必须多于3个\n");exit(-1);}// 输入输出文件路径char *src = argv[1];char *dst = argv[2];// ...
}

3. 打开输入文件

使用avformat_open_input打开输入文件。
avformat_open_input 是 FFmpeg 库中的一个函数,用于打开输入媒体文件并读取其格式信息。这个函数是 FFmpeg 中处理多媒体文件的基础之一,通常准备解码和处理音视频流。

  1. 函数原型
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
  1. 输入参数

    1. AVFormatContext **ps:

      • 这是一个指向 AVFormatContext 指针的指针。AVFormatContext 是一个结构体,包含了关于输入媒体文件的所有信息,包括流的信息、格式、时长等。
      • 在调用 avformat_open_input 之前,通常需要先分配一个 AVFormatContext 结构体的内存(可以使用 avformat_alloc_context() 函数),然后将其地址传递给该参数。
      • 如果函数成功,*ps 将指向一个已填充的 AVFormatContext 结构体。
    2. const char *url:

      • 这是一个指向字符串的指针,表示要打开的媒体文件的路径或 URL。可以是本地文件路径,也可以是网络流的 URL(如 HTTP、RTSP 等)。
    3. AVInputFormat *fmt:

      • 这是一个指向 AVInputFormat 结构体的指针,表示希望使用的输入格式。如果为 NULL,FFmpeg 将自动检测输入文件的格式。
      • 你可以通过 av_find_input_format 函数来查找特定的输入格式。
    4. AVDictionary *options:

      • 这是一个指向字典的指针,用于传递额外的选项给输入格式。字典中的每个键值对都可以用来设置特定的解码选项,例如缓冲区大小、超时设置等。
      • 如果没有额外的选项,可以将此参数设置为 NULL
  2. 返回值

  • 函数返回一个整数值:
    • 如果成功,返回 0
    • 如果失败,返回一个负数,表示错误代码。可以使用 av_strerror 函数将错误代码转换为可读的错误信息。
  1. 示例代码

以下是一个简单的示例,展示如何使用 avformat_open_input 打开一个媒体文件:

#include <libavformat/avformat.h>int main() {AVFormatContext *formatContext = NULL;const char *filename = "input.mp4"; // 输入文件名int ret;// 注册所有的文件格式和编解码器av_register_all();// 打开输入文件ret = avformat_open_input(&formatContext, filename, NULL, NULL);if (ret < 0) {char errbuf[128];av_strerror(ret, errbuf, sizeof(errbuf));fprintf(stderr, "Could not open source file %s: %s\n", filename, errbuf);return ret;}// 打印文件信息av_dump_format(formatContext, 0, filename, 0);// 关闭输入文件avformat_close_input(&formatContext);return 0;
}

avformat_open_input 是 FFmpeg 中用于打开和读取媒体的关键函数。通过正确设置输入参数,可以方便地打开各种格式的音视频文件,并获取其相关信息。

ret = avformat_open_input(&pFmtCtx, src, nullptr, nullptr);
if (ret < 0) {av_log(nullptr, AV_LOG_ERROR, "打开输入文件失败\n");exit(-1);
}

4. 获取文件信息

调用avformat_find_stream_info获取多媒体文件的流信息。
avformat_find_stream_info 是 FFmpeg 库中的一个函数,用于读取媒体文件的流信息(如音频流、视频流、字幕流等)。这个函数通常在成功打开输入文件后调用,以获取有关各个流的详细信息。

  1. 函数原型
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
  1. 输入参数

    1. AVFormatContext *ic:

      • 这是一个指向 AVFormatContext 结构体的指针,该结构体在调用 avformat_open_input 时被填充。它包含了关于打开的媒体文件的所有信息,包括流的数量、每个流的类型、编解码器信息等。
      • 在调用 avformat_find_stream_info 之前,必须确保已经成功打开了媒体文件并且 AVFormatContext 已经被正确初始化。
    2. AVDictionary **options:

      • 这是一个指向字典的指针,用于传递额外的选项给流信息查找过程。字典中的每个键值对可以用来设置特定的选项,例如解码器的参数、缓冲区大小等。
      • 如果没有额外的选项,可以将此参数设置为 NULL
  2. 返回值

  • 函数返回一个整数值:
    • 如果成功,返回 0
    • 如果失败,返回一个负数,表示错误代码。可以使用 av_strerror 函数将错误代码转换为可读的错误信息。

avformat_find_stream_info 函数的主要作用是填充 AVFormatContext 中的流信息,以便后续处理和解码。通过正确设置输入参数,可以有效地获取媒体文件中各个流的详细信息。

if ((ret = avformat_find_stream_info(pFmtCtx, nullptr)) < 0) {av_log(nullptr, AV_LOG_INFO, "获取文件信息失败\n");exit(-1);
}

5. 查找音频流

遍历所有流,找到音频流的索引。

for (int i = 0; i < pFmtCtx->nb_streams; ++i) {if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {idx = i;break;}
}

6. 分配输出文件上下文

使用avformat_alloc_context分配输出文件的格式上下文。

  1. 输入参数
  • 无输入参数
    • 该函数不需要任何输入参数。
  1. 返回值
  • **返回值:
    • 返回一个指向 AVFormatContext 结构体的指针。
    • 如果内存分配失败,返回 NULL
  1. 总结
  • 功能:分配并初始化一个新的 AVFormatContext 结构体,用于存储媒体文件的格式信息。
  • 用途:在打开媒体文件之前,通常需要调用此函数以准备好格式上下文。
oFmtCtx = avformat_alloc_context();
if (!oFmtCtx) {av_log(nullptr, AV_LOG_ERROR, "分配输出文件上下文失败\n");goto _ERROR;
}

7. 猜测输出文件格式

使用av_guess_format猜测输出文件的格式。
av_guess_format 是 FFmpeg 库中的一个函数,用于根据给定的文件扩展名或 MIME 类型来猜测媒体文件的格式。这个函数在处理多媒体文件时非常有用,尤其是在需要确定输入或输出格式时。

AVInputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type);
  1. 功能
  • 根据文件扩展名:通过提供的文件名的扩展名来猜测媒体格式。
  • 根据 MIME 类型:如果提供了 MIME 类型,可以根据 MIME 类型来猜测格式。
  • 返回相应的格式:返回一个指向 AVInputFormat 结构体的指针,表示猜测的输入格式。如果无法猜测,则返回 NULL
  1. 参数
  • *const char short_name:短名称(如 “mp4”、“avi” 等),用于直接匹配格式。
  • *const char filename:文件名,通常包含扩展名,用于推断格式。
  • *const char mime_type:MIME 类型字符串(如 “video/mp4”),用于进一步确认格式。
  1. 用途
  • 在打开媒体文件之前,使用 av_guess_format 可以帮助确定合适的输入格式,从而为后续的解码和处理做好准备。
outFmt = av_guess_format(nullptr, dst, nullptr);
oFmtCtx->oformat = outFmt;

8. 创建新的音频流

为输出文件创建一个新的音频流,并复制输入音频流的参数。
avformat_new_stream 函数:

  1. 输入参数

    1. *AVFormatContext s:

      • 指向 AVFormatContext 结构体的指针,表示要在其中添加新流的格式上下文。
    2. *AVCodec c:

      • 指向 AVCodec 结构体的指针,表示新流所使用的编解码器。如果为 NULL,则会使用默认编解码器。
    3. 返回值:

      • 返回一个指向新创建的 AVStream 结构体的指针。如果创建失败,则返回 NULL
  2. 总结

  • avformat_new_stream 用于在给定的格式上下文中创建一个新的流,并可以指定其使用的编解码器。
outStream = avformat_new_stream(oFmtCtx, nullptr);
avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
outStream->codecpar->codec_tag = 0;

9. 打开输出文件

使用avio_open2打开输出文件准备写入。
avio_open2 是 FFmpeg 库中的一个函数,用于打开一个 I/O 设备(如文件、网络流等)以进行读写操作。这个函数提供了更灵活的选项来配置打开的方式和行为。

int avio_open2(AVIOContext **s, const char *url, int flags, AVDictionary **options);
  1. 功能
  • 打开 I/O 设备:根据提供的 URL(可以是文件路径或网络地址)打开一个 I/O 设备。
  • 配置选项:允许通过字典传递额外的选项,以定制打开设备的行为。
  1. 输入参数

    1. **AVIOContext s:

      • 指向 AVIOContext 指针的指针,用于返回打开的 I/O 上下文。如果成功,*s 将指向一个已初始化的 AVIOContext 结构体。
    2. *const char url:

      • 指向字符串的指针,表示要打开的设备的 URL(例如文件路径或网络地址)。
    3. int flags:

      • 整数标志,用于指定打开设备的模式。常见的标志包括:
        • AVIO_FLAG_READ:以只读模式打开。
        • AVIO_FLAG_WRITE:以写入模式打开。
        • AVIO_FLAG_READ_WRITE:以读写模式打开。
    4. **AVDictionary options:

      • 指向字典的指针,用于传递额外的选项给打开过程。可以用来设置特定的参数,如超时、缓冲区大小等。如果没有额外的选项,可以将此参数设置为 NULL
  2. 返回值

  • 函数返回一个整数值:
    • 如果成功,返回 0
    • 如果失败,返回一个负数,表示错误代码。
  1. 总结
    avio_open2 是用于打开 I/O 设备的关键函数,支持多种打开模式和配置选项,适用于文件和网络流的读写操作。
ret = avio_open2(&oFmtCtx->pb, dst, AVIO_FLAG_WRITE, nullptr, nullptr);
if (ret < 0) {av_log(nullptr, AV_LOG_ERROR, "打开输出文件失败\n");goto _ERROR;
}

10. 写入文件头信息

调用avformat_write_header写入文件头信息。

写入文件头:在输出文件中写入必要的头部信息,以便后续的数据流可以正确地被解析和播放。
初始化输出格式:根据 AVFormatContext 中的信息,设置输出格式并准备写入数据。
输入参数
*AVFormatContext s:

指向 AVFormatContext 结构体的指针,表示要写入的输出格式上下文。该结构体应在调用此函数之前被正确初始化,并且流信息应已设置。
**AVDictionary options:

指向字典的指针,用于传递额外的选项给写入头部的过程。可以用来设置特定的参数,如编码器选项、元数据等。如果没有额外的选项,可以将此参数设置为 NULL。

ret = avformat_write_header(oFmtCtx, nullptr);
if (ret < 0) {av_log(nullptr, AV_LOG_ERROR, "写入文件头失败\n");goto _ERROR;
}

11. 读取并写入音频数据

读取输入文件的音频数据,转换时间戳,并写入输出文件。

while (av_read_frame(pFmtCtx, &pkt) >= 0) {if (pkt.stream_index == idx) {// 转换时间戳等pkt.pts = av_rescale_q_rnd(pkt.pts, inStream->time_base, outStream->time_base, AV_ROUND_NEAR_INF);pkt.dts = pkt.pts;// 写入输出文件av_interleaved_write_frame(oFmtCtx, &pkt);}av_packet_unref(&pkt);
}

12. 写入文件尾部信息并释放资源

写入文件尾部信息,关闭文件,并释放所有分配的资源。

av_write_trailer(oFmtCtx);
avio_close(oFmtCtx->pb);
avformat_free_context(oFmtCtx);_ERROR:// 清理资源if (pFmtCtx) {avformat_free_context(pFmtCtx);#  avformat_close_input(&pFmtCtx);}if (oFmtCtx) {avformat_free_context(oFmtCtx);# avformat_close_input(&oFmtCtx); // 注意:应使用 avformat_free_context 代替}
}

请注意,错误处理部分应使用avformat_free_context代替avformat_close_input来正确释放oFmtCtx资源。另外,程序中存在一些潜在的内存泄漏和错误处理问题,应进一步优化。

运行程序

程序需要传入至少两个参数:输入文件路径和输出文件路径。例如:

./my_ffmpeg_tool input.mp3 output.aac

注意事项

- 确保FFmpeg开发库已正确安装且可链接。
- 检查程序输出的错误信息以进行调试。
- 程序可能需要适当的读取和写入权限。

抽取音频完整代码

cmake_minimum_required(VERSION 3.27)
project(FFmpeg_exercise)
set(CMAKE_CXX_STANDARD 14)# 定义FFmpeg的安装路径变量
set(FFMPEG_INSTALL_DIR "/usr/local/ffmpeg")# 将FFmpeg的头文件目录添加到包含路径
include_directories(${FFMPEG_INSTALL_DIR}/include)# 定义FFmpeg库的基础名称(根据你的需要调整)
set(FFMPEG_LIBS "avcodec;avformat;avutil") # 用分号分隔库名# 寻找并链接FFmpeg库
foreach(FFMPEG_LIB ${FFMPEG_LIBS})find_library(${FFMPEG_LIB}_LIBRARY NAMES ${FFMPEG_LIB}PATHS ${FFMPEG_INSTALL_DIR}/lib NO_DEFAULT_PATH)list(APPEND FFMPEG_LIBRARIES ${${FFMPEG_LIB}_LIBRARY})
endforeach()add_executable(FFmpeg_exercise # main.cppextra_audic.cpp)
# 链接FFmpeg库
target_link_libraries(FFmpeg_exercise ${FFMPEG_LIBRARIES})
//
// Created by 陈伟峰 on 2024/6/22.
//
#ifdef __cplusplus
extern "C" {
#endif
// 包含FFmpeg的头文件
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#ifdef __cplusplus}
#endif
#include <iostream>int main(int argc,char *argv[]){int ret = -1;int idx = -1;//1.处理一些参数;char *src {nullptr};char *dst {nullptr};AVFormatContext *pFmtCtx {nullptr};AVFormatContext *oFmtCtx {nullptr};AVOutputFormat *outFmt {nullptr};AVStream *inStream {nullptr};AVStream *outStream {nullptr};AVPacket pkt {nullptr};//    设置日志级别av_log_set_level(AV_LOG_DEBUG);if(argc<3){av_log(nullptr,AV_LOG_INFO,"arguments must be more than 3\n");exit(-1);}src = argv[1];dst = argv[2];//2.打开输入多媒体文件ret = avformat_open_input(&pFmtCtx,src,nullptr,nullptr);if (ret<0){av_log(nullptr,AV_LOG_ERROR,"avformat_open_input failed\n");exit(-1);}//3.获取多媒体文件信息if ((ret= avformat_find_stream_info(pFmtCtx,nullptr))<0){av_log(nullptr,AV_LOG_INFO,"avformat_find_stream_info failed\n");exit(-1);}//4.遍历所有流,找到音频流for (int i = 0; i < pFmtCtx->nb_streams; ++i) {if (pFmtCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){idx = i;av_log(nullptr,AV_LOG_INFO,"find_stream_info Successed!\n");break;}}if (idx<0){av_log(nullptr,AV_LOG_ERROR,"can not find audio stream\n");exit(-1);}// 打开目的文件上下文oFmtCtx = avformat_alloc_context();if(!oFmtCtx){av_log(nullptr,AV_LOG_ERROR,"avformat_alloc_context failed\n");goto _ERROR;}outFmt = av_guess_format(nullptr,dst,nullptr);oFmtCtx->oformat = outFmt;// 为目的文件,创建一个新的音频流outStream = avformat_new_stream(oFmtCtx,nullptr);// 设置输出音频参数inStream = pFmtCtx->streams[idx];avcodec_parameters_copy(outStream->codecpar,inStream->codecpar);outStream->codecpar->codec_tag = 0;// 绑定ret = avio_open2(&oFmtCtx->pb,dst,AVIO_FLAG_WRITE,nullptr,nullptr);if(ret<0){av_log(nullptr,AV_LOG_ERROR,"avio_open2 failed\n");goto _ERROR;}// 写多媒体文件到目的文件ret = avformat_write_header(oFmtCtx,nullptr);if(ret<0){av_log(nullptr,AV_LOG_ERROR, "error:%s",av_err2str(ret));goto _ERROR;}// 读取输入文件中的音频数据while (av_read_frame(pFmtCtx,&pkt)>=0) {if(pkt.stream_index==idx){// 写入输出文件pkt.pts = av_rescale_q_rnd(pkt.pts,inStream->time_base,outStream->time_base,(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt.dts = pkt.pts;pkt.duration = av_rescale_q(pkt.duration,inStream->time_base,outStream->time_base);pkt.stream_index = 0;pkt.pos = -1;av_interleaved_write_frame(oFmtCtx,&pkt);}av_packet_unref(&pkt);}// 写入文件尾av_write_trailer(oFmtCtx);// 释放资源avio_close(oFmtCtx->pb);avformat_free_context(oFmtCtx);_ERROR:if(pFmtCtx){
//        avformat_close_input(&pFmtCtx);avformat_free_context(pFmtCtx);pFmtCtx = nullptr;}if(oFmtCtx){
//        avformat_close_input(&oFmtCtx);avformat_free_context(oFmtCtx);oFmtCtx = nullptr;}
};
  • 执行结果
 ./FFmpeg_exercise demo.mp4 test.aac

image-20240622111917818


http://www.ppmy.cn/news/1520834.html

相关文章

k8s集群环境搭建(一主二从--kubeadm安装)

前置条件 版本&#xff1a;CentOS Linux release 7.5.1804 (Core) 内存&#xff1a;2G CPU&#xff1a;2 主机名解析 vim /etc/hosts 192.168.109.100 master 192.168.109.101 node1 192.168.109.102 node2时间同步&#xff0c;这里直接使用chronyd服务从网络同步时间syste…

ESP32-IDF http请求崩溃问题分析与解决

文章目录 esp32s3 http请求崩溃问题代码讨论修正后不崩溃的代码esp32相关文章 ESP32S3板子, 一运行http请求百度网站的例子, 就会panic死机, 记录下出现及解决过程. esp32s3 http请求崩溃 一执行http请求的perform就会崩溃, 打印如图 ESP32-IDF 的http请求代码是根据官方dem…

【亚马逊云】注册登录AWS 合作伙伴网络(APN)操作流程

文章目录 1、什么是APN&#xff1f;2、登录AWS官网3、加入 AWS 合作伙伴网络4、登录 AWS 合作伙伴网络5、常见问题5.1 忘记密码5.2 修改信息 6、活动上新1️⃣「云上驰骋&#xff0c;考证无忧」云从业者认证考试优惠活动2️⃣ Amazon 动手实验3️⃣AWS Certified 助理级认证挑战…

[Tools: LoRA] Diffusers中Stable Diffusion的实现

实现底层原理 Diffusers中的Attention操作实现在AttnProcessor类&#xff08;diffusers.models.attention_processor.py&#xff09;&#xff0c;里面定义了单次Attention操作。添加LoRA&#xff0c;本质上是用LoRAAttnProcessor类替换AttnProcessor类。LoRAAttnProcessor中新…

强连通分量专题总结

~~~~~ 总题单链接 ~~~~~ 对于只需要考虑强连通分量的题&#xff0c;就可以用强连通分量&#xff08;大雾 ~~~~~ 我想了很久&#xff0c;确实没有什么好说的 … \ldots …

ECCV2024|RegionDrag:基于区域的图像编辑方法,通过手动拖拽实现图像编辑!

香港大学和牛津大学提出了一种使用扩散模型进行基于区域的快速图像编辑方法RegionDrag&#xff0c; RegionDrag 是一种基于区域的图像编辑方法&#xff0c;通过使用户能够通过 手柄和 目标区域表达指令&#xff0c;提供比点拖动方法更快、更精确的图像编辑&#xff0c;在速度上…

el-table利用折叠面板 type=“expand“ 嵌套el-table,并实现 明细数据多选,选中明细数据后返回原数据得嵌套格式

效果图: 废话不多说直接上代码&#xff0c;完整代码展示&#xff1a; <template><el-tableborderref"multipleTable":data"tableData"tooltip-effect"dark"style"width: 100%"><el-table-columnwidth"50"la…

Java | Leetcode Java题解之第385题迷你语法分析器

题目&#xff1a; 题解&#xff1a; class Solution {int index 0;public NestedInteger deserialize(String s) {if (s.charAt(index) [) {index;NestedInteger ni new NestedInteger();while (s.charAt(index) ! ]) {ni.add(deserialize(s));if (s.charAt(index) ,) {in…

创新之光闪耀,点赋科技在第十三届创新创业大赛中绽放光彩

近日&#xff0c;第十三届创新创业大赛决赛落下帷幕&#xff0c;这场充满激情与挑战的赛事吸引了众多优秀企业参与角逐。在激烈的竞争中&#xff0c;点赋科技脱颖而出&#xff0c;荣获第三名的佳绩。 创新创业大赛一直是企业展示实力、交流创新理念的重要平台。本次大赛中&…

前端防抖和节流函数的实现原理

在前端开发中&#xff0c;防抖&#xff08;Debounce&#xff09;和节流&#xff08;Throttle&#xff09;是两种常用的优化技术&#xff0c;它们主要用于减少事件处理函数的执行频率&#xff0c;从而提高程序性能和用户体验。 防抖&#xff08;Debounce&#xff09; 防抖的目…

iomuxc、pinctrl子系统、gpio子系统(学习总结)

iomuxc、pinctrl子系统、gpio子系统三者的关系 相互依赖&#xff1a;IOMUXC、pinctrl子系统和gpio子系统在功能上相互依赖。IOMUXC提供了引脚复用和电气属性的配置能力&#xff0c;pinctrl子系统负责从设备树中获取这些配置信息并完成初始化&#xff0c;而gpio子系统则在引脚被…

UE 【材质编辑】自定义材质节点

使用UE的材质编辑器&#xff0c;蓝图提供了大量的节点函数&#xff1a; 实际上&#xff0c;这是一段封装好的包含一串HLSL代码的容器。打开“Source/Runtime/Engine/Classes/Material”&#xff0c;可以看到很多不同节点的头文件&#xff1a; 照葫芦画瓢 以UMaterialExpressi…

notepad++将换行替换成空

将多行里的换行置为一行&#xff0c;例如将下面的6行置为3行 crrlH打开替换框&#xff0c; 替换目标为【,\r\n】&#xff0c;替换成空&#xff0c;勾选循环查找和 正则表达式&#xff0c;全部替换即可。 替换后的效果

应该怎么从0搭建一个图像识别系统,如果想考计算机的研究生应该如何准备

搭建一个图像识别系统的过程可以分为以下几个步骤&#xff1a; 数据收集和准备&#xff1a;收集包含标注的图像数据集&#xff0c;并将其准备为训练集和测试集。确保数据集的多样性和代表性。 特征提取和选择&#xff1a;选择适当的特征提取方法&#xff0c;如卷积神经网络&am…

如何配置iSAID_Devkit环境

这个库有点年头了&#xff0c;使用README.md里的conda env create -f environment.yml会说包之间有冲突, 没法安装. 解决方法: 自己建立一个conda env, conda create -n py_isaid pip python3.6.8 记得自己提前定好python版本use gpt to transform environment.yml to setup.p…

mac安装spark

参考&#xff1a;在Mac上安装Spark apache-spark-3.5.1_mac安装spark-CSDN博客 几个需要用到的路径&#xff1a; hadoop的bin目录&#xff1a;/opt/homebrew/Cellar/hadoop/3.4.0/bin spark的conf目录/opt/homebrew/Cellar/apache-spark/3.5.2/libexec/conf spark的bin目录&am…

Elasticsearch之原理详解

简介 ES是使用 Java 编写的一种开源搜索引擎&#xff0c;它在内部使用 Lucene 做索引与搜索&#xff0c;通过对 Lucene 的封装&#xff0c;隐藏了 Lucene 的复杂性&#xff0c;取而代之的提供一套简单一致的 RESTful API 然而&#xff0c;Elasticsearch 不仅仅是 Lucene&#…

SpringCloud Alibaba】(十三)学习 RocketMQ 消息队列

目录 1、MQ 使用场景与选型对比1.1、MQ 的使用场景1.2、引入 MQ 后的注意事项1.3、MQ 选型对比 2、下载、安装 RocketMQ 及 RocketMQ 控制台2.1、下载安装 RocketMQ2.2、测试 RocketMQ 环境2.3、RocketMQ 控制台【图形化管理控制台】2.3.1、下载、安装2.3.2、验证 RocketMQ 控制…

day-49 使数组中所有元素相等的最小操作数

思路 第一个数和最后一个数要变为一致&#xff0c;需要操作n-1次&#xff0c;然后第二个数和倒数第二个数要操作n-3次 解题过程 以此类推即可得出答案 Code class Solution {public int minOperations(int n) {int ans0;int t(n-1);while(t>0){anst;t-2;}return ans;} }作…

String核心设计模式——建造者模式

目录 建造者模式 优点 缺点 使用场景 结构 步骤 1 Item.java Packing.java 步骤 2 Wrapper.java Bottle.java 步骤 3 Burger.java ColdDrink.java 步骤 4 VegBurger.java ChickenBurger.java Coke.java Pepsi.java 步骤 5 Meal.java 步骤 6 MealBuilder…