对于用FFMPEG2.01和SDL2.01最新的版本来做音频播放器,这篇文章和版本是很有值得参考的价值
这篇文章解决了我在做简易播放器的时候,用tutorial07的代码的时候,声音播放出现杂音的问题
出现杂音的问题原因可以从http://blog.csdn.net/leixiaohua1020/article/details/10528443中找到原因,
因为:
“新版本中使用了最新版本的FFMPEG类库(2014.5.7)。
FFMPEG在新版本中的音频解码方面发生了比较大的变化。
如果将旧版的主程序和新版的类库组合使用的话,会出现听到的都是杂音这一现象。
经过研究发现,新版中avcodec_decode_audio4()解码后输出的音频采样数据格式为AV_SAMPLE_FMT_FLTP(float, planar)
而不再是AV_SAMPLE_FMT_S16(signed 16 bits)。因此无法直接使用SDL进行播放。
最后的解决方法是使用SwrContext对音频采样数据进行转换之后,再进行输出播放,问题就可以得到解决了。”
/*=======================来自:http://www.cnblogs.com/lidabo/p/3701074.html================================*/
FFMPEG + SDL音频播放分析
目录 [hide]
1 抽象流程:
2 关键实现:
2.1 main()函数
2.2 decode_thread()读取文件信息和音频包
2.3 stream_component_open():设置音频参数和打开设备
2.4 audio_callback(): 回调函数,向SDL缓冲区填充数据
2.5 audio_decode_frame():解码音频
3 FFMPEG结构体
3.1 channel_layout_map
4 FFMPEG宏定义
4.1 Audio channel convenience macros
5 SDL宏定义
5.1 SDL_AudioSpec format
抽象流程:
设置SDL的音频参数 —-> 打开声音设备,播放静音 —-> ffmpeg读取音频流中数据放入队列 —-> SDL调用用户设置的函数来获取音频数据 —-> 播放音频
SDL内部维护了一个buffer来存放解码后的数据,这个buffer中的数据来源是我们注册的回调函数(audio_callback),
audio_callback调用audio_decode_frame来做具体的音频解码工作,需要引起注意的是:
从流中读取出的一个音频包(avpacket)可能含有多个音频桢(avframe),所以需要多次调用avcodec_decode_audio4来完成整个包的解码,
解码出来的数据存放在我们自己的缓冲中(audio_buf2)。SDL每一次回调都会引起数据从audio_buf2拷贝到SDL内部缓冲区,
当audio_buf2中的数据大于SDL的缓冲区大小时,需要分多次拷贝。
关键实现:
1 main()函数 2 1 int main(int argc, char **argv){ 3 2 SDL_Event event; //SDL事件变量 4 3 VideoState *is; // 纪录视频及解码器等信息的大结构体 5 4 is = (VideoState*) av_mallocz(sizeof(VideoState)); 6 5 if(argc < 2){ 7 6 fprintf(stderr, "Usage: play <file>\n"); 8 7 exit(1); 9 8 } 10 9 av_register_all(); //注册所有ffmpeg的解码器 11 10 /* 初始化SDL,这里只实用了AUDIO,如果有视频,好需要SDL_INIT_VIDEO等等 */ 12 11 if(SDL_Init(SDL_INIT_AUDIO)){ 13 12 fprintf(stderr, "Count not initialize SDL - %s\n", SDL_GetError()); 14 13 exit(1); 15 14 } 16 15 is_strlcpy(is->filename, argv[1], sizeof(is->filename)); 17 16 /* 创建一个SDL线程来做视频解码工作,主线程进入SDL事件循环 */ 18 17 is->parse_tid = SDL_CreateThread(decode_thread, is); 19 18 if(!is->parse_tid){ 20 19 SDL_WaitEvent(&event); 21 20 switch(event.type){ 22 21 case FF_QUIT_EVENT: 23 22 case SDL_QUIT: 24 23 is->quit = 1; 25 24 SDL_Quit(); 26 25 exit(0); 27 26 break; 28 27 default: 29 28 break; 30 29 } 31 30 } 32 31 return 0; 33 32 }
decode_thread()读取文件信息和音频包:
1 1 static int decode_thread(void *arg){ 2 2 VideoState *is = (VideoState*)arg; 3 3 AVFormatContext *ic = NULL; 4 4 AVPacket pkt1, *packet = &pkt1; 5 5 int ret, i, audio_index = -1; 6 6 7 7 is->audioStream = -1; 8 8 global_video_state = is; 9 9 /* 使用ffmpeg打开视频,解码器等 常规工作 */ 10 10 if(avFormat_open_input(&ic, is->filename, NULL, NULL) != 0) { 11 11 fprintf(stderr, "open file error: %s\n", is->filename); 12 12 return -1; 13 13 } 14 14 is->ic = ic; 15 15 if(avformat_find_stream_info(ic, NULL) < 0){ 16 16 fprintf(stderr, "find stream info error\n"); 17 17 return -1; 18 18 } 19 19 av_dump_format(ic, 0, is->filename, 0); 20 20 for(i = 0; i < ic->nb_streams; i++){ 21 21 if(ic->streams[i])->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index == -1){ 22 22 audio_index = i; 23 23 break; 24 24 } 25 25 } 26 26 if(audio_index >= 0) { 27 27 /* 所有设置SDL音频流信息的步骤都在这个函数里完成 */ 28 28 stream_component_open(is, audio_index); 29 29 } 30 30 if(is->audioStream < 0){ 31 31 fprintf(stderr, "could not open codecs for file: %s\n", is->filename); 32 32 goto fail; 33 33 } 34 34 /* 读包的主循环, av_read_frame不停的从文件中读取数据包(这里只取音频包)*/ 35 35 for(;;){ 36 36 if(is->quit) break; 37 37 /* 这里audioq.size是指队列中的所有数据包带的音频数据的总量,并不是包的数量 */ 38 38 if(is->audioq.size > MAX_AUDIO_SIZE){ 39 39 SDL_Delay(10); // 毫秒 40 40 continue; 41 41 } 42 42 ret = av_read_frame(is->ic, packet); 43 43 if(ret < 0){ 44 44 if(ret == AVERROR_EOF || url_feof(is->ic->pb)) break; 45 45 if(is->ic->pb && is->ic->pb->error) break; 46 46 contiue; 47 47 } 48 48 if(packet->stream_index == is->audioStream){ 49 49 packet_queue_put(&is->audioq, packet); 50 50 } else{ 51 51 av_free_packet(packet); 52 52 } 53 53 } 54 54 while(!is->quit) SDL_Delay(100); 55 55 fail: { 56 56 SDL_Event event; 57 57 event.type = FF_QUIT_EVENT; 58 58 event.user.data1 = is; 59 59 SDL_PushEvent(&event); 60 60 } 61 61 return 0; 62 62 }
stream_component_open():设置音频参数和打开设备
1 1 int stream_component_open(videoState *is, int stream_index){ 2 2 AVFormatContext *ic = is->ic; 3 3 AVCodecContext *codecCtx; 4 4 AVCodec *codec; 5 5 /* 在用SDL_OpenAudio()打开音频设备的时候需要这两个参数*/ 6 6 /* wanted_spec是我们期望设置的属性,spec是系统最终接受的参数 */ 7 7 /* 我们需要检查系统接受的参数是否正确 */ 8 8 SDL_AudioSpec wanted_spec, spec; 9 9 int64_t wanted_channel_layout = 0; // 声道布局(SDL中的具体定义见“FFMPEG结构体”部分) 10 10 int wanted_nb_channels; // 声道数 11 11 /* SDL支持的声道数为 1, 2, 4, 6 */ 12 12 /* 后面我们会使用这个数组来纠正不支持的声道数目 */ 13 13 const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 }; 14 14 15 15 if(stream_index < 0 || stream_index >= ic->nb_streams) return -1; 16 16 codecCtx = ic->streams[stream_index]->codec; 17 17 wanted_nb_channels = codecCtx->channels; 18 18 if(!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) { 19 19 wanted_channel_layout = av_get_default_channel_lauout(wanted_channel_nb_channels); 20 20 wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; 21 21 } 22 22 wanted_spec.channels = av_get_channels_layout_nb_channels(wanted_channel_layout); 23 23 wanted_spec.freq = codecCtx->sample_rate; 24 24 if(wanted_spec.freq <= 0 || wanted_spec.channels <=0){ 25 25 fprintf(stderr, "Invaild sample rate or channel count!\n"); 26 26 return -1; 27 27 } 28 28 wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分 29 29 wanted_spec.silence = 0; // 0指示静音 30 30 wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; // 自定义SDL缓冲区大小 31 31 wanted_spec.callback = audio_callback; // 音频解码的关键回调函数 32 32 wanted_spec.userdata = is; // 传给上面回调函数的外带数据 33 33 34 34 /* 打开音频设备,这里使用一个while来循环尝试打开不同的声道数(由上面 */ 35 35 /* next_nb_channels数组指定)直到成功打开,或者全部失败 */ 36 36 while(SDL_OpenAudio(&wanted_spec, &spec) < 0){ 37 37 fprintf(stderr, "SDL_OpenAudio(%d channels): %s\n", wanted_spec.channels, SDL_GetError()); 38 38 wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)]; // FFMIN()由ffmpeg定义的宏,返回较小的数 39 39 if(!wanted_spec.channels){ 40 40 fprintf(stderr, "No more channel to try\n"); 41 41 return -1; 42 42 } 43 43 wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels); 44 44 } 45 45 /* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */ 46 46 if(spec.format != AUDIO_S16SYS){ 47 47 fprintf(stderr, "SDL advised audio format %d is not supported\n", spec.format); 48 48 return -1; 49 49 } 50 50 if(spec.channels != wanted_spec.channels) { 51 51 wanted_channel_layout = av_get_default_channel_layout(spec.channels); 52 52 if(!wanted_channel_layout){ 53 53 fprintf(stderr, "SDL advised channel count %d is not support\n", spec.channels); 54 54 return -1; 55 55 } 56 56 } 57 57 /* 把设置好的参数保存到大结构中 */ 58 58 is->audio_src_fmt = is->audio_tgt_fmt = AV_SAMPLE_FMT_S16; 59 59 is->audio_src_freq = is->audio_tgt_freq = spec.freq; 60 60 is->audio_src_channel_layout = is->audio_tgt_layout = wanted_channel_layout; 61 61 is->audio_src_channels = is->audio_tat_channels = spec.channels; 62 62 63 63 codec = avcodec_find_decoder(codecCtx>codec_id); 64 64 if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)){ 65 65 fprintf(stderr, "Unsupported codec!\n"); 66 66 return -1; 67 67 } 68 68 ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; //具体含义请查看“FFMPEG宏定义”部分 69 69 is->audioStream = stream_index; 70 70 is->audio_st = ic->streams[stream_index]; 71 71 is->audio_buf_size = 0; 72 72 is->audio_buf_index = 0; 73 73 memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); 74 74 packet_queue_init(&is->audioq); 75 75 SDL_PauseAudio(0); // 开始播放静音 76 76 }
audio_callback(): 回调函数,向SDL缓冲区填充数据
1 void audio_callback(void *userdata, Uint8 *stream, int len) 2 { 3 VideoState *is = (VideoState*)userdata; 4 int len1, audio_data_size; 5 6 /* len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 */ 7 while(len > 0){ 8 /* audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/ 9 /* 这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/ 10 /* 们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更 11 /* 多的桢数据 */ 12 if(is->audio_buf_index >= is->audio_buf_size){ 13 audio_data_size = audio_decode_frame(is); 14 /* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */ 15 if(audio_data_size < 0) 16 { 17 is->audio_buf_size = 1024; 18 /* 清零,静音 */ 19 memset(is->audio_buf, 0, is->audio_buf_size); 20 } 21 else 22 { 23 is->audio_buf_size = audio_data_size; 24 } 25 is->audio_buf_index = 0; 26 } 27 /* 查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */ 28 len1 = is->audio_buf_size - is->audio_buf_index; 29 if(len1 > len) len1 = len; 30 31 memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1); 32 len -= len1; 33 stream += len1; 34 is->audio_buf_index += len1; 35 } 36 } 37 38 39 40 audio_decode_frame():解码音频 41 int audio_decode_frame(VideoState *is) 42 { 43 int len1, len2, decoded_data_size; 44 AVPacket *pkt = &is->audio_pkt; 45 int got_frame = 0; 46 int64_t dec_channel_layout; 47 int wanted_nb_samples, resampled_data_size; 48 49 for(;;) 50 { 51 while(is->audio_pkt_size > 0) 52 { 53 if(!is->audio_frame) 54 { 55 if(!(is->audio_frame = avacodec_alloc_frame())) 56 { 57 return AVERROR(ENOMEM); 58 } 59 } 60 else 61 avcodec_get_frame_defaults(is->audio_frame); 62 63 len1 = avcodec_decode_audio4(is->audio_st_codec, is->audio_frame, got_frame, pkt); 64 /* 解码错误,跳过整个包 */ 65 if(len1 < 0) 66 { 67 is->audio_pkt_size = 0; 68 break; 69 } 70 is->audio_pkt_data += len1; 71 is->audio_pkt_size -= len1; 72 if(!got_frame) continue; 73 /* 计算解码出来的桢需要的缓冲大小 */ 74 decoded_data_size = av_samples_get_buffer_size(NULL, 75 is->audio_frame_channels, 76 is->audio_frame_nb_samples, 77 is->audio_frame_format, 1); 78 dec_channel_layout = (is->audio_frame->channel_layout && is->audio_frame->channels== av_get_channel_layout_nb_channels(is->audio_frame->channel_layout)) 79 ? is->audio_frame->channel_layout : av_get_default_channel_layout(is->audio_frame->channels); 80 wanted_nb_samples = is->audio_frame->nb_samples; 81 if (is->audio_frame->format != is->audio_src_fmt || 82 dec_channel_layout != is->audio_src_channel_layout || 83 is->audio_frame->sample_rate != is->audio_src_freq || 84 (wanted_nb_samples != is->audio_frame->nb_samples && !is->swr_ctx)) 85 { 86 if (is->swr_ctx) 87 swr_free(&is->swr_ctx); 88 is->swr_ctx = swr_alloc_set_opts(NULL, 89 is->audio_tgt_channel_layout, 90 is->audio_tgt_fmt, 91 is->audio_tgt_freq, 92 dec_channel_layout, 93 is->audio_frame->format, 94 is->audio_frame->sample_rate, 95 0, NULL); 96 if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) 97 { 98 fprintf(stderr, "swr_init() failed\n"); 99 break; 100 } 101 is->audio_src_channel_layout = dec_channel_layout; 102 is->audio_src_channels = is->audio_st->codec->channels; 103 is->audio_src_freq = is->audio_st->codec->sample_rate; 104 is->audio_src_fmt = is->audio_st->codec->sample_fmt; 105 } 106 /* 这里我们可以对采样数进行调整,增加或者减少,一般可以用来做声画同步 */ 107 if (is->swr_ctx) 108 { 109 const uint8_t **in = (const uint8_t **)is->audio_frame->extended_data; 110 uint8_t *out[] = { is->audio_buf2 }; 111 if (wanted_nb_samples != is->audio_frame->nb_samples) 112 { 113 if(swr_set_compensation(is->swr_ctx, 114 (wanted_nb_samples - is->audio_frame->nb_samples)*is->audio_tgt_freq/is->audio_frame->sample_rate, 115 wanted_nb_samples * is->audio_tgt_freq/is->audio_frame->sample_rate) < 0) 116 { 117 fprintf(stderr, "swr_set_compensation() failed\n"); 118 break; 119 } 120 } 121 len2 = swr_convert(is->swr_ctx, out, sizeof(is->audio_buf2)/is->audio_tgt_channels/av_get_bytes_per_sample(is->audio_tgt_fmt), 122 in, is->audio_frame->nb_samples); 123 if (len2 < 0) 124 { 125 fprintf(stderr, "swr_convert() failed\n"); 126 break; 127 } 128 if(len2 == sizeof(is->audio_buf2)/is->audio_tgt_channels/av_get_bytes_per_sample(is->audio_tgt_fmt)) 129 { 130 fprintf(stderr, "warning: audio buffer is probably too small\n"); 131 swr_init(is->swr_ctx); 132 } 133 is->audio_buf = is->audio_buf2; 134 resampled_data_size = len2*is->audio_tgt_channels*av_get_bytes_per_sample(is->audio_tgt_fmt); 135 } else { 136 resampled_data_size = decoded_data_size; 137 is->audio_buf = is->audio_frame->data[0]; 138 } 139 /* 返回得到的数据 */ 140 return resampled_data_size; 141 } 142 if (pkt->data) av_free_packet(pkt); 143 memset(pkt, 0, sizeof(*pkt)); 144 if (is->quit) return -1; 145 if (packet_queue_get(&is->audioq, pkt, 1) < 0) return -1; 146 is->audio_pkt_data = pkt->data; 147 is->audio_pkt_size = pkt->size; 148 149 } 150 }
FFMPEG结构体
channel_layout_map
1 static const struct {
2 const char *name;
3 int nb_channels;
4 uint64_t layout;
5 } channel_layout_map[] = {
6 { "mono", 1, AV_CH_LAYOUT_MONO },
7 { "stereo", 2, AV_CH_LAYOUT_STEREO },
8 { "2.1", 3, AV_CH_LAYOUT_2POINT1 },
9 { "3.0", 3, AV_CH_LAYOUT_SURROUND },
10 { "3.0(back)", 3, AV_CH_LAYOUT_2_1 },
11 { "4.0", 4, AV_CH_LAYOUT_4POINT0 },
12 { "quad", 4, AV_CH_LAYOUT_QUAD },
13 { "quad(side)", 4, AV_CH_LAYOUT_2_2 },
14 { "3.1", 4, AV_CH_LAYOUT_3POINT1 },
15 { "5.0", 5, AV_CH_LAYOUT_5POINT0_BACK },
16 { "5.0(side)", 5, AV_CH_LAYOUT_5POINT0 },
17 { "4.1", 5, AV_CH_LAYOUT_4POINT1 },
18 { "5.1", 6, AV_CH_LAYOUT_5POINT1_BACK },
19 { "5.1(side)", 6, AV_CH_LAYOUT_5POINT1 },
20 { "6.0", 6, AV_CH_LAYOUT_6POINT0 },
21 { "6.0(front)", 6, AV_CH_LAYOUT_6POINT0_FRONT },
22 { "hexagonal", 6, AV_CH_LAYOUT_HEXAGONAL },
23 { "6.1", 7, AV_CH_LAYOUT_6POINT1 },
24 { "6.1", 7, AV_CH_LAYOUT_6POINT1_BACK },
25 { "6.1(front)", 7, AV_CH_LAYOUT_6POINT1_FRONT },
26 { "7.0", 7, AV_CH_LAYOUT_7POINT0 },
27 { "7.0(front)", 7, AV_CH_LAYOUT_7POINT0_FRONT },
28 { "7.1", 8, AV_CH_LAYOUT_7POINT1 },
29 { "7.1(wide)", 8, AV_CH_LAYOUT_7POINT1_WIDE },
30 { "octagonal", 8, AV_CH_LAYOUT_OCTAGONAL },
31 { "downmix", 2, AV_CH_LAYOUT_STEREO_DOWNMIX, },
32 };
FFMPEG宏定义
Audio channel convenience macros
1 #define AV_CH_LAYOUT_MONO (AV_CH_FRONT_CENTER)
2 #define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
3 #define AV_CH_LAYOUT_2POINT1 (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)
4 #define AV_CH_LAYOUT_2_1 (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)
5 #define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
6 #define AV_CH_LAYOUT_3POINT1 (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY)
7 #define AV_CH_LAYOUT_4POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER)
8 #define AV_CH_LAYOUT_4POINT1 (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY)
9 #define AV_CH_LAYOUT_2_2 (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
10 #define AV_CH_LAYOUT_QUAD (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
11 #define AV_CH_LAYOUT_5POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
12 #define AV_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY)
13 #define AV_CH_LAYOUT_5POINT0_BACK (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
14 #define AV_CH_LAYOUT_5POINT1_BACK (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY)
15 #define AV_CH_LAYOUT_6POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER)
16 #define AV_CH_LAYOUT_6POINT0_FRONT (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
17 #define AV_CH_LAYOUT_HEXAGONAL (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER)
18 #define AV_CH_LAYOUT_6POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER)
19 #define AV_CH_LAYOUT_6POINT1_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER)
20 #define AV_CH_LAYOUT_6POINT1_FRONT (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY)
21 #define AV_CH_LAYOUT_7POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
22 #define AV_CH_LAYOUT_7POINT0_FRONT (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
23 #define AV_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
24 #define AV_CH_LAYOUT_7POINT1_WIDE (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
25 #define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER)
26 #define AV_CH_LAYOUT_OCTAGONAL (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT)
27 #define AV_CH_LAYOUT_STEREO_DOWNMIX (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT)
SDL宏定义
SDL_AudioSpec format
1 AUDIO_U8 Unsigned 8-bit samples
2 AUDIO_S8 Signed 8-bit samples
3 AUDIO_U16LSB Unsigned 16-bit samples, in little-endian byte order
4 AUDIO_S16LSB Signed 16-bit samples, in little-endian byte order
5 AUDIO_U16MSB Unsigned 16-bit samples, in big-endian byte order
6 AUDIO_S16MSB Signed 16-bit samples, in big-endian byte order
7 AUDIO_U16 same as AUDIO_U16LSB (for backwards compatability probably)
8 AUDIO_S16 same as AUDIO_S16LSB (for backwards compatability probably)
9 AUDIO_U16SYS Unsigned 16-bit samples, in system byte order
10 AUDIO_S16SYS Signed 16-bit samples, in system byte order
git clone https://github.com/lnmcc/musicPlayer.git