【Qt+FFMPEG】- FFMPEG转码详细流程

一:FFMPEG转码过程

输入视频的封装格式是FLV,视频编码标准是H.264,音频编码标准是AAC;输出视频的封装格式是AVI,视频编码标准是MPEG2,音频编码标准是MP3

 

 

二:FFMPEG转码流程

 

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓ 

三:函数介绍

 

四:转码具体代码实现

4.1 转码类的定义如下

#ifndef TRANSCODINGVIDEO_H
#define TRANSCODINGVIDEO_H
 
#include <QObject>
#include<QThread>
 
//当前C++兼容C语言
extern "C"
{
//avcodec:编解码(最重要的库)
#include <libavcodec/avcodec.h>
//avformat:封装格式处理
#include <libavformat/avformat.h>
//swscale:视频像素数据格式转换
#include <libswscale/swscale.h>
//avdevice:各种设备的输入输出
#include <libavdevice/avdevice.h>
//avutil:工具库(大部分库都需要这个库的支持)
#include <libavutil/avutil.h>
}
 
class transcodingVideo : public QThread
{
    Q_OBJECT
public:
 
    transcodingVideo();
 
    //打开H264视频文件
    void openFile(QString file);
    //根据我们需要的封装格式进行处理
    void outPut(QString fileout);
 
    AVFormatContext *forContext,*formatout;//保存数据的结构体 forContext存输入进来的视频信息;formatout存储最终输出的视频信息
    AVPacket *pkt;//pkt
    int videoType;
 
 
 
signals:
 
public slots:
};
 
#endif // TRANSCODINGVIDEO_H

4.2 转码具体实现过程如下 

#include "transcodingvideo.h"
#include<QDebug>
 
transcodingVideo::transcodingVideo()
{
    //注册组件
    av_register_all();
    forContext= avformat_alloc_context();
}
 
void transcodingVideo::openFile(QString file)
{
    //打开输入视频
    int res=avformat_open_input(&forContext,file.toStdString().c_str(),nullptr,nullptr);
    //判断是否打开成功
    if(res<0)
    {
        qDebug()<<"打开失败";
        return;
    }
 
    //打开视频文件成功,获取文件信息
    res = avformat_find_stream_info(forContext,nullptr);//查看有没有相关视频流信息
    if(res<0)//判断是否有流媒体
    {
        qDebug()<<"没有流媒体信息"<<endl;
        return;
    }
 
    //一个视频流有多股码流,存在forContentext中streams数组中
    int videoType=-1;
    for(int i=0;i<forContext->nb_streams;i++) //i小于流的个数
    {
        if(forContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)//视频流
        {
            videoType=i;//标识类型
            break;
        }
    }
    if(videoType==-1)
    {
        qDebug()<<"没有视频流相关信息"<<endl;
        return;
    }
    qDebug()<<"输入的准备已经完成";
}
 
