刚入职编解码开发岗,之前做的都是算法设计,没有接触过sdk开发。
开始用博客来记录自己的学习过程吧,希望有一天自己也可以成为大佬。
调用ffmpeg sdk来实现mp4格式到avi格式的转换
...本来想通过实现各个格式的相互转换来熟悉ffmpeg的,但是发现内容有点多,就只实现了mp4转成avi格式。
1.ffmpeg库编译
①直接从官网下载已经编译好的ffmpeg,这个网上很多介绍的文章
大佬的博客:【精选】Windows编译和使用ffmpeg_windows ffmpeg_心愿许得无限大的博客-CSDN博客
②自己动手,编译源码得到自己想要的库。在linux下比较简单,编译三步曲就OK了。
./configure
make
make install
windows下面得下载MINGW来编译,听说是专门用来编译ffmpeg的,确实不错。
编译完以后就可以得到这样一个文件夹了,想要的东西里面都有
2.c++调用ffmpeg的库
用我们宇宙最强IDE VS来创建C++项目,整个空项目就行
配置一下项目链接器里的常规和输入两个地方把咱们的ffmpeg库给它配进去
常规->附加库目录:把lib库的路径加进去
输入->附加依赖项:把需要用到的dll.a库全部写进去
3.编写格式转换代码
环境没问题了就开始写我们的格式转换吧。首先整体的流程,在网上看到有大佬给出了基于ffmpeg格式转换的流程,如下图,是FLV转到AVI的,我只有几个MP4文件,所以就参考大佬的流程做一个MP4转AVI吧。
总体来说流程还不是太难
①解封装,包括了视频和音频,两个咱们分开处理
②解码,用输入文件对应的解码器将音视频码流解码成普通数据(YUV、PCM等等)
③编码,我们想要输出的格式对应的编码器将数据再编码成码流
④再封装,装好就可以输出了,得到我们想要的格式了。
在网上也看到有大佬总结了各种音视频封装格式可以使用的编解码器,如下图
下面直接给上代码,每一步都有相应的注释
/*
* @Time: 2023.10.24
* @Author: Wu Liu
* @File: T_format6.cpp
* @Function: format conversion
*/
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
#include <iostream>
/* @T_format6.cpp
* 基本流程:
* 1.解析输入文件,获得流信息,确定音视频解码器参数、上下文。
* 2.根据输出要求配置音视频编码器参数
* 3.循环每一帧解码、再编码输出
* 4.内存清理
*/
bool Format_conver(const std::string& inputFile, const std::string& outputFileName, const std::string& Format) {
avformat_network_init(); // 初始化网络库
AVFormatContext* inputFormatContext = nullptr;
AVCodecContext* videoCodecContext = nullptr;
AVCodecContext* audioCodecContext = nullptr;
AVFormatContext* outputFormatContext = nullptr;
AVStream* videoStream = nullptr;
AVStream* audioStream = nullptr;
SwsContext* swsContext = nullptr;
AVCodecID videoCodecId ;
AVCodecID audioCodecId ;
if (Format == "avi")
{
videoCodecId = AV_CODEC_ID_MPEG2VIDEO;
audioCodecId = AV_CODEC_ID_PCM_S16LE;
}
else if (Format == "mp4")
{
videoCodecId = AV_CODEC_ID_H264;
audioCodecId = AV_CODEC_ID_AAC;
}
else if (Format == "wmv")
{
videoCodecId = AV_CODEC_ID_MSMPEG4V3;
audioCodecId = AV_CODEC_ID_WMAV2;
}
else if (Format == "mkv")
{
videoCodecId = AV_CODEC_ID_H264;
audioCodecId = AV_CODEC_ID_MP3;
}
else if (Format == "flv")
{
videoCodecId = AV_CODEC_ID_MPEG4;
audioCodecId = AV_CODEC_ID_AAC;
}
else {
std::cout << "不支持转换为这种格式" << std::endl;
return false;
}
// 打开输入文件
if (avformat_open_input(&inputFormatContext, inputFile.c_str(), nullptr, nullptr) != 0) {
std::cout << "无法打开输入文件" << std::endl;
return false;
}
// 获取流信息
if (avformat_find_stream_info(inputFormatContext, nullptr) < 0) {
std::cout << "无法获取输入文件流信息" << std::endl;
avformat_close_input(&inputFormatContext);
return false;
}
// 查找视频流和音频流索引
int videoStreamIndex = -1;
int audioStreamIndex = -1;
for (int i = 0; i < inputFormatContext->nb_streams; i++) {
if (inputFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
}
else if (inputFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audioStreamIndex = i;
}
}
if (videoStreamIndex == -1 || audioStreamIndex == -1) {
std::cout << "没有找到视频流" << std::endl;
avformat_close_input(&inputFormatContext);
return false;
}
// 获取视频和音频流
videoStream = inputFormatContext->streams[videoStreamIndex];
audioStream = inputFormatContext->streams[audioStreamIndex];
// 获取视频解码器
const AVCodec* videoCodec = avcodec_find_decoder(videoStream->codecpar->codec_id);
if (!videoCodec) {
std::cout << "没有找到视频解码器" << std::endl;
avformat_close_input(&inputFormatContext);
return false;
}
// 创建并打开视频解码器上下文
videoCodecContext = avcodec_alloc_context3(videoCodec);
if (!videoCodecContext) {
std::cout << "创建视频解码器上下文失败"<< std::endl;
avformat_close_input(&inputFormatContext);
return false;
}
//视频流参数去填充上下文context
avcodec_parameters_to_context(videoCodecContext, videoStream->codecpar);
if (avcodec_open2(videoCodecContext, videoCodec, nullptr) < 0) {
std::cout << "打开视频解码器失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
return false;
}
// 获取音频编码器
const AVCodec* audioCodec = avcodec_find_decoder(audioStream->codecpar->codec_id);
if (!audioCodec) {
std::cout << "获取音频编码器失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
return false;
}
// 创建并打开音频解码器上下文
audioCodecContext = avcodec_alloc_context3(audioCodec);
if (!audioCodecContext) {
std::cout << "创建音频编码器上下文失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
return false;
}
//音频流参数填充上下文
avcodec_parameters_to_context(audioCodecContext, audioStream->codecpar);
if (avcodec_open2(audioCodecContext, audioCodec, nullptr) < 0) {
std::cout << "打开音频编码器失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
return false;
}
// 创建输出文件的上下文
avformat_alloc_output_context2(&outputFormatContext, nullptr, nullptr, outputFileName.c_str());
if (!outputFormatContext) {
std::cout << "创建输出文件的上下文失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
return false;
}
// 添加视频流到输出上下文
AVStream* outVideoStream = avformat_new_stream(outputFormatContext, nullptr);
if (!outVideoStream) {
std::cout << "添加视频流到输出文件失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
return false;
}
outVideoStream->id = outputFormatContext->nb_streams - 1;
// avcodec_parameters_copy(outVideoStream->codecpar, videoStream->codecpar);
outVideoStream->codecpar->codec_tag = 0;
// 设置视频编码器
const AVCodec* outVideoCodec = avcodec_find_encoder(videoCodecId);
if (!outVideoCodec) {
std::cout << "设置视频编码器失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
return false;
}
AVCodecContext* outVideoCodecContext = avcodec_alloc_context3(outVideoCodec);
if (!outVideoCodecContext) {
std::cout << "设置视频编码器上下文失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
return false;
}
//视频编码器参数设置
//avcodec_parameters_to_context(outVideoCodecContext, outVideoStream->codecpar);
outVideoCodecContext->codec_id = videoCodecId;
//outVideoCodecContext->time_base = videoStream->time_base;
outVideoCodecContext->time_base.den = 25;
outVideoCodecContext->time_base.num = 1;
outVideoCodecContext->gop_size = 13;
outVideoCodecContext->bit_rate = 8000000;
outVideoCodecContext->refs = 0;
outVideoCodecContext->max_b_frames = 4;
outVideoCodecContext->width = 1920;
outVideoCodecContext->height = 1080;
outVideoCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
//从输出上下文中复制参数到输出流
avcodec_parameters_from_context(outVideoStream->codecpar, outVideoCodecContext);
// 打开视频编码器
if (avcodec_open2(outVideoCodecContext, outVideoCodec, nullptr) < 0) {
std::cout << "无法打开视频编码器" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
return false;
}
// 添加音频流到输出文件
AVStream* outAudioStream = avformat_new_stream(outputFormatContext, nullptr);
if (!outAudioStream) {
std::cout << "添加音频流到输出文件失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
return false;
}
outAudioStream->id = outputFormatContext->nb_streams - 1;
//输出音频流参数复制
avcodec_parameters_copy(outAudioStream->codecpar, audioStream->codecpar);
outAudioStream->codecpar->codec_tag = 0;
// 设置音频编码器
const AVCodec* outAudioCodec = avcodec_find_encoder(audioCodecId);
if (!outAudioCodec) {
std::cout << "设置音频编码器失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
return false;
}
AVCodecContext* outAudioCodecContext = avcodec_alloc_context3(outAudioCodec);
if (!outAudioCodecContext) {
std::cout << "设置音频编码器上下文失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
return false;
}
//音频编码器参数
avcodec_parameters_to_context(outAudioCodecContext, outAudioStream->codecpar);
outAudioCodecContext->codec_id = audioCodecId;
outAudioCodecContext->time_base = audioStream->time_base;
outAudioCodecContext->sample_fmt = AV_SAMPLE_FMT_S16;
avcodec_parameters_from_context(outAudioStream->codecpar, outAudioCodecContext);
// 打开音频编码器
if (avcodec_open2(outAudioCodecContext, outAudioCodec, nullptr) < 0) {
std::cout << "无法打开音频编码器" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
return false;
}
// 打开输出文件
if (!(outputFormatContext->oformat->flags & AVFMT_NOFILE)) {
if (avio_open(&outputFormatContext->pb, outputFileName.c_str(), AVIO_FLAG_WRITE) < 0) {
std::cout << "无法打开输出文件" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
return false;
}
}
// 写入输出文件头
if (avformat_write_header(outputFormatContext, nullptr) < 0) {
std::cout << "无法写入输出文件头" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
return false;
}
//打印输出相关信息
av_dump_format(outputFormatContext, 0, outputFileName.c_str(), 1);
// 分配帧对象
AVFrame* videoFrame = av_frame_alloc();
AVFrame* audioFrame = av_frame_alloc();
AVPacket* inputPacket = av_packet_alloc();
AVPacket* videoOutputPacket = av_packet_alloc();
AVPacket* audioOutputPacket = av_packet_alloc();
if (!videoFrame || !audioFrame || !inputPacket || !videoOutputPacket || !audioOutputPacket) {
std::cout << "分配帧对象失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
return false;
}
// 初始化像素格式转换器
swsContext = sws_getContext(videoCodecContext->width, videoCodecContext->height, videoCodecContext->pix_fmt,
outVideoCodecContext->width, outVideoCodecContext->height, outVideoCodecContext->pix_fmt,
SWS_BILINEAR, nullptr, nullptr, nullptr);
if (!swsContext) {
std::cout << "初始化像素格式转换器失败" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
av_frame_free(&videoFrame);
av_frame_free(&audioFrame);
av_packet_free(&inputPacket);
av_packet_free(&videoOutputPacket);
av_packet_free(&audioOutputPacket);
return false;
}
// 解码并编码每一帧
int ret = 0;
int nVideoCount = 0;
while (av_read_frame(inputFormatContext, inputPacket) >= 0) {
if (inputPacket->stream_index == videoStreamIndex) {
// 视频流处理
ret = avcodec_send_packet(videoCodecContext, inputPacket);
if (ret < 0) {
break;
}
while (ret >= 0) {
ret = avcodec_receive_frame(videoCodecContext, videoFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if (ret < 0) {
std::cout << "视频解码 ret 异常" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
av_frame_free(&videoFrame);
av_frame_free(&audioFrame);
av_packet_free(&inputPacket);
av_packet_free(&videoOutputPacket);
av_packet_free(&audioOutputPacket);
return false;
}
// 转换像素格式
sws_scale(swsContext, videoFrame->data, videoFrame->linesize, 0, videoCodecContext->height,
videoFrame->data, videoFrame->linesize);
// 编码视频帧
videoFrame->pts = (int64_t)(40 * (nVideoCount) / av_q2d(outVideoCodecContext->time_base) / 1000.0);//时间
nVideoCount++;
ret = avcodec_send_frame(outVideoCodecContext, videoFrame);
if (ret < 0) {
break;
}
while (ret >= 0) {
ret = avcodec_receive_packet(outVideoCodecContext, videoOutputPacket);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if (ret < 0) {
std::cout << "视频编码 ret 异常" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
av_frame_free(&videoFrame);
av_frame_free(&audioFrame);
av_packet_free(&inputPacket);
av_packet_free(&videoOutputPacket);
av_packet_free(&audioOutputPacket);
return false;
}
av_packet_rescale_ts(videoOutputPacket, outVideoCodecContext->time_base, outVideoStream->time_base);
videoOutputPacket->stream_index = outVideoStream->index;
// 写入视频帧到输出文件
ret = av_interleaved_write_frame(outputFormatContext, videoOutputPacket);
if (ret < 0) {
break;
}
}
}
}
else if (inputPacket->stream_index == audioStreamIndex) {
// 音频流处理
ret = avcodec_send_packet(audioCodecContext, inputPacket);
if (ret < 0) {
break;
}
while (ret >= 0) {
ret = avcodec_receive_frame(audioCodecContext, audioFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if (ret < 0) {
std::cout << "音频解码 ret 异常" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
av_frame_free(&videoFrame);
av_frame_free(&audioFrame);
av_packet_free(&inputPacket);
av_packet_free(&videoOutputPacket);
av_packet_free(&audioOutputPacket);
return false;
}
// 编码音频帧
ret = avcodec_send_frame(outAudioCodecContext, audioFrame);
if (ret < 0) {
break;
}
while (ret >= 0) {
ret = avcodec_receive_packet(outAudioCodecContext, audioOutputPacket);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if (ret < 0) {
std::cout << "音频编码 ret 异常" << std::endl;
avformat_close_input(&inputFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avformat_free_context(outputFormatContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
av_frame_free(&videoFrame);
av_frame_free(&audioFrame);
av_packet_free(&inputPacket);
av_packet_free(&videoOutputPacket);
av_packet_free(&audioOutputPacket);
return false;
}
av_packet_rescale_ts(audioOutputPacket, outAudioCodecContext->time_base, outAudioStream->time_base);
audioOutputPacket->stream_index = outAudioStream->index;
// 写入音频帧到输出文件
ret = av_interleaved_write_frame(outputFormatContext, audioOutputPacket);
if (ret < 0) {
break;
}
}
}
}
av_packet_unref(inputPacket);
}
// 写入输出文件尾部
av_write_trailer(outputFormatContext);
// 释放资源
av_frame_free(&videoFrame);
av_frame_free(&audioFrame);
av_packet_free(&inputPacket);
av_packet_free(&videoOutputPacket);
av_packet_free(&audioOutputPacket);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);
avcodec_free_context(&outVideoCodecContext);
avcodec_free_context(&outAudioCodecContext);
avformat_close_input(&inputFormatContext);
avformat_free_context(outputFormatContext);
sws_freeContext(swsContext);
return true;
}
int main() {
// 输入文件名和输出文件名
std::string inputFilename, outputFilename, Format;
std::cout << "请输入输入文件名(带后缀):";
std::cin >> inputFilename;
std::cout << "请输入输出格式(avi,mp4,wmv,mkv,flv...):";
std::cin >> Format;
std::cout << "请输入输出文件名(带后缀):";
std::cin >> outputFilename;
if (!Format_conver(inputFilename, outputFilename, Format)) {
std::cout << "Failed to convert!" << std::endl;
return -1;
}
std::cout << "Conversion complete!" << std::endl;
return 0;
}
这里我也想多转换点格式的,但是发现好像有点麻烦,就算了吧。里面主要是关于参数和时间戳的设计,我处理了很久是比较复杂的一点。一开始我想直接就用输入源的参数,参数太多了懒得一个一个报错了再去改,谁知道错的离谱,代码里面我自己设定了几个参数,可能不太全,但是代码可以运行了,结果也是准确的,还是不错的,毕竟自己也是刚开始学习。以后慢慢的接触的多了,再回来好好改现在存在的问题吧。