目录
一:开发项目目录结构的要求
bin:工作和测试目录
doc:开发文档目录
src:源码目录
include:ffmpeg头文件配置目录
lib:ffmpeg静态库配置目录
二:编解码基础知识的了解
2.1.封装格式:
封装格式一般指的是音视频的组合格式
常见的封装格式有:mp4 mp3 flv等
在日常使用中接触到的那些带有后缀的音视频文件都是一种封装格式。
不同的封装格式遵循不同的协议标准。
2.2.编码格式:
以mp4为例,通常应该包含有视频和音频。
视频的编码格式为YUV420P,音频的编码格式为PCM
以YUV420P为例:通常图像显示是RGB,当视频压缩时会首先将代表每一帧画面的RGB压缩为YUV。再根据关键帧(/帧),过渡帧(P帧或B帧)进行运算和编码。
解码的过程是相反的。解码器会读取到帧,并根据/帧运算和解码P帧和B帧,最终根据视频文件预设的FPS还原每一帧画面的RGB数据信息,最后给显卡。
通常的编码过程:画面采集,转码,编码再封装
2.3.视频解码和音频解码:
通常玩lol的帧率按照(25帧/秒或60帧/秒)设定图像的FPS值,【身为韩服第一厄斐琉斯觉得22-25体验最佳】。
1.视频存在关键帧和过渡帧的区别,关键帧保存了完整的画面,过渡帧只是保存了与前一帧画面的变化部分,需要通过关键帧计算获得。
需要对每一帧都进行解码(获取画面的YUV数据),只对我们所需要的显示的画面进行转码(将YUV数据转换为RGB数据)。
2.音频的播放则必须和采集保持同步,提高或降低音频的播放速度都会对音质造成影响。在实际开发中为保证播放音视频同步,因此通常会按照音频的播放速度来控制视频的解码转码速度。
三:框架思路
解码准备工作步骤:
1.注册所有组件
2.打开视频输入文件
3.查找视频流信息
4.查找解码器
5.打开解码器
四:代码
videodecode.h .cpp
#ifndef VIDEODECODE_H
#define VIDEODECODE_H
#include <QObject>
//当前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 videoDecode : public QObject
{
Q_OBJECT
public:
explicit videoDecode(QObject *parent = 0);
AVFormatContext *pFormatContext;
AVCodecContext* avcodec_context;
signals:
public slots:
};
#endif // VIDEODECODE_H
#include "videodecode.h"
#include<QDebug>
#include<QCoreApplication>
//1.注册所有组件
//2.打开视频输入文件
//3.查找视频流信息
//4.查找解码器
//5.打开解码器
videoDecode::videoDecode(QObject *parent) : QObject(parent)
{
qDebug()<<"1.注册所有组件";
av_register_all();
qDebug()<<"2.打开视频输入文件";
QString filename = QCoreApplication::applicationDirPath();
qDebug()<<"获取程序运行目录"<<filename;
QString fileurl = "中国合伙人.avi";
pFormatContext = avformat_alloc_context();
// int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
// 函数主要研究:形参代表什么 返回的是什么
int ret = avformat_open_input(&pFormatContext,fileurl.toStdString().c_str(),nullptr,nullptr);
if(ret !=0 )
{
//获取异常信息
char* error_info = new char[32];
av_strerror(ret, error_info , 1024);
qDebug()<<QString("异常信息 %1 %2").arg(ret).arg(error_info);
}
qDebug()<<"打开的视频文件格式:"<<pFormatContext->iformat->name;
qDebug()<<"3.查找视频流信息";
//参数一:封装格式上下文->AVFormatContext
//参数二:配置
//返回值:0>=返回OK,否则失败
ret = avformat_find_stream_info(pFormatContext, NULL);
if (ret < 0)
{
//获取失败
char* error_info = new char[32];
av_strerror(ret, error_info, 1024);
qDebug()<<QString("异常信息 %1").arg(error_info);
}
qDebug()<<"4.查找解码器";
qDebug()<<"流信息"<<pFormatContext->nb_streams;
//第一点:获取当前解码器是属于什么类型解码器->找到了视频流
//音频解码器、视频解码器、字幕解码器等等...
//获取视频解码器流引用
int av_stream_index = -1;
for (int i = 0; i < pFormatContext->nb_streams; ++i) {
//循环遍历每一流
//视频流、音频流、字幕流等等...
if (pFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
//找到了
av_stream_index = i;
break;
}
}
if (av_stream_index == -1)
{
qDebug()<<QString("没有找到视频流");
}
//第二点:根据视频流->查找到视频解码器上下文->视频压缩数据
//编解码器上下文
avcodec_context = pFormatContext->streams[av_stream_index]->codec;
//第三点:根据解码器上下文->获取解码器ID
AVCodec* avcodec = avcodec_find_decoder(avcodec_context->codec_id);
if (avcodec == NULL)
{
qDebug()<<QString("没有找到视频解码器");
}
qDebug()<<"获取解码器文件格式"<<avcodec->name;
qDebug()<<"分辨率是"<<avcodec_context->width<<avcodec_context->height;
qDebug()<<"5.打开解码器";
ret = avcodec_open2(avcodec_context,avcodec,NULL);
if (ret != 0)
{
char* error_info = new char[32];
av_strerror(ret, error_info, 1024);
qDebug()<<QString("异常信息 %1").arg(error_info);
}
av_dump_format(pFormatContext,0,fileurl.toStdString().c_str(),0);
}
结果:(中文文件命名会乱码)