void transcodingVideo::outPut(QString fileout)
{
    //猜测编码器
    AVOutputFormat *avformat = av_guess_format(nullptr,fileout.toStdString().c_str(),nullptr);
    if(avformat==nullptr)
    {
        qDebug()<<"没有编码器!";
        return;
    }
    qDebug()<<"AVOutputFormat";
 
    //保存输出视频信息的结构体
    formatout = avformat_alloc_context();
    //设置输出格式
    formatout->oformat = avformat;
 
    //打开视频流 文件流
    //参数1:输入输出的上下文对象
    //参数2:文件流路径
    //参数3:文件打开格式 写的方式
    int res=avio_open(&formatout->pb,fileout.toStdString().c_str(),AVIO_FLAG_WRITE);
    if(res<0)
    {
        qDebug()<<"open file error";
        return;
    }
    qDebug()<<"avio_open";
    //新建视频流
    //参数1:视频信息结构体
    //参数2:新建流 的 返回新建流 的地址
    AVStream *newStream =avformat_new_stream(formatout,nullptr);
    if(newStream==nullptr)
    {
        qDebug()<<"打开视频流失败";
        return;
    }
    qDebug()<<"newStream";
    //编码器对应参数设置  拷贝参数设置   newStream:输入进入流的参数设置
    res = avcodec_parameters_copy(newStream->codecpar,forContext->streams[videoType]->codecpar);
    qDebug()<<"res="<<res;
    if(res<0)
    {
        qDebug()<<"拷贝失败!";
        return;
    }
    qDebug()<<"res="<<res;
    //设置新的流里面 codec_tag 设置为0
    newStream->codecpar->codec_tag = 0;
 
    //头部信息写入----写入成功与否
    res = avformat_write_header(formatout,nullptr);//formatout封装格式的结构体
    //判断写入成功与否
    if(res<0)
    {
        qDebug()<<"写入头部信息失败!";
        return;
    }
    qDebug()<<"res="<<res;
 
    //开始读取码流数据
    pkt = (AVPacket*)malloc(sizeof(AVPacket));
    //算出这张图有多大
    int size = newStream->codecpar->width*newStream->codecpar->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)
            {
                //时间基  time_base AVRational属性
                AVRational timebase=forContext->streams[videoType]->time_base;
                //计算帧之间的长度(duration)   double强制转换
                int64_t duration=(double)AV_TIME_BASE/av_q2d(forContext->streams[videoType]->r_frame_rate);
                //计算显示时间基(pts):公式:(当前帧数*两帧之间的长度))/(输入时间基*AV_TIME_BASE)
                pkt->pts = (double)(frameCount*duration)/(av_q2d(timebase)*AV_TIME_BASE);
                //解码时间基(dts)
                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;
            //数据包的标记:结合AV_PKT_FLAG_KEY使用  最小为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);//对应avio_open()
    qDebug()<<"avio_close";
 
    //释放malloc的空间 释放保存信息的结构体
    av_free(formatout);
    qDebug()<<"av_free";
 
    //关闭输入流
    avformat_close_input(&forContext);//对应avformat_open_inpu
    qDebug()<<"avformat_close_input";
 
    //释放forContext结构体空间
    av_free(forContext);
    qDebug()<<"av_free";
}

4.3 转码结果测试如下 

    //视频转码操作
        transcodingVideo *p = new transcodingVideo;       //转码
        p->openFile("outfile.h264");
        p->outPut("outfile.mp4");

 

 

编码所得到的.h264文件经过转码操作生成.mp4文件,可打开.mp4文件进行播放

如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论! 

 本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
敬告:本课程基于最新版FFmpeg4.3.1开发,仅供学习参考,不可以直接商用(想直接购买商用源代码的同学,请放弃!)。我将带领大家从以下几个方面来深入剖析ffmpeg4.3:“拿来主义”、“为我所用”。通过学习本课程,您将收获:三大多媒体框架(DirectShow、GStreamer、FFmpeg)简介与分析FFmpeg4.3的开发环境搭建(重要讲解windows下的源码编译及vs2015的环境)ffmpeg.c(4.3.1)源码剖析(分析开源大师们的设计理念和编码技巧)亲手封装私有函数:获取音视频流信息(类似于MediaInfo)、获取实时转码进度(改善用户体验)两套实现代码: MFC和Qt。-------------------------------------------------------------------音视频是一门很复杂的技术,涉及的概念、原理、理论非常多,很多初学者不学 基础理论,而是直接做项目,往往会看到c/c++的代码时一头雾水,不知道代码到底是什么意思,这是为什么呢? 因为没有学习音视频的基础理论,就比如学习英语,不学习基本单词,而是天天听英语新闻,总也听不懂。所以呢,一定要认真学习基础理论,然后再学习播放器、转码器、非编、流媒体直播、视频监控、等等。 梅老师从事音视频与流媒体行业18年;曾在永新视博、中科大洋、百度、美国Harris广播事业部等公司就职,经验丰富;曾亲手主导广电直播全套项目,精通h.264/h.265/aac,曾亲自参与百度app上的网页播放器等实战产品。目前全身心自主创业,主要聚焦音视频+流媒体行业,精通音视频加密、流媒体在线转码快编等热门产品。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值