目錄 [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