windows安装:在VS2019中使用

1.官网下载:

解压

2:新建vs空项目

3:配置:

包含目录:include目录

库目录:lib/x64

添加依赖项:

窗口更改

4:将dll放在exe文件夹下(或者项目同级目录下)

5:测试

#include<SDL.h>
//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
int main(int argc, char* argv[]) {
    //The window we'll be rendering to
    SDL_Window* window = NULL;
    //The surface contained by the window
    SDL_Surface* screenSurface = NULL;
    //Initialize SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        SDL_Log("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
    }
    else {
        //Create window
        window = SDL_CreateWindow("title", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
        if (window == NULL) {
            SDL_Log("Window could not be created! SDL_Error: %s\n", SDL_GetError());
        }
        else {
            //Get window surface
            screenSurface = SDL_GetWindowSurface(window);

            //Fill the surface white
            SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 0xFF, 0xFF, 0xFF));

            //Update the surface
            SDL_UpdateWindowSurface(window);

            //Hack to get window to stay up
            SDL_Event e;
            bool quit = false;
            while (quit == false) {
                while (SDL_PollEvent(&e)) {
                    if (e.type == SDL_QUIT) quit = true;
                }
            }
        }
        //Destroy window
        SDL_DestroyWindow(window);

        //Quit SDL subsystems
        SDL_Quit();

        return 0;
    }
}

结果:

SDL简单使用(播放yuv,与pcm数据)

1:窗口

SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("SDL2 Window", 200, 200, 640, 480, SDL_WINDOW_SHOWN );
render = SDL_CreateRenderer(window, -1, 0);
SDL_RenderClear(render);
SDL_RenderPresent(render);

2:事件检测

SDL_Event event;
SDL_WaitEvent(&event);
event.type==SDL_QUIT;

3:纹理渲染

3.1:纹理相关API

3.2:渲染相关API

4:播放视频(YUV)

下面说明一下使用SDL播放YUV视频的基本流程,主要分为两大部分:初始化SDL、循环显示画面。

4.1 初始化SDL

1). 初始化SDL (SDL_Init)
2). 创建窗口(SDL_CreateWindow)
3). 基于窗口创建渲染器(SDL_CreateRenderer)
4). 创建纹理(SDL_CreateTexture)

4.2 循环显示画面

1). 设置纹理的数据(SDL_UpdateTexture)
2). 纹理复制给渲染目标(SDL_RenderCopy)
3). 显示(SDL_RenderPresent)

4.3 代码实现:

/*
功能:使用SDL播放本地yuv视频
问题:宽高设置,缓冲区使用malloc分配堆区
        播放一会就停止
        原因:video_fd = fopen(path, "r");
        要用rb模式,二进制方式读取
*/
#include <iostream>

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

//Bit per Pixel
const int bpp = 12;

int screen_w = 960, screen_h = 640;

// 根据不同的YUV视频,来设置不同的 宽 * 高 数据 
const int pixel_w = 854, pixel_h = 480;

unsigned char buffer[pixel_w * pixel_h * bpp / 8];

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

int thread_exit = 0;

int refresh_video(void* opaque) {
    while (thread_exit == 0) {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }
    return 0;
}

int main(int argc, char* argv[])
{
    if (SDL_Init(SDL_INIT_VIDEO)) {
        printf("Could not initialize SDL - %s\n", SDL_GetError());
        return -1;
    }

    SDL_Window* window;

    // 创建SDL窗口
    window = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);

    // 判断是否创建窗口成功
    if (!window) {
        printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
        return -1;
    }

    // 创建SDL渲染器
    SDL_Renderer* sdlRenderer = SDL_CreateRenderer(window, -1, 0);

    // 声明像素格式
    Uint32 pixformat = 0;

    // IYUV: Y + U + V  (3 planes)
    // YV12: Y + V + U  (3 planes)
    // I420也叫IYUV, 也叫YUV420
    pixformat = SDL_PIXELFORMAT_IYUV;

    // 按照YUV视频的宽高创建SDL纹理对象
    SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);

    FILE* fp = NULL;

    fp = fopen("F:/learn_ffmpeg/vs_works/test_FFmpeg/test_FFmpeg/test.yuv", "rb+");
    if (fp == NULL) {
        printf("cannot open this file\n");
        return -1;
    }

    SDL_Rect sdlRect;

    SDL_Thread* refresh_thread = SDL_CreateThread(refresh_video, NULL, NULL);
    SDL_Event event;
    while (1) {
        //Wait
        SDL_WaitEvent(&event);
        if (event.type == REFRESH_EVENT) {
            // 读取一帧数数据到缓冲区
            if (fread(buffer, 1, pixel_w * pixel_h * bpp / 8, fp) != pixel_w * pixel_h * bpp / 8) {
                // Loop
                fseek(fp, 0, SEEK_SET);
                fread(buffer, 1, pixel_w * pixel_h * bpp / 8, fp);
            }
            // 将数据更新到纹理
            SDL_UpdateTexture(sdlTexture, NULL, buffer, pixel_w);
            //FIX: If window is resize
            sdlRect.x = 0;
            sdlRect.y = 0;
            sdlRect.w = screen_w;
            sdlRect.h = screen_h;

            SDL_RenderClear(sdlRenderer);
            // 将更新后的纹理拷贝到渲染器
            SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
            // 渲染器显示画面
            SDL_RenderPresent(sdlRenderer);
            //Delay 40ms -- Dealy时常根据帧率进行调整
            SDL_Delay(40);
        }
        else if (event.type == SDL_WINDOWEVENT) {
            //If Resize
            SDL_GetWindowSize(window, &screen_w, &screen_h);
        }
        else if (event.type == SDL_QUIT) {
            break;
        }
    }
    return 0;
}

5:播放音频(pcm)

5.1 函数调用步骤如下:

* 函数调用步骤如下:
    *
    * [初始化]
    * SDL_Init(): 初始化SDL。
* SDL_OpenAudio(): 根据参数(存储于SDL_AudioSpec)打开音频设备。
* SDL_PauseAudio(): 播放音频数据。
*
* [循环播放数据]
* SDL_Delay(): 延时等待播放完成。

5.2 代码实现


/**
 * 功能:最简单的SDL2播放音频的例子(SDL2播放PCM)
 *
 * SDL实际上是对底层绘图 API(Direct3D,OpenGL)的封装
 *
 * 函数调用步骤如下:
 *
 * [初始化]
 * SDL_Init(): 初始化SDL。
 * SDL_OpenAudio(): 根据参数(存储于SDL_AudioSpec)打开音频设备。
 * SDL_PauseAudio(): 播放音频数据。
 *
 * [循环播放数据]
 * SDL_Delay(): 延时等待播放完成。
 *
 */
#include<SDL.h>
#include<stdio.h>

#define BLOCK_SIZE 4096000//太小的话会播放一半就结束了
static Uint8* audio_buf = NULL;
static size_t buffer_len = 0;
static Uint8 *audio_pos = NULL;

void read_audio_data(void* udata, Uint8* stream, int len) {
	if (buffer_len == 0) {
		return;
	}

	//SDL2中必须首先使用SDL_memset()将stream中的数据设置为0。
	SDL_memset(stream, 0, len);//清空缓冲区
	len = (len < buffer_len) ? len : buffer_len;
	SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);

	audio_pos += len;
	buffer_len -= len;
}

int main(int argc, char* argv[]) {
	int ret = -1;
	char* path = "1.pcm";
	FILE * audio_fd = NULL;

	//SDL_Init该函数可以确定希望激活的子系统
	if (SDL_Init(SDL_INIT_AUDIO)) {
		SDL_Log("Failed to init!\n");
		return ret;
	}
	
	audio_fd = fopen(path, "rb");//rb??
	if (!audio_fd) {
		SDL_Log("Failed to open pcm file!\n");
		goto __FAIL;
	}

	audio_buf = (Uint8*)malloc(BLOCK_SIZE);
	if (!audio_buf) {
		SDL_Log("Failed to alloc memory!\n");
		goto __FAIL;
	}
	SDL_AudioSpec spec;
	spec.freq = 48000;
	spec.channels = 5.1;
	spec.format = AUDIO_S16LSB;
	spec.callback = read_audio_data; //回调函数的作用是填充音频缓冲区,当音频设备需要更多的数据的时候会调用该回调函数
	spec.userdata = NULL;

	if (SDL_OpenAudio(&spec, NULL))
	{
		SDL_Log("Failed to open audio device!\n");
		goto __FAIL;
	}
	
	//当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候,将会播放静音的值。
	SDL_PauseAudio(0);
	do {
		buffer_len = fread(audio_buf,1, BLOCK_SIZE, audio_fd);
		audio_pos = audio_buf;
		while (audio_buf < (audio_buf + buffer_len)) {
			SDL_Delay(1);
		}

	} while (buffer_len != 0);

	SDL_CloseAudio();
	ret = 0;

__FAIL:
	if (audio_fd) {
		fclose(audio_fd);
	}
	if (audio_buf) {
		free(audio_buf);
	}
	SDL_Quit();
	return 0;
}