通过portaudio异步播放libav decodec音频
我正在编写一个python扩展,它可以使用libav和portaudio播放音频。 我有。然而,这是一个阻塞函数。我试图使用portaudio回调函数实现异步播放,但是我得到了一个段错误,我不知道是什么导致了它 我目前拥有以下代码:通过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
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;
}