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分量

流程图

SDL实现简单YUV播放器_第1张图片

重要的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 
#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;
}

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

你可能感兴趣的:(音视频)