您的位置:首页 > 其它

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的音频参数—->打开声音设备,播放静音—->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
gitclonehttps://github.com/lnmcc/musicPlayer.git
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: