SDL实现简单YUV播放器

概述

本文借助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分量

流程图

在这里插入图片描述

重要的API

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 <iostream>
#include <SDL2/SDL.h>

#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;
}

本文参考学习了雷霄骅大神的遗作, 深切缅怀雷神,感谢雷神为我辈提供了宝贵的学习教程

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SDL2.0 支持渲染 YUV4:2:0 格式的视频,需要使用 SDL_Texture 和 SDL_RenderCopy 函数来实现。 以下是一个简单的示例代码: ```c #include <SDL2/SDL.h> #include <stdio.h> const int SCREEN_WIDTH = 640; const int SCREEN_HEIGHT = 480; int main(int argc, char* argv[]) { SDL_Window* window = NULL; SDL_Renderer* renderer = NULL; SDL_Texture* texture = NULL; if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); return 1; } window = SDL_CreateWindow("YUV4:2:0 Video", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); if (window == NULL) { printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } renderer = SDL_CreateRenderer(window, -1, 0); if (renderer == NULL) { printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); if (texture == NULL) { printf("Texture could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } // YUV data Uint8* yPlane = NULL; Uint8* uPlane = NULL; Uint8* vPlane = NULL; int yPitch = 0; int uPitch = 0; int vPitch = 0; int w = 0; int h = 0; // read YUV file FILE* fp = fopen("video.yuv", "rb"); if (fp == NULL) { printf("Could not open YUV file!\n"); return 1; } // allocate memory for YUV data int bufferSize = SCREEN_WIDTH * SCREEN_HEIGHT * 3 / 2; Uint8* buffer = (Uint8*)malloc(bufferSize); // main loop SDL_Event event; bool quit = false; while (!quit) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = true; } } // read YUV data if (fread(buffer, 1, bufferSize, fp) != bufferSize) { // end of file rewind(fp); } // set YUV data yPlane = buffer; uPlane = yPlane + SCREEN_WIDTH * SCREEN_HEIGHT; vPlane = uPlane + SCREEN_WIDTH * SCREEN_HEIGHT / 4; yPitch = SCREEN_WIDTH; uPitch = SCREEN_WIDTH / 2; vPitch = SCREEN_WIDTH / 2; w = SCREEN_WIDTH; h = SCREEN_HEIGHT; // update texture SDL_UpdateYUVTexture(texture, NULL, yPlane, yPitch, uPlane, uPitch, vPlane, vPitch); // clear screen SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(renderer); // render texture SDL_RenderCopy(renderer, texture, NULL, NULL); // update screen SDL_RenderPresent(renderer); } // free memory free(buffer); // cleanup SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ``` 在代码中,我们首先创建一个 SDL 窗口和渲染器。然后,使用 SDL_CreateTexture 函数创建一个 YV12 格式的纹理。接下来,读取 YUV 文件,并将数据设置到纹理中。在主循环中,使用 SDL_RenderCopy 函数将纹理渲染到屏幕上。最后,记得在程序结束前释放所有内存和资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值