音视频-SDL的简单使用

简介: 音视频-SDL的简单使用

使用SDL进行音视频的播放


SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。


SDL的下载地址: https://www.libsdl.org/


现在较为新的版本在main函数中需要添加#define SDL_MAIN_HANDLED定义, 初步代码如下:

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#define SDL_MAIN_HANDLED
#include <SDL.h>
//自定义消息类型
#define REFRESH_EVENT   (SDL_USEREVENT + 1)     // 请求画面刷新事件
#define QUIT_EVENT      (SDL_USEREVENT + 2)     // 退出事件
//定义分辨率
// YUV像素分辨率
#define YUV_WIDTH   320
#define YUV_HEIGHT  240
//定义YUV格式
#define YUV_FORMAT  SDL_PIXELFORMAT_IYUV
int s_thread_exit = 0;  // 退出标志 = 1则退出
int refresh_video_timer(void *data)
{
    while (!s_thread_exit)
    {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }
    s_thread_exit = 0;
    //push quit event
    SDL_Event event;
    event.type = QUIT_EVENT;
    SDL_PushEvent(&event);
    return 0;
}
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO)) {
        fprintf(stderr, "Can't not initialize SDL, err: %s\n", SDL_GetError());
    }
    SDL_Event event;                    // 事件
    SDL_Rect rect;                      // 矩形
    SDL_Window *window = NULL;          // 窗口
    SDL_Renderer *renderer = NULL;      // 渲染
    SDL_Texture *texture = NULL;        // 纹理
    SDL_Thread *timer_thread = NULL;    // 请求刷新线程
    uint32_t pixformat = YUV_FORMAT;    // YUV420P,即是SDL_PIXELFORMAT_IYUV
    // 分辨率
    // 1. YUV的分辨率
    int video_width = YUV_WIDTH;
    int video_height = YUV_HEIGHT;
    // 2.显示窗口的分辨率
    int win_width = YUV_WIDTH;
    int win_height = YUV_WIDTH;
    // YUV文件句柄
    FILE *video_fd = NULL;
    const char *yuv_path = "yuv420p_320x240.yuv";
    size_t video_buff_len = 0;
    uint8_t *video_buf = NULL; //读取数据后先把放到buffer里面
    // 我们测试的文件是YUV420P格式
    uint32_t y_frame_len = video_width * video_height;
    uint32_t u_frame_len = video_width * video_height / 4; // 420p是4个y对应一个u
    uint32_t v_frame_len = video_width * video_height / 4; // 420p是4个y对应一个v
    uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;
    window = SDL_CreateWindow("Simplest YUV Player",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              video_width, video_height,
                              SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
    if (!window) {
        printf("Can't create window, err: %s\n", SDL_GetError());
        goto _FAIL;
    }
    // 基于窗口创建渲染器
    renderer = SDL_CreateRenderer(window, -1, 0);
    // 基于渲染器创建纹理
    texture = SDL_CreateTexture(renderer,
                                pixformat,
                                SDL_TEXTUREACCESS_STREAMING,
                                video_width,
                                video_height);
    // 分配空间
    video_buf = (uint8_t*)malloc(yuv_frame_len);
    if (!video_buf) {
        fprintf(stderr, "Failed to alloce yuv frame space!\n");
        goto _FAIL;
    }
    // 打开YUV文件
    video_fd = fopen(yuv_path, "rb");
    if (!video_fd) {
        fprintf(stderr, "Failed to open yuv file\n");
        goto _FAIL;
    }
    // 创建请求刷新线程
    timer_thread = SDL_CreateThread(refresh_video_timer, NULL, NULL);
    while (1) {
        SDL_WaitEvent(&event);
        if (event.type == REFRESH_EVENT) {
            video_buff_len = fread(video_buf, 1, yuv_frame_len, video_fd);
            if (video_buff_len <= 0) {
                fprintf(stderr, "Failed to read data from yuv file!\n");
                goto _FAIL;
            }
            // 设置纹理的数据
            SDL_UpdateTexture(texture, NULL, video_buf, video_width);
            // 显示区域, 可以通过修改w和h进行缩放
            rect.x = 0;
            rect.y = 0;
            float w_ratio = win_width * 1.0 / video_width;
            float h_ratio = win_height * 1.0 / video_height;
            rect.w = video_width * w_ratio;
            rect.h = video_height * h_ratio;
            // 清屏
            SDL_RenderClear(renderer);
            // 将纹理的数据拷贝给渲染器
            SDL_RenderCopy(renderer, texture, NULL, &rect);
            // 显示
            SDL_RenderPresent(renderer);
        } else if (event.type == SDL_WINDOWEVENT) {
            SDL_GetWindowSize(window, &win_width, &win_height);
            printf("SDL_WINDOWEVENT win_width:%d, win_height:%d\n", win_width, win_height);
        } else if (event.type == SDL_QUIT) {
            s_thread_exit = 1;
        } else if (event.type == QUIT_EVENT) {
            break;
        }
    }
_FAIL:
    s_thread_exit = 1;
    // 释放资源
    if(timer_thread)
        SDL_WaitThread(timer_thread, NULL); // 等待线程退出
    if(video_buf)
        free(video_buf);
    if(video_fd)
        fclose(video_fd);
    if(texture)
        SDL_DestroyTexture(texture);
    if(renderer)
        SDL_DestroyRenderer(renderer);
    if(window)
        SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}
相关文章
|
6月前
|
机器学习/深度学习 人工智能 算法
普通人怎么学人工智能?这些隐藏学习秘籍大揭秘,生成式人工智能认证(GAI认证)来助力
在人工智能(AI)快速发展的今天,普通人学习AI已成为必然趋势。本文从明确学习目标与路径、利用多元化资源、注重实践应用、关注GAI认证及持续自我提升五个方面,为普通人提供系统化的AI学习指南。通过设定目标、学习编程语言、参与项目实践和获取专业认证,普通人可逐步掌握AI技能,在未来职场中占据优势并开启智能时代新篇章。
|
11月前
|
前端开发 JavaScript 测试技术
前端测试技术中,如何提高集成测试的效率?
前端测试技术中,如何提高集成测试的效率?
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
运维 Windows
[故障处理]WindowsServer休眠后黑屏
[故障处理]WindowsServer休眠后黑屏
270 0
|
存储 网络安全 开发工具
Git 中文参考(一)(7)
Git 中文参考(一)
149 2
|
缓存 前端开发 NoSQL
设计与实现个人博客系统的技术架构与最佳实践
设计与实现个人博客系统的技术架构与最佳实践
|
存储 算法 网络虚拟化
交换机的工作原理(含实例,华为ensp操作)
交换机的工作原理(含实例,华为ensp操作)
434 0
|
前端开发
前端ElementPlus框架中的几种图标按钮使用方式
本文介绍了如何在Element Plus前端框架中使用带有图标的按钮,包括设置按钮大小、图标大小、按钮类型以及如何为图标添加点击事件。
1261 0
|
JavaScript Java 测试技术
基于SpringBoot+Vue的云上水果超市的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的云上水果超市的详细设计和实现(源码+lw+部署文档+讲解等)
80 5
|
Web App开发 前端开发 数据可视化
requestAnimationFrame是什么?介绍 如何使用?适用场景?有哪些缺点和优点,兼容性怎么样?
requestAnimationFrame是什么?介绍 如何使用?适用场景?有哪些缺点和优点,兼容性怎么样?
462 0