• pulseaudio官网
https://www.freedesktop.org/wiki/Software/PulseAudio/
• pulseaudio-under-the-hood
https://gavv.github.io/articles/pulseaudio-under-the-hood/
• Developer Documentation
https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/
• PulseAudio Documentation
https://freedesktop.org/software/pulseaudio/doxygen/
过了压力测试
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <string.h>
#include <pulse/pulseaudio.h>
#include <math.h>
#include "err_code.hpp"
static int latency = 20000; // start latency in micro seconds
static int sampleoffs = 0;
static uint8_t sampledata[98382];
static pa_buffer_attr bufattr;
static int underflows = 0;
static pa_sample_spec ss;
// This callback gets called when our context changes state. We really only
// care about when it's ready or if it has failed
void pa_state_cb(pa_context *c, void *userdata) {
pa_context_state_t state;
int *pa_ready = userdata;
state = pa_context_get_state(c);
LOGI(TAG,"ctx state is %d\n",state);
switch (state) {
// These are just here for reference
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
default:
break;
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
*pa_ready = 2;
break;
case PA_CONTEXT_READY:
*pa_ready = 1;
break;
}
}
static pa_stream *playstream;
static pa_context *pa_ctx;
static pa_threaded_mainloop *pa_ml;
static pa_mainloop_api *pa_mlapi;
//#include "./src/MiscUtils.cpp"
#include <mutex>
#include <condition_variable>
typedef enum PLAY_STATE {
UNKNOWN_YFCF,
STOPPED_YFCF, /**< The stream is DISCONNECT */
PAUSED_YFCF, /***/
PLAYING_YFCF,
STOPPING_YFCF,
STARTING_YFCF
} play_state_t;
static std::mutex gLock;
static std::condition_variable gCond;
static volatile play_state_t gPlayState = UNKNOWN_YFCF; //-1 unknow;0 end;1 playing
static void context_drain_complete(pa_context*c, void *userdata) {
pa_context_disconnect(c);
LOGI(TAG,"DISCONNECT ctx ");
}
static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
if (!success) {
LOGE(TAG,"Failed to drain stream: %s", pa_strerror(pa_context_errno(pa_ctx)));
}
int r;
pa_operation *o = NULL;
r = pa_stream_disconnect(playstream);
LOGI(TAG,"DISCONNECT S %d",r);
pa_stream_unref(playstream);
playstream = NULL;
if (!(o = pa_context_drain(pa_ctx, context_drain_complete, NULL))){
pa_context_disconnect(pa_ctx);
LOGI(TAG,"DISCONNECT ctx %d",r);
}
else {
pa_operation_unref(o);
}
}
static void start_drain(void) {
if (playstream) {
pa_operation *o;
pa_stream_set_write_callback(playstream, NULL, NULL);
if (!(o = pa_stream_drain(playstream, stream_drain_complete, NULL))) {
LOGW(TAG,"pa_stream_drain(): %s", pa_strerror(pa_context_errno(pa_ctx)));
}
pa_operation_unref(o);
}
}
static uint32_t cork_requests = 0;
static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
LOGI(TAG,"Cork ev %s",name);
}
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
if (length > 98382-sampleoffs) {
length = 98382-sampleoffs;
}
//printf("off=%d len=%d\n",sampleoffs,length);
pa_stream_write(s, &sampledata[sampleoffs], length, NULL, 0LL, PA_SEEK_RELATIVE);
sampleoffs += length;
//printf("----------off=%d len=%d\n",sampleoffs,length);
if (sampleoffs + length >= 98382) {
sampleoffs = 0;
}
}
static void pa_stream_st_cb(pa_stream *p, void *userdata){
pa_stream_state_t st = pa_stream_get_state(p);
LOGI(TAG,"stream state is %d\n",st);
}
static void stream_underflow_cb(pa_stream *s, void *userdata) {
// We increase the latency by 50% if we get 6 underflows and latency is under 2s
// This is very useful for over the network playback that can't handle low latencies
#if 0
printf("underflow\n");
underflows++;
if (underflows >= 6 && latency < 2000000) {
latency = (latency*3)/2;
bufattr.maxlength = pa_usec_to_bytes(latency,&ss);
bufattr.tlength = pa_usec_to_bytes(latency,&ss);
pa_stream_set_buffer_attr(s, &bufattr, NULL, NULL);
underflows = 0;
printf("latency increased to %d\n", latency);
}
#endif
}
void pauseStream(){
}
void stopStream(){
pa_threaded_mainloop_stop(pa_ml);
pa_stream_disconnect(playstream);
pa_stream_unref(playstream);
pa_context_disconnect(pa_ctx);
pa_context_unref(pa_ctx);
pa_threaded_mainloop_free(pa_ml);
}
int startStream(float vol) {
int r;
int pa_ready = 0;
int retval = 0;
unsigned int a;
double amp;
// Create some data to play
#if 0
for (a=0; a<sizeof(sampledata)/2; a++) {
amp = cos(5000*(double)a/44100.0);
sampledata[a] = amp * 32000.0;
}
#endif
///usr/yf/Game/etc
FILE * sFp = fopen("/home/cf/myprj/shock_0_p5s_45hz.wav","r");
if(sFp == NULL)
{
LOGE(TAG, "fopen err:%s", strerror(errno));
return -1;
}
size_t readed = 0;
size_t wed = 0;
int32_t ret = 0;
while( (readed += fread(sampledata,1,98382-readed,sFp)) != 98382 ){
}
fclose(sFp);
pa_ready = 0;
// Create a mainloop API and connection to the default server
pa_ml = pa_threaded_mainloop_new();
pa_mlapi = pa_threaded_mainloop_get_api(pa_ml);
pa_ctx = pa_context_new(pa_mlapi, "yfcf_player ctx");
pa_context_connect(pa_ctx, NULL, 0, NULL);
// This function defines a callback so the server will tell us it's state.
// Our callback will wait for the state to be ready. The callback will
// modify the variable to 1 so we know when we have a connection and it's
// ready.
// If there's an error, the callback will set pa_ready to 2
pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);
// We can't do anything until PA is ready, so just iterate the mainloop
// and continue
pa_threaded_mainloop_start(pa_ml);
ss.rate = 48000;
ss.channels = 2;
ss.format = PA_SAMPLE_S16LE;
playstream = pa_stream_new(pa_ctx, "yfcf_stream", &ss, NULL);
if (!playstream) {
LOGE(TAG,"pa_stream_new failed\n");
}
pa_threaded_mainloop_lock(pa_ml);
pa_stream_set_write_callback(playstream, stream_request_cb, NULL);
pa_stream_set_underflow_callback(playstream, stream_underflow_cb, NULL);
pa_stream_set_state_callback(playstream,pa_stream_st_cb,NULL);
pa_stream_set_event_callback(playstream, stream_event_callback, NULL);
bufattr.fragsize = (uint32_t)-1;
bufattr.maxlength = pa_usec_to_bytes(latency,&ss);
bufattr.minreq = pa_usec_to_bytes(0,&ss);
bufattr.prebuf = (uint32_t)-1;
bufattr.tlength = pa_usec_to_bytes(latency,&ss);
//alsa_output.pci-0000_07_00.6.analog-stereo
r = pa_stream_connect_playback(playstream, "alsa_output.pci-0000_02_02.0.analog-stereo", &bufattr,
PA_STREAM_INTERPOLATE_TIMING
|PA_STREAM_ADJUST_LATENCY
|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
if (r < 0) {
// Old pulse audio servers don't like the ADJUST_LATENCY flag, so retry without that
r = pa_stream_connect_playback(playstream, "alsa_output.pci-0000_07_00.6.analog-stereo", &bufattr,
PA_STREAM_INTERPOLATE_TIMING|
PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
}
// pa_operation_unref(pa_context_set_sink_input_volume(c, pa_stream_get_index(playstream), &cv, simple_callback, NULL));
if (r < 0) {
LOGE(TAG,"pa_stream_connect_playback failed\n");
retval = -1;
}
pa_threaded_mainloop_unlock(pa_ml);
// Run the mainloop until pa_mainloop_quit() is called
// (this example never calls it, so the mainloop runs forever).
gPlayState=PLAYING_YFCF;
return 0;
}