使用SDL2播放yuv文件,采用C++实现SDL2视频播放器

雷神的SDL2播放器基础上用C++将播放器封装成类,里面部分元素采用C++的相关语法,如使用shared_ptr分配内存、async代替SDL的SDL_CreateThread创建线程方法等。

使用样例:

    SimpleSDLPlayer simpleSDLPlayer(352, 288, 176, 144, "SDL2 Player");
    simpleSDLPlayer.play("suzie_qcif.yuv");

类代码如下:
头文件:

#pragma once

extern "C"
{
#include "SDL/SDL.h"
}

#include <stdio.h>
#include <string>

class SimpleSDLPlayer
{
public:
    SimpleSDLPlayer(int playerWidth, int playerHeight, int yuvImagePixelWidth, int yuvImagePixelHeight, const std::string& playerName = "");
    ~SimpleSDLPlayer();

public:
    bool play(const std::string& filePath);

private:
    bool initSDLInfo();

private:
    // 播放器宽度
    int m_playerWindowWidth{ 0 };
    // 播放器高度
    int m_playerWindowHeight{ 0 };
    // 图像像素宽度
    int m_imagePixelWidth{ 0 };
    // 图像像素高度
    int m_imagePixelHeight{ 0 };

    std::string m_playerName{};
    SDL_Window *m_playerWindow{ nullptr };
    SDL_Renderer* m_sdlRenderer{ nullptr };
    SDL_Texture* m_sdlTexture{ nullptr };

    //IYUV: Y + U + V  (3 planes)
    //YV12: Y + V + U  (3 planes)
    Uint32 m_pixformat = SDL_PIXELFORMAT_IYUV;
};

cpp文件如下:

#include "SimpleSDLPlayer.h"
#include <future>
#include <iostream>

using namespace std;

//Refresh Event
#define REFRESH_EVENT  (SDL_USEREVENT + 1)
#define BREAK_EVENT  (SDL_USEREVENT + 2)

/*
 * @func   SimpleSDLPlayer::SimpleSDLPlayer 
 * @brief  构造函数
 * @param  [in]  int playerWidth  播放器宽度
 * @param  [in]  int playerHeight  播放器高度
 * @param  [in]  int yuvImagePixelWidth  yuv文件图像像素宽度
 * @param  [in]  int yuvImagePixelHeight  yuv文件图像像素高度
 * @param  [in]  const std::string & playerName  播放器名称
 * @return   
 */ 
SimpleSDLPlayer::SimpleSDLPlayer(int playerWidth, int playerHeight, int yuvImagePixelWidth, int yuvImagePixelHeight, const std::string& playerName /*= ""*/)
{
    m_playerWindowWidth = playerWidth;
    m_playerWindowHeight = playerHeight;
    m_imagePixelWidth = yuvImagePixelWidth;
    m_imagePixelHeight = yuvImagePixelHeight;
    m_playerName = playerName;

    if (!initSDLInfo())
        return;
}

SimpleSDLPlayer::~SimpleSDLPlayer()
{
}

/*
 * @func   SimpleSDLPlayer::initSDLInfo 
 * @brief  初始化SDL
 * @return bool  
 */ 
bool SimpleSDLPlayer::initSDLInfo()
{
    if (SDL_Init(SDL_INIT_VIDEO))
    {
        cout << "Could not initialize SDL : " << SDL_GetError() << endl;
        return false;
    }

    // 播放窗口创建
    m_playerWindow = SDL_CreateWindow(m_playerName.c_str(),
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        m_playerWindowWidth, m_playerWindowHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);

    if (!m_playerWindow)
    {
        cout << "SDL: could not create window - exiting: " << SDL_GetError() << endl;
        return false;
    }

    // 渲染
    m_sdlRenderer = SDL_CreateRenderer(m_playerWindow, -1, 0);
    if (!m_sdlRenderer)
    {
        cout << "SDL: could not create render - exiting: " << SDL_GetError() << endl;
        return false;
    }

    // 纹理
    m_sdlTexture = SDL_CreateTexture(m_sdlRenderer, m_pixformat, SDL_TEXTUREACCESS_STREAMING, m_imagePixelWidth, m_imagePixelHeight);
    if (!m_sdlTexture)
    {
        cout << "SDL: could not create texture - exiting: " << SDL_GetError() << endl;
        return false;
    }

    return true;
}

/*
 * @func   SimpleSDLPlayer::play 
 * @brief  播放文件
 * @param  [in]  const std::string & filePath  文件路径
 * @return bool  
 */ 
bool SimpleSDLPlayer::play(const std::string& filePath)
{
    FILE *fp = NULL;
    fp = fopen(filePath.c_str(), "rb+");
    if (fp == NULL) 
    {
        cout << "cannot open this file" << endl;
        return false;
    }

    bool isOver{ false };
    // 使用std::async异步执行刷新任务 相当于开子线程执行任务
    auto fut = std::async([&]
    {
        while (!isOver)
        {
            SDL_Event event;
            event.type = REFRESH_EVENT;
            SDL_PushEvent(&event);
            SDL_Delay(40);
        }
        isOver = false;
        //Break
        SDL_Event event;
        event.type = BREAK_EVENT;
        SDL_PushEvent(&event);

        return 0;
    });

    SDL_Event event;
    SDL_Rect sdlRect;

    while (true) 
    {
        const int bpp = 12;
        shared_ptr<char*> pBuffer = make_shared<char*>(new char[m_imagePixelWidth * m_imagePixelHeight * bpp / 8]);
        // 等待有效事件
        SDL_WaitEvent(&event);
        if (event.type == REFRESH_EVENT) 
        {
            if (fread(*pBuffer.get(), 1, m_imagePixelWidth*m_imagePixelHeight*bpp / 8, fp) != m_imagePixelWidth*m_imagePixelHeight*bpp / 8)
            {
                // Loop
                fseek(fp, 0, SEEK_SET);
                fread(*pBuffer.get(), 1, m_imagePixelWidth*m_imagePixelHeight*bpp / 8, fp);
            }

            SDL_UpdateTexture(m_sdlTexture, NULL, *pBuffer.get(), m_imagePixelWidth);

            //FIX: If window is resize
            sdlRect.x = 0;
            sdlRect.y = 0;
            sdlRect.w = m_playerWindowWidth;
            sdlRect.h = m_playerWindowHeight;

            SDL_RenderClear(m_sdlRenderer);
            SDL_RenderCopy(m_sdlRenderer, m_sdlTexture, NULL, &sdlRect);
            SDL_RenderPresent(m_sdlRenderer);
        }
        else if (event.type == SDL_WINDOWEVENT) 
        {
            //If Resize
            SDL_GetWindowSize(m_playerWindow, &m_playerWindowWidth, &m_playerWindowHeight);
        }
        else if (event.type == SDL_QUIT) 
        {
            isOver = true;
        }
        else if (event.type == BREAK_EVENT) 
        {
            break;
        }
    }
    SDL_Quit();
}

完整可运行工程,github路径:
https://github.com/lesliefish/FFMPEG

main函数测试代码如下:

#include "players/SimpleSDLPlayer/SimpleSDLPlayer.h"

int main(int argc, char *argv[])
{
    SimpleSDLPlayer simpleSDLPlayer(352, 288, 176, 144, "SDL2 Player");
    simpleSDLPlayer.play("suzie_qcif.yuv");

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值