美文网首页工作生活
SDL实现YUV播放器

SDL实现YUV播放器

作者: 一叶知秋0830 | 来源:发表于2019-08-09 17:26 被阅读0次

    用SDL来实现YUV播放器主要是通过一个播放时间管理线程,每隔一段时间(比如1秒播放25帧那就每隔40毫秒)发送一个刷新事件,启动事件监听后,每次监听到刷新事件后就从YUV文件中读取一帧图片在SDT创建的窗口中进行纹理渲染展示。

    /*YUC播放器*/
    #include <stdio.h>
    #include <string.h>
    #include <SDL.h>
    
    #define BLOCK_SIZE 4096000
    
    //event message
    #define REFRESH_EVENT  (SDL_USEREVENT + 1)
    #define QUIT_EVENT  (SDL_USEREVENT + 2)
    
    int thread_exit=0;
    
    int refresh_video_timer(void *udata){
    
        thread_exit=0;
    
        while (!thread_exit) { //  每隔40毫秒发送一个REFRESH_EVENT事件,也就是一秒播放25帧。
            SDL_Event event;
            event.type = REFRESH_EVENT;
            SDL_PushEvent(&event);
            SDL_Delay(40);
        }
    
        thread_exit=0;
    
        // 线程结束时发送一个QUIT_EVENT事件
        SDL_Event event;
        event.type = QUIT_EVENT;
        SDL_PushEvent(&event);
    
        return 0;
    }
    
    int main(int argc, char* argv[])
    {
    
        FILE *video_fd = NULL; // 播放文件的指针
     
        SDL_Event event; // 监听事件
        SDL_Rect rect; // 矩形框
    
        Uint32 pixformat = 0; // 像素格式
    
        SDL_Window *win = NULL; // 窗口
        SDL_Renderer *renderer = NULL; // 渲染器
        SDL_Texture *texture = NULL; // 纹理
    
        SDL_Thread *timer_thread = NULL; // 每帧切换时间管理线程
    
        int w_width = 608, w_height = 368; // 窗口宽高
        const int video_width = 608, video_height = 368; // 视频展示宽高(纹理宽高)
    
        Uint8 *video_pos = NULL; // video_buf的首地址
        Uint8 *video_end = NULL; // video_buf的尾地址
    
        unsigned int remain_len = 0;
        unsigned int video_buff_len = 0; // 一张YUV图片的长度
        unsigned int blank_space_len = 0;
        Uint8 video_buf[BLOCK_SIZE]; // 读取的一张yuv图片的指针
    
        const char *path = "test.yuv";
    
        // 一帧YUV数据的大小是 width * height *1.5(即12/8),用 12/8是为了计算的更快
        const unsigned int yuv_frame_len = video_width * video_height * 12 / 8;
    
        // 初始化 SDL
        if(SDL_Init(SDL_INIT_VIDEO)) {
            printf("初始化SDL失败!");
            return -1;
        }
    
        // 创建窗口
        win = SDL_CreateWindow("YUV 播放器",
                               SDL_WINDOWPOS_UNDEFINED,
                               SDL_WINDOWPOS_UNDEFINED,
                               w_width, w_height,
                               SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
        if(!win) {
            printf("创建窗口失败!");
            goto __FAIL;
        }
    
        // 创建渲染器
        renderer = SDL_CreateRenderer(win, -1, 0);
    
        //IYUV: Y + U + V  (3 planes)
        //YV12: Y + V + U  (3 planes)
        // 像素格式
        pixformat= SDL_PIXELFORMAT_IYUV;
    
        // 创建纹理
        texture = SDL_CreateTexture(renderer,
                                    pixformat,
                                    SDL_TEXTUREACCESS_STREAMING,
                                    video_width,
                                    video_height);
    
        // 打开yuv文件
        video_fd = fopen(path, "r");
        if( !video_fd ){
            fprintf(stderr, "Failed to open yuv file\n");
            goto __FAIL;
        }
    
        // 从yuv文件中读取一张图片
        if((video_buff_len = fread(video_buf, 1, BLOCK_SIZE, video_fd)) <= 0){
            fprintf(stderr, "Failed to read data from yuv file!\n");
            goto __FAIL;
        }
    
        //set video positon
        video_pos = video_buf;
        video_end = video_buf + video_buff_len;
        blank_space_len = BLOCK_SIZE - video_buff_len;
    
        // 创建一个线程管理渲染更新时间,refresh_video_timer是函数名
        timer_thread = SDL_CreateThread(refresh_video_timer,
                                        NULL,
                                        NULL);
    
        // 监听事件,每次收到REFRESH_EVENT事件就读取下一帧图片进行刷新
        do {
            //Wait
            SDL_WaitEvent(&event);
            if(event.type==REFRESH_EVENT){ // 收到刷新事件(需要展示一张图时就需要刷新)
                //not enought data to render
                if((video_pos + yuv_frame_len) > video_end){
    
                    //have remain data, but there isn't space
                    remain_len = video_end - video_pos;
                    if(remain_len && !blank_space_len) {
                        //copy data to header of buffer
                        memcpy(video_buf, video_pos, remain_len);
    
                        blank_space_len = BLOCK_SIZE - remain_len;
                        video_pos = video_buf;
                        video_end = video_buf + remain_len;
                    }
    
                    //at the end of buffer, so rotate to header of buffer
                    if(video_end == (video_buf + BLOCK_SIZE)){
                        video_pos = video_buf;
                        video_end = video_buf;
                        blank_space_len = BLOCK_SIZE;
                    }
    
                    //read data from yuv file to buffer
                    if((video_buff_len = fread(video_end, 1, blank_space_len, video_fd)) <= 0){
                        fprintf(stderr, "eof, exit thread!");
                        thread_exit = 1;
                        continue;// to wait event for exiting
                    }
    
                    //reset video_end
                    video_end += video_buff_len;
                    blank_space_len -= video_buff_len;
                    printf("not enought data: pos:%p, video_end:%p, blank_space_len:%d\n", video_pos, video_end, blank_space_len);
                }
    
                // 更新纹理
                SDL_UpdateTexture( texture, NULL, video_pos, video_width);
    
                //FIX: If window is resize
                // 矩形区域时视频显示区域
                rect.x = 0;
                rect.y = 0;
                rect.w = w_width;
                rect.h = w_height;
    
                SDL_RenderClear( renderer );
                SDL_RenderCopy( renderer, texture, NULL, &rect); // 将纹理拷贝到渲染器,rect是视频显示区域
                SDL_RenderPresent( renderer ); // 展示视频
    
                // printf("not enought data: pos:%p, video_end:%p, blank_space_len:%d\n", video_pos, video_end, blank_space_len);
                // 处理完后将video_pos指向下一帧
                video_pos += yuv_frame_len;
    
            }else if(event.type==SDL_WINDOWEVENT){
                // 窗口缩放
                SDL_GetWindowSize(win, &w_width, &w_height);
            }else if(event.type==SDL_QUIT){ 
                thread_exit=1;
            }else if(event.type==QUIT_EVENT){
                break;
            }
        }while ( 1 );
    
    __FAIL:
    
        //close file
        if(video_fd){
            fclose(video_fd);
        }
    
        SDL_Quit();
    
        return 0;
    }
    
    
    

    相关文章

      网友评论

        本文标题:SDL实现YUV播放器

        本文链接:https://www.haomeiwen.com/subject/ypzlhctx.html