##本文旨在记录使用SDL(Simple DirectMedia Layer)播放Audio中遇到的问题
- SDL2库使用SDL_OpenAudioDevice播放不成功,使用SDL_OpenAudio播放成功,如下代码所示
//使用SDL_OpenAudioDevice进行播放
dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, NULL, SDL_AUDIO_ALLOW_ANY_CHANGE);
if (dev == 0){
fprintf(stderr, "\nFailed to open audio: %s\n", SDL_GetError());
return -1;
}
//使用SDL_OpenAudio进行播放
if (SDL_OpenAudio(&wanted_spec, NULL) < 0){
fprintf(stderr, "\nFailed to open audio: %s\n", SDL_GetError());
return -1;
}
通过查阅SDL_OpenAudioDevice的 API文档,可以发现一下的描述:
For compatibility with SDL 1.2, this will never return 1, since SDL reserves that ID for the legacy SDL_OpenAudio() function.
通过查看SDL2的源代码,如下:
int
SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
{
SDL_AudioDeviceID id = 0;
/* Start up the audio driver, if necessary. This is legacy behaviour! */
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
return (-1);
}
}
/* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
if (open_devices[0] != NULL) {
SDL_SetError("Audio device is already opened");
return (-1);
}
if (obtained) {
id = open_audio_device(NULL, 0, desired, obtained,
SDL_AUDIO_ALLOW_ANY_CHANGE, 1);
} else {
id = open_audio_device(NULL, 0, desired, desired, 0, 1);
}
SDL_assert((id == 0) || (id == 1));
return ((id == 0) ? -1 : 0);
}
SDL_AudioDeviceID
SDL_OpenAudioDevice(const char *device, int iscapture,
const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
int allowed_changes)
{
return open_audio_device(device, iscapture, desired, obtained,
allowed_changes, 2);
}
可以看出SDL_OpenAudioDevice和SDL_OpenAudio函数都是通过调用open_audio_device来实现打开Audio device的操作的。
open_audio_device的函数原型如下:
static SDL_AudioDeviceID
open_audio_device(const char *devname, int iscapture,
const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
int allowed_changes, int min_id);
所以SDL2为了与SDL1.x兼容,SDL_OpenAudioDevice函数只能用来打开AudioDeviceID大于等于2的设备,
而默认的设备,即AudioDeviceID等于1,只能用SDL_OpenAudio函数打开。
2. Linux下SDL_OpenAudio失败
在网上找到下面这个解决方案,经验证可以解决问题,但还不是很理解
If you built your own SDL, you probably didn't have development headers
for PulseAudio (or ALSA), so it's trying to use /dev/dsp, which doesn't
exist on many modern Linux systems (hence, SDL_Init(SDL_INIT_AUDIO)
succeeds, but no devices are found when you try to open one). "apt-get
install libasound2-dev libpulse-dev" and rebuild SDL...let the configure
script find the new headers so it includes PulseAudio and ALSA support.
If you didn't build your own SDL, maybe you can force it to use a
different audio path:
SDL_AUDIODRIVER=pulse ./mytestprogram
or
SDL_AUDIODRIVER=alsa ./mytestprogram
One of those two solutions will (probably) fix your problem.
3. 编译出现underfined reference to "av_register_all()"错误----2015.12.8更新
造成这种错误的原因是因为FFMPEG库是用纯C语言写的,如果用按C++的编译语法进行编译时回出错,
解决办法就是用extern "C"将FFMPEG库指定按C语言编译
extern "C"{
#include
#include
#include
}
4. 雷博参考程序中resample部分的修正
out_buffer_size的计算不对
int out_nb_samples = pCodecContx->frame_size;
int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
fwrite(out_buffer, 1, out_buffer_size, pFile);
通过swr_convert的返回值获取resample后的sample number per channel
这里需要提的是resample最后可能会有一些sample遗留在SwrContext中,所以最后要在调用一次swr_convert函数
while(av_read_frame(pFormatCtx, packet)>=0){
if(packet->stream_index==audioStream){
ret = avcodec_decode_audio4( pCodecCtx, pFrame,&got_picture, packet);
if ( ret < 0 ) {
printf("Error in decoding audio frame.\n");
return -1;
}
if ( got_picture > 0 ){
return_num = swr_convert(au_convert_ctx,&out_buffer, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)pFrame->data , pFrame->nb_samples);
out_buffer_size = return_num * (pCodecCtx->channels) * av_get_bytes_per_sample(out_sample_fmt);
#if 1
printf("index:%5d\t pts:%lld\t packet size:%d\n",index,packet->pts,packet->size);
#endif
#if OUTPUT_PCM
//Write PCM
fwrite(out_buffer, 1, out_buffer_size, pFile);
#endif
index++;
}
#if USE_SDL
while(audio_len>0)//Wait until finish
SDL_Delay(1);
//Set audio buffer (PCM data)
audio_chunk = (Uint8 *) out_buffer;
//Audio buffer length
audio_len =out_buffer_size;
audio_pos = audio_chunk;
//Play
SDL_PauseAudio(0);
#endif
}
av_free_packet(packet);
}
//flush buffer in SwrContext
return_num = swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, NULL, 0);
out_buffer_size = return_num * (pCodecCtx->channels) * av_get_bytes_per_sample(out_sample_fmt);
#if OUTPUT_PCM
//Write PCM
fwrite(out_buffer, 1, out_buffer_size, pFile);
#endif
最后附上源码中关于resample使用的详细说明
/**
* @defgroup lswr Libswresample
* @{
*
* Libswresample (lswr) is a library that handles audio resampling, sample
* format conversion and mixing.
*
* Interaction with lswr is done through SwrContext, which is
* allocated with swr_alloc() or swr_alloc_set_opts(). It is opaque, so all parameters
* must be set with the @ref avoptions API.
*
* The first thing you will need to do in order to use lswr is to allocate
* SwrContext. This can be done with swr_alloc() or swr_alloc_set_opts(). If you
* are using the former, you must set options through the @ref avoptions API.
* The latter function provides the same feature, but it allows you to set some
* common options in the same statement.
*
* For example the following code will setup conversion from planar float sample
* format to interleaved signed 16-bit integer, downsampling from 48kHz to
* 44.1kHz and downmixing from 5.1 channels to stereo (using the default mixing
* matrix). This is using the swr_alloc() function.
* @code
* SwrContext *swr = swr_alloc();
* av_opt_set_channel_layout(swr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0);
* av_opt_set_channel_layout(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
* av_opt_set_int(swr, "in_sample_rate", 48000, 0);
* av_opt_set_int(swr, "out_sample_rate", 44100, 0);
* av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
* av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
* @endcode
*
* The same job can be done using swr_alloc_set_opts() as well:
* @code
* SwrContext *swr = swr_alloc_set_opts(NULL, // we're allocating a new context
* AV_CH_LAYOUT_STEREO, // out_ch_layout
* AV_SAMPLE_FMT_S16, // out_sample_fmt
* 44100, // out_sample_rate
* AV_CH_LAYOUT_5POINT1, // in_ch_layout
* AV_SAMPLE_FMT_FLTP, // in_sample_fmt
* 48000, // in_sample_rate
* 0, // log_offset
* NULL); // log_ctx
* @endcode
*
* Once all values have been set, it must be initialized with swr_init(). If
* you need to change the conversion parameters, you can change the parameters
* using @ref AVOptions, as described above in the first example; or by using
* swr_alloc_set_opts(), but with the first argument the allocated context.
* You must then call swr_init() again.
*
* The conversion itself is done by repeatedly calling swr_convert().
* Note that the samples may get buffered in swr if you provide insufficient
* output space or if sample rate conversion is done, which requires "future"
* samples. Samples that do not require future input can be retrieved at any
* time by using swr_convert() (in_count can be set to 0).
* At the end of conversion the resampling buffer can be flushed by calling
* swr_convert() with NULL in and 0 in_count.
*
* The samples used in the conversion process can be managed with the libavutil
* @ref lavu_sampmanip "samples manipulation" API, including av_samples_alloc()
* function used in the following example.
*
* The delay between input and output, can at any time be found by using
* swr_get_delay().
*
* The following code demonstrates the conversion loop assuming the parameters
* from above and caller-defined functions get_input() and handle_output():
* @code
* uint8_t **input;
* int in_samples;
*
* while (get_input(&input, &in_samples)) {
* uint8_t *output;
* int out_samples = av_rescale_rnd(swr_get_delay(swr, 48000) +
* in_samples, 44100, 48000, AV_ROUND_UP);
* av_samples_alloc(&output, NULL, 2, out_samples,
* AV_SAMPLE_FMT_S16, 0);
* out_samples = swr_convert(swr, &output, out_samples,
* input, in_samples);
* handle_output(output, out_samples);
* av_freep(&output);
* }
* @endcode
*
* When the conversion is finished, the conversion
* context and everything associated with it must be freed with swr_free().
* A swr_close() function is also available, but it exists mainly for
* compatibility with libavresample, and is not required to be called.
*
* There will be no memory leak if the data is not completely flushed before
* swr_free().
*/
参考资料:
- 雷博的博客:http://blog.csdn.net/leixiaohua1020/article/details/38979615