ffmpeg —— SDL2播放yuv文件(使用事件驱动和多线程,支持按键暂停/退出)

几个结构体:

SDL_Window:视频播放时弹出的窗口(可以创建多个窗口)。
SDL_Texture:显示YUV数据。一个SDL_Texture对应一帧YUV数据。
SDL_Renderer:渲染SDL_Texture至SDL_Window。
SDL_Rect:确定图像显示的位置。


代码1:直接读取yuv数据进行播放,40ms一帧。
#include <stdio.h>
#include "SDL2/SDL.h"

void play_yuv420p(const char *yuvfile, int w, int h) {
    FILE *fp = fopen(yuvfile, "r");
    int yuv_size = w*h*3/2;
    char buf[yuv_size];

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
        printf("failed to init SDL\n");
        goto _Error;
    }

    // 创建窗口
    int screen_w = w;   // 窗口大小
    int screen_h = h;
    SDL_Window *window = SDL_CreateWindow("SDL2.0 Video Sample", \
            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, \
            screen_w, screen_h, SDL_WINDOW_RESIZABLE);
    if (window == NULL) {
        printf("failed to create window.\n");
        goto _Error;
    }

    // 创建渲染器
    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
    if (renderer == NULL) {
        printf("failed to create renderer\n");
        goto _Error;
    }

    // 创建纹理
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, \
            SDL_TEXTUREACCESS_STREAMING, screen_w, screen_h);
    if (texture == NULL) {
        printf("failed to create texture\n");
        goto _Error;
    }

	// 图像显示位置
    SDL_Rect rect = {
        .x = 0,
        .y = 0,
        .w = screen_w,
        .h = screen_h,
    };


    while (1) {
        if (fread(buf, 1, yuv_size, fp) <= 0) {
            break;
        }

        // yuv420p数据存储时是1字节对齐,使用SDL_UpdateTexture
        SDL_UpdateTexture(texture, &rect, buf, screen_w);
        // 如果是其它对齐方式使用SDL_UpdateYUVTexture函数
        // SDL_UpdateYUVTexture(texture, &rect, \
        //              frame->data[0], frame->linesize[0], \
        //              frame->data[1], frame->linesize[1], \
        //              frame->data[2], frame->linesize[2])

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL, &rect);
        SDL_RenderPresent(renderer);

        SDL_Delay(40);
    }


_Error:
    if (fp) {
        fclose(fp);
    }
    SDL_Quit();
}


int main(int argc, char const* argv[])
{
    play_yuv420p("../testvideo/output.yuv", 640, 272);    
    return 0;
}

代码2:使用事件驱动和多线程,扩展按键暂停和退出功能
#include <stdio.h>
#include "SDL2/SDL.h"

#define REFRESH_EVENT (SDL_USEREVENT + 0)
#define STOP_EVENT    (SDL_USEREVENT + 1)
#define QUIT_EVENT    (SDL_USEREVENT + 2)

int thread_quit = 0;
int thread_stop = 0;

// 事件更新线程:40ms刷新一次事件,正常时一直播放
// 发生按键操作时,会使播放主线程暂停或退出
int refresh_thread(void *p) {
    SDL_Event event;
    while (!thread_quit) {
        if (!thread_stop) {
            event.type = REFRESH_EVENT;
        } else {
            event.type = STOP_EVENT;
        }
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }
    event.type = QUIT_EVENT;
    SDL_PushEvent(&event);
    return 0;
}

void play_yuv420p(const char *yuvfile, int w, int h) {
    FILE *fp = fopen(yuvfile, "r");
    int yuv_size = w*h*3/2;
    char buf[yuv_size];

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
        printf("failed to init SDL\n");
        goto _Error;
    }

    // 创建窗口
    int screen_w = w;   // 窗口大小
    int screen_h = h;
    SDL_Window *window = SDL_CreateWindow("SDL2.0 Video Sample", \
            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, \
            screen_w, screen_h, SDL_WINDOW_RESIZABLE);
    if (window == NULL) {
        printf("failed to create window.\n");
        goto _Error;
    }

    // 创建渲染器
    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
    if (renderer == NULL) {
        printf("failed to create renderer\n");
        goto _Error;
    }

    // 创建纹理
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, \
            SDL_TEXTUREACCESS_STREAMING, screen_w, screen_h);
    if (texture == NULL) {
        printf("failed to create texture\n");
        goto _Error;
    }

    SDL_Rect rect = {
        .x = 0,
        .y = 0,
        .w = screen_w,
        .h = screen_h,
    };
    
    SDL_CreateThread(refresh_thread, "video thread", NULL);

    SDL_Event event;
    while (1) {
        if (event.type == SDL_KEYDOWN) {
            // 空格键暂停,ESC键退出
            if (event.key.keysym.sym == SDLK_SPACE) {
                thread_stop = !thread_stop;
            } else if (event.key.keysym.sym == SDLK_ESCAPE) {
                thread_quit = 1;
            }
        } 
        
        SDL_WaitEvent(&event);
        if (event.type == REFRESH_EVENT) {
            if (!thread_stop) {
                if (fread(buf, 1, yuv_size, fp) <= 0) {
                    break;
                }

                SDL_UpdateTexture(texture, &rect, buf, screen_w);

                SDL_RenderClear(renderer);
                SDL_RenderCopy(renderer, texture, NULL, &rect);
                SDL_RenderPresent(renderer);
            }
        } else if (event.type == QUIT_EVENT) {
            break;
        } else {   // STOP_EVENT 
            // do nothing...
        }
    }

_Error:
    if (fp) {
        fclose(fp);
    }
    if (window) {
        SDL_DestroyWindow(window);
    }
    if (renderer) {
        SDL_DestroyRenderer(renderer);
    }
    if (texture) {
        SDL_DestroyTexture(texture);
    }
    SDL_Quit();
}

int main(int argc, char const* argv[])
{
    play_yuv420p("../testvideo/output.yuv", 640, 272);    
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值