目录 [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){
|
4 |
is = (VideoState*) av_mallocz( sizeof (VideoState));
|
6 |
fprintf (stderr, "Usage: play <file>\n" );
|
11 |
if (SDL_Init(SDL_INIT_AUDIO)){
|
12 |
fprintf (stderr, "Count not initialize SDL - %s\n" , SDL_GetError());
|
15 |
is_strlcpy(is->filename, argv[1], sizeof (is->filename));
|
17 |
is->parse_tid = SDL_CreateThread(decode_thread, is);
|
19 |
SDL_WaitEvent(&event);
|
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;
|
8 |
global_video_state = is;
|
10 |
if (avFormat_open_input(&ic, is->filename, NULL, NULL) != 0) {
|
11 |
fprintf (stderr, "open file error: %s\n" , is->filename);
|
15 |
if (avformat_find_stream_info(ic, NULL) < 0){
|
16 |
fprintf (stderr, "find stream info error\n" );
|
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){
|
26 |
if (audio_index >= 0) {
|
28 |
stream_component_open(is, audio_index);
|
30 |
if (is->audioStream < 0){
|
31 |
fprintf (stderr, "could not open codecs for file: %s\n" , is->filename);
|
38 |
if (is->audioq.size > MAX_AUDIO_SIZE){
|
42 |
ret = av_read_frame(is->ic, packet);
|
44 |
if (ret == AVERROR_EOF || url_feof(is->ic->pb)) break ;
|
45 |
if (is->ic->pb && is->ic->pb->error) break ;
|
48 |
if (packet->stream_index == is->audioStream){
|
49 |
packet_queue_put(&is->audioq, packet);
|
51 |
av_free_packet(packet);
|
54 |
while (!is->quit) SDL_Delay(100);
|
57 |
event.type = FF_QUIT_EVENT;
|
58 |
event.user.data1 = is;
|
59 |
SDL_PushEvent(&event);
|
stream_component_open():设置音频参数和打开设备
1 |
int stream_component_open(videoState *is, int stream_index){
|
2 |
AVFormatContext *ic = is->ic;
|
3 |
AVCodecContext *codecCtx;
|
8 |
SDL_AudioSpec wanted_spec, spec;
|
9 |
int64_t wanted_channel_layout = 0;
|
10 |
int wanted_nb_channels;
|
13 |
const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 };
|
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;
|
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" );
|
28 |
wanted_spec.format = AUDIO_S16SYS;
|
29 |
wanted_spec.silence = 0;
|
30 |
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
|
31 |
wanted_spec.callback = audio_callback;
|
32 |
wanted_spec.userdata = is;
|
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)];
|
39 |
if (!wanted_spec.channels){
|
40 |
fprintf (stderr, "No more channel to try\n" );
|
43 |
wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
|
46 |
if (spec.format != AUDIO_S16SYS){
|
47 |
fprintf (stderr, "SDL advised audio format %d is not supported\n" , spec.format);
|
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);
|
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;
|
63 |
codec = avcodec_find_decoder(codecCtx>codec_id);
|
64 |
if (!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)){
|
65 |
fprintf (stderr, "Unsupported codec!\n" );
|
68 |
ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
|
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);
|
audio_callback(): 回调函数,向SDL缓冲区填充数据
1 |
void audio_callback( void *userdata, Uint8 *stream, int len){
|
2 |
VideoState *is = (VideoState*)userdata;
|
3 |
int len1, audio_data_size;
|
11 |
if (is->audio_buf_index >= is->audio_buf_size){
|
12 |
audio_data_size = audio_decode_frame(is);
|
14 |
is(audio_data_size < 0){
|
15 |
is->audio_buf_size = 1024;
|
17 |
memset (is->audio_buf, 0, is->audio_buf_size);
|
19 |
is->audio_buf_size = audio_data_size;
|
21 |
is->audio_buf_index = 0;
|
24 |
len1 = is->audio_buf_size - is->audio_buf_index;
|
25 |
if (len1 > len) len1 = len;
|
27 |
memcpy (stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1);
|
30 |
is->audio_buf_index += len1;
|
audio_decode_frame():解码音频
1 |
int audio_decode_frame(VideoState *is){
|
2 |
int len1, len2, decoded_data_size;
|
3 |
AVPacket *pkt = &is->audio_pkt;
|
5 |
int64_t dec_channel_layout;
|
6 |
int wanted_nb_samples, resampled_data_size;
|
9 |
while (is->audio_pkt_size > 0){
|
11 |
if (!(is->audio_frame = avacodec_alloc_frame())){
|
12 |
return AVERROR(ENOMEM);
|
15 |
avcodec_get_frame_defaults(is->audio_frame);
|
17 |
len1 = avcodec_decode_audio4(is->audio_st_codec, is->audio_frame, got_frame, pkt);
|
20 |
is->audio_pkt_size = 0;
|
23 |
is->audio_pkt_data += len1;
|
24 |
is->audio_pkt_size -= len1;
|
25 |
if (!got_frame) continue ;
|
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,
|
45 |
is->audio_frame->format,
|
46 |
is->audio_frame->sample_rate,
|
48 |
if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
|
49 |
fprintf (stderr, "swr_init() failed\n" );
|
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;
|
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" );
|
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);
|
73 |
fprintf (stderr, "swr_convert() failed\n" );
|
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);
|
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);
|
83 |
resampled_data_size = decoded_data_size;
|
84 |
is->audio_buf = is->audio_frame->data[0];
|
87 |
return resampled_data_size;
|
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;
|
FFMPEG结构体
channel_layout_map
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, },
|
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