本文借助SDL框架, 实现一个简单的YUV播放器。
运行环境:Windows10, Qt5.13, SDL2.1
实现功能:空格键控制暂停/继续, ESC退出,z、x、c调整播放速度。
PS:
SDL入门操练
SDL检测按键
ffmpeg 制作YUV视频
对非专业人士来说, 是没有机会接触YUV数据的, 更不会有播放YUV的需求,算是比较小众的了。
YUV数据跟平时大家接触的视频数据不一样, 平时接触的mp4、avi等格式,是封装好的,可以直接用播放器播放, YUV数据只是在视频处理过程的中间数据, 除了每一帧的图像数据, 没有任何说明信息,也就是所说的头, 所以YUV播放器就需要用户指定yuv采集格式,分辨率信息, 而不是自动识别。素材也需要朋友们自己做:请参考ffmpeg 分离视频中的YUV分量
SDL_UpdateTexture:
把存在字符数组里的像素数据更新到纹理上指定的一个矩形里
/**
* \brief Update the given texture rectangle with new pixel data.
*
* \param texture The texture to update
* \param rect A pointer to the rectangle of pixels to update, or NULL to
* update the entire texture.
* \param pixels The raw pixel data in the format of the texture.
* \param pitch The number of bytes in a row of pixel data, including padding between lines.
*
* The pixel data must be in the format of the texture. The pixel format can be
* queried with SDL_QueryTexture.
*
* \return 0 on success, or -1 if the texture is not valid.
*
* \note This is a fairly slow function.
*/
extern DECLSPEC int SDLCALL SDL_UpdateTexture(SDL_Texture * texture,
const SDL_Rect * rect,
const void *pixels, int pitch);
SDL_RenderCopy:
把纹理上矩形区域的数据copy到渲染器上指定的矩形里
/**
* \brief Copy a portion of the texture to the current rendering target.
*
* \param renderer The renderer which should copy parts of a texture.
* \param texture The source texture.
* \param srcrect A pointer to the source rectangle, or NULL for the entire
* texture.
* \param dstrect A pointer to the destination rectangle, or NULL for the
* entire rendering target.
*
* \return 0 on success, or -1 on error
*/
extern DECLSPEC int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,
SDL_Texture * texture,
const SDL_Rect * srcrect,
const SDL_Rect * dstrect);
#include
#include
#undef main
using namespace std;
#define FILE_NAME "D:\\ZLJ\\stuff\\Vedio\\dest_yuv.yuv"
#define EVENT_FREASH (SDL_USEREVENT + 1)
static bool QUITFREASH = false;
static bool PAUSE = false;
static unsigned int delayTime = 40;
static unsigned int step = 4;
int threadFunc(void *arg) {
SDL_Event event;
event.type = EVENT_FREASH;
while (!QUITFREASH ) {
if (!PAUSE) {
SDL_PushEvent(&event);
SDL_Delay(delayTime);
}
}
event.type = SDL_QUIT;
SDL_PushEvent(&event);
return 0;
}
int main()
{
int rst = 0;
SDL_Window *window = nullptr;
SDL_Renderer *render = nullptr;
SDL_Texture *texture= nullptr;
SDL_Thread *thread = nullptr;
SDL_Event event;
SDL_Rect rec;
FILE *file = nullptr;
char *buff = nullptr;
size_t len = 0;
int pix_w = 1280;
int pix_h = 720;
int screen_w= 1280;
int screen_h= 720;
rst = SDL_Init(SDL_INIT_VIDEO);
if (rst != 0) {
SDL_Log("rst = %d\n", rst);
return -1;
}
window = SDL_CreateWindow("YUV player", 30, 30, screen_w, screen_h, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
if (window == nullptr) {
SDL_Log("create window failed\n");
goto _QUIT_SDL;
}
render = SDL_CreateRenderer(window, -1, 0);
if (render == nullptr) {
SDL_Log("create render failed\n");
goto _DESTROY_WINDOW;
}
texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_TARGET, pix_w, pix_h);
if (texture == nullptr) {
SDL_Log("create texture failed\n");
goto _DESTROY_RENDER;
}
file = fopen(FILE_NAME, "rb+");
if (file == nullptr) {
SDL_Log("open source file failed\n");
goto _DESTROY_TEXTURE;
}
buff = static_cast<char *>(malloc(static_cast<size_t>(pix_w * pix_h * 3 / 2)));
if (buff == nullptr) {
SDL_Log("malloc buff failed\n");
goto _CLOSE_FILE;
}
rec.x = 10;
rec.y = 10;
rec.w = screen_w - 20;
rec.h = screen_h - 20;
thread = SDL_CreateThread(threadFunc, "dealDelay", nullptr);
SDL_SetRenderDrawColor(render, 255, 0, 100, 200);
while (1) {
SDL_WaitEvent(&event);
if (event.type == EVENT_FREASH) {
len = fread(buff, 1, static_cast<size_t>(pix_w * pix_h * 3 / 2), file);
if (len <= 0) {
SDL_Log("arrive to the end or read failed len = %d\n", static_cast<int>(len));
QUITFREASH = 1;
break;
}
else {
if (len == static_cast<size_t>(pix_w * pix_h * 3 / 2)) {
SDL_RenderClear(render);
SDL_UpdateTexture(texture, nullptr, buff, pix_w);
SDL_RenderCopy(render, texture, nullptr, &rec);
SDL_RenderPresent(render);
} else {
SDL_Log(" read failed len = %d\n", static_cast<int>(len));
QUITFREASH = 1;
break;
}
}
} else if (event.type == SDL_QUIT) {
QUITFREASH = 1;
SDL_Log("recive a SDL_QUIT event\n");
break;
} else if (event.type == SDL_WINDOWEVENT) {
SDL_GetWindowSize(window, &screen_w, &screen_h);
rec.w = screen_w - 20;
rec.h = screen_h - 20;
SDL_Log("recive a SDL_WINDOWEVENT event\n");
} else if (event.type == SDL_KEYUP) {
if (event.key.keysym.sym == SDLK_SPACE) {
PAUSE = PAUSE ? false : true;
} else if (event.key.keysym.sym == SDLK_ESCAPE) {
QUITFREASH = 1;
SDL_Log("ESC is pressed , it will exit \n");
break;
} else if (event.key.keysym.sym == SDLK_z) {
delayTime += step;
delayTime = delayTime > 80 ? 80 : delayTime;
SDL_Log("z is pressed , delayTime = %u \n", delayTime);
} else if (event.key.keysym.sym == SDLK_x) {
delayTime = 40;
} else if (event.key.keysym.sym == SDLK_c) {
delayTime -= step;
delayTime = delayTime > 0 ? delayTime : step;
SDL_Log("z is pressed , delayTime = %u \n", delayTime);
}
}
}
free(buff);
_CLOSE_FILE:
fclose(file);
_DESTROY_TEXTURE:
SDL_DestroyTexture(texture);
_DESTROY_RENDER:
SDL_DestroyRenderer(render);
_DESTROY_WINDOW:
SDL_DestroyWindow(window);
_QUIT_SDL:
SDL_Quit();
return 0;
}
本文参考学习了雷霄骅大神的遗作, 深切缅怀雷神,感谢雷神为我辈提供了宝贵的学习教程