赞
踩
1、在编码过程中涉及到一些时间戳相关概念主要:
PTS:度量解码后的视频帧什么时候被显示出来;
DTS:读入内存中的bit流在什么时候开始送入解码器中进行解码;
duration:表示当前帧的持续时间占多少个时间单位;
相关参数的理解可以参考:https://www.jianshu.com/p/1bc03e97004e/
2、实现流程图:
3、程序实现:
- void MainWindow::h264Tomp4()
- {
- AVFormatContext* pInFmtCtx = nullptr;
- int nInStreamIndex = -1;
-
- AVFormatContext* pOutFmtCtx = nullptr;
- AVStream* pOutVStream = nullptr;
- int nOutStreamIndex = -1;
- AVCodecContext* pOutCodecCtx = nullptr;
-
-
- do{
- //打开输入文件,并填充AVFormatContext上下文
- QString strInFile = "in.h264";
- if(avformat_open_input(&pInFmtCtx, strInFile.toStdString().c_str(), NULL, NULL) < 0)
- {
- qDebug() << "cannot open input file." << endl;
- break;
- }
-
- //查找输入文件中的流信息
- if(avformat_find_stream_info(pInFmtCtx, NULL) < 0){
- qDebug() << "cannot find stream info." << endl;
- break;
- }
-
- //查找视频流在流信息中的索引的索引
- for(size_t i = 0;i < pInFmtCtx->nb_streams;i++){
- if(pInFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
- nInStreamIndex = (int)i;
- break;
- }
- }
-
- //获取输入视频流的编码器参数
- AVCodecParameters *codecPara = pInFmtCtx->streams[nInStreamIndex]->codecpar;
-
- //创建AVFormatContext上下文
- QString strOutFile = "out.mp4";
- if(avformat_alloc_output_context2(&pOutFmtCtx, NULL, NULL, strOutFile.toStdString().c_str()) < 0){
- qDebug() << "cannot alloc outfile context." << endl;
- break;
- }
-
- //打开输出文件并填充AVFormatContext上下文数据
- if(avio_open(&pOutFmtCtx->pb, strOutFile.toStdString().c_str(), AVIO_FLAG_READ_WRITE) < 0){
- qDebug() << "cannot open output file." << endl;
- break;
- }
-
- //为输出AVFormatContext上下文添加视频流,并设置参数
- if(nullptr == (pOutVStream = avformat_new_stream(pOutFmtCtx, NULL)))
- {
- qDebug() << "cannot new output stream." << endl;
- break;
- }
- pOutVStream->time_base.den = 25;
- pOutVStream->time_base.num = 1;
- nOutStreamIndex = pOutVStream->index;
-
- //mp4中的视频也是h264编码,所以输入和输出编码器一致:
- //查找编码器
- AVCodec* outCodec = const_cast<AVCodec*>(avcodec_find_encoder(codecPara->codec_id));
- if(outCodec==NULL){
- printf("Cannot find any encoder.\n");
- break;
- }
-
- //创建输出编码器上下文
- pOutCodecCtx = avcodec_alloc_context3(outCodec);
- AVCodecParameters* outCodecPara = pOutFmtCtx->streams[pOutVStream->index]->codecpar;
- if(avcodec_parameters_copy(outCodecPara, codecPara)<0){
- qDebug() << "cannot copy codecparam";
- break;
- }
- if(avcodec_parameters_to_context(pOutCodecCtx, outCodecPara)<0){
- qDebug() << "avcodec_parameters_to_context failed";
- break;
- }
- pOutCodecCtx->time_base.den=25;
- pOutCodecCtx->time_base.num=1;
-
- //打开输出流编码器
- if(avcodec_open2(pOutCodecCtx,outCodec,NULL) < 0){
- qDebug() << "Cannot open output codec";
- break;
- }
-
- //写入文件头
- if(avformat_write_header(pOutFmtCtx, NULL) < 0){
- qDebug() << "Cannot write header to file";
- break;
- }
-
- //
- AVPacket pkt;
- std::int64_t llFrameIndex = 0;
- AVStream* inVStream = pInFmtCtx->streams[nInStreamIndex];
-
- //循环读取帧
- while(av_read_frame(pInFmtCtx, &pkt) >= 0){
-
- //判断是视频流
- if(pkt.stream_index == nInStreamIndex){
-
- if(pkt.pts == AV_NOPTS_VALUE){
-
- //PTS:度量解码后的视频帧什么时候被显示出来
- //DTS:读入内存中的bit流在什么时候开始送入解码器中进行解码
-
- //获取时间基:即用来度量单位刻度时间
- AVRational time_base1 = inVStream->time_base;
- //计算两帧视频之间的实际间隔时间,单位是us
- int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(inVStream->r_frame_rate);
-
- //计算PTS,即经过多少个刻度显示该帧视频
- pkt.pts = (double)(llFrameIndex*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
- pkt.dts = pkt.pts;
- //两帧间隔时间占多少个刻度
- pkt.duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
- llFrameIndex++;
- }
-
- //将输入流中时间基的PTS转换到输出流时间基中的PTS
- pkt.pts = av_rescale_q_rnd(pkt.pts, inVStream->time_base, pOutVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
- //将输入流中时间基的DTS转换到输出流时间基中的DTS
- pkt.dts = av_rescale_q_rnd(pkt.dts, inVStream->time_base, pOutVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
- //将输入流中时间基的duration转换到输出流时间基中的duration
- pkt.duration = av_rescale_q(pkt.duration, inVStream->time_base, pOutVStream->time_base);
- pkt.pos = -1;
- pkt.stream_index = nOutStreamIndex;
- //写入一帧数据
- if (av_interleaved_write_frame(pOutFmtCtx, &pkt) < 0) {
- break;
- }
- av_packet_unref(&pkt);
- }
- }
-
- av_write_trailer(pOutFmtCtx);
-
-
- }while(0);
-
- if(pOutCodecCtx)
- {
- avcodec_close(pOutCodecCtx);
- avcodec_free_context(&pOutCodecCtx);
- }
-
- if(pInFmtCtx)
- {
- avformat_free_context(pInFmtCtx);
- }
-
- if(pOutFmtCtx)
- {
- if(pOutFmtCtx->pb)
- {
- avio_close(pOutFmtCtx->pb);
- }
- avformat_free_context(pOutFmtCtx);
- }
-
- }
4、下面使用自己写的播放器播放一下MP4文件:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。