当前位置:   article > 正文

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

ffmpeg 提取音频

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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];
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3. 打开输入文件

使用avformat_open_input打开输入文件。

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

4. 获取文件信息

调用avformat_find_stream_info获取多媒体文件的流信息。

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

5. 查找音频流

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

for (int i = 0; i < pFmtCtx->nb_streams; ++i) {
    if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
        idx = i;
        break;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6. 分配输出文件上下文

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

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

7. 猜测输出文件格式

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

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

8. 创建新的音频流

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

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

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5

10. 写入文件头信息

调用avformat_write_header写入文件头信息。

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

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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 代替
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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

运行程序

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

./my_ffmpeg_tool input.mp3 output.aac
  • 1

注意事项

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

抽取音频完整代码

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})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
//
// 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;
    }
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 执行结果
 ./FFmpeg_exercise demo.mp4 test.aac
  • 1

image-20240622111917818

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/994974
推荐阅读
相关标签
  

闽ICP备14008679号