天天看点

SDL播放音视频简单使用

一、什么是SDL?

SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL通过OpenGL和2D视频帧缓冲,提供了数种控制音频、视频、键盘、鼠标、控制杆及3D硬件的访问接口,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。

二、SDL linux环境搭建

1、下载SDL源码库,SDL2-2.0.10.tar.gz(下载网址:https://www.libsdl.org/download-2.0.php)

2、解压,然后执行以下命令

$ ./configure
$ make 
$ sudo make install
           

3、如果出现Could not initialize SDL - No available video device(Did you set the DISPLAY variable?)错误,说明系统没有安装x11的库文件,因此编译出来的SDL库实际上不能用。安装x11库文件:

$ sudo apt-get install libx11-dev
$ sudo apt-get install xorg-dev
           

三、SDL 主要函数

1、函数功能说明

1)初始化函数

SDL_Init():初始化SDL系统

SDL_CreateWindow():创建窗口SDL_Window

SDL_CreateRenderer():创建渲染器SDL_Renderer

SDL_CreateTexture():创建纹理SDL_Texture

2)循环渲染函数

SDL_UpdateTexture():设置纹理的数据

SDL_RenderCopy():将纹理的数据拷贝给渲染器

SDL_RenderPresent():显示

3)事件函数

SDL_WaitEvent():等待一个事件

SDL_PushEvent():发送一个事件

SDL_PumpEvents():将硬件设备产生的事件放入事件队列,用于读取事件,在调用该函数之前,必须调用SDL_PumpEvents搜集键盘等事件

SDL_PeepEvents():从事件队列提取一个事件

4)线程函数

SDL_CreateThread():SDL线程创建

SDL_WaitThead():SDL线程等待

SDL_CreateMutex/SDL_DestroyMutex():SDL互斥锁

SDL_LockMutex/SDL_UnlockMutex():SDL锁定互斥

SDL_CreateCond/SDL_DestoryCond():SDL条件变量(信号量)

SDL_CondWait/SDL_CondSingal():SDL条件变量(信号量)等待/通知

5)其他函数

SDL_Delay():工具函数,用于延时

SDL_Quit():退出SDL系统

2、数据结构说明

1)SDL_Window

SDL_Window代表一个“窗口”。

SDL_Window结构体定义了一个SDL2中的窗口。

有关它的定义在接口头文件中只有一行代码,但是这一行定义前面的注释非常之多,如下所示:

/**
 *  \brief The type used to identify a window
 *
 *  \sa SDL_CreateWindow()
 *  \sa SDL_CreateWindowFrom()
 *  \sa SDL_DestroyWindow()
 *  \sa SDL_GetWindowData()
 *  \sa SDL_GetWindowFlags()
 *  \sa SDL_GetWindowGrab()
 *  \sa SDL_GetWindowPosition()
 *  \sa SDL_GetWindowSize()
 *  \sa SDL_GetWindowTitle()
 *  \sa SDL_HideWindow()
 *  \sa SDL_MaximizeWindow()
 *  \sa SDL_MinimizeWindow()
 *  \sa SDL_RaiseWindow()
 *  \sa SDL_RestoreWindow()
 *  \sa SDL_SetWindowData()
 *  \sa SDL_SetWindowFullscreen()
 *  \sa SDL_SetWindowGrab()
 *  \sa SDL_SetWindowIcon()
 *  \sa SDL_SetWindowPosition()
 *  \sa SDL_SetWindowSize()
 *  \sa SDL_SetWindowBordered()
 *  \sa SDL_SetWindowResizable()
 *  \sa SDL_SetWindowTitle()
 *  \sa SDL_ShowWindow()
 */
typedef struct SDL_Window SDL_Window;
           

2)SDL_Renderer

SDL_Renderer代表一个“渲染器”。

SDL_Renderer结构体定义了一个SDL2中的渲染器。

有关它的定义在接口头文件中只有一行代码,如下所示:

/**
 *  \brief A structure representing rendering state
 */
struct SDL_Renderer;
typedef struct SDL_Renderer SDL_Renderer;
           

3)SDL_Texture

SDL_Texture代表一个“纹理”。

SDL_Texture结构定义了一个SDL2中的纹理。

有关它的定义在接口头文件中只有一行代码,如下所示:

/**
 *  \brief An efficient driver-specific representation of pixel data
 */
struct SDL_Texture;
typedef struct SDL_Texture SDL_Texture;
           

4)SDL_Rect

SDL_Rect代表一个简单的矩形结构。

四、SDL 播放YUV视频实现流程

SDL播放音视频简单使用

五、SDL 播放音频PCM实现

1、实现流程

SDL播放音视频简单使用

2、函数说明

1)打开音频设备

函数原型:

int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained);

参数说明:

desired:期望参数;

obtained:实际音频设备参数,一般情况设置为NULL即可。

SDL_AudioSpec 结构体:

typedef struct SDL_AudioSpec {
	int freq; 					// 音频采样率
	SDL_AudioFormat format; 	// 音频数据格式
	Uint8 channels; 			// 声道数: 1 单声道, 2 立体声
	Uint8 silence; 				// 设置静音的值, 因为声音采样是有符号的, 所以0当然就是这个值
	Uint16 samples; 			// 音频缓冲区中的采样个数,要求必须是2的n次
	Uint16 padding; 			// 考虑到兼容性的一个参数
	Uint32 size; 				// 音频缓冲区的大小,以字节为单位
	SDL_AudioCallback callback; // 填充音频缓冲区的回调函数
	void *userdata; 			// 用户自定义的数据
} SDL_AudioSpec;
           

2)回调函数

函数原型:

void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 *stream, int len);

参数说明:

userdata: SDL_AudioSpec结构中的用户自定义数据,一般情况下可以不用;

stream:该指针指向需要填充的音频缓冲区;

len:音频缓冲区的大小(以字节为单位) 102422。

3)播放音频数据函数

函数原型:

void SDLCALL SDL_PauseAudio(int pause_on)

参数说明:

pause_on:0开始播放音频数据。1播放静音的值。

3、实例源码

/**
 * SDL2播放PCM
 */

#include <stdio.h>
#include <SDL.h>

#define PCM_BUFFER_SIZE (1024*2*2*2)

static Uint8 *s_audio_buf = NULL;
static Uint8 *s_audio_pos = NULL;
static Uint8 *s_audio_end = NULL;

//音频设备回调函数
void fill_audio_pcm(void *udata, Uint8 *stream, int len)
{
    SDL_memset(stream, 0, len);
    if(s_audio_pos >= s_audio_end) // 数据读取完毕
    {
        return;
    }

    int remain_buffer_len = s_audio_end - s_audio_pos;
    len = (len < remain_buffer_len) ? len : remain_buffer_len;
    // 拷贝数据到stream并调整音量
    SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/8);
    printf("len = %d\n", len);
    s_audio_pos += len;  // 移动缓存指针
}

int main(int argc, char *argv[])
{
    int ret = -1;
    FILE *audio_fd = NULL;
    SDL_AudioSpec spec;
    const char *path = "test.pcm";
    size_t read_buffer_len = 0;

    //SDL 初始化
    if(SDL_Init(SDL_INIT_AUDIO))    // 支持AUDIO
    {
        return ret;
    }

    //打开PCM文件
    audio_fd = fopen(path, "rb");
    if(!audio_fd)
    {
        goto _FAIL;
    }

    s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);

    // 音频参数设置SDL_AudioSpec
    spec.freq = 44100;         	 	// 采样频率
    spec.format = AUDIO_S16SYS; 	// 采样点格式
    spec.channels = 2;          	// 2通道
    spec.silence = 0;
    spec.samples = 1024;       		// 23.2ms -> 46.4ms 每次读取的采样数量,多久产生一次回调和 samples
    spec.callback = fill_audio_pcm; // 回调函数
    spec.userdata = NULL;

    //打开音频设备
    if(SDL_OpenAudio(&spec, NULL))
    {
        goto _FAIL;
    }

    //播放音频
    SDL_PauseAudio(0);

    int data_count = 0;
    while(1)
    {
        // 从文件读取PCM数据
        read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);
        if(read_buffer_len == 0)
        {
            break;
        }
        data_count += read_buffer_len; 						// 统计读取的数据总字节数
        printf("now playing %10d bytes data.\n",data_count);
        s_audio_end = s_audio_buf + read_buffer_len;    	// 更新buffer的结束位置
        s_audio_pos = s_audio_buf;  						// 更新buffer的起始位置
        //the main thread wait for a moment
        while(s_audio_pos < s_audio_end)
        {
            SDL_Delay(10);  								// 等待PCM数据消耗
        }
    }
    printf("play finish\n");
    // 关闭音频设备
    SDL_CloseAudio();

_FAIL:
    if(s_audio_buf)
        free(s_audio_buf);

    if(audio_fd)
        fclose(audio_fd);

    //退出SDL
    SDL_Quit();
    return 0;
}

           

继续阅读