当前位置:   article > 正文

QT中使用FFmpeg视频转码_ffmpmg在qt上的使用

ffmpmg在qt上的使用

视频转码

视频转流程图

转码步骤

①转码类设计,数据的初始化

②构造函数中进行开辟内存空间和组件的注册

③封装函数用于接收需要转码的视频文件

打开视频文件,并判断是否打开成功

查找文件中的数据流,判断是否找到数据流

找到后从中找出视频流,判断是否有视频流

④封装函数用于对视频文件进行转码的一系列操作,并输出一个转码后的视频文件

猜测编码器,并判断有无数据文件并设置视频信息最终的输出格式

 

打开文件流(打开格式:写入的方式打开)并判断是否打开成功

新建视频流并判断是否新建成功

编辑器对应的设置使用函数进行拷贝,并判断是否拷贝成功

写入头部信息并判断是否写入成功

开始读取码流数据用一个while循环读取一帧一帧的数据

解码时间基真正的转换

while循环结束后记得写入尾巴帧

最后进行关闭流操作

⑤测试

创建转码类对象,调用两个函数

在工程文件中查看生产的cover.mp4文件

 

转码类package_coverH264代码

package_coverh264.h

#ifndef PACKAGE_COVERH264_H
#define PACKAGE_COVERH264_H
#include <QDebug>
#include <QImage>
#include <QThread>
#include <QString>
 
extern "C"
{
    #include <libavcodec/avcodec.h>
    #include <libavdevice/avdevice.h>
    #include <libavformat/avformat.h>
    #include <libswresample/swresample.h>
    #include <libavfilter/avfilter.h>
    #include <libavutil/adler32.h>
    #include <libswscale/swscale.h>
}
 
class package_coverH264
{
public:
    package_coverH264();
    void openfile(QString file);
    void output(QString fileout);
private:
    AVFormatContext *forContext;
    AVFormatContext *formatOut;
    AVPacket *pkt;
    AVOutputFormat * avformat;
    AVStream *newStream;
    int videoType;
};
 
#endif // PACKAGE_COVERH264_H

package_coverh264.cpp

#include "package_coverh264.h"
 
package_coverH264::package_coverH264()
{
 
    av_register_all();
    forContext =avformat_alloc_context();
    formatOut =avformat_alloc_context();
    videoType=-1;
    pkt=(AVPacket *)malloc(sizeof (AVPacket));
}
 
void package_coverH264::openfile(QString file)
{
    //打开视频文件
    int res=avformat_open_input(&forContext,file.toStdString().c_str(),nullptr,nullptr);
    if(res<0)
    {
        qDebug()<<"打开失败";
    }
    //查找流数据
    res=avformat_find_stream_info(forContext,nullptr);
    if(res<0)
    {
        qDebug()<<"没有流数据";
    }
    //从查找到的流信息中找到视频码流信息
    for(int i=0;i<forContext->nb_streams;i++)
    {
        if(forContext->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)//视频流
        {
            videoType=i;
            break;
        }
    }
    if(videoType==-1)
    {
         qDebug()<<"没有视频流数据";
    }
}
 
void package_coverH264::output(QString fileout)
{
    //猜测编码器
    avformat =av_guess_format(nullptr,fileout.toStdString().c_str(),nullptr);
    if(avformat==nullptr)//判断有无数据文件(.h264)
    {
        qDebug()<<"没有数据文件";
        return;
    }
    //设置视频信息最终的输出格式
    formatOut->oformat=avformat;
    //打开文件流(打开格式:写入的方式打开)
    int res=avio_open(&formatOut->pb,fileout.toStdString().c_str(),AVIO_FLAG_WRITE);
    if(res<0)
    {
        qDebug()<<"打开文件流对象失败";
        return;
    }
    //新建视频流
    newStream=avformat_new_stream(formatOut,nullptr);
    if(newStream==nullptr)
    {
        qDebug()<<"新建流失败";
        return;
    }
    //编辑器对应的设置
    res =avcodec_parameters_copy(newStream->codecpar,
                                 forContext->streams[videoType]->codecpar);
    if(res<0)
    {
        qDebug()<<"拷贝参数失败";
    }
    newStream->codecpar->codec_tag=0;
    //写入头部信息
    res=avformat_write_header(formatOut,nullptr);
    if(res<0)
    {
        qDebug()<<"写入失败";
    }
    //开始读取码流数据
    int size=newStream->codec->width*newStream->codec->height;
    av_new_packet(pkt,size);
    //一帧一帧的读取
    int frameCount=0;//计算第几帧
    while(av_read_frame(forContext,pkt)==0)
    {
        //判断这一帧是否为视频流
        if(pkt->stream_index==videoType)
        {
            frameCount++;
            //如果是视频流,判断有没有设置过时间基
            if(pkt->pts==AV_NOPTS_VALUE)//没有设置过
            {
                //时间基的转换
                AVRational timeBase=forContext->streams[videoType]->time_base;
                //计算帧与帧之间的长度
                int64_t duration=(double)AV_TIME_BASE/av_q2d(forContext->streams[videoType]->r_frame_rate);
                //计算显示时间基:(当前帧数*两帧之间的长度)/(输入时间基*AV_TIME_BASE)
                pkt->pts=(double)(frameCount*duration)/(av_q2d(timeBase)*AV_TIME_BASE);
                //解码时间基(没有设置过,时间基相等)
                pkt->dts=pkt->pts;
                //目标两帧之间的长度
                pkt->duration=duration/(double)(av_q2d(timeBase)*AV_TIME_BASE);
            }
            else if(pkt->pts<pkt->dts)//显示时间基小于解码时间基
            {
                continue;
            }
            //解码时间基真正的转换
            //显示时间基转换
            pkt->pts=av_rescale_q_rnd(pkt->pts,forContext->streams[videoType]->time_base,
                                      newStream->time_base,
                                      (AVRounding)(AV_ROUND_INF|AV_ROUND_PASS_MINMAX));
            //解码时间基转换
            pkt->dts=av_rescale_q_rnd(pkt->dts,forContext->streams[videoType]->time_base,
                                      newStream->time_base,
                                      (AVRounding)(AV_ROUND_INF|AV_ROUND_PASS_MINMAX));
            //设置数据时长
            pkt->duration=av_rescale_q(pkt->duration,forContext->streams[videoType]->time_base,
                                       newStream->time_base);
            //数据在流信息中的位置设置
            pkt->pos=-1;
            //数据包的标记
            pkt->flags |=AV_PKT_FLAG_KEY;
            //标记下当前写入的这一帧是视频流
            pkt->stream_index=0;
            //转码后的数据包写入目标视频结构体中
            av_interleaved_write_frame(formatOut,pkt);
 
        }
        av_packet_unref(pkt);//使用完之后记得重置
 
    }
    av_write_trailer(formatOut);//写入尾巴帧
    //关闭流和文件的操作
    avio_close(formatOut->pb);
    av_free(formatOut);
    avformat_close_input(&forContext);
    av_free(forContext);
 
}

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/758440
推荐阅读
相关标签
  

闽ICP备14008679号