FFMPEG + SDL音频播放分析


目录 [hide]

抽象流程:

设置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的缓冲区大小时,需要分多次拷贝。

关键实现:

main()函数


1
 int main(int argc, char **argv){
 
    
2
 SDL_Event event; //SDL事件变量
 
    
3
 VideoState    *is; // 纪录视频及解码器等信息的大结构体
 
    
4
 is = (VideoState*) av_mallocz(sizeof(VideoState));
 
    
5
 if(argc < 2){
 
    
6
 fprintf(stderr, "Usage: play <file>\n");
 
    
7
 exit(1);
 
    
8
 }
 
    
9
 av_register_all(); //注册所有ffmpeg的解码器
 
    
10
 /* 初始化SDL,这里只实用了AUDIO,如果有视频,好需要SDL_INIT_VIDEO等等 */
 
    
11
 if(SDL_Init(SDL_INIT_AUDIO)){
 
    
12
 fprintf(stderr, "Count not initialize SDL - %s\n", SDL_GetError());
 
    
13
 exit(1);
 
    
14
 }
 
    
15
 is_strlcpy(is->filename, argv[1], sizeof(is->filename));
 
    
16
 /* 创建一个SDL线程来做视频解码工作,主线程进入SDL事件循环 */
 
    
17
 is->parse_tid = SDL_CreateThread(decode_thread, is);
 
    
18
 if(!is->parse_tid){
 
    
19
 SDL_WaitEvent(&event);
 
    
20
 switch(event.type){
 
    
21
 case FF_QUIT_EVENT:
 
    
22
 case SDL_QUIT:
 
    
23
 is->quit = 1;
 
    
24
 SDL_Quit();
 
    
25
 exit(0);
 
    
26
 break;
 
    
27
 default:
 
    
28
 break;
 
    
29
 }
 
    
30
 }
 
    
31
 return 0;
 
    
32
 }


decode_thread()读取文件信息和音频包

1
 static int decode_thread(void *arg){
 
    
2
 VideoState *is = (VideoState*)arg;
 
    
3
 AVFormatContext *ic = NULL;
 
    
4
 AVPacket pkt1, *packet = &pkt1;
 
    
5
 int ret, i, audio_index = -1;
 
    
6
  
 
    
7
 is->audioStream = -1;
 
    
8
 global_video_state = is; 
 
    
9
 /*  使用ffmpeg打开视频,解码器等 常规工作 */
 
    
10
 if(avFormat_open_input(&ic, is->filename, NULL,  NULL) != 0)  {
 
    
11
 fprintf(stderr, "open file error: %s\n", is->filename);
 
    
12
 return -1;
 
    
13
 }
 
    
14
 is->ic = ic;
 
    
15
 if(avformat_find_stream_info(ic, NULL) < 0){
 
    
16
 fprintf(stderr, "find stream info error\n");
 
    
17
 return -1;
 
    
18
 }
 
    
19
 av_dump_format(ic, 0, is->filename, 0);
 
    
20
 for(i  = 0; i < ic->nb_streams; i++){
 
    
21
 if(ic->streams[i])->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index == -1){
 
    
22
 audio_index = i;
 
    
23
 break;
 
    
24
 }
 
    
25
 }
 
    
26
 if(audio_index >= 0) {
 
    
27
 /* 所有设置SDL音频流信息的步骤都在这个函数里完成 */
 
    
28
 stream_component_open(is, audio_index);
 
    
29
 }
 
    
30
 if(is->audioStream < 0){
 
    
31
 fprintf(stderr, "could not open codecs for file: %s\n", is->filename);
 
    
32
 goto fail;
 
    
33
 }
 
    
34
 /* 读包的主循环, av_read_frame不停的从文件中读取数据包(这里只取音频包)*/
 
    
35
 for(;;){
 
    
36
 if(is->quit) break;
 
    
37
 /* 这里audioq.size是指队列中的所有数据包带的音频数据的总量,并不是包的数量 */
 
    
38
 if(is->audioq.size > MAX_AUDIO_SIZE){
 
    
39
 SDL_Delay(10); // 毫秒
 
    
40
 continue;
 
    
41
 }
 
    
42
 ret = av_read_frame(is->ic, packet);
 
    
43
 if(ret < 0){
 
    
44
 if(ret == AVERROR_EOF || url_feof(is->ic->pb))    break;
 
    
45
 if(is->ic->pb && is->ic->pb->error)    break;
 
    
46
 contiue;                  
 
    
47
 }  
 
    
48
 if(packet->stream_index == is->audioStream){
 
    
49
 packet_queue_put(&is->audioq, packet);
 
    
50
 } else{
 
    
51
 av_free_packet(packet);
 
    
52
 }
 
    
53
 }
 
    
