1、创建工程文件
建立一个新的文件,以后就按照这个工程逐渐修改,成为最终的项目。
这里需要注意的是需要勾选Multimedia这个多媒体库,才能进行多媒体播放。
然后base class选择Qwidget
同样的,需要配置输出目录,我们制定到一个地方,这样省得编译调试的时候乱糟糟的,然后将平台工具集选择支持win xp的即可。
然后到安装目录中的Qt\Qt5.8.0\5.8\msvc2013\bin拷贝这四个dll文件到刚刚我们设置的bin文件夹中,运行xplay.exe。是可以看到跳出编译好的窗口的。这样qt部分应该是没问题。
接下来就是ffmpe部分的,当然,还是老样子,记得先把include和lib路径添加进来。然后还要设置调试路径和调试控制台。(参考第一章后面几部)编译完之后再运行,就可以出现以下的窗口了。
之后在main.cpp中,添加代码,然后再编译。
#include "xplay.h"
#include <QtWidgets/QApplication>
#pragma comment(lib,"avformat.lib")
extern "C"{
#include <libavformat/avformat.h>
}
int main(int argc, char *argv[])
{
int totalSec = 0;
char re = 0;
av_register_all();
char *path = "my.mp4";
AVFormatContext *ic = NULL;
re = avformat_open_input(&ic, path, 0, 0);
if (re == 0)
{
totalSec = ic->duration / AV_TIME_BASE;
printf("file totalSec is %d\n", totalSec);
avformat_close_input(&ic);
}
QApplication a(argc, argv);
Xplay w;
w.show();
return a.exec();
}
这段代码用于测试这个视频的长短。
2、其他
追加错误信息的打印,这里只做笔记,所以不做过多过程的记录。
if (re != 0)
{
char buf[1024] = { 0 };
av_strerror(re, buf, sizeof(buf));
printf("open failed %s\n",path, buf);
getchar();
return -1;
}
av_strerror这个函数是用来储存错误信息的,然后它需要用到#pragma comment(lib,"avutil.lib")这个lib。
for (;;)
{
re = av_read_frame(ic, &pkt);
av_packet_unref(&pkt); //用于清理空间,防止内存泄露。
}
这里要说一点,读取的一帧视频大小是不固定的,因为这个视频是压缩视频。所以这个av_read_frame会为我们分配空间。
av_packet_unref这个函数是用来清除我们刚刚读取到pkt的数据的,这样做是为了防止内存泄露。然后这个函数需要用到#pragma comment(lib,"avcodec.lib")这个lib。
pkt里面有pts和dts
pts存放的是时间戳,是以AVStream->time_bash作为单位的。这样可以推算出时间的,这个就是一个显示的时间。dts是解码时间
压缩算法有帧类压缩和根据前后对应关系进行压缩这两种。对于运动视频,使用帧率压缩效果不咋地,大致就是先压缩成jpg图片再合成视频,这样的视频要么非常大要么效果差。
h264就可以使用根据前后关系对比压缩。根据前一帧、这一帧、下一帧来压缩的,所以就有了关键帧、P帧、B帧。
关键帧是完整的帧、可以设置为50帧来一个关键帧。
P帧是根据关键帧来的,P帧是存储与上一帧的变化。
B帧是存储上一帧与下一帧的变化。
B帧的出现,就需要考虑到一个问题。当前帧要解码就必先读出下一帧,所以就需要缓冲压缩机制,先把下一帧压缩。但是显示的时候不能先显示下一帧,所以就会出现显示帧会与解码帧顺序不对应。
最终代码写到了现在这个样子#include "xplay.h"
#include <QtWidgets/QApplication>
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
extern "C"{
#include <libavformat/avformat.h>
}
//为了将pkt.pts转换成秒的函数。
static double r2d(AVRational r)
{
return r.num == 0 || r.den == 0 ? 0. : (double)r.num / (double)r.den;
}
int main(int argc, char *argv[])
{
int totalSec = 0;
int pts = 0;
char re = 0;
av_register_all();
char *path = "my.mp4";
AVFormatContext *ic = NULL;
AVPacket pkt; //存放读取的数据包
re = avformat_open_input(&ic, path, 0, 0);
if (re != 0)
{
char buf[1024] = { 0 };
av_strerror(re, buf, sizeof(buf));
printf("open failed %s\n",path, buf);
getchar();
return -1;
}
totalSec = ic->duration / AV_TIME_BASE; //计算出进度条时间
printf("file totalSec is %d\n", totalSec);
for (;;)
{
re = av_read_frame(ic, &pkt);
if (re != 0) break;
pts = pkt.pts * r2d(ic->streams[pkt.stream_index]->time_base) *1000;
printf("pts = %d\n", pts);
av_packet_unref(&pkt); //用于清理空间,防止内存泄露。
}
avformat_close_input(&ic);
QApplication a(argc, argv);
Xplay w;
w.show();
return a.exec();
}