我正在编写一个 python 扩展,它可以使用 libav 和 portaudio 播放音频。我有这个工作代码。但是,这是一个阻塞函数。我正在尝试使用 portaudio 回调函数来实现异步播放,但是我遇到了一个段错误,我不知道是什么原因造成的。
我目前有以下代码:
typedef struct {
/* Python */
PyObject_HEAD
PyObject *filepath;
PyObject *duration;
PyObject *sample_rate;
PyObject *channels;
/* av */
AVFormatContext *fmt_ctx;
AVStream *audio_stream;
AVCodecContext *codec_ctx;
AVDictionaryEntry *current_tag; /* Used for iteration: for tag in song */
/* portaudio */
PaStream *pa_stream;
unsigned int frame_count;
unsigned int frame_index;
unsigned int data_index;
AVFrame *frames;
} Song;
...
#define PaPy_CHECK_ERROR(error) \
if (error != paNoError) { \
PyErr_SetString(PyExc_OSError, Pa_GetErrorText(error)); \
return NULL; \
}
static int pa_callback(const void *input_buffer,
void *output_buffer,
unsigned long frames_per_buffer,
const PaStreamCallbackTimeInfo* time_info,
PaStreamCallbackFlags status_flags,
void *user_data)
{
Song *self = (Song *)user_data;
unsigned int i = 0;
int finished = 0;
(void) input_buffer;
(void) time_info;
uint16_t *out = (uint16_t *)output_buffer;
AVFrame frame = self->frames[self->frame_index];
for (; i < frames_per_buffer; i++) {
if (self->data_index++ > frame.nb_samples) {
frame = self->frames[self->frame_index++];
self->data_index = 0;
}
if (self->frame_index >= self->frame_count -1) {
return -1;
}
*out++ = (*frame.data)[self->data_index];
}
return finished;
}
static PyObject *
Song_play(Song *self)
{
AVCodec *codec = avcodec_find_decoder(self->audio_stream->codec->codec_id);
if (codec == NULL) {
return NULL;
}
if (avcodec_find_decoder(self->codec_ctx->codec_id) < 0) {
return NULL;
}
if (avcodec_open2(self->codec_ctx, codec, NULL) < 0) {
return NULL;
}
PaSampleFormat sample_fmt;
switch (self->codec_ctx->sample_fmt) {
case AV_SAMPLE_FMT_U8:
sample_fmt = paUInt8;
printf("uint 8\n");
break;
case AV_SAMPLE_FMT_S16:
sample_fmt = paInt16;
printf("uint 16\n");
break;
case AV_SAMPLE_FMT_S32:
sample_fmt = paInt32;
printf("int 16\n");
break;
case AV_SAMPLE_FMT_FLT:
sample_fmt = paFloat32;
printf("float\n");
break;
default:
PyErr_SetString(PyExc_OSError,
"Unable to parse audio sample format.");
return NULL;
}
PaError err = Pa_OpenDefaultStream(&self->pa_stream,
0,
self->codec_ctx->channels,
sample_fmt,
self->codec_ctx->sample_rate,
paFramesPerBufferUnspecified,
pa_callback,
self);
PaPy_CHECK_ERROR(err)
AVPacket packet;
self->frames = malloc(self->frame_count * sizeof(AVFrame));
unsigned int i = 0;
while (av_read_frame(self->fmt_ctx, &packet) >= 0) {
if (packet.stream_index != self->audio_stream->index) {
continue;
}
AVFrame frame;
int got_frame;
int ret = avcodec_decode_audio4(self->codec_ctx, &frame,
&got_frame, &packet);
if (ret < 0) {
continue;
}
if (ret != packet.size) {
continue;
}
if (got_frame) {
self->frames[i] = frame;
/* This worked, but it is a blocking call. */
/*err = Pa_WriteStream(self->pa_stream, *frame.data,*/
/* frame.nb_samples);*/
/*PaPy_CHECK_ERROR(err)*/
i++;
}
/* av_free_packet(&packet);*/
}
err = Pa_StartStream(self->pa_stream);
PaPy_CHECK_ERROR(err)
av_seek_frame(self->fmt_ctx, self->audio_stream->index, 0, 0);
Py_RETURN_NONE;
}
但这只会给我带来噪音。完整的代码可以在这里看到。
有人可以告诉我这段代码有什么问题吗?