54
 while(!is->quit)    SDL_Delay(100);
 
    
55
 fail: {
 
    
56
 SDL_Event event;
 
    
57
 event.type = FF_QUIT_EVENT;
 
    
58
 event.user.data1 = is;
 
    
59
 SDL_PushEvent(&event);
 
    
60
 }
 
    
61
 return 0;
 
    
62
 }


stream_component_open():设置音频参数和打开设备


1
 int stream_component_open(videoState *is, int stream_index){
 
    
2
 AVFormatContext *ic = is->ic;
 
    
3
 AVCodecContext *codecCtx;
 
    
4
 AVCodec *codec;
 
    
5
 /* 在用SDL_OpenAudio()打开音频设备的时候需要这两个参数*/
 
    
6
 /* wanted_spec是我们期望设置的属性,spec是系统最终接受的参数 */
 
    
7
 /* 我们需要检查系统接受的参数是否正确 */
 
    
8
 SDL_AudioSpec wanted_spec, spec;
 
    
9
 int64_t wanted_channel_layout = 0; // 声道布局(SDL中的具体定义见“FFMPEG结构体”部分) 
 
    
10
 int wanted_nb_channels; // 声道数
 
    
11
 /*  SDL支持的声道数为 1, 2, 4, 6 */
 
    
12
 /*  后面我们会使用这个数组来纠正不支持的声道数目 */
 
    
13
 const int next_nb_channels[] = { 0, 0, 1, 6,  2, 6, 4, 6 }; 
 
    
14
  
 
    
15
 if(stream_index < 0 || stream_index >= ic->nb_streams)    return -1;
 
    
16
 codecCtx = ic->streams[stream_index]->codec;
 
    
17
 wanted_nb_channels = codecCtx->channels;
 
    
18
 if(!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
 
    
19
 wanted_channel_layout = av_get_default_channel_lauout(wanted_channel_nb_channels);
 
    
20
 wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
 
    
21
 }
 
    
22
 wanted_spec.channels = av_get_channels_layout_nb_channels(wanted_channel_layout);
 
    
23
 wanted_spec.freq = codecCtx->sample_rate;
 
    
24
 if(wanted_spec.freq <= 0 || wanted_spec.channels <=0){
 
    
25
 fprintf(stderr, "Invaild sample rate or channel count!\n");
 
    
26
 return -1;
 
    
27
 }
 
    
28
 wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分
 
    
29
 wanted_spec.silence = 0; // 0指示静音
 
    
30
 wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; // 自定义SDL缓冲区大小
 
    
31
 wanted_spec.callback = audio_callback; // 音频解码的关键回调函数
 
    
32
 wanted_spec.userdata = is; // 传给上面回调函数的外带数据
 
    
33
  
 
    
34
 /*  打开音频设备,这里使用一个while来循环尝试打开不同的声道数(由上面 */
 
    
35
 /*  next_nb_channels数组指定)直到成功打开,或者全部失败 */
 
    
36
 while(SDL_OpenAudio(&wanted_spec, &spec) < 0){
 
    
37
 fprintf(stderr, "SDL_OpenAudio(%d channels): %s\n", wanted_spec.channels, SDL_GetError());
 
    
38
 wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)]; // FFMIN()由ffmpeg定义的宏,返回较小的数
 
    
39
 if(!wanted_spec.channels){
 
    
40
 fprintf(stderr, "No more channel to try\n");
 
    
41
 return -1;
 
    
42
 }
 
    
43
 wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
 
    
44
 }
 
    
45
 /* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */
 
    
46
 if(spec.format != AUDIO_S16SYS){
 
    
47
 fprintf(stderr, "SDL advised audio format %d is not supported\n", spec.format);
 
    
48
 return -1;
 
    
49
 }
 
    
50
 if(spec.channels != wanted_spec.channels) {
 
    
51
 wanted_channel_layout = av_get_default_channel_layout(spec.channels);
 
    
52
 if(!wanted_channel_layout){
 
    
53
 fprintf(stderr, "SDL advised channel count %d is not support\n", spec.channels);
 
    
54
 return -1;
 
    
55
 }
 
    
56
 }
 
    
