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

主函数与参数处理

程序入口点是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);
}

打开输入文件

使用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);
}

获取流信息

调用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;
}

分配输出文件上下文

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

oFormatCtx = avformat_alloc_context();
if (oFormatCtx == nullptr) {
    av_log(nullptr, AV_LOG_ERROR, "Failed to allocate output context\n");
    goto _ERROR;
}

猜测输出文件格式

使用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;

创建视频流并设置参数

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

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

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

使用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;
}

读取、转换并写入帧数据

读取输入文件的视频帧,转换时间戳,并使用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);
}

写入尾信息并释放资源

使用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;
}

运行程序

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

./my_ffmpeg_tool input.mp4 output.mkv

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

注意事项

  • 确保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})


  • 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;
}

  • 执行
./main demo.mp4 demo2.h264
  • 运行
ffplay demo2.h264

image-20240622143747704

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/734005.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

tp5学习基本控制器和视图

1 文件结构 正在上传…重新上传取消 application 主要操作目录 extend 扩展 public 入口文件 runtime 运行时文件 thinkphp 核心代码 vendor 三方扩展 2 public/index.php 解析 正在上传…重新上传取消 .htaccess Apache 可写文件 index.php 主目录 router.php 路由文件 3 inde…

LLC开关电源开发:第四节,LLC软件设计报告

LLC源代码链接 数控全桥LLC开发板软件设计报告  1. LLC硬件及软件框架2. LLC软件设计2.1 工程文件说明2.2 LLC中断设计2.2.1 20us中断2.2.2 5ms中断 2.3 LLC状态机设计2.3.1 初始化状态2.3.2 空闲状态2.3.3 软启动状态2.3.4 正常运行状态2.3.5 故障状态 2.4 环路设计2.4.1 环路…

YOLOv8中的C2f模块

文章目录 一、结构概述二、模块功能 一、结构概述 C2f块:首先由一个卷积块(Conv)组成&#xff0c;该卷积块接收输入特征图并生成中间特征图特征图拆分:生成的中间特征图被拆分成两部分&#xff0c;一部分直接传递到最终的Concat块&#xff0c;另一部分传递到多个Botleneck块进…

three.js 第八节 - gltf加载器、解码器

// ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls // 导入hdr加载器&#xff08;专门加载hdr的&#xff09; import { RGBELoader } from three/examples/jsm/loaders…

Unit redis-server.service could not be found.

我的报错如下Unit redis-server.service could not be found. 关键是刷新后台服务 sudo systemctl daemon-reload启动redis-server sudo systemctl start redis-server查看redis-Server服务状态 sudo systemctl status redis-server

【JUC并发编程】

Java并发常见面试题总结&#xff08;上&#xff09; 线程 什么是线程和进程? 何为进程? 进程是程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff0c;因此进程是动态的。系统运行一个程序即是一个进程从创建&#xff0c;运行到消亡的过程。 在 Java 中&am…

Zigbee协议详解:低功耗无线通信的理想选择

什么是Zigbee协议 Zigbee是一种基于IEEE 802.15.4标准的无线通信协议&#xff0c;专为低功耗、低数据速率和短距离通信设计。它广泛应用于物联网&#xff08;IoT&#xff09;设备&#xff0c;如智能家居、工业自动化和健康监测等领域。Zigbee协议由Zigbee联盟维护和推广&#x…

[Redis]持久化机制

众所周知&#xff0c;Redis是内存数据库&#xff0c;也就是把数据存在内存上&#xff0c;读写速度很快&#xff0c;但是&#xff0c;内存的数据容易丢失&#xff0c;为了数据的持久性&#xff0c;还得把数据存储到硬盘上 也就是说&#xff0c;内存有一份数据&#xff0c;硬盘也…

【iOS】编译二进制文件说明

编译二进制文件说明 如何生成文件路径文件说明第一部分&#xff1a;.o文件第二部分&#xff1a;link第三部分&#xff1a;Segment第四部分&#xff1a;Symbol 如何生成 使用Xcode进行编译 &#xff0c;会生成二进制相关文件&#xff0c;可以更详细看产物的布局 项目Target -&…

使用粒子滤波(particle filter)进行视频目标跟踪

