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

news2025/3/13 12:41:57

文章目录

    • 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.cpp
        extra_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.coloradmin.cn/o/1850858.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue中的状态管理

第1部分&#xff1a;引言 状态管理是应用中数据流动和变更的核心机制。在Vue应用中&#xff0c;状态管理不仅涉及到组件间的数据共享&#xff0c;还包括了数据的持久化、异步操作的处理等复杂场景。良好的状态管理策略可以提高应用的响应速度&#xff0c;降低组件间的耦合度&a…

经典游戏案例:植物大战僵尸

学习目标&#xff1a;植物大战僵尸核心玩法实现 游戏画面 项目结构目录 部分核心代码 using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using Random UnityEngine.Random;public enum Z…

(2024)豆瓣电影详情页内容爬虫详解和源码

&#xff08;2024&#xff09;豆瓣电影详情页内容爬虫详解和源码 这是一个Python爬虫程序&#xff0c;用于抓取豆瓣电影详情页面如https://movie.douban.com/subject/1291560/的数据。它首先发送GET请求&#xff0c;使用PyQuery解析DOM&#xff0c;然后根据<br>标签分割H…

C语言第17篇:预处理详解

1、预定义符号 C语言设置了一些预定义符号&#xff0c;可以直接使用。预定义符号也是在预处理期间处理的。 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI…

LaTeX中添加矩阵分块虚线并设置虚线疏密

对于大型矩阵&#xff0c;有时需要添加分块虚线。 方法为使用arydshln宏包&#xff0c;然后在array环境中设置虚线。需要注意的是&#xff0c;使用矩阵环境需要搭配amsmath宏包使用&#xff0c;且需放在amsmath宏包之后。即导言区设置为 \usepackage{amsmath} \usepackage{ary…

人人讲视频如何下载

一、工具准备 1.VLC media player 2.谷歌浏览器 二、视频下载 1.打开人人讲网页&#xff0c;需要下载的视频 谷歌浏览器打开调试窗口 搜索m3u8链接 拷贝到VLCplayer打开网络串流方式打开测试是否能正常播放 2.下载视频 能正常播放后&#xff0c;切换播放为转换选择mp4格式…

【CPP】归并排序

目录 1.归并排序简介代码分析归并的非递归形式 1.归并排序 归并排序&#xff08;MERGE-SORT&#xff09; 是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide andConquer&#xff09;的一个非常典型的应用。 将已有序的子序列合并&#xff0c;得到…

扩散模型 GLIDE:35 亿参数的情况下优于 120 亿参数的 DALL-E 模型

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

com域名注册多少钱

COM域名注册价格视具体注册商而定&#xff0c;不同的注册商可能会有不同的收费标准。一般来说&#xff0c;COM域名注册价格在10美元到20美元之间&#xff0c;可根据不同的需求选择注册时间的长短&#xff0c;从1年到10年等不同时间段的注册费用也不同。以下是关于COM域名注册价…

Python爬虫学习 | Scrapy框架详解

一.Scrapy框架简介 何为框架&#xff0c;就相当于一个封装了很多功能的结构体&#xff0c;它帮我们把主要的结构给搭建好了&#xff0c;我们只需往骨架里添加内容就行。scrapy框架是一个为了爬取网站数据&#xff0c;提取数据的框架&#xff0c;我们熟知爬虫总共有四大部分&am…

【LLM之KG】KoPA论文阅读笔记

研究背景 知识图谱补全&#xff08;KGC&#xff09;是通过预测知识图谱中缺失的三元组来完善知识图谱的信息。传统方法主要基于嵌入和预训练语言模型&#xff0c;但这些方法往往忽视了知识图谱的结构信息&#xff0c;导致预测效果不佳。 研究目标 本文的研究目标是探索如何将…

海外短剧系统如何征服观众心

海外短剧系统要征服观众的心&#xff0c;需要综合考虑多个方面。 1、紧凑的剧情设计&#xff1a; 短小精悍&#xff1a;海外短剧通常每集时长不超过半小时&#xff0c;甚至有的仅有几分钟。这种紧凑的剧情设计让观众能够在短时间内迅速沉浸在故事中&#xff0c;无需花费大量时间…

分页查询前端对接

文章目录 添加角色修改角色当点击修改按钮后,那么就会弹出对话框,所以要设置显示为true点击修改的时候就是 要显示对话框 制作用户管理页面开发后端接口用户查询前端整合新增接口功能实现修改 添加角色 首先添加 添加表单的组件 那么总结一下 就是使用 组件 然后再使用变量接…

docker简单快速使用上手

1.Docker是什么&#xff1f; Docker 是一个开源的容器化平台&#xff0c;主要用于开发、运输和运行应用程序。它通过提供轻量级的虚拟化机制&#xff0c;使得开发者可以在一个隔离的环境中运行和管理应用程序及其依赖项。Docker 的核心组件包括镜像&#xff08;Image&#xff…

FOC方案大合集!

获取链接&#xff01;&#xff01;&#xff01; 本次小编给大家带来了一份FOC的方案大合集。此套方案是基于峰岹科技FU68系列MCU的系列方案&#xff0c;包含常用的无感&#xff0c;有感无刷电机的应用&#xff0c;每份方案都包含了原理图&#xff0c;PCB&#xff0c;代码文件&…

游戏大厂“脱钩”安卓商店: 独立渠道TapTap们能否渔利

一纸公告将游戏厂商与渠道的博弈再度摆上了台面。 近日&#xff0c;腾讯控股旗下手游《地下城与勇士&#xff1a;起源》&#xff08;下称“DNF手游”&#xff09;运营团队发布公告称&#xff0c;自6月20日起&#xff0c;DNF手游将不再上架部分安卓平台的头部应用商店。 下架的…

Google Vortex流式存储引擎分析

作者&#xff1a;archimekai&#xff0c;转载请注明出处 参考文献&#xff1a; Edara, P., Forbesj, J., & Li, B. (2024). Vortex: A Stream-oriented Storage Engine For Big Data Analytics. Companion of the 2024 International Conference on Management of Data, 17…

ClickHouse 24.5.3:全面解析与RPM单节点轻松部署!

简介 ClickHouse是由俄罗斯的Yandex公司开发的开源列式数据库管理系统。它最早于2016年开源&#xff0c;主要用于实时数据分析。ClickHouse通过列存储、向量化执行、并行计算等技术&#xff0c;实现了对大规模数据集的快速查询和分析&#xff0c;特别适合实时数据分析和商业智能…

LayoutSystem布局系统

简介: LayoutSystem,是UGUI中由CanvasUpdateSystem发起(m_LayoutRebuildQueue中大部分都是LayoutRebuilder)的关于布局排列的处理系统。 类图: 布局过程 核心代码讲解: LayoutRebuilder

001.VMware Workstation Pro虚拟平台安装

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…