总体思路:在主函数中初始化SDL,读取文件,将数据交给线程Main,在线程Main中,通过while循环,以及用户设置的fps,根据fps的计算,读取数据进入View()函数中进行处理。

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
SDLQtRGB::SDLQtRGB(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);

// 打开yuv文件
yuv_file.open("400_300_25.yuv", std::ios::binary);

// 拿到文件大小
yuv_file.seekg(0, ios::end); // 移到文件结尾
file_size = yuv_file.tellg(); // 文件指针位置,获取文件大小
yuv_file.seekg(0, ios::beg); // 移到文件开头

// 绑定渲染函数信号槽
// 当调用ViewS()函数,就会触发View()函数
connect(this, SIGNAL(ViewS()), this, SLOT(View()));

// 显示FPS的控件
view_fps = new QLabel(this);
view_fps->setText("fps: 100");

// 设置fps
set_fps = new QSpinBox(this);
set_fps->move(200, 0);
set_fps->setValue(25);
set_fps->setRange(1, 50);


sdl_width = 400;
sdl_height = 300;
this->resize(sdl_width, sdl_height); // 调整主窗口的尺寸
ui.label->resize(sdl_width, sdl_height);
view = XVideoView::Create();

view->Init(sdl_width, sdl_height,
XVideoView::YUV420P, (void*)ui.label->winId());

// 生成frame对象空间
frame = av_frame_alloc();
frame->width = sdl_width;
frame->height = sdl_height;
frame->format = AV_PIX_FMT_YUV420P;

frame->linesize[0] = sdl_width; // Y
frame->linesize[1] = sdl_width / 2; // U
frame->linesize[2] = sdl_width / 2; // V

// 生成存储图像空间 默认32字节对齐
auto re = av_frame_get_buffer(frame, 16);

if (re != 0)
{
char buf[1024] = { 0 };
av_strerror(re, buf, sizeof(buf));
std::cerr << buf << std::endl;
}

// startTimer(40);使用线程处理,而不使用startTimer
// 重新创建一个线程处理Main函数,并且将this指针传递给Main方法
th_ = std::thread(&SDLQtRGB::Main, this);
}

AVFrame 介绍

线程Main

控制播放速度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void SDLQtRGB::Main()
{
// is_eixt_会在主线程的析构函数中设置
// 此时当前线程才会结束
while (!is_exit_)
{
ViewS();
if (fps > 0) // 用户设置了的情况
{
// 自定义休眠函数
MSleep(1000 / fps);
}
else
MSleep(40);
}
}

// 自定义休眠函数
void MSleep(unsigned int ms)
{
auto beg = clock();
for (int i = 0; i < ms; i++)
{
this_thread::sleep_for(1ms);
if ((clock() - beg) / (CLOCKS_PER_SEC / 1000) >= ms)
break;
}
}

View函数

YUV格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void SDLQtRGB::View()
{
yuv_file.read((char*)frame->data[0], sdl_width * sdl_height); // Y
yuv_file.read((char*)frame->data[1], sdl_width * sdl_height / 4); // U
yuv_file.read((char*)frame->data[2], sdl_width * sdl_height / 4); // V

// 用于循环播放
if (yuv_file.tellg() == file_size)
{
yuv_file.seekg(0, ios::beg);
}

if (view->IsExit())
{
view->Close();
exit(0);
}

view->DrawFrame(frame);
stringstream ss;
ss << "fps: " << view->render_fps();

// 这个函数只能在槽函数中调用
view_fps->setText(ss.str().c_str());
fps = set_fps->value(); // 拿到用户设置的播放帧率
}