最近闲着无聊研究了一下ffmpeg,详细过程不说了,主要是上点干货,当然可能研究的不透彻勿喷,谢谢。我的环境是基于QT的,所以有QString字串,QtDebug日志,QMessageBox提示用的,QWidget,首先这些内容都不重要。
QString 转char*:
//因ffmpeg是c风格字符串,所以需要将QString转c风格字符,所但凡看到inTmp.data(),outTmp.data()就是获取c风格字符;
//转换操作如下:
QString in("d:/1.mp4");
QByteArray inTmp;
inTmp.append(in);
const char* in_file = inTmp.data();
直接上货:抽取mp4音频并输出为aac格式,qDebug()理解为cout就可以
//in输入mp4文件如:d:/1.mp4,out输出h264文件:d:/2.aac
void extraAudio(QString in,QString out){
QByteArray inTmp;
inTmp.append(in);
QByteArray outTmp;
outTmp.append(out);
//定义上下文
AVFormatContext *ifmt_ctx = NULL;
AVFormatContext *ofmt_ctx = NULL;
AVOutputFormat *out_fmt = NULL;
//输入流
AVStream *in_stream = NULL;
//输出流
AVStream *out_stream = NULL;
//存储数据包
AVPacket packet;
//需要拷贝的流索引
int audio_stream_index = -1;
int err_code;
///打开输入文件//
err_code = avformat_open_input(&ifmt_ctx,inTmp.data(),NULL,NULL);
if(err_code < 0){
avformat_close_input(&ifmt_ctx);
qDebug()<<"avformat_open_input error: ";
return;
}
if(ifmt_ctx->nb_streams < 2){
qDebug()<<"nb_streams error: ";
avformat_close_input(&ifmt_ctx);
return;
}
audio_stream_index = av_find_best_stream(ifmt_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
if(audio_stream_index < 0){
qDebug()<<"寻找最好音频流失败,请检查输入文件!";
avformat_close_input(&ifmt_ctx);
return;
}
in_stream = ifmt_ctx->streams[audio_stream_index];
AVCodecParameters *in_parameter = in_stream->codecpar;
///打开输出文件///
// 输出上下文
ofmt_ctx = avformat_alloc_context();
out_fmt = av_guess_format(NULL,outTmp.data(),NULL);
if(!out_fmt){
qDebug()<<"avcodec_parameters_copy error:out_fmt ";
avformat_close_input(&ifmt_ctx);
avformat_close_input(&ofmt_ctx);
return ;
}
ofmt_ctx->oformat = out_fmt;
//新建输出流
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if(!out_stream){
qDebug()<<"创建输出流失败! ";
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
return;
}
初始化各种配置//
// 将参数信息拷贝到输出流中,我们只是抽取音频流,并不做音频处理,所以这里只是Copy
if((err_code = avcodec_parameters_copy(out_stream->codecpar, in_parameter)) < 0 ){
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
qDebug()<<"avcodec_parameters_copy error: ";
return;
}
//初始化AVIOContext,文件操作由它完成
if((err_code = avio_open(&ofmt_ctx->pb, outTmp.data(), AVIO_FLAG_WRITE)) < 0) {
qDebug()<<"avio_open error: ";
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
return;
}
av_init_packet(&packet);
packet.data = NULL;
packet.size = 0;
if (avformat_write_header(ofmt_ctx, NULL) < 0) {
qDebug()<<"Error occurred when opening output file";
avformat_close_input(&ifmt_ctx);
avformat_free_context(ofmt_ctx);
return;
}
while(av_read_frame(ifmt_ctx, &packet) >=0 ){
if(packet.stream_index == audio_stream_index){
//时间基计算,音频pts和dts一致
packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
//packet.dts = packet.pts;
packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, (AVRounding) (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base);
packet.pos = -1;
packet.stream_index = 0;
//将包写到输出媒体文件
av_interleaved_write_frame(ofmt_ctx, &packet);
//减少引用计数,避免内存泄漏
av_packet_unref(&packet);
}
}
av_write_trailer(ofmt_ctx);
avformat_close_input(&ifmt_ctx);
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
}
直接上货:抽取mp4音频并输出为h264格式
//in输入mp4文件如:d:/1.mp4,out输出h264文件:d:/2.h264
void extraVideo(QString in,QString out,QWidget* _this){
QByteArray inTmp;
inTmp.append(in);
QByteArray outTmp;
outTmp.append(out);
AVFormatContext *ifmt_ctx = NULL,*ofmt_ctx = NULL;
AVOutputFormat *out_fmt = NULL;
AVStream *in_stream = NULL,*out_stream = NULL;
AVPacket packet;
int video_stream_idx = -1;
int error_code;
char* error = {};
size_t buff_size = 200;
//打开输入
error_code = avformat_open_input(&ifmt_ctx,inTmp.data(),NULL,NULL);
if(error_code<0){
av_strerror(error_code, error, buff_size);
QString error_str(error);
QMessageBox::warning(_this,"错误",error_str);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
return;
}
if(ifmt_ctx->nb_streams < 2){
QMessageBox::warning(_this,"错误","文件异常,无法打开");
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
return;
}
video_stream_idx = av_find_best_stream(ifmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
if(video_stream_idx < 0){
QMessageBox::warning(_this,"错误","未找到视频流");
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx)
avformat_free_context(ofmt_ctx);
return;
}
in_stream = ifmt_ctx->streams[video_stream_idx];
AVCodecParameters *in_codec_parameters = in_stream->codecpar;
//打开输出
ofmt_ctx = avformat_alloc_context();
out_fmt = av_guess_format(NULL,outTmp.data(),NULL);
ofmt_ctx->oformat = out_fmt;
out_stream = avformat_new_stream(ofmt_ctx,NULL);
if(!out_stream){
QMessageBox::warning(_this,"错误","创建新流异常");
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx)
avformat_free_context(ofmt_ctx);
return;
}
error_code = avcodec_parameters_copy(out_stream->codecpar,in_codec_parameters);
if(error_code<0){
av_strerror(error_code, error, buff_size);
QString error_str(error);
QMessageBox::warning(_this,"错误",error_str);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx)
avformat_free_context(ofmt_ctx);
return;
}
if((error_code = avio_open(&ofmt_ctx->pb, outTmp.data(), AVIO_FLAG_WRITE)) < 0) {
av_strerror(error_code, error, buff_size);
QString error_str(error);
QMessageBox::warning(_this,"错误",error_str);
if(ofmt_ctx->pb)
avio_close(ofmt_ctx->pb);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx)
avformat_free_context(ofmt_ctx);
return;
}
av_init_packet(&packet);
packet.data = NULL;
packet.size = 0;
if ((error_code = avformat_write_header(ofmt_ctx, NULL)) < 0) {
av_strerror(error_code, error, buff_size);
QString error_str(error);
QMessageBox::warning(_this,"错误",error_str);
if(ofmt_ctx->pb)
avio_close(ofmt_ctx->pb);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx)
avformat_free_context(ofmt_ctx);
return;
}
while(av_read_frame(ifmt_ctx, &packet) >=0 ){
if(packet.stream_index == video_stream_idx){
//时间基计算,音频pts和dts一致
packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
//packet.dts = packet.pts;
packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, (AVRounding) (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base);
packet.pos = -1;
packet.stream_index = 0;
av_interleaved_write_frame(ofmt_ctx, &packet);
}
av_packet_unref(&packet);
}
av_write_trailer(ofmt_ctx);
if(ofmt_ctx->pb)
avio_close(ofmt_ctx->pb);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx)
avformat_free_context(ofmt_ctx);
}
格式转换:这里需要说明一下 因为QMessageBox需要父窗口所以这里再方法处加了一个QWidget* _this,分别将QWidget* _this,QMessageBox去掉,将qDebug换成cout,就是一个普通的函数
void converson_format(QString in,QString extend,QWidget* _this){
QByteArray inTmp;
inTmp.append(in);
QByteArray outTmp;
outTmp.append(extend);
AVFormatContext *ifmt_ctx = NULL,*ofmt_ctx = NULL;
AVOutputFormat *ofmt = NULL;
AVPacket pkt;
int error_code;
char* error;
int buff_size = 200;
int *stream_idx_map;
int stream_length = 0;
int idx = 0;
error_code = avformat_open_input(&ifmt_ctx,inTmp.data(),NULL,NULL);
if(error_code < 0){
av_strerror(error_code, error, buff_size);
QString error_str(error);
QMessageBox::warning(_this,"错误",error_str);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
}
error_code = avformat_find_stream_info(ifmt_ctx,NULL);
if(error_code<0){
av_strerror(error_code, error, buff_size);
QString error_str(error);
QMessageBox::warning(_this,"错误",error_str);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
}
error_code = avformat_alloc_output_context2(&ofmt_ctx,NULL,NULL,outTmp.data());
if(error_code < 0){
av_strerror(error_code, error, buff_size);
QString error_str(error);
QMessageBox::warning(_this,"错误",error_str);
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx)
avformat_free_context(ofmt_ctx);
}
stream_length = ifmt_ctx->nb_streams;
stream_idx_map = (int*)av_mallocz_array(stream_length,sizeof(*stream_idx_map));
if(!stream_idx_map){
QMessageBox::warning(_this,"错误","系统异常");
if(ifmt_ctx)
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx)
avformat_free_context(ofmt_ctx);
if(stream_idx_map)
av_freep(stream_idx_map);
return;
}
//5
ofmt = ofmt_ctx->oformat;
//qDebug() <<"stream_length for";
for(int i=0;i<stream_length;i++){
AVStream *out_stream = NULL,*in_stream = NULL;
in_stream = ifmt_ctx->streams[i];
AVCodecParameters *in_codecParameters = in_stream->codecpar;
if (in_codecParameters->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecParameters->codec_type != AVMEDIA_TYPE_VIDEO &&
in_codecParameters->codec_type != AVMEDIA_TYPE_SUBTITLE) {
stream_idx_map[i] = -1;
continue;
}
stream_idx_map[i] = idx++;
out_stream = avformat_new_stream(ofmt_ctx,NULL);
if (!out_stream) {
avformat_free_context(ofmt_ctx);
avformat_close_input(&ifmt_ctx);
av_freep(&stream_idx_map);
QMessageBox::warning(_this,"错误","avformat_new_stream");
return;
}
//qDebug() <<"stream_idx_map:"<<stream_idx_map;
error_code = avcodec_parameters_copy(out_stream->codecpar, in_codecParameters);
if(error_code < 0 ){
avformat_free_context(ofmt_ctx);
avformat_close_input(&ifmt_ctx);
av_freep(&stream_idx_map);
QMessageBox::warning(_this,"错误","avcodec_parameters_copy");
return;
}
out_stream->codecpar->codec_tag = 0;
}
if (!(ofmt->flags & AVFMT_NOFILE)) {
if (avio_open(&ofmt_ctx->pb, outTmp.data(), AVIO_FLAG_WRITE) < 0) {
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
avformat_close_input(&ifmt_ctx);
av_freep(&stream_idx_map);
QMessageBox::warning(_this,"错误","avio_open");
return;
}
}
//qDebug() <<"avformat_write_header";
if (avformat_write_header(ofmt_ctx, NULL) < 0) {
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
avformat_close_input(&ifmt_ctx);
av_freep(&stream_idx_map);
QMessageBox::warning(_this,"错误","avformat_write_header");
return;
}
av_init_packet(&pkt);
//av_dump_format(ofmt_ctx,NULL,outTmp.data(),NULL);
//qDebug() <<"while";
while (true){
AVStream* in_stream, * out_stream;
//循环读取每一帧数据
if (av_read_frame(ifmt_ctx, &pkt) < 0) //读取完后退出循环
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
if (pkt.stream_index >= stream_length ||
stream_idx_map[pkt.stream_index] < 0) {
av_packet_unref(&pkt);
continue;
}
pkt.stream_index = stream_idx_map[pkt.stream_index]; // 按照输出流的index给pkt重新编号
out_stream = ofmt_ctx->streams[pkt.stream_index]; // 根据pkt的stream_index获取对应的输出流
// 对pts、dts、duration进行时间基转换,不同格式时间基都不一样,不转换会导致音视频同步问题
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base,(AVRounding) (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding) (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
// 将处理好的pkt写入输出文件
av_interleaved_write_frame(ofmt_ctx, &pkt);
av_packet_unref(&pkt);
}
av_write_trailer(ofmt_ctx);
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
av_freep(&stream_idx_map);
avformat_free_context(ofmt_ctx);
avformat_close_input(&ifmt_ctx);
}