57
 /* 把设置好的参数保存到大结构中 */
 
    
58
 is->audio_src_fmt = is->audio_tgt_fmt = AV_SAMPLE_FMT_S16;
 
    
59
 is->audio_src_freq = is->audio_tgt_freq = spec.freq;
 
    
60
 is->audio_src_channel_layout = is->audio_tgt_layout = wanted_channel_layout;
 
    
61
 is->audio_src_channels = is->audio_tat_channels = spec.channels;
 
    
62
  
 
    
63
 codec = avcodec_find_decoder(codecCtx>codec_id);
 
    
64
 if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)){
 
    
65
 fprintf(stderr, "Unsupported codec!\n");
 
    
66
 return -1;
 
    
67
 }
 
    
68
 ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; //具体含义请查看“FFMPEG宏定义”部分
 
    
69
 is->audioStream = stream_index;
 
    
70
 is->audio_st = ic->streams[stream_index];
 
    
71
 is->audio_buf_size = 0;
 
    
72
 is->audio_buf_index = 0;
 
    
73
 memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
 
    
74
 packet_queue_init(&is->audioq);
 
    
75
 SDL_PauseAudio(0); // 开始播放静音
 
    
76
 }


audio_callback(): 回调函数,向SDL缓冲区填充数据

1
 void audio_callback(void *userdata, Uint8 *stream, int len){
 
    
2
 VideoState *is = (VideoState*)userdata;
 
    
3
 int len1, audio_data_size;
 
    
4
  
 
    
5
 /*   len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 */
 
    
6
 while(len > 0){
 
    
7
 /*  audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/
 
    
8
 /*   这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/
 
    
9
 /*   们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更
 
    
10
 /*   多的桢数据 */
 
    
11
 if(is->audio_buf_index >= is->audio_buf_size){
 
    
12
 audio_data_size = audio_decode_frame(is);
 
    
13
 /* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */
 
    
14
 is(audio_data_size < 0){
 
    
15
 is->audio_buf_size = 1024;
 
    
16
 /* 清零,静音 */
 
    
17
 memset(is->audio_buf, 0, is->audio_buf_size);
 
    
18
 } else{
 
    
19
 is->audio_buf_size = audio_data_size;
 
    
20
 }
 
    
21
 is->audio_buf_index = 0;
 
    
22
 }
 
    
23
 /*  查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */
 
    
24
 len1 = is->audio_buf_size - is->audio_buf_index;
 
    
25
 if(len1 > len)    len1 = len;
 
    
26
  
 
    
27
 memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1);
 
    
28
 len -= len1;
 
    
29
 stream += len1;
 
    
30
 is->audio_buf_index += len1;
 
    
31
 }
 
    
32
 }

audio_decode_frame():解码音频

