我正在用 C++ 制作一个视频播放器,它在 linux 帧缓冲区上显示视频。帧在更新后不直接可见,但只有在 tty 更新后才可见(例如,光标闪烁或我按住某个键)。
我不认为 cpu 会变慢/做太多事情,因为没有一个内核有 100% 的使用率,而程序最多使用 30% 的 cpu。
如果我运行 htop,它将光标隐藏在 tty 上,帧缓冲区只会在 htop 刷新时更新(每 1.5 秒)。
我在带有双核 Intel Core i3-8145U 和集成显卡(Intel UHD Graphics 620) 带有 i915 驱动程序。
有没有办法告诉帧缓冲区更新? 我尝试使用 fsync,但它没有做任何事情。
在 tty 上打印换行符会使 tty 更新,但这似乎没有必要。
我尝试了 ioctl(tty1_fd, KDSETMODE, KD_GRAPHICS)
,但这会冻结输出,我必须通过 ssh 登录并运行 ioctl(tty1_fd, KDSETMODE, KD_TEXT)
才能对笔记本电脑执行任何操作或查看更新的帧缓冲区。
这只会影响我的笔记本电脑。如果我在配备双核 AMD E-350 和(集成?)Advanced Micro Devices [AMD/ATI] Wrestler [Radeon HD 6310] 显卡和 linux 5.11.10-gentoo-x86_64 的 PC 上运行它,一切正常。
This framebuffer example 也有同样的问题。
我尝试用 mplayer 播放视频,但它也不起作用。
如果我转到 tty3 并使用 mplayer -vo fbdev2:/dev/fb0 video.mp4
运行它,视频将正常播放而没有任何延迟。
如果我切换到 tty4,视频(不是声音)会开始卡顿。
看起来它仅在 tty 更新帧缓冲区后才显示帧缓冲区的内容(即光标闪烁或写入新字符)
它适用于 tty3,因为 mplayer 在每次屏幕更新后写入当前时间,这将使帧缓冲区更新。
最小示例:
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <chrono>
#include <thread>
#include <sys/mman.h>
constexpr size_t WIDTH = 1920; // framebuffer width (in pixels)
constexpr size_t HEIGHT = 1080; // framebuffer height (in pixel)
constexpr size_t BYTES_PER_PIXEL = 4; // bytes per pixel (bgra)
constexpr size_t MEM_WIDTH = WIDTH * BYTES_PER_PIXEL; // framebuffer width in memory
constexpr size_t FRAME_SIZE = MEM_WIDTH * HEIGHT; // size of one frame
constexpr size_t FPS = 24; // framerate
static constexpr size_t NSPF = FPS ? std::nano::den / (FPS * std::nano::num) : 0; // nanoseconds per frame
static constexpr std::chrono::nanoseconds SLEEP_DURATION{NSPF};
int fb_fd; // framebuffer filedescriptor
uint8_t *fb_mem; // pointer to mmaped framebuffer
static void set_pixel(size_t x, size_t y, bool black)
{
auto *pixel = &fb_mem[MEM_WIDTH * y + x * BYTES_PER_PIXEL];
std::fill_n(pixel, 4, black ? 0 : 255);
}
int main()
{
fb_fd = open("/dev/fb0", O_RDWR);
fb_mem = static_cast<uint8_t *>(mmap(nullptr, FRAME_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0));
size_t x = 0;
size_t y = 100;
bool black = false;
while (true)
{
for (size_t _x = x; x < _x + 10; x++)
set_pixel(x, y, black);
if (x >= WIDTH)
{
x %= WIDTH; // go back to the beginning side of the line
black = !black;
}
std::this_thread::sleep_for(SLEEP_DURATION); // wait for the next frame
}
}