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

2024-06-22 14:20

本文主要是介绍ffmpeg音视频开发从入门到精通——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打开输入文件。

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获取多媒体文件的流信息。

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分配输出文件的格式上下文。

oFmtCtx = avformat_alloc_context();
if (!oFmtCtx) {av_log(nullptr, AV_LOG_ERROR, "分配输出文件上下文失败\n");goto _ERROR;
}

7. 猜测输出文件格式

使用av_guess_format猜测输出文件的格式。

outFmt = av_guess_format(nullptr, dst, nullptr);
oFmtCtx->oformat = outFmt;

8. 创建新的音频流

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

outStream = avformat_new_stream(oFmtCtx, nullptr);
avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
outStream->codecpar->codec_tag = 0;

9. 打开输出文件

使用avio_open2打开输出文件准备写入。

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写入文件头信息。

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

这篇关于ffmpeg音视频开发从入门到精通——ffmpeg实现音频抽取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1084533

相关文章

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyQt5 GUI 开发的基础知识

《PyQt5GUI开发的基础知识》Qt是一个跨平台的C++图形用户界面开发框架,支持GUI和非GUI程序开发,本文介绍了使用PyQt5进行界面开发的基础知识,包括创建简单窗口、常用控件、窗口属性设... 目录简介第一个PyQt程序最常用的三个功能模块控件QPushButton(按钮)控件QLable(纯文本

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q

Python实现批量提取BLF文件时间戳

《Python实现批量提取BLF文件时间戳》BLF(BinaryLoggingFormat)作为Vector公司推出的CAN总线数据记录格式,被广泛用于存储车辆通信数据,本文将使用Python轻松提取... 目录一、为什么需要批量处理 BLF 文件二、核心代码解析:从文件遍历到数据导出1. 环境准备与依赖库

linux下shell脚本启动jar包实现过程

《linux下shell脚本启动jar包实现过程》确保APP_NAME和LOG_FILE位于目录内,首次启动前需手动创建log文件夹,否则报错,此为个人经验,供参考,欢迎支持脚本之家... 目录linux下shell脚本启动jar包样例1样例2总结linux下shell脚本启动jar包样例1#!/bin

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont