音视频技术(4)-SDL播放PCM

  • 2020 年 3 月 27 日
  • 笔记

SDL 是一套跨平台的音视频播放框架,对齐了底层硬件的差异,开发者可以专注于音视频的处理,本文记录在iOS上使用SDL播放pcm音频裸数据的实现过程。pcm是音频的裸数据。

  1. sdl编译:可以直接在sdl官网下载源码编译成iOS平台使用的.a静态库,编译方式有两种,1)xcode编译;2)shell脚本编译,详细可以参考源码里的readme文档 源码目录:

image.png

用sh脚本编译的.a文件,最后运行不过去,会报错,在sdl论坛里提问了,也没有人回答,建议用第一种方式编译,笔者在学习的过程中节省时间,直接扣了别人已经编译好的.a文件

  1. 集成:1)copy 头文件到工程目录下,2) 添加库引用,参考如下库引用,可能有用不到的

image.png

  1. 代码编写,代码不多,100行左右即可实现,核心代码实现如下

执行流程: 初始化SDL –> 打开pcm文件–>调用SDL播放–>SDL回调里取每一帧的播放数据–>当前线程等待pcm播放完,然后再去取下一个缓存的数据

#import "AudioPlayerSDL2.h"    static Uint32 audio_len;  static Uint8 *audio_pos;    void fill_audio(void *udata, Uint8 *stream, int len){      SDL_memset(stream, 0, len);      if(audio_len == 0){          return;      }        len = (len > audio_len? audio_len : len);      printf("audio %d %d n", audio_len, len);      SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);      audio_pos += len;      audio_len -= len;  }     int cppTest(){      SDL_SetMainReady();        if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)){          printf("Could not initialize SDL -%sn", SDL_GetError());          return -1;      }        SDL_AudioSpec wanted_spec;      wanted_spec.freq = 44100;      wanted_spec.format = AUDIO_S16SYS;      wanted_spec.channels = 2;      wanted_spec.silence = 0;      wanted_spec.samples = 1024;      wanted_spec.callback = fill_audio;        if (SDL_OpenAudio(&wanted_spec, NULL) < 0){          printf("can't open audio.n");          return -1;      }        NSString *pcmdir = [NSString stringWithFormat:@"resource.bundle/test1.pcm"];        NSString *ppth = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:pcmdir];      FILE *fp = fopen([ppth UTF8String], "r");        if(fp == NULL){          printf("cannot open this filen");          return -1;      } else {          printf("open file :%s", [pcmdir UTF8String]);      }        int pcm_buffer_size = 4096;      char *pcm_buffer = (char *)malloc(pcm_buffer_size);      int data_count = 0;      SDL_PauseAudio(0);        while (1) {          if(fread(pcm_buffer, 1, pcm_buffer_size, fp) != pcm_buffer_size){  //            fseek(fp, 0, SEEK_SET);  //            fread(pcm_buffer, 1, pcm_buffer_size, fp);  //            data_count = 0;              break;          }            printf("Now Playing %10d Bytes data.n", data_count);          data_count += pcm_buffer_size;            audio_len = pcm_buffer_size;          audio_pos = (Uint8 *) pcm_buffer;    //        SDL_PauseAudio(0);          while (audio_len > 0) {              SDL_Delay(1);          }      }       if(pcm_buffer){           free(pcm_buffer);       }        fclose(fp);       SDL_Quit();        return 0;  }

使用ffmpeg从mp4中提取pcm文件 提取:ffmpeg -i 1.mp4 -vn -ar 44100 -ac 2 -f s16le out.pcm 播放:ffplay -ac 2 -ar 44100 -f s16le out.pcm