ffmpeg:一个简单的格式转换器
来源:互联网 发布:网络打字员兼职招聘网 编辑:程序博客网 时间:2024/05/23 02:03
参考ffmpegAPI提供的例子实现了一个简单的封装格式转换器,在实际转换时候,需要考虑对h264的两种封装格式分别是h264和avc1。
AVC1 描述:H.264 bitstream without start codes.一般通过ffmpeg转码生成的视频,是不带起始码0×00000001的。
H264 描述:H.264 bitstream with start codes.一般对于一下HDVD等电影的压制格式,是带有起始码0×00000001的。
一般的像(mp4、mkv、flv)的封装格式都是没有startcode的AVC1。 而像avi的封装格式则是有start codes的H264 。ffmpeg提供了一个名称为“h264_mp4toannexb”的bitstream filter。其会将像mp4这样的没有start code的格式转换为像avi这样的有start code。 使用方法如下:
//注册filter AVBitStreamFilterContext * vbsf = av_bitstream_filter_init("h264_mp4toannexb");//转换if (pkt.stream_index == AVMEDIA_TYPE_VIDEO && in_stream->codecpar->codec_id == AV_CODEC_ID_H264) { AVPacket fpkt = pkt; int a = av_bitstream_filter_filter(vbsf, out_stream->codec, NULL, &fpkt.data, &fpkt.size, pkt.data, pkt.size, pkt.flags & AV_PKT_FLAG_KEY); pkt.data = fpkt.data; pkt.size = fpkt.size; }
一个完整的格式转换代码如下:
if(In_File.isEmpty() || In_File.isNull() || Out_File.isEmpty() || Out_File.isNull()) return -1; AVOutputFormat *ofmt = NULL; AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL; AVPacket pkt; int ret,i; int frame_index = 0; // --[2]初始化输入文件的格式上下文 //将文件头信息复制给avformatContext if((ret = avformat_open_input(&ifmt_ctx,In_File.toLocal8Bit(),NULL,NULL)) < 0) { qDebug()<<"could not open input file!"; goto end; } //将流的参数信息复制给ifmt_ctx->streams if((ret = avformat_find_stream_info(ifmt_ctx,NULL)) < 0) { qDebug()<<"can not find a stream!\n"; goto end; }#if USE_DEBUG //cout<<"----------------------Input file Information-----------------"<<endl; av_dump_format(ifmt_ctx,0,In_File.toLocal8Bit(),0); // cout<<"-------------------------------------------------------------"<<endl;#endif //[2] ifmt_ctx->streams[0]->codec_info_nb_frames)*(ifmt_ctx->streams[0]->codec_info_nb_frames +ifmt_ctx->streams[1]->codec_info_nb_frames); emit sendProgressMaxValue(frame_num); //[3] --初始化输出文件的格式上下文 avformat_alloc_output_context2(&ofmt_ctx,NULL,NULL,Out_File.toLocal8Bit()); if(!ofmt_ctx) { qDebug()<<"could not creat output context"; goto end; } ofmt = ofmt_ctx->oformat; //[3] //[4] --将输入文件内容复制到输出文件中 for( i =0;i<ifmt_ctx->nb_streams;++i) { //获取输入流 AVStream *in_stream = ifmt_ctx->streams[i]; //创建输出流 AVStream *out_stream = avformat_new_stream(ofmt_ctx,in_stream->codec->codec); if(!out_stream) { qDebug()<<"failed allocating output stram"; ret = AVERROR_UNKNOWN; goto end; } //复制输入流的AVCodeContext到输出流的AVCodeContext out_stream->time_base.num = in_stream->time_base.num; out_stream->time_base.den = in_stream->time_base.den; out_stream->avg_frame_rate = in_stream->avg_frame_rate; if(avcodec_copy_context(out_stream->codec,in_stream->codec) < 0) { qDebug()<<"failed to copy CodecContext!"; goto end; } out_stream->codec->codec_tag = 0; if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; } //[4]#if USE_DEBUG //cout<<"----------------------Output file Information-----------------"<<endl; av_dump_format(ofmt_ctx,0,Out_File.toLocal8Bit(),1); //cout<<"-------------------------------------------------------------"<<endl;#endif //打开输出文件 if(!(ofmt->flags & AVFMT_NOFILE)) { ret = avio_open(&ofmt_ctx->pb,Out_File.toLocal8Bit(),AVIO_FLAG_WRITE); if (ret < 0) { qDebug()<<"Could not open output file: "<<Out_File.toLocal8Bit(); goto end; } } //写头文件 if(avformat_write_header(ofmt_ctx,NULL) < 0) { qDebug()<<"Error occurred when opening output file"; goto end; } AVBitStreamFilterContext * vbsf = av_bitstream_filter_init("h264_mp4toannexb"); //写入文件内容 while(1) { AVStream *in_stream,*out_stream; ret = av_read_frame(ifmt_ctx,&pkt); if(ret < 0) break; in_stream = ifmt_ctx->streams[pkt.stream_index]; out_stream = ofmt_ctx->streams[pkt.stream_index]; //qDebug()<<pkt.pts<<" "<<pkt.dts; QStringList inExt = QString(ifmt_ctx->iformat->extensions).split(","); QStringList outExt = QString(ofmt_ctx->oformat->extensions).split(","); //qDebug()<<"inExt:"<<inExt; //qDebug()<<"outExt:"<<outExt; if(!((inExt.contains("mp4") || inExt.contains("mkv") || inExt.contains("flv")) && (outExt.contains("mp4") || outExt.contains("mkv")|| outExt.contains("flv")))) {qDebug()<<"run"; if (pkt.stream_index == AVMEDIA_TYPE_VIDEO && in_stream->codecpar->codec_id == AV_CODEC_ID_H264) { AVPacket fpkt = pkt; int a = av_bitstream_filter_filter(vbsf, out_stream->codec, NULL, &fpkt.data, &fpkt.size, pkt.data, pkt.size, pkt.flags & AV_PKT_FLAG_KEY); pkt.data = fpkt.data; pkt.size = fpkt.size; } } //转换PTS/DTS /* copy packet */ 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; //写入 if(av_interleaved_write_frame(ofmt_ctx,&pkt) < 0) { qDebug()<<"Error muxing packet"; break; }#if USE_DEBUG qDebug()<<"Write "<<frame_index<<" frames to output file";#endif av_free_packet(&pkt); frame_index++; } //写文件尾 av_write_trailer(ofmt_ctx);end: avformat_close_input(&ifmt_ctx); if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) avio_close(ofmt_ctx->pb); avformat_free_context(ofmt_ctx); av_bitstream_filter_close(vbsf); if (ret < 0 && ret != AVERROR_EOF) { qDebug()<<"Error occurred."; return -1; } return 1;}
本人使用Qt完成了格式转换器的GUI,并提供了一个进度条来预估当前转换进度。程序效果如下:
github下载地址: https://github.com/pencilmonster/Remuxer
参考文献:h264封装视频复用器AVC1、h264点击打开链接
1 0
- ffmpeg:一个简单的格式转换器
- 基于FFMPEG的封装格式转换器
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
- 一个简单的Json转换器
- 一个实用的在线文档格式转换器
- 转换器(简单日期格式)
- PDF格式转换器的使用方法
- 强大的PDF格式转换器
- 颜色格式转换: FFmpeg源代码简单分析:libswscale的sws_getContext()
- 颜色格式转换:FFmpeg源代码简单分析:libswscale的sws_scale()
- 网络格式转换器 摆脱没有转换器的烦恼
- 一个自定义转换器的模板。
- 数据库三范式的简单理解
- 环信收发文本消息
- OpenGL实验(二三代码整理)桌子
- 安卓AndroidScrollView嵌套ListView的问题及其优化
- 基于Java反射实现简易ORM
- ffmpeg:一个简单的格式转换器
- Android 代码实现TextView 数组的应用
- Oracle10g安装过程中的Configuration Assistant配置失败的问题。
- ORM
- linux-配置mysql
- csv文件load到mysql数据库
- @RequestParam 参数详解
- SQL truncate 、delete与drop区别
- QT中调用caffe的pro写法