当前位置:   article > 正文

ffmpeg音视频开发从入门到精通——ffmpeg 视频数据抽取_ffmpeg从入门到精通

ffmpeg从入门到精通

FFmpeg视频处理工具使用总结

环境配置

在C++程序中使用FFmpeg之前,需要包含相应的头文件,并根据是否使用C++编译器,可能需要添加extern "C"块。


```cpp
#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
  • 10
  • 11

主函数与参数处理

程序入口点是main函数,它处理命令行参数并设置日志级别。

int main(int argc, char *argv[]) {
    // 参数检查
    if (argc < 3) {
        av_log(nullptr, AV_LOG_ERROR, "Usage: %s <input file> <output file>\n", argv[0]);
        exit(-1);
    }
    // 输入输出文件路径
    char *src = argv[1];
    char *dst = argv[2];
    // 设置日志级别
    av_log_set_level(AV_LOG_DEBUG);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

打开输入文件

使用avformat_open_input打开输入文件,并检查返回值。

int ret = avformat_open_input(&pFormatCtx, src, nullptr, nullptr);
if (ret < 0) {
    av_log(nullptr, AV_LOG_ERROR, "Could not open input file: %s\n", src);
    exit(-1);
}
  • 1
  • 2
  • 3
  • 4
  • 5

获取流信息

调用av_find_best_stream找到最佳的视频流。

ret = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (ret < 0) {
    av_log(nullptr, AV_LOG_ERROR, "Failed to retrieve input stream information\n");
    goto _ERROR;
}
  • 1
  • 2
  • 3
  • 4
  • 5

分配输出文件上下文

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

oFormatCtx = avformat_alloc_context();
if (oFormatCtx == nullptr) {
    av_log(nullptr, AV_LOG_ERROR, "Failed to allocate output context\n");
    goto _ERROR;
}
  • 1
  • 2
  • 3
  • 4
  • 5

猜测输出文件格式

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

outFmt = av_guess_format(nullptr, dst, nullptr);
if (outFmt == nullptr) {
    av_log(nullptr, AV_LOG_ERROR, "Failed to guess output format\n");
    goto _ERROR;
}
oFormatCtx->oformat = outFmt;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

创建视频流并设置参数

为输出文件创建视频流,并复制输入视频流的参数。

outStream = avformat_new_stream(oFormatCtx, nullptr);
avcodec_parameters_copy(outStream->codecpar, pFormatCtx->streams[ret]->codecpar);
outStream->codecpar->codec_tag = 0;
  • 1
  • 2
  • 3

打开输出文件并写入头信息

使用avio_open2打开输出文件,并使用avformat_write_header写入文件头信息。

ret = avio_open2(&oFormatCtx->pb, dst, AVIO_FLAG_WRITE, nullptr, nullptr);
if (ret < 0) {
    av_log(nullptr, AV_LOG_ERROR, "Failed to open output file: %s\n", dst);
    goto _ERROR;
}
ret = avformat_write_header(oFormatCtx, nullptr);
if (ret < 0) {
    av_log(nullptr, AV_LOG_ERROR, "Failed to write output file header\n");
    goto _ERROR;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

读取、转换并写入帧数据

读取输入文件的视频帧,转换时间戳,并使用av_interleaved_write_frame写入输出文件。

while (av_read_frame(pFormatCtx, &pkt) >= 0) {
    if (pkt.stream_index == ret) {
        // 转换时间戳等
        pkt.pts = av_rescale_q_rnd(pkt.pts, inSteam->time_base, outStream->time_base, AV_ROUND_NEAR_INF);
        pkt.dts = av_rescale_q_rnd(pkt.dts, inSteam->time_base, outStream->time_base, AV_ROUND_NEAR_INF);
        pkt.duration = av_rescale_q(pkt.duration, inSteam->time_base, outStream->time_base);
        // 写入帧数据
        av_interleaved_write_frame(oFormatCtx, &pkt);
    }
    av_packet_unref(&pkt);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

写入尾信息并释放资源

使用av_write_trailer写入文件尾信息,并释放所有资源。

av_write_trailer(oFormatCtx);

_ERROR:
// 清理资源
if (oFormatCtx && oFormatCtx->pb) {
    avio_close(oFormatCtx->pb);
    oFormatCtx->pb = nullptr;
}
if (oFormatCtx) {
    avformat_free_context(oFormatCtx);
    oFormatCtx = nullptr;
}
if (pFormatCtx) {
    avformat_free_context(pFormatCtx);
    pFormatCtx = nullptr;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

运行程序

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

./my_ffmpeg_tool input.mp4 output.mkv
  • 1

确保替换为您的实际文件名和所需的输出格式。

注意事项

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

源代码

  • cmake 源文件
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
        extra_video.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
  • 26
  • 27
  • 28
  • 29
  • 30
  • cpp
//
// 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 *pFormatCtx {nullptr};
    AVFormatContext *oFormatCtx {nullptr};

    AVOutputFormat *outFmt {nullptr};
    AVStream *outStream {nullptr};
    AVStream *inSteam {nullptr};
    AVPacket pkt {nullptr};

    // 日志信息
    av_log_set_level(AV_LOG_DEBUG);
    if(argc<3){
        av_log(nullptr,AV_LOG_ERROR,"Usage:%s <input file> <output file>\n",argv[0]);
        exit(-1);
    }

    src = argv[1];
    dst = argv[2];

    // 2.打开多媒体输入文件
    ret = avformat_open_input(&pFormatCtx,src,nullptr,nullptr);
    if(ret<0){
        av_log(nullptr,AV_LOG_ERROR,"Could not open input file:%s\n",src);
        exit(-1);
    }

    // 3.获取多媒体文件信息
    ret = av_find_best_stream(pFormatCtx,AVMEDIA_TYPE_VIDEO,-1,-1,nullptr,0);
    if(ret<0){
        av_log(nullptr,AV_LOG_ERROR,"Failed to retrieve input stream information\n");
        goto _ERROR;
    }
    // 打开目的多媒体文件
    oFormatCtx = avformat_alloc_context();
    if(oFormatCtx==nullptr){
        av_log(nullptr,AV_LOG_ERROR,"Failed to allocate output context\n");
        goto _ERROR;
    }

    outFmt = av_guess_format(nullptr,dst,nullptr);
    if(outFmt==nullptr){
        av_log(nullptr,AV_LOG_ERROR,"Failed to guess output format\n");
        goto _ERROR;
    }
    oFormatCtx->oformat = outFmt;

    // 为目的文件,创建一个新的视频流
    outStream = avformat_new_stream(oFormatCtx,nullptr);
    // 设置视频参数
    inSteam = pFormatCtx->streams[idx];
    avcodec_parameters_copy(outStream->codecpar,pFormatCtx->streams[ret]->codecpar);
    outStream->codecpar->codec_tag = 0;

    // 绑定
    ret = avio_open2(&oFormatCtx->pb,dst,AVIO_FLAG_WRITE, nullptr, nullptr);
    if(ret<0){
        av_log(nullptr,AV_LOG_ERROR,"Failed to open output file:%s\n",dst);
        goto _ERROR;
    }
    // 写入头信息
    ret = avformat_write_header(oFormatCtx,nullptr);
    if(ret<0){
        av_log(nullptr,AV_LOG_ERROR,"Failed to write output file header\n");
        goto _ERROR;
    }

    // 写多媒体文件到目的文件
    ret = avformat_write_header(oFormatCtx,nullptr);
    if(ret<0){
        av_log(nullptr,AV_LOG_ERROR,"Failed to write output file header:%s\n", av_err2str(ret));
        goto _ERROR;
    }
    while(av_read_frame(pFormatCtx,&pkt)>=0) {
        if (pkt.stream_index == idx) {

            pkt.pts = av_rescale_q_rnd(pkt.pts, inSteam->time_base, outStream->time_base,
                                       (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            pkt.dts = av_rescale_q_rnd(pkt.dts, inSteam->time_base, outStream->time_base,
                                       (AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            pkt.duration = av_rescale_q(pkt.duration, inSteam->time_base, outStream->time_base);
            pkt.stream_index = 0;
            pkt.pos = -1;
            av_interleaved_write_frame(oFormatCtx, &pkt);
        }
        av_packet_unref(&pkt);
    }
    // 写入尾信息
    av_write_trailer(oFormatCtx);

_ERROR:

    if(oFormatCtx->pb){
        avio_close(oFormatCtx->pb);
        oFormatCtx->pb = nullptr;
    }
    if(oFormatCtx){
        avformat_free_context(oFormatCtx);
        oFormatCtx = nullptr;
    }
    if(pFormatCtx){
        avformat_free_context(pFormatCtx);
        pFormatCtx = nullptr;
    }
    return 0;
}

  • 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
  • 执行
./main demo.mp4 demo2.h264
  • 1
  • 运行
ffplay demo2.h264
  • 1

image-20240622143747704

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

闽ICP备14008679号