虽然有许多用于目标跟踪的算法&#xff0c;包括较新的基于深度学习的算法&#xff0c;但对于这项任务&#xff0c;粒子滤波仍然是一个有趣的算法。所以在这篇文章中&#xff0c;我们将介绍视频中的目标跟踪&#xff1a;预测下一帧中物体的位置。在粒子滤波以及许多其他经典跟踪…

容器之布局容器的演示

代码; #include <gtk-2.0/gtk/gtk.h> #include <glib-2.0/glib.h> #include <gtk-2.0/gdk/gdkkeysyms.h> #include <stdio.h>void change_image(GtkFileChooserButton *filebutton, // GdkEvent *event,GtkImage *image) {gtk_image_set_from_file(im…

Vue3 - 在项目中使用vue-i18n不生效的问题

检查和配置 Vue I18n 确保你已经正确安装了Vue I18n并且配置了组合API模式。 安装 Vue I18n npm install vue-i18nnext配置 i18n.js import { createI18n } from vue-i18n; import messages from ./messages;const i18n createI18n({legacy: false, // 使用组合 API 模式l…

DC-DC 高压降压、非隔离AC-DC、提供强大的动力,选择优质电源芯片-(昱灿)

畅享长续航&#xff0c;尽在我们的充电芯片&#xff01; 无论是手机、平板还是智能设备&#xff0c;长时间使用后电量不足总是令人头疼。然而&#xff0c;我们的充电芯片将为您带来全新的充电体验&#xff01;采用先进的技术&#xff0c;我们的充电芯片能够提供快速而稳定的充电…

逻辑地址 线性地址 物理地址 Linux kernel 内存管理设计

linux kernel 2.6以后的MM&#xff0c;受到了兼容 risc arch cpu 的 MM 的启发&#xff0c;新的 MM 架构对 x86 上任务切换的效率上也有明显提高。 新的MM架构&#xff0c;GDT 不再随着进程的创建与结束而创建和删除 新的表项。 TSS段 也只有一个&#xff0c;进程切换时&…

upload-labs实验过程中遇到的问题

第6题问题&#xff1a;500异常码 发现500异常码&#xff0c;这个应该是apache版本问题&#xff0c;可更换其他版本&#xff0c;或者更换为nginx 12题问题&#xff1a;上传出错 出现上传错误&#xff0c;大概率是php版本问题&#xff0c;需要下载php5.2.17版本的php或者更换其他…

华为云下Ubuntu20.04中Docker的部署

我想用Docker拉取splash&#xff0c;Docker目前已经无法使用&#xff08;镜像都在国外&#xff09;。这导致了 docker pull 命令的失败&#xff0c;原因是timeout。所以我们有必要将docker的源设置在国内&#xff0c;直接用国内的镜像。 1.在华为云下的Ubuntu20.04因为源的原因…

使用Spring Boot实现用户认证和授权

文章目录 引言第一章 Spring Boot概述1.1 什么是Spring Boot1.2 Spring Boot的主要特性 第二章 用户认证和授权基础知识2.1 用户认证2.2 用户授权2.3 Spring Security概述 第三章 项目初始化第四章 实现用户认证和授权4.1 定义用户实体类和角色实体类4.2 创建Repository接口4.3…

点击旋转箭头样式

实现效果&#xff1a; html界面&#xff0c;主要通过isdown来控制箭头是上还是下 <el-popoverplacement"bottom"trigger"click":visible-arrow"false"v-model"isdown"popper-class"user-popover"><divslot"re…

IF膨胀时代,“水刊”当赢?2023热门“水刊”影响因子详解!

【欧亚科睿学术】 1 “四大水刊”详情 图片来源&#xff1a;欧亚科睿学术整理 “四大水刊”的影响因子均有所下跌&#xff0c;其中&#xff0c;曾经被列入中科院预警名单的期刊MEDICINE&#xff0c;其影响因子已是连续三年持续下降。从JCR分区来看&#xff0c;四本期刊分区均…

【已解决】Qwen2:KeyError: ‘qwen2‘

问题背景&#xff1a; 在运行 Qwen2-7B-Instruct 时&#xff0c;报错&#xff1a;KeyError: qwen2 原因说明&#xff1a; Transformer版本过低&#xff0c;需要升级版本 解决方案&#xff1a; pip install -U transformers 参考&#xff1a; 【modelscope_Qwen2-7B-Instr…