1
 int audio_decode_frame(VideoState *is){
 
    
2
 int len1, len2, decoded_data_size;
 
    
3
 AVPacket *pkt = &is->audio_pkt;
 
    
4
 int got_frame = 0;
 
    
5
 int64_t dec_channel_layout;
 
    
6
 int wanted_nb_samples, resampled_data_size;
 
    
7
  
 
    
8
 for(;;){
 
    
9
 while(is->audio_pkt_size > 0){
 
    
10
 if(!is->audio_frame){
 
    
11
 if(!(is->audio_frame = avacodec_alloc_frame())){
 
    
12
 return AVERROR(ENOMEM);
 
    
13
 }
 
    
14
 } else
 
    
15
 avcodec_get_frame_defaults(is->audio_frame);
 
    
16
  
 
    
17
 len1 = avcodec_decode_audio4(is->audio_st_codec, is->audio_frame, got_frame, pkt);
 
    
18
 /* 解码错误,跳过整个包 */
 
    
19
 if(len1 < 0){
 
    
20
 is->audio_pkt_size = 0;
 
    
21
 break;
 
    
22
 }
 
    
23
 is->audio_pkt_data += len1;
 
    
24
 is->audio_pkt_size -= len1;
 
    
25
 if(!got_frame)   continue;
 
    
26
 /* 计算解码出来的桢需要的缓冲大小 */
 
    
27
 decoded_data_size = av_samples_get_buffer_size(NULL,
 
    
28
 is->audio_frame_channels,
 
    
29
 is->audio_frame_nb_samples,
 
    
30
 is->audio_frame_format, 1);
 
    
31
 dec_channel_layout = (is->audio_frame->channel_layout && is->audio_frame->channels
 
    
32
 == av_get_channel_layout_nb_channels(is->audio_frame->channel_layout))
 
    
33
 ? is->audio_frame->channel_layout : av_get_default_channel_layout(is->audio_frame->channels);                       
 
    
34
 wanted_nb_samples =  is->audio_frame->nb_samples;
 
    
35
 if (is->audio_frame->format != is->audio_src_fmt || 
 
    
36
 dec_channel_layout != is->audio_src_channel_layout ||
 
    
37
 is->audio_frame->sample_rate != is->audio_src_freq || 
 
    
38
 (wanted_nb_samples != is->audio_frame->nb_samples && !is->swr_ctx)) {
 
    
39
 if (is->swr_ctx) swr_free(&is->swr_ctx);
 
    
40
 is->swr_ctx = swr_alloc_set_opts(NULL,
 
    
41
 is->audio_tgt_channel_layout,
 
    
42
 is->audio_tgt_fmt,
 
    
43
 is->audio_tgt_freq,
 
    
44
 dec_channel_layout,
 
    
45
 is->audio_frame->format,
 
    
46
 is->audio_frame->sample_rate,
 
    
47
 0, NULL);
 
    
48
 if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
 
    
49
 fprintf(stderr, "swr_init() failed\n");
 
    
50
 break;
 
    
51
 }
 
    
52
 is->audio_src_channel_layout = dec_channel_layout;
 
    
53
 is->audio_src_channels = is->audio_st->codec->channels;
 
    
54
 is->audio_src_freq = is->audio_st->codec->sample_rate;
 
    
55
 is->audio_src_fmt = is->audio_st->codec->sample_fmt;
 
    
56
 }
 
    
57
 /* 这里我们可以对采样数进行调整,增加或者减少,一般可以用来做声画同步 */
 
    
58
 if (is->swr_ctx) {
 
    
59
 const uint8_t **in = (const uint8_t **)is->audio_frame->extended_data;
 
    
60
 uint8_t *out[] = { is->audio_buf2 };
 
    
61
 if (wanted_nb_samples != is->audio_frame->nb_samples) {
 
    
62
 if(swr_set_compensation(is->swr_ctx, 
 
    
63
 (wanted_nb_samples - is->audio_frame->nb_samples)*is->audio_tgt_freq/is->audio_frame->sample_rate,
 
    
64
 wanted_nb_samples * is->audio_tgt_freq/is->audio_frame->sample_rate) < 0) {
 
    
65
 fprintf(stderr, "swr_set_compensation() failed\n");
 
    
66
 break;
 
    
67
 }
 
    
68
 }
 
    
69
 len2 = swr_convert(is->swr_ctx, out,  
 
    
70
 sizeof(is->audio_buf2)/is->audio_tgt_channels/av_get_bytes_per_sample(is->audio_tgt_fmt),  
 
    
71
 in, is->audio_frame->nb_samples);
 
    
72
 if (len2 < 0) {
 
    
73
 fprintf(stderr, "swr_convert() failed\n");
 
    
74
 break;
 
    
75
 }
 
    
76
 if(len2 == sizeof(is->audio_buf2)/is->audio_tgt_channels/av_get_bytes_per_sample(is->audio_tgt_fmt)) {
 
    
77
 fprintf(stderr, "warning: audio buffer is probably too small\n");
 
    
78
 swr_init(is->swr_ctx);
 
    
79
 }
 
    
80
 is->audio_buf = is->audio_buf2;
 
    
81
 resampled_data_size = len2*is->audio_tgt_channels*av_get_bytes_per_sample(is->audio_tgt_fmt);
 
    
82
 } else {
 
    
83
 resampled_data_size = decoded_data_size;
 
    
84
 is->audio_buf = is->audio_frame->data[0];
 
    
85
 }
 
    
86
 /*  返回得到的数据 */
 
    
87
 return resampled_data_size;
 
    
88
 }
 
    
89
 if (pkt->data) av_free_packet(pkt);
 
    
90
 memset(pkt, 0, sizeof(*pkt));
 
    
91
 if (is->quit) return -1;
 
    
92
 if (packet_queue_get(&is->audioq, pkt, 1) < 0) return -1;
 
    
93
 is->audio_pkt_data = pkt->data;
 
    
94
 is->audio_pkt_size = pkt->size;
 
    
95
  
 
    
96
 }
 
    
97
 }

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