FFMPEG + SDL音频播放分析
2014-04-30 12:36
302 查看
目录[hide]
1抽象流程:
2关键实现:
2.1main()函数
2.2decode_thread()读取文件信息和音频包
2.3stream_component_open():设置音频参数和打开设备
2.4audio_callback():回调函数,向SDL缓冲区填充数据
2.5audio_decode_frame():解码音频
3FFMPEG结构体
3.1channel_layout_map
4FFMPEG宏定义
4.1Audiochannelconveniencemacros
5SDL宏定义
5.1SDL_AudioSpecformat
SDL内部维护了一个buffer来存放解码后的数据,这个buffer中的数据来源是我们注册的回调函数(audio_callback),audio_callback调用audio_decode_frame来做具体的音频解码工作,需要引起注意的是:从流中读取出的一个音频包(avpacket)可能含有多个音频桢(avframe),所以需要多次调用avcodec_decode_audio4来完成整个包的解码,解码出来的数据存放在我们自己的缓冲中(audio_buf2)。SDL每一次回调都会引起数据从audio_buf2拷贝到SDL内部缓冲区,当audio_buf2中的数据大于SDL的缓冲区大小时,需要分多次拷贝。
查看源代码
打印帮助
gitclonehttps://github.com/lnmcc/musicPlayer.git
抽象流程:
设置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_Eventevent; //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, "CountnotinitializeSDL-%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 | AVPacketpkt1,*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, "openfileerror:%s\n" ,is->filename); |
12 | return -1; |
13 | } |
14 | is->ic=ic; |
15 | if (avformat_find_stream_info(ic,NULL)<0){ |
16 | fprintf (stderr, "findstreaminfoerror\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, "couldnotopencodecsforfile:%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_Eventevent; |
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_AudioSpecwanted_spec,spec; |
9 | int64_twanted_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, "Invaildsamplerateorchannelcount!\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(%dchannels):%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, "Nomorechanneltotry\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, "SDLadvisedaudioformat%disnotsupported\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, "SDLadvisedchannelcount%disnotsupport\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, "Unsupportedcodec!\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_tdec_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:audiobufferisprobablytoosmall\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_tlayout; |
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宏定义
Audiochannelconveniencemacros
1 | #defineAV_CH_LAYOUT_MONO(AV_CH_FRONT_CENTER) |
2 | #defineAV_CH_LAYOUT_STEREO(AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT) |
3 | #defineAV_CH_LAYOUT_2POINT1(AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY) |
4 | #defineAV_CH_LAYOUT_2_1(AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER) |
5 | #defineAV_CH_LAYOUT_SURROUND(AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER) |
6 | #defineAV_CH_LAYOUT_3POINT1(AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY) |
7 | #defineAV_CH_LAYOUT_4POINT0(AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER) |
8 | #defineAV_CH_LAYOUT_4POINT1(AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY) |
9 | #defineAV_CH_LAYOUT_2_2(AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT) |
10 | #defineAV_CH_LAYOUT_QUAD(AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) |
11 | #defineAV_CH_LAYOUT_5POINT0(AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT) |
12 | #defineAV_CH_LAYOUT_5POINT1(AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY) |
13 | #defineAV_CH_LAYOUT_5POINT0_BACK(AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) |
14 | #defineAV_CH_LAYOUT_5POINT1_BACK(AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY) |
15 | #defineAV_CH_LAYOUT_6POINT0(AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER) |
16 | #defineAV_CH_LAYOUT_6POINT0_FRONT(AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) |
17 | #defineAV_CH_LAYOUT_HEXAGONAL(AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER) |
18 | #defineAV_CH_LAYOUT_6POINT1(AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER) |
19 | #defineAV_CH_LAYOUT_6POINT1_BACK(AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER) |
20 | #defineAV_CH_LAYOUT_6POINT1_FRONT(AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY) |
21 | #defineAV_CH_LAYOUT_7POINT0(AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) |
22 | #defineAV_CH_LAYOUT_7POINT0_FRONT(AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) |
23 | #defineAV_CH_LAYOUT_7POINT1(AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) |
24 | #defineAV_CH_LAYOUT_7POINT1_WIDE(AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) |
25 | #defineAV_CH_LAYOUT_7POINT1_WIDE_BACK(AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) |
26 | #defineAV_CH_LAYOUT_OCTAGONAL(AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT) |
27 | #defineAV_CH_LAYOUT_STEREO_DOWNMIX(AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT) |
SDL宏定义
SDL_AudioSpecformat
1 | AUDIO_U8Unsigned8-bitsamples |
2 | AUDIO_S8Signed8-bitsamples |
3 | AUDIO_U16LSBUnsigned16-bitsamples,inlittle-endianbyteorder |
4 | AUDIO_S16LSBSigned16-bitsamples,inlittle-endianbyteorder |
5 | AUDIO_U16MSBUnsigned16-bitsamples,inbig-endianbyteorder |
6 | AUDIO_S16MSBSigned16-bitsamples,inbig-endianbyteorder |
7 | AUDIO_U16sameasAUDIO_U16LSB( for backwardscompatabilityprobably) |
8 | AUDIO_S16sameasAUDIO_S16LSB( for backwardscompatabilityprobably) |
9 | AUDIO_U16SYSUnsigned16-bitsamples,in system byteorder |
10 | AUDIO_S16SYSSigned16-bitsamples,in system byteorder |
相关文章推荐
- FFMPEG + SDL音频播放分析
- FFMPEG + SDL音频播放分析
- FFMPEG + SDL音频播放分析
- FFMPEG + SDL音频播放分析
- SDL播放wav音频分析
- ffmpeg+SDL的使用之使用SDL播放音频流
- ffmpeg+SDL Tutorial 03--播放音频
- ffmpeg+SDL播放音频(无杂音)
- FFmpeg+SDL2.0 音频解码播放(部分格式杂音处理)
- FFMPEG学习----使用SDL播放PCM数据
- 最简单的基于FFMPEG+SDL的音频播放器
- 深度分析:Android4.3下MMS发送到附件为音频文件(音频为系统内置音频)的彩信给自己,添加音频-发送彩信-接收彩信-下载音频附件-预览-播放(一,添加附件)
- ffmpeg实战教程(二)用SDL播放YUV,并结合ffmpeg实现简易播放器
- 用Qt线程及媒体类播放FFmpeg解码的音频数据
- ffmpeg中音频解码方法(附代码)+ffmpeg音频解码播放速度快的问题(随手笔记,以供查阅)
- Xcode模拟器中无法播放音频文件的原因分析
- 最简单的视音频播放示例9:SDL2播放PCM
- Ffmpeg和SDL如何同步音频
- 应用FFMPEG和SDL,播放视频文件
- ffmpeg音频播放代码示例-avcodec_decode_audio4