zoukankan      html  css  js  c++  java
  • FFmpeg编程(三)SDL开发

    一:SDL介绍与安装

    (一)SDL介绍

    (二)SDL安装

     

    1.源码下载:http://www.libsdl.org/download-2.0.php

    2.生成Makefile文件

    ./configure --prefix=/usr/local

    3.安装

    sudo make -j 8 && sudo make install

    二:SDL的简单使用

    SDL播放视频的代码流程如下所示:

    初始化: 

    SDL_Init(): 初始化SDL。 
    
    SDL_CreateWindow(): 创建窗口(Window)。 
    
    SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。 
    
    SDL_CreateTexture(): 创建纹理(Texture)。

    循环渲染数据: 

    SDL_UpdateTexture(): 设置纹理的数据。 
    
    SDL_RenderCopy(): 纹理复制给渲染器。
    SDL_RenderPresent(): 显示。

    (一)基本使用步骤

    (二)SDL渲染窗口

    1.SDL_Init/SDL_Quit 初始化和退出操作

    https://blog.csdn.net/leixiaohua1020/article/details/40680907

    int SDLCALL SDL_Init(Uint32 flags)

    其中,flags可以取下列值:

    SDL_INIT_TIMER:定时器
    SDL_INIT_AUDIO:音频
    SDL_INIT_VIDEO:视频
    SDL_INIT_JOYSTICK:摇杆
    SDL_INIT_HAPTIC:触摸屏
    SDL_INIT_GAMECONTROLLER:游戏控制器
    SDL_INIT_EVENTS:事件
    SDL_INIT_NOPARACHUTE:不捕获关键信号(这个不理解)
    SDL_INIT_EVERYTHING:包含上述所有选项

    2.SDL_CreateWindow()/SDL_DestroyWindow() 创建窗口(比如将图片渲染到窗口)

    https://blog.csdn.net/leixiaohua1020/article/details/40701203

    SDL_Window * SDLCALL SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags);

    参数含义如下:https://blog.csdn.net/qq_25333681/article/details/89787867

    title :窗口标题
    x :窗口位置x坐标。也可以设置为SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。
    y :窗口位置y坐标。同上。
    w :窗口的宽
    h :窗口的高
    flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性。
           ::SDL_WINDOW_FULLSCREEN,    ::SDL_WINDOW_OPENGL,
           ::SDL_WINDOW_HIDDEN,        ::SDL_WINDOW_BORDERLESS,
           ::SDL_WINDOW_RESIZABLE,     ::SDL_WINDOW_MAXIMIZED,
           ::SDL_WINDOW_MINIMIZED,     ::SDL_WINDOW_INPUT_GRABBED,
           ::SDL_WINDOW_ALLOW_HIGHDPI.
     返回创建完成的窗口的ID。如果创建失败则返回0。

    3.SDL_CreateRenderer() 创建渲染器(将图像/视频帧汇聚到窗口)

    SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags);

    参数含义如下:

    window : 渲染的目标窗口。
    index :打算初始化的渲染设备的索引。设置“-1”则初始化默认的渲染设备。
    flags :支持以下值(位于SDL_RendererFlags定义中)
        SDL_RENDERER_SOFTWARE :使用软件渲染
        SDL_RENDERER_ACCELERATED :使用硬件加速
        SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步
        SDL_RENDERER_TARGETTEXTURE :不太懂
    返回创建完成的渲染器的ID。如果创建失败则返回NULL。

    (三)简单实例(未渲染)

    #include <stdio.h>
    #include <SDL.h>
    
    int main(int argc,char* argv[]){
        SDL_Window* wind = NULL;
    
        SDL_Init(SDL_INIT_VIDEO);    //进行视频初始化
    
        wind = SDL_CreateWindow("SDL2 Window",
            200,200,
            640,480,
            SDL_WINDOW_SHOWN);    //显示、有边界
    
        if(!wind){
            printf("Failed to Create window!
    ");
            goto __EXIT;
        }
    
        while(1){
            sleep(5000);
        }
    
        SDL_DestroyWindow(wind);
    
    __EXIT:
        SDL_Quit();
        return 0;
    }
    View Code
    gcc 01_sdl_sample.c -o 01ss -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2

    可以看到出现窗口,但是没有渲染,导致出现透明状态,显示屏幕原本信息 

    (四)简单实例(使用渲染器)

    SDL_Renderer表示渲染上下文。这意味着它包含与渲染相关的所有当前设置,以及有关如何渲染当前帧的说明。

    要创建渲染上下文,可以使用函数SDL_CreateWindowAndRenderer()或SDL_CreateRenderer()。前者同时创建窗口和渲染器。后者要求我们先创建一个窗口。

    在程序中,您将使用SDL_SetRenderDrawColor()等函数更改上下文中的设置,并使用SDL_RenderDrawPoint()等函数执行渲染操作。

    int SDL_RenderClear(SDL_Renderer* renderer) ,该函数的作用是用指定的颜色清空缓冲区。renderer是上面创建的渲染器上下文。

    void SDL_RenderPresent(SDL_Renderer* renderer),将缓冲区中的内容输出到目标上,也就是 windows 窗口上。注意:渲染的数据也可以是从GPU渲染到屏幕!!

    #include <stdio.h>
    #include <SDL.h>
    
    int main(int argc,char* argv[]){
        SDL_Window* wind = NULL;
        SDL_Renderer* rend = NULL;
    
        SDL_Init(SDL_INIT_VIDEO);    //进行视频初始化
    
        wind = SDL_CreateWindow("SDL2 Window",
            200,200,
            640,480,
            SDL_WINDOW_SHOWN);    //显示、有边界
    
        if(!wind){
            printf("Failed to Create window!
    ");
            goto __EXIT;
        }
    
        rend = SDL_CreateRenderer(wind,-1,0);    //创建Render渲染器
        if(!rend){
            printf("Failed to create render
    ");
            goto __DWIND;
        }
        SDL_SetRenderDrawColor(rend,0,0,0,255);    //设置颜色和透明度(可选操作)
        SDL_RenderClear(rend);            //清屏处理(清空render之前的数据)
        SDL_RenderPresent(rend);            //将渲染器结果放入窗口中显示出来
    
        SDL_Delay(5000);
    __DWIND:
        SDL_DestroyWindow(wind);
    
    __EXIT:
        SDL_Quit();
        return 0;
    }
    View Code

    (五)SDL_Surface与SDL_Texture

    SDL 渲染的工作原理:

    在SDL_Render对象中有一个视频缓冲区,该缓冲区我们称之为SDL_Surface,它是按照像素存放图像的。
    我们一般把真彩色的像素称为RGB24数据。
    也就是说,每一个像素由24位组成,每8位代表一种颜色,像素的最终颜色是由RGB三种颜色混合而成的。

    SDL_Texture 与SDL_Surface相似,也是一种缓冲区

    只不过它存放的不是真正的像素数据,而是存放的图像的描述信息。这些描述信息通过OpenGL、D3D 或 Metal等技术操作GPU,从而绘制出与SDL_Surface一样的图形,且效率更高(因为它是GPU硬件计算的)。

    (六)SDL_Window与SDL_Render

    SDL_Window代表的是窗口的逻辑概念,它是存放在主内存中的一个对象。

    SDL_Render 是渲染器,它也是主存中的一个对象。对Render操作时实际上分为两个阶段

    一、渲染阶段。在该阶段,用户可以画各种图形渲染到SDL_Surface或SDL_Texture 中;

    二、显示阶段。参SDL_Texture为数据,通过OpenGL操作GPU,最终将 SDL_Surfce 或SDL_Texture中的数据输出到显示器上。

    三:SDL事件 

    (一)SDL事件基本原理

    (二)SDL事件种类

    (三)SDL事件处理

    1.poll 论询机制,处理不及时,占CPU
    2.wait 事件触发(类似epoll池),处理及时,不占用过多CPU。

    对于wait,可能出现线程阻塞,导致无法处理到来的其他事件,应该为每一个时间处理设置Timeout,所以出现了WaitEventTimeOut

    (四)事件机制简单使用 

    #include <stdio.h>
    #include <SDL.h>
    
    int main(int argc,char* argv[]){
        int exitFlag=1;
        SDL_Window* wind = NULL;
        SDL_Renderer* rend = NULL;
        SDL_Event event;
    
        SDL_Init(SDL_INIT_VIDEO);    //进行视频初始化
    
        wind = SDL_CreateWindow("SDL2 Window",
            200,200,
            640,480,
            SDL_WINDOW_SHOWN);    //显示、有边界
    
        if(!wind){
            printf("Failed to Create window!
    ");
            goto __EXIT;
        }
    
        rend = SDL_CreateRenderer(wind,-1,0);    //创建Render渲染器
        if(!rend){
            printf("Failed to create render
    ");
            goto __DWIND;
        }
        SDL_SetRenderDrawColor(rend,0,0,0,255);    //设置颜色和透明度(可选操作)
        SDL_RenderClear(rend);            //清屏处理(清空render之前的数据)
        SDL_RenderPresent(rend);            //将渲染器结果放入窗口中显示出来
    
        while(exitFlag){
            SDL_WaitEvent(&event);    //如果没有事件的话,会阻塞在这里的,不会一直while轮询
            switch(event.type){
                case SDL_QUIT:
                    exitFlag = 0;
                    break;
                default:
                    SDL_Log("event type is %d",event.type);
                    break;
            }
        }
    
    
    __DWIND:
        SDL_DestroyWindow(wind);
    
    __EXIT:
        SDL_Quit();
        return 0;
    }
    View Code
    gcc 02_sdl_event.c -o 02se -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2

    四:纹理渲染

    依据SDL编程方式,这里又分为两种情况:

    1. 不使用纹理

    由cpu直接绘制一幅画(cpu需要将最原始的rgb/YUV数据,刷到屏幕上), 相当于学生小A直接在墙上画画

    2. 使用纹理

    相当于小A同学(cpu)指挥画家(gpu)在纸上画, 然后把纸贴在墙上。 
    这个过程中画是由画家(gpu)画的, 小A同学负责发号施令(即告诉画家画什么), 纸代表纹理, 画家代表gpu, 所有绘制的操作都是在纹理上进行。
    事实上,纹理的概念并不仅仅是一张纸, 还包括小A同学中对这幅画的构思,可以理解成画画的算法, 而纸相当于是一个载体(内存空间,用于保存这些构思)。
    gpu根据纹理就可以计算出这幅图每个像素点的颜色( 相当于画家根据小A同学的描述,画出一幅画一样)

    可以看出,使用纹理,可以减轻cpu的负担, cpu处于一个发号施令的角色,图片的计算过程交给效率更好的gpu来做,可以提高渲染的效率

    (一)纹理渲染基本原理

    图像结果渲染器,变为纹理(即对这张图像的描述数据),将纹理交给显卡GPU,GPU经过计算,将图像显示在窗口中! 

    (二)SDL纹理相关API

    SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h); 

    参数的含义如下:

    renderer:目标渲染器。
    format :纹理的格式。后面会详述。
    access :可以取以下值(定义位于SDL_TextureAccess中)
        SDL_TEXTUREACCESS_STATIC :变化极少
        SDL_TEXTUREACCESS_STREAMING :变化频繁
        SDL_TEXTUREACCESS_TARGET :暂时没有理解
    w :纹理的宽
    h :纹理的高
    创建成功则返回纹理的ID,失败返回0。

    (三)渲染相关API

    SDL_CreateTexture用于创建纹理, 若要使用SDL_SetRenderTarget()设置渲染到纹理时, 必须使用SDL_TEXTUREACCESS_TARGET方式来创建纹理。

    函数SDL_SetRenderTarget()用于在渲染到纹理或屏幕之间进行选择。切换方式如下:

            SDL_SetRenderTarget(renderer, texture) // 渲染到纹理
    
            SDL_SetRenderTarget(renderer, NULL) // 渲染到屏幕(默认渲染到窗口/屏幕

    int SDL_RenderCopy(SDL_Renderer*   renderer, SDL_Texture*  texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect)将纹理拷贝到显卡GPU中去,通过SDL_RenderPresent进行渲染到屏幕

    renderer:渲染目标。
    texture:输入纹理。
    srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入。
    dstrect:选择渲染目标的一块矩形区域作为输出。设置为NULL的时候整个渲染目标作为输出。

    (四)纹理使用

    #include <stdio.h>
    #include <SDL.h>
    
    int main(int argc,char* argv[]){
        int exitFlag=1;
        SDL_Window* wind = NULL;
        SDL_Renderer* rend = NULL;
        SDL_Texture* text = NULL;
        SDL_Event event;
        SDL_Rect rect;    //创建矩形
    
        SDL_Init(SDL_INIT_VIDEO);    //进行视频初始化
    
        wind = SDL_CreateWindow("SDL2 Window",
            200,200,
            640,480,
            SDL_WINDOW_SHOWN);    //显示、有边界
    
        if(!wind){
            printf("Failed to Create window!
    ");
            goto __EXIT;
        }
    
        rend = SDL_CreateRenderer(wind,-1,0);    //创建Render渲染器
        if(!rend){
            printf("Failed to create render
    ");
            goto __DWIND;
        }
        SDL_SetRenderDrawColor(rend,0,0,0,255);    //设置颜色和透明度(可选操作)
        SDL_RenderClear(rend);            //清屏处理(清空render之前的数据)
        SDL_RenderPresent(rend);            //将渲染器结果放入窗口中显示出来
    
        text = SDL_CreateTexture(rend,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,
                    640,480);    //创建纹理
        if(!text){
            printf("Failed to create texture
    ");
            goto __DREND;
        }
    
        rect.w = 30;
        rect.h = 30;
    
        while(exitFlag){
            SDL_PollEvent(&event);    //如果没有事件的话,会一直while轮询
            switch(event.type){
                case SDL_QUIT:
                    exitFlag = 0;
                    break;
                default:
                    SDL_Log("event type is %d",event.type);
                    break;
            }
    
            rect.x = rand() % 610;
            rect.y = rand() % 450;
    
            SDL_SetRenderTarget(rend,text);    //渲染到纹理,下面的操作都会转为纹理
            SDL_SetRenderDrawColor(rend,0,0,0,0);
            SDL_RenderClear(rend);
    
            //绘制矩形
            SDL_RenderDrawRect(rend,&rect);
            SDL_SetRenderDrawColor(rend,255,0,0,0);    //为矩形绘制颜色
            SDL_RenderFillRect(rend,&rect);    //将渲染器设置到矩形中去,使得clear只在矩形中生效
    
            SDL_SetRenderTarget(rend,NULL);    //使用默认渲染,到窗口
            
            SDL_RenderCopy(rend,text,NULL,NULL);    //拷贝纹理到显卡
            SDL_RenderPresent(rend);                //将结果显示到窗口
        }
    
        SDL_DestroyTexture(text);
    __DREND:
        SDL_DestroyRenderer(rend);
    __DWIND:
        SDL_DestroyWindow(wind);
    __EXIT:
        SDL_Quit();
        return 0;
    }
    View Code
    gcc 03_sdl_texture.c -o 03st -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2

            SDL_SetRenderTarget(rend,text);       //渲染到纹理,下面的操作都会转为纹理!!!
    
    SDL_SetRenderDrawColor(rend,0,0,0,0);   SDL_RenderClear(rend);            //绘制矩形 SDL_RenderDrawRect(rend,&rect); SDL_SetRenderDrawColor(rend,255,0,0,0); //为矩形绘制颜色 SDL_RenderFillRect(rend,&rect);    //将渲染器设置到矩形中去,使得内部clear只在矩形中生效 SDL_SetRenderTarget(rend,NULL);    //使用默认渲染,到窗口;下面的操作会显示在窗口,不经过纹理转换 SDL_RenderCopy(rend,text,NULL,NULL); //拷贝纹理到显卡---在窗口层面处理,而不是纹理 SDL_RenderPresent(rend); //GPU显卡将计算的结果显示到窗口

    五:实战YUV播放器

    (一)创建线程(提高效率)

    创建线程:SDL_Thread* SDL_CreateThread(SDL_ThreadFunction fn, const char*  name, void*  data)

    fn: 线程要运行的函数。
    name: 线程名。
    data: 函数参数。

    等待线程:void SDL_WaitThread(SDL_Thread* thread, int*   status) 等待线程结束

    创建互斥量:SDL_mutex* SDL_CreateMutex(void) 也就是创建一个稀有资源,这样大家就去抢这个资源。从而达到为真正资源加锁的目的。

    销毁互斥量:void SDL_DestroyMutex(SDL_mutex* mutex)

    加锁: int SDL_LockMutex(SDL_mutex* mutex)

    解锁: int SDL_UnlockMutex(SDL_mutex* mutex)

    (二)纹理更新

    int SDLCALL SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch); 

    参数的含义如下:

    texture:目标纹理。
    rect:更新像素的矩形区域。设置为NULL的时候更新整个区域。
    pixels:像素数据。
    pitch:一行像素数据的字节数。
    
    成功的话返回0,失败的话返回-1

    SDL_UpdateTexture()的大致流程如下:

    1. 检查输入参数的合理性。例如像素格式是否支持,宽和高是否小于等于0等等。
    2. 如果是一些特殊的格式,进行一定的处理:
    a) 如果输入的像素数据是YUV格式的,则会调用SDL_UpdateTextureYUV()进行处理。
    b) 如果输入的像素数据的像素格式不是渲染器支持的格式,则会调用SDL_UpdateTextureNative()进行处理3. 调用SDL_Render的UpdateTexture()方法更新纹理。这一步是整个函数的核心。

    通过SDL_CreateTexture中指定纹理格式format为SDL_PIXELFORMAT_IYUV即可处理YUV数据!!

    (三)编程实现

    ffmpeg -i gfxm.mp4 -an -c:v rawvideo -pix_fmt yuv420p out.yuv
    #include <stdio.h>
    #include <string.h>
    #include <SDL.h>
    
    //下面定义自定义事件,SDL_USEREVENT 0x8000之后,到0xFFFF都可以
    #define REFRASH_EVENT (SDL_USEREVENT + 1)
    #define QUIT_EVENT (SDL_USEREVENT + 2)
    
    int thread_exit = 0;    //控制线程退出
    
    void refresh_video_timer(void* udata){
        thread_exit = 0;
    
        while(!thread_exit){
            SDL_Event event;
            event.type = REFRASH_EVENT;
            SDL_PushEvent(&event);
            SDL_Delay(40);    //25帧/S
        }
    
        thread_exit = 0;
        //开始退出
        SDL_Event event;
        event.type = QUIT_EVENT;
        SDL_PushEvent(&event);
        return 0;
    }
    
    int main(int argc,char* argv[]){
        int exitFlag=1;    //控制循环
    
        char* filename = NULL;
        FILE* fp = NULL;
        int len;
    
        uint8_t* src_data = NULL,*src_start=NULL;    //获取视频帧数据
        int v_w,v_h,w_w=1280,w_h=720;    //视频宽高、窗口宽高
        unsigned int frame_len,frame_temp_len;
    
        SDL_Window* wind = NULL;
        SDL_Renderer* rend = NULL;
        SDL_Texture* text = NULL;
        SDL_Event event;
        SDL_Rect rect;    //创建矩形
        SDL_Thread* timer_thread = NULL;    //线程
    
        if(argc<4){
            printf("The number of parameter must be than 3
    ");
            return 0;
        }
    
        filename = argv[1];    //获取YUV文件----YUV为YUV420数据
        v_w = atoi(argv[2]);    //获取文件的宽
        v_h = atoi(argv[3]);    //获取视频的高
    
        frame_temp_len = v_h*v_w*1.5;    //YUV420数据大小,需要对数据进行对齐操作
        frame_len = frame_temp_len;
        if(frame_temp_len&0xF){    //后4位存在数据,则表示不是16对齐
            frame_len = (frame_temp_len&0xFFF0) + 0x10;    //进16
        }
    
        if(SDL_Init(SDL_INIT_VIDEO)){    //进行视频初始化
            printf("Can`t initialize SDL
    ");
            return -1;
        }
    
        wind = SDL_CreateWindow("YUV Player",
            SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,    //不指定左上位置
            w_w,w_h,            //设置窗口宽高,与视频一般或者大些都可以
            SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);    //窗口可用于OpenGL上下文 窗口可以调整大小
    
        if(!wind){
            printf("Failed to Create window!
    ");
            goto __EXIT;
        }
    
        rend = SDL_CreateRenderer(wind,-1,0);    //创建Render渲染器
        if(!rend){
            printf("Failed to create render
    ");
            goto __DWIND;
        }
    
        //创建纹理
        text = SDL_CreateTexture(rend,
                                SDL_PIXELFORMAT_IYUV,    //< Planar mode: Y + U + V  (3 planes)
                                SDL_TEXTUREACCESS_TARGET,
                                v_w,v_h);    //创建纹理
        if(!text){
            printf("Failed to create texture
    ");
            goto __DREND;
        }
    
        //开始读取YUV数据
        fp = fopen(filename,"rb");
        if(!fp){
            printf("Failed to open file:%s
    ",filename);
            goto __TEXT;
        }
    
        src_data = (uint8_t*)malloc(frame_len);    //分配空间
        if(!src_data){
            printf("Failed to alloc memory for src_data!
    ");
            goto __FILE;
        }
        src_start = src_data;    //开始位置
    
        //开启线程,处理其他控制事件
        timer_thread = SDL_CreateThread(refresh_video_timer,NULL,NULL);
    
        //要显示纹理数据的矩形
        rect.x = 0;
        rect.y = 0;
    
        while(exitFlag){
            SDL_PollEvent(&event);    //如果没有事件的话,会一直while轮询
            switch(event.type){
                case SDL_QUIT:
                    exitFlag = 0;
                    break;
                case REFRASH_EVENT:    //更新纹理
                    //先读取一帧数据
                    if((len=fread(src_data,1,frame_len,fp))<=0){    //数据读取完成,退出
                        thread_exit = 1;
                        continue;
                    }
    
                    SDL_UpdateTexture(text,NULL,src_start,v_w);    //内部设置
                    rect.w = w_w;    //因为窗口可以被重置
                    rect.h = w_h;    
    
                    SDL_RenderClear(rend);
    
                    SDL_RenderCopy(rend,text,NULL,&rect);    //拷贝纹理到显卡,选择渲染目标的一块矩形区域作为输出
                    SDL_RenderPresent(rend);                //将结果显示到窗口
                    break;
                case QUIT_EVENT:    //退出播放
                    exitFlag = 0;
                    break;
                default:
                    SDL_Log("event type is %d",event.type);
                    break;
            }
        }
    
        if(src_data)
            free(src_data);
    __FILE:
        if(fp)
            fclose(fp);
    __TEXT:
        SDL_DestroyTexture(text);
    __DREND:
        SDL_DestroyRenderer(rend);
    __DWIND:
        SDL_DestroyWindow(wind);
    __EXIT:
        SDL_Quit();
        return 0;
    }
    View Code
    gcc 04_sdl_yuv.c -o 04sy -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2 
    ./04sy out.yuv 864 486

    六:实战PCM播放器

    使用 SDL 的音频 API 来播放声音。其基本流程是,从 pcm 文件一块一块的读数据。然后通过 read_audio_data 这个回调函数给声卡喂数据。

    如果一次没用完,SDL会再次调用回调函数读数据。如果audio_buf中的数据用完了,则再次从文件中读一块数据,直到读到文件尾。

    (一)播放音频基本流程

    (二)播放音频原则

    (三)相关API

    1.打开音频设备    int SDL_OpenAudio(SDL_AudioSpec* desired,SDL_AudioSpec* obtained) 

    desired: 设置音频参数。

    参数说明
    freq 每秒采频率
    SDL_AudioFormat 音频数据存储格式
    channels 通道数
    silence 静音值
    samples 采样个数
    size 音频缓冲区大小
    SDL_AudioCallback 回调函数
    userdata 回调函数参数指针

    obtained: 返回参数。

    2.关闭音频设备    void SDL_CloseAudio(void)

    3.播放与暂停  void SDL_PauseAudio(int pause_on)

    pause_on: 0, 暂停播放;1, 播放;

    4.喂数据  void SDL_MixAudio(Uint8* dst,const Uint8* src,Uint32 len,int volume)

    dst: 目的缓冲区
    src: 源缓冲区
    len: 音频数据长度
    volume: 音量大小,0-128 之间的数。SDL_MIX_MAXVOLUME代表最大音量。

    (四)编程实现

    ffmpeg -i out.aac -ar 44100 -ac 2 -f s16le out.pcm
    #include <stdio.h>
    #include <string.h>
    #include <SDL.h>
    
    #define BLOCK_SIZE 4096000 //4096k,设置一个较大的数值空间
    
    //将数据存放在全局
    static uint8_t* audio_buf = NULL;
    static uint8_t* audio_pos = NULL;
    static int buffer_len = 0;
    
    //回调函数
    void read_audio_data(void* udata,uint8_t* stream,int len){    //回调数据,目的缓冲区,和缓冲区长度
        if(buffer_len == 0)
            return;
    
        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 exitFlag=1;    //控制循环
    
        char* filename = NULL;
        FILE* fp = NULL;
    
        SDL_AudioSpec spec;    //用来设置音频参数
    
        if(argc<2){
            printf("The number of parameter must be than 1
    ");
            return 0;
        }
    
        filename = argv[1];    //获取pcm文件
    
        if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){    //进行视频初始化
            printf("Can`t initialize SDL
    ");
            return -1;
        }
    
        fp = fopen(filename,"rb");
        if(!fp){
            printf("Failed to open file:%s",filename);
            goto __EXIT;
        }
    
        audio_buf = (uint8_t*)malloc(BLOCK_SIZE);    //分配空间
        if(!audio_buf){
            printf("Failed to alloc memory for audio buffer
    ");
            goto __FILE;
        }
    
        //开始开启音频设备
        spec.freq = 44100;
        spec.format = AUDIO_S16SYS;
        spec.channels = 2;
        spec.silence = 0;
        spec.samples = 1024;    //每帧数据,单通道的采样数量
        spec.callback = read_audio_data;
        spec.userdata = NULL;
    
        if(SDL_OpenAudio(&spec,NULL)){
            printf("Failed to open audio device:%s
    ",SDL_GetError());
            goto __BUFFER;
        }
    
        //开始播放
        SDL_PauseAudio(0);
    
        while(1){
            buffer_len = fread(audio_buf,1,BLOCK_SIZE,fp);
            if(buffer_len<=0)
                break;
            printf("read block size:%d 
    ",buffer_len);
    
            audio_pos = audio_buf;
    
            while(audio_pos<(audio_buf+buffer_len)){    //数据没有读取完成
                SDL_Delay(1);    //延迟1毫秒,等等设备继续读取
            }
        }
    
        SDL_CloseAudio();    //关闭音频设备
    __BUFFER:
        if(audio_buf)
            free(audio_buf);
    __FILE:
        if(fp)
            fclose(fp);
    __EXIT:
        SDL_Quit();
        return 0;
    }
    View Code

      gcc 05_sdl_pcm.c -o 05sp -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2

    ./05sp out.pcm

  • 相关阅读:
    生成Ipa安装包的plist文件后生成下载链接
    ssh_key登录服务器,免密码登录
    Ruby中类的进阶(继承,private, public, protect)
    Ruby中的类
    Redis- redis.conf
    Active Job 基础
    rails框架配置
    rails中使用CarrierWave实现文件上传的功能
    Flask-SQLAlchemy
    Linux自动共享USB设备:udev+Samba
  • 原文地址:https://www.cnblogs.com/ssyfj/p/14732427.html
Copyright © 2011-2022 走看看