通过portaudio异步播放libav decodec音频

通过portaudio异步播放libav decodec音频,c,libav,portaudio,C,Libav,Portaudio,我正在编写一个python扩展,它可以使用libav和portaudio播放音频。 我有。然而,这是一个阻塞函数。我试图使用portaudio回调函数实现异步播放,但是我得到了一个段错误,我不知道是什么导致了它 我目前拥有以下代码: typedef struct { /* Python */ PyObject_HEAD PyObject *filepath; PyObject *duration; PyObject *sample_rate; Py

我正在编写一个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;

#定义PaPy检查错误(错误)\
如果(错误!=错误){\
PyErr_SetString(PyExc_OSError,Pa_GetErrorText(error))\
返回NULL\
}
静态int pa_回调(常量无效*输入缓冲区,
void*输出缓冲区,
每个缓冲区的无符号长帧,
const Pastream CallbackTimeInfo*时间信息,
PaStreamCallbackFlags状态标志,
void*用户数据)
{
Song*self=(Song*)用户_数据;
无符号整数i=0;
int=0;
(void)输入缓冲区;
(作废)时间信息;
uint16_t*out=(uint16_t*)输出缓冲区;
AVFrame frame=self->frames[self->frame_index];
对于(;idata\u index++>frame.nb\u示例){
frame=self->frames[self->frame_index++];
self->data\u index=0;
}
如果(self->frame\u index>=self->frame\u count-1){
返回-1;
}
*out++=(*frame.data)[self->data_index];
}
返回完成;
}
静态PyObject*
歌曲播放(歌曲*自我)
{
AVCodec*codec=AVCodec\u find\u解码器(self->audio\u stream->codec->codec\u id);
如果(编解码器==NULL){
返回NULL;
}
if(avcodec\u find\u解码器(self->codec\u ctx->codec\u id)<0){
返回NULL;
}
if(avcodec_open2(self->codec_ctx,codec,NULL)<0){
返回NULL;
}
PaSampleFormat样品;
开关(self->codec\u ctx->sample\u fmt){
案例AVU样本FMT U8:
样本_fmt=paUInt8;
printf(“uint 8\n”);
打破
案例AVU样本FMT S16:
样本_fmt=油漆16;
printf(“uint 16\n”);
打破
案例AVU样本FMT S32:
样本_fmt=32;
printf(“int 16\n”);
打破
案例AVU样本FMT FLT:
样本_fmt=32;
printf(“float\n”);
打破
违约:
PyErr_SetString(PyExc_OSError,
“无法分析音频样本格式。”);
返回NULL;
}
PaError err=Pa_OpenDefaultStream(&self->Pa_stream),
0,
self->codec_ctx->频道,
样本_fmt,
self->codec\u ctx->sample\u rate,
PaFramesPerbuffer未指定,
帕乌,
自身);
PaPy_检查错误(err)
数据包;
self->frames=malloc(self->frame_count*sizeof(AVFrame));
无符号整数i=0;
而(av_读取_帧(自->fmt_ctx和数据包)>=0){
if(packet.stream\u index!=self->audio\u stream->index){
持续
}
AVFrame框架;
int-got_框架;
int-ret=avcodec\u decode\u audio4(self->codec\u ctx和frame,
&获取(帧和数据包);
如果(ret<0){
持续
}
if(ret!=数据包大小){
持续
}
如果(得到了框架){
自->帧[i]=帧;
/*这是有效的,但它是一个阻塞调用*/
/*err=Pa_WriteStream(self->Pa_stream,*frame.data*/
/*框架(nb_样品)*/
/*PaPy_检查错误(err)*/
i++;
}
/*av_免费_数据包(&数据包)*/
}
err=Pa_开始流(自->Pa_流);
PaPy_检查错误(err)
av搜索帧(自->fmt\U ctx,自->音频流->索引,0,0);
Py_返回_无;
}
但这只会给我带来噪音。完整的代码可以看到

有人能告诉我这个代码有什么问题吗

#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;
}