static void *pa_thread_hnd_out(void *arg) {
struct pa_strm *pa_stream = (struct pa_strm *) arg;
int err;
struct audio_dev *audio_dev;
uint8_t *out_buf = NULL;
int inp_frames;
int buf_len;
int div;
audio_dev = audio_dev_get_by_idx(pa_stream->devid);
if(!audio_dev || !audio_dev->ad_ops || !audio_dev->ad_ops->ad_ops_start || !audio_dev->ad_ops->ad_ops_resume) {
printf("pa_thread_hnd: failed audio_dev=%p\n",audio_dev);
return NULL;
}
buf_len = audio_dev->ad_ops->ad_ops_ioctl(audio_dev, ADIOCTL_BUFLEN, NULL);
if (buf_len == -1) {
printf("Couldn't get audio device buf_len\n");
return NULL;
}
if (!pa_stream->callback) {
printf("No callback provided for PA thread.\n");
return NULL;
}
pa_cond_wait(pa_stream);
audio_dev->ad_ops->ad_ops_start(audio_dev);
// inp_frames — это строго то количество фреймов, которое просит PJSIP (например, 320 моно фреймов)
inp_frames = pa_stream->frames_per_buffer;
int audio_dev_rate = audio_dev->ad_ops->ad_ops_ioctl(audio_dev, ADIOCTL_GET_RATE, NULL);
div = audio_dev_rate / pa_stream->rate;
if(div <= 0) div = 1;
printf("pa_thread_hnd_out) Active codec framesPerBuffer=%d, hardware buf_len=%d\n", inp_frames, buf_len);
while (1) {
pa_cond_wait(pa_stream);
if (pa_stream->state != PA_STREAM_RUNNING) {
printf("Exit pa_out_thread\n");
audio_dev_close_out_stream();
return NULL;
}
if (audio_dev->dir == AUDIO_DEV_OUTPUT) {
out_buf = audio_dev_get_out_cur_ptr(audio_dev);
memset(out_buf, 0, buf_len);
}
// Передаем в callback ИМЕННО inp_frames. PJSIP заполнит ровно inp_frames сэмплов (320 сэмплов = 640 байт моно)
err = pa_stream->callback(NULL, out_buf, inp_frames, NULL, 0, pa_stream->user_data);
switch (err) {
case paContinue: break;
case paComplete: Pa_StopStream(pa_stream); break;
default:
printf("pa_thread_hnd_out: User callback error: %d\n", err);
Pa_StopStream(pa_stream);
break;
}
if (audio_dev->dir == AUDIO_DEV_OUTPUT) {
// ВАЖНО: Разворачиваем in-place из моно (320) в стерео (320 фреймов = 640 сэмплов = 1280 байт)
pa_do_channel_conversion(pa_stream, audio_dev, out_buf, inp_frames);
// Затем делаем ресемплинг, если частоты железа и стрима не совпадают
pa_do_rate_conversion(pa_stream, audio_dev, out_buf, inp_frames * div);
}
pa_stream->active = 0;
audio_dev->ad_ops->ad_ops_resume(audio_dev);
}
audio_dev_close_out_stream();
return NULL;
}