大致思路
创建window窗口 -> 基于窗口去创建渲染器 -> 基于渲染器去创建纹理
-> 事件 -> 通过读取buffer设置纹理数据 -> 将纹理数据在拷贝给渲染器 -> 显示
代码如下
// 3-FFmpeg-SDL视频播放器.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <iomanip>
#include <thread>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libswresample/swresample.h"
#include <libavutil/samplefmt.h>
#include "SDL/SDL.h"
#include "stdio.h"
}
#define REFRASH_EVENT (SDL_USEREVENT + 1)
#define QUIT_EVENT (SDL_USEREVENT + 2)
#define YUV_WIDTH 320
#define YUV_HEIGHT 180
size_t thread_exit = 0;
static uint8_t *video_buf = NULL;
static uint8_t *video_buf_pos = NULL;
static uint8_t *video_buf_end = NULL;
int refresh_video_timer(void *data) {
//std::cout << "thread " << std::endl;
/*if ((video_buf_pos >= video_buf_end) && (flag == 0)) {
return 0;
}*/
while (!thread_exit) {
SDL_Event event;
event.type = REFRASH_EVENT;
SDL_PushEvent(&event);
SDL_Delay(40);
}
thread_exit = 0;
SDL_Event event;
event.type = QUIT_EVENT;
SDL_PushEvent(&event);
return 0;
}
#undef main
int main() {
if (SDL_Init(SDL_INIT_VIDEO)) {
std::cout << "Could not init SDL - "<< SDL_GetError() << std::endl;
}
//SDL
SDL_Event event; //事件
SDL_Rect rect; //矩形
SDL_Window *window = NULL; //窗口
SDL_Renderer *renderer = NULL; //渲染
SDL_Texture *texture = NULL; //纹理
uint32_t pixformat = SDL_PIXELFORMAT_IYUV;
SDL_Thread *timer_thread = NULL;
//std::thread r_thead(refresh_video_timer,NULL);
//分辨率
int video_width = YUV_WIDTH;
int video_height = YUV_HEIGHT;
int win_width = YUV_WIDTH;
int win_height = YUV_HEIGHT;
int win_x = 100;
int win_y = 100;
//文件句柄
const char *filepath = "test_yuv420p_320x180.yuv";
FILE *video_fd = NULL;
size_t video_buf_len = 0;
//YUV420格式,并不是说只有Y分量和U分量,没有V分量。U分量和V分量是交替出现的,例如第一行为4:2:0,则第二行为4:0:2,如此反复依次交替。
uint32_t y_frame_len = video_width * video_height;
uint32_t u_frame_len = video_width * video_height / 4;
uint32_t v_frame_len = video_width * video_height / 4;
uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;
//创建窗口
window = SDL_CreateWindow("YUV PLAYER", 100,
100,
video_width, video_height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!window) {
std::cout << "Could not create window - " << SDL_GetError() << std::endl;
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) {
std::cout << "Failed to alloce yuv frame space!" << SDL_GetError() << std::endl;
goto _FAIL;
}
video_fd = fopen(filepath, "rb");
if (!video_fd) {
std::cout << "Could not open File" << std::endl;
goto _FAIL;
}
//std::thread r_thead(refresh_video_timer);
//r_thead.detach();
timer_thread = SDL_CreateThread(refresh_video_timer,NULL,NULL);
while (1) {
//收取SDL系统里的事件
SDL_WaitEvent(&event);
if (event.type == REFRASH_EVENT) {
video_buf_len = fread(video_buf, 1, yuv_frame_len, video_fd);
//std::cout << video_buf << std::endl;
if (video_buf_len <= 0) {
std::cout << " Failed to read data from file !" << std::endl;
goto _FAIL;
}
//设置纹理的数据video_width = 320 plane
SDL_UpdateTexture(texture, NULL, video_buf,video_width);
rect.x = 0;
rect.y = 0;
float_t w_ratio = win_width * 1.0 / video_width;
float_t 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);
std::cout << "SDL_WINDOWEVENT win_width: " << win_width << " win_height:" << win_height << std::endl;
}
else if (event.type == SDL_QUIT) {
thread_exit = 1;
}
else if (event.type == SDL_KEYDOWN) {
win_x = event.tfinger.x;
win_y = event.tfinger.y;
window = SDL_CreateWindow("YUV PLAYER", win_x,
win_y,
video_width, video_height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!window) {
std::cout << "Could not create window - " << SDL_GetError() << std::endl;
goto _FAIL;
}
//基于窗口创建渲染器
renderer = SDL_CreateRenderer(window, -1, 0);
//基于渲染器创建纹理
texture = SDL_CreateTexture(renderer, pixformat, SDL_TEXTUREACCESS_STREAMING, video_width, video_height);
//分配空间
}
else if(event.type == QUIT_EVENT){
break;
}
}
_FAIL:
thread_exit = 1; //保证线程能够正常退出
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;
}