重庆职业能力建设投稿网站,网站专题页面策划,自己做网站需要什么条件,小型企业网络配置实例文章目录 FFmpeg视频处理工具使用总结环境配置主函数与参数处理打开输入文件获取流信息分配输出文件上下文猜测输出文件格式创建视频流并设置参数打开输出文件并写入头信息读取、转换并写入帧数据写入尾信息并释放资源运行程序注意事项源代码 FFmpeg视频处理工具使用总结
环境… 文章目录 FFmpeg视频处理工具使用总结环境配置主函数与参数处理打开输入文件获取流信息分配输出文件上下文猜测输出文件格式创建视频流并设置参数打开输出文件并写入头信息读取、转换并写入帧数据写入尾信息并释放资源运行程序注意事项源代码 FFmpeg视频处理工具使用总结
环境配置
在C程序中使用FFmpeg之前需要包含相应的头文件并根据是否使用C编译器可能需要添加extern C块。在C中当包含C语言的头文件时需要使用extern C来告诉编译器这是一个C语言的函数避免C的名称修饰导致链接错误。这个例子中包含了FFmpeg库的头文件用于处理多媒体文件。在C中当包含C语言的头文件时需要使用extern C来告诉编译器这是一个C语言的函数避免C的名称修饰导致链接错误。
#ifdef __cplusplus
extern C {
#endif
#include libavformat/avformat.h
#include libavutil/avutil.h
#include libavutil/log.h
#ifdef __cplusplus
}
#endif主函数与参数处理
程序入口点是main函数它处理命令行参数并设置日志级别。 检查传入的参数个数是否大于等于3如果参数个数小于3就输出错误信息并退出程序。 将第一个参数赋值给变量src将第二个参数赋值给变量dst这样就得到了输入文件路径和输出文件路径。 调用av_log_set_level函数设置日志级别为AV_LOG_DEBUG这样在程序执行过程中会打印出调试级别的日志信息。
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打开输入文件并检查返回值。avformat_open_input是libavformat库中的一个函数用于打开一个输入文件并初始化输入上下文。 输入参数如下
AVFormatContext **ps指向要初始化的AVFormatContext指针的指针。函数将分配一个AVFormatContext结构并将其指针保存在该参数指向的地址中。const char *url输入文件的URL或文件名。AVInputFormat *fmt强制指定要使用的输入格式。如果为NULL则函数将根据输入文件的扩展名自动选择适当的格式。AVDictionary **options一个指向AVDictionary结构指针的指针用于设置差异化选项。可以使用该参数来设置例如输入缓冲区大小、超时值等选项。int (*interrupt_callback)(void)设置中断回调函数。如果输入操作需要中断则调用此回调函数。如果为NULL则不设置中断回调。
函数执行成功后ps将指向已初始化的AVFormatContext结构可以使用该结构来进行后续的输入操作。执行失败时ps将保持不变而函数将返回一个负值以表示错误原因。
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找到最佳的视频流。 av_find_best_stream是FFmpeg库中的一个函数用于查找最佳的流索引。它的定义如下
int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, AVCodec **decoder_ret, int flags);参数说明如下
ic指向AVFormatContext的指针表示输入的封装格式上下文。type表示所需流的媒体类型可以是AVMEDIA_TYPE_AUDIO、AVMEDIA_TYPE_VIDEO或AVMEDIA_TYPE_SUBTITLE。wanted_stream_nb表示所需流的索引号。related_stream表示关联的流的索引号。通常设置为负值表示没有关联的流。decoder_ret指向AVCodec指针的指针用于返回找到的解码器。flags表示查找流的标志位可以是AVFMT_FLAG_NOFILE、AVFMT_FLAG_GENPTS等。
函数返回值为找到的流的索引号如果未找到则返回负值错误代码。
这个函数的主要功能是根据指定的媒体类型和索引号在输入的封装格式上下文中查找最佳的流索引。它会根据一些条件如流的媒体类型、时间基等进行评估然后返回找到的最佳流索引并通过decoder_ret参数返回对应的解码器。av_find_best_stream是FFmpeg库中的一个函数用于查找最佳的流索引。它的定义如下
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分配输出文件的格式上下文。
avformat_alloc_context是FFmpeg中的一个函数用于分配一个AVFormatContext结构体并初始化其成员变量。
输入参数为void。avformat_alloc_context是FFmpeg中的一个函数用于分配一个AVFormatContext结构体并初始化其成员变量。
输入参数为void。
oFormatCtx avformat_alloc_context();
if (oFormatCtx nullptr) {av_log(nullptr, AV_LOG_ERROR, Failed to allocate output context\n);goto _ERROR;
}猜测输出文件格式
使用av_guess_format猜测输出文件的格式。 av_guess_format是FFmpeg库中的一个函数用于猜测输入参数的格式。它的输入参数包括
const char *short_name输入参数的短名称。这是一个字符串通常是文件的扩展名或格式的简称。const char *filename输入参数的文件名。这是一个字符串指定要处理的文件路径。const char *mime_type输入参数的MIME类型。这是一个字符串指定要处理的媒体类型。
这些输入参数可根据实际需求来选择设置也可以通过设置为NULL来忽略某些参数。根据提供的输入参数av_guess_format函数会尝试猜测并返回最有可能的格式。
注意在使用av_guess_format函数时应确保FFmpeg库已正确初始化并且所需的输入参数有效且符合可接受的格式。av_guess_format是FFmpeg库中的一个函数用于猜测输入参数的格式。它的输入参数包括
const char *short_name输入参数的短名称。这是一个字符串通常是文件的扩展名或格式的简称。const char *filename输入参数的文件名。这是一个字符串指定要处理的文件路径。const char *mime_type输入参数的MIME类型。这是一个字符串指定要处理的媒体类型。
这些输入参数可根据实际需求来选择设置也可以通过设置为NULL来忽略某些参数。根据提供的输入参数av_guess_format函数会尝试猜测并返回最有可能的格式。
注意在使用av_guess_format函数时应确保FFmpeg库已正确初始化并且所需的输入参数有效且符合可接受的格式。
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写入文件头信息。 avio_open2 是用于打开一个输入输出数据流的函数它的输入参数包括
AVIOContext **s指向用于输入输出的 AVIOContext 结构体的指针。const char *filename输入输出的文件名或者URL地址使用 “r” 模式打开用于输入使用 “w” 模式打开用于输出。int flags打开文件的参数可以是 AVIO_FLAG_READ 表示输入流、AVIO_FLAG_WRITE 表示输出流、AVIO_FLAG_READ_WRITE 表示输入输出流。const AVIOInterruptCB *int_cb输入输出中断回调函数的指针。AVDictionary **options用于传递其他选项的字典指针。可以设置 NULL 以忽略其他选项。
avformat_write_header 是用于写入文件头信息的函数它的输入参数包括
AVFormatContext *s指向 AVFormatContext 结构体的指针表示待写入文件的格式上下文。AVDictionary **options用于传递其他选项的字典指针。可以设置 NULL 以忽略其他选项。
这些函数用于打开输入输出数据流和写入文件头信息时需要通过传入一些参数来配置打开的流和文件的属性和选项。avio_open2 是用于打开一个输入输出数据流的函数它的输入参数包括
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写入输出文件。 av_interleaved_write_frame函数是FFmpeg库中的一个函数用于将音视频数据包写入容器文件。
函数的参数如下 AVFormatContext *s: AVFormatContext结构体指针表示容器格式上下文用于管理容器格式相关的信息。 AVPacket *pkt: AVPacket结构体指针表示音视频数据包包含音视频数据以及相关的参数信息。
函数的返回值为整形表示函数的执行结果。如果返回值为0则表示成功写入数据包如果返回值为负值则表示写入失败。
注意在调用av_interleaved_write_frame函数之前需要先调用avformat_write_header函数来写入容器文件的头部信息在所有音视频数据包写入完成后还需要调用av_write_trailer函数来写入容器文件的尾部信息。
此外av_interleaved_write_frame函数只能用于写入已经交错存储的音视频数据包如果数据包未经过交错处理则需要使用av_write_frame函数来分别写入音频数据包和视频数据包。av_interleaved_write_frame函数是FFmpeg库中的一个函数用于将音视频数据包写入容器文件。
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的参数是AVFormatContext。
AVFormatContext是一个存储了整个音视频文件信息的结构体包含了音视频流、容器格式信息以及封装器相关的信息。
av_write_trailer函数的功能是将容器头信息写入到输出文件中并完成文件封装的最后步骤。具体而言它会将封装器中剩余的音视频数据写入到输出文件中并更新容器头的相关信息如文件大小、时长等。
在调用av_write_trailer函数之前需要保证所有的音视频数据已经被写入到容器中否则可能会导致文件不完整或者损坏。
调用av_write_trailer函数之后应释放AVFormatContext相关的资源包括关闭文件和释放相关的内存。
示例代码如下
AVFormatContext* outputContext nullptr;
// 初始化outputContext的代码
// ...
// 写入所有音视频数据到容器
// ...
av_write_trailer(outputContext);
// 释放相关资源
avformat_close_input(outputContext);
avformat_free_context(outputContext);函数av_write_trailer的参数是AVFormatContext。
AVFormatContext是一个存储了整个音视频文件信息的结构体包含了音视频流、容器格式信息以及封装器相关的信息。
av_write_trailer函数的功能是将容器头信息写入到输出文件中并完成文件封装的最后步骤。具体而言它会将封装器中剩余的音视频数据写入到输出文件中并更新容器头的相关信息如文件大小、时长等。
在调用av_write_trailer函数之前需要保证所有的音视频数据已经被写入到容器中否则可能会导致文件不完整或者损坏。
调用av_write_trailer函数之后应释放AVFormatContext相关的资源包括关闭文件和释放相关的内存。
示例代码如下
AVFormatContext* outputContext nullptr;
// 初始化outputContext的代码
// ...
// 写入所有音视频数据到容器
// ...
av_write_trailer(outputContext);
// 释放相关资源
avformat_close_input(outputContext);
avformat_free_context(outputContext);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.cppextra_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 iostreamint 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(argc3){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(ret0){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(ret0){av_log(nullptr,AV_LOG_ERROR,Failed to retrieve input stream information\n);goto _ERROR;}// 打开目的多媒体文件oFormatCtx avformat_alloc_context();if(oFormatCtxnullptr){av_log(nullptr,AV_LOG_ERROR,Failed to allocate output context\n);goto _ERROR;}outFmt av_guess_format(nullptr,dst,nullptr);if(outFmtnullptr){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(ret0){av_log(nullptr,AV_LOG_ERROR,Failed to open output file:%s\n,dst);goto _ERROR;}// 写入头信息ret avformat_write_header(oFormatCtx,nullptr);if(ret0){av_log(nullptr,AV_LOG_ERROR,Failed to write output file header\n);goto _ERROR;}// 写多媒体文件到目的文件ret avformat_write_header(oFormatCtx,nullptr);if(ret0){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