📄 audio_sdl.cpp
字号:
// buffers are ready, and what time they start. It is used to determine
// when we should start.
int CSDLAudioSync::is_audio_ready (uint64_t &disptime)
{
disptime = m_buffer_time[m_play_index];
return (m_dont_fill == 0 && m_buffer_filled[m_play_index] == 1);
}
// Used by the sync thread to see if resync is needed.
// 0 - in sync. > 0 - sync time we need. -1 - need to do sync
uint64_t CSDLAudioSync::check_audio_sync (uint64_t current_time, int &have_eof)
{
if (get_eof() != 0) {
have_eof = m_audio_paused;
return (0);
}
// audio is initialized.
if (m_resync_required) {
if (m_audio_paused && m_buffer_filled[m_resync_buffer]) {
// Calculate the current time based on the latency
SDL_LockAudio();
if (m_use_SDL_delay) {
current_time +=SDL_AudioDelayMsec();
} else {
current_time += m_buffer_latency;
}
uint64_t cmptime;
int freed = 0;
// Compare with times in buffer - we may need to skip if we fell
// behind.
do {
cmptime = m_buffer_time[m_resync_buffer];
if (cmptime < current_time) {
#ifdef DEBUG_SYNC
audio_message(LOG_INFO, "Passed time " LLU " " LLU " %u",
cmptime, current_time, m_resync_buffer);
#endif
m_buffer_filled[m_resync_buffer] = 0;
m_resync_buffer++;
m_resync_buffer %= DECODE_BUFFERS_MAX;
freed = 1;
}
} while (m_buffer_filled[m_resync_buffer] == 1 &&
cmptime < current_time - 2);
SDL_UnlockAudio();
if (m_buffer_filled[m_resync_buffer] == 0) {
cmptime = 0;
} else {
if (cmptime >= current_time) {
m_play_index = m_resync_buffer;
play_audio();
#if 1
audio_message(LOG_INFO, "Resynced audio at " LLU " %u %u", current_time, m_resync_buffer, m_play_index);
#endif
cmptime = 0;
}
}
if (freed != 0 && m_audio_waiting_buffer) {
m_audio_waiting_buffer = 0;
SDL_SemPost(m_audio_waiting);
// audio_message(LOG_DEBUG, "post free passed time");
}
return cmptime;
} else {
return (0);
}
}
return (0);
}
// Audio callback from SDL.
void CSDLAudioSync::audio_callback (Uint8 *stream, int ilen)
{
int freed_buffer = 0;
uint32_t len = (uint32_t)ilen;
uint64_t this_time;
int delay = 0;
if (m_resync_required) {
// resync required from codec side. Shut down, and notify sync task
if (m_resync_buffer == m_play_index) {
SDL_PauseAudio(1);
m_audio_paused = 1;
m_psptr->wake_sync_thread();
#ifdef DEBUG_SYNC
audio_message(LOG_DEBUG, "sempost");
#endif
return;
}
}
m_play_time = m_psptr->get_current_time();
if (m_use_SDL_delay != 0) {
delay = SDL_AudioDelayMsec();
if (delay < 0) delay = 0;
#ifdef DEBUG_DELAY
audio_message(LOG_DEBUG, "Audio delay is %d %llu", delay, m_play_time);
#endif
}
if ((m_first_time == 0) &&
(m_use_SDL_delay == 0)) {
/*
* If we're not the first time, see if we're about a frame or more
* around the current time, with latency built in. If not, skip
* the buffer. This prevents us from playing past-due buffers.
*/
int time_okay = 0;
this_time = 0;
while ((m_buffer_filled[m_play_index] == 1) &&
(time_okay == 0)) {
uint64_t buffertime, playtime;
buffertime = m_buffer_time[m_play_index];
if (m_play_sample_index != 0) {
uint64_t temp;
temp = (uint64_t) m_play_sample_index * (uint64_t)m_msec_per_frame;
temp /= (uint64_t) m_buffer_size;
buffertime += temp;
}
if (m_use_SDL_delay != 0)
playtime = m_play_time + delay; // m_buffer_latency;
else
playtime = m_play_time + m_buffer_latency;
if (m_play_time != 0 && buffertime + m_msec_per_frame < playtime) {
audio_message(LOG_DEBUG,
"Skipped audio buffer " LLU "("LLU") at " LLU,
m_buffer_time[m_play_index],
buffertime,
playtime);
m_buffer_filled[m_play_index] = 0;
m_play_index++;
m_play_index %= DECODE_BUFFERS_MAX;
m_skipped_buffers++;
m_buffer_latency = 0; // recalculate...
m_first_time = 0;
m_play_sample_index = 0;
uint32_t diff;
diff = m_buffer_size - m_play_sample_index;
if (m_samples_loaded >= diff) {
m_samples_loaded -= diff;
} else {
m_samples_loaded = 0;
}
m_consec_wrong_latency = 0; // reset all latency calcs..
m_wrong_latency_total = 0;
} else {
time_okay = 1;
this_time = buffertime;
}
}
} else {
this_time = m_buffer_time[m_play_index];
if (m_first_time == 0) {
if (m_play_sample_index != 0) {
uint64_t temp;
temp = (uint64_t) m_play_sample_index * (uint64_t)m_msec_per_frame;
temp /= (uint64_t) m_buffer_size;
this_time += temp;
}
}
}
// Do we have a buffer ? If no, see if we need to stop.
if (m_samples_loaded == 0) {
if (get_eof()) {
SDL_PauseAudio(1);
m_audio_paused = 1;
m_psptr->wake_sync_thread();
return;
}
#ifdef DEBUG_SYNC
audio_message(LOG_DEBUG, "No buffer in audio callback %u %u",
m_samples_loaded, len);
#endif
m_consec_no_buffers++;
if (m_consec_no_buffers > 10) {
SDL_PauseAudio(1);
m_audio_paused = 1;
m_resync_required = 1;
m_resync_buffer = m_play_index;
m_psptr->wake_sync_thread();
}
if (m_audio_waiting_buffer) {
m_audio_waiting_buffer = 0;
SDL_SemPost(m_audio_waiting);
//audio_message(LOG_DEBUG, "post no data");
}
audio_message(LOG_DEBUG, "return - no samples");
return;
}
// We have a valid buffer. Push it to SDL.
m_consec_no_buffers = 0;
while (len > 0) {
uint32_t thislen;
thislen = m_buffer_size - m_play_sample_index;
if (len < thislen) thislen = len;
SDL_MixAudio(stream,
(const unsigned char *)&m_sample_buffer[m_play_index][m_play_sample_index],
thislen,
m_volume);
len -= thislen;
stream += thislen;
if (thislen <= m_samples_loaded)
m_samples_loaded -= thislen;
else
m_samples_loaded = 0;
m_play_sample_index += thislen;
if (m_play_sample_index >= m_buffer_size) {
#ifdef DEBUG_SYNC
audio_message(LOG_DEBUG, "finished with buffer %d %d",
m_play_index, m_samples_loaded);
#endif
m_buffer_filled[m_play_index] = 0;
m_play_index++;
m_play_index %= DECODE_BUFFERS_MAX;
m_play_sample_index = 0;
freed_buffer = 1;
}
}
// Increment past this buffer.
if (m_first_time != 0) {
// First time through - tell the sync task we've started, so it can
// keep sync time.
m_first_time = 0;
if (m_use_SDL_delay != 0)
m_buffer_latency = delay;
else
m_buffer_latency = 0;
m_psptr->audio_is_ready(m_buffer_latency, this_time);
m_consec_wrong_latency = 0;
m_wrong_latency_total = 0;
}
else if (m_do_sync) {
#define ALLOWED_LATENCY 2
if (m_use_SDL_delay != 0) {
// Here, we're using the delay value from the audio buffers,
// rather than the calculated time...
// Okay - now we check for latency changes.
uint64_t index_time = delay + m_play_time;
if (this_time > index_time + ALLOWED_LATENCY ||
this_time < index_time - ALLOWED_LATENCY) {
#if DEBUG_SYNC
audio_message(LOG_DEBUG,
"potential change - index time "LLU" time "LLU,
index_time, this_time);
#endif
m_consec_wrong_latency++;
m_wrong_latency_total += this_time - index_time;
int64_t test;
test = m_wrong_latency_total / m_consec_wrong_latency;
if (test > ALLOWED_LATENCY || test < -ALLOWED_LATENCY) {
if (m_consec_wrong_latency > 3) {
m_consec_wrong_latency = 0;
m_wrong_latency_total = 0;
m_psptr->adjust_start_time(test);
}
} else {
// average wrong latency is not greater than allowed latency
m_consec_wrong_latency = 0;
m_wrong_latency_total = 0;
}
} else {
m_consec_wrong_latency = 0;
m_wrong_latency_total = 0;
}
} else {
// We're using the calculate latency values - they're not very
// accurate, but better than nothing...
// we have a latency number - see if it really is correct
uint64_t index_time = delay + m_play_time;
#if DEBUG_SYNC
audio_message(LOG_DEBUG,
"latency - time " LLU " index " LLU " latency " LLU " %u",
this_time, index_time, m_buffer_latency, m_samples_loaded);
#endif
if (this_time > index_time + ALLOWED_LATENCY ||
this_time < index_time - ALLOWED_LATENCY) {
m_consec_wrong_latency++;
m_wrong_latency_total += this_time - index_time;
int64_t test;
test = m_wrong_latency_total / m_consec_wrong_latency;
if (test > ALLOWED_LATENCY || test < -ALLOWED_LATENCY) {
if (m_consec_wrong_latency > 20) {
m_consec_wrong_latency = 0;
if (test < 0 && test + m_buffer_latency > 0) {
m_buffer_latency = 0;
} else {
m_buffer_latency += test;
}
m_psptr->audio_is_ready(m_buffer_latency, this_time);
audio_message(LOG_INFO, "Latency off by " LLD " - now is " LLU,
test, m_buffer_latency);
}
} else {
// average wrong latency is not greater 5 or less -5
m_consec_wrong_latency = 0;
m_wrong_latency_total = 0;
}
} else {
m_consec_wrong_latency = 0;
m_wrong_latency_total = 0;
}
}
} else {
#ifdef DEBUG_SYNC
audio_message(LOG_DEBUG, "playing %llu %llu latency %llu",
this_time, m_play_time, m_buffer_latency);
#endif
}
// If we had the decoder task waiting, signal it.
if (freed_buffer != 0 && m_audio_waiting_buffer) {
m_audio_waiting_buffer = 0;
SDL_SemPost(m_audio_waiting);
//audio_message(LOG_DEBUG, "post freed");
}
}
void CSDLAudioSync::play_audio (void)
{
m_first_time = 1;
m_resync_required = 0;
m_audio_paused = 0;
m_play_sample_index = 0;
SDL_PauseAudio(0);
}
// Called from the sync thread when we want to stop. Pause the audio,
// and indicate that we're not to fill any more buffers - this should let
// the decode thread get back to receive the pause message. Only called
// when pausing - could cause m_dont_fill race conditions if called on play
void CSDLAudioSync::flush_sync_buffers (void)
{
// we don't need to signal the decode task right now -
// Go ahead
clear_eof();
SDL_PauseAudio(1);
m_dont_fill = 1;
if (m_audio_waiting_buffer) {
m_audio_waiting_buffer = 0;
SDL_SemPost(m_audio_waiting);
//audio_message(LOG_DEBUG, "post flush sync");
}
// player_debug_message("Flushed sync");
}
// this is called from the decode thread. It gets called on entry into pause,
// and entry into play. This way, m_dont_fill race conditions are resolved.
void CSDLAudioSync::flush_decode_buffers (void)
{
int locked = 0;
if (m_audio_initialized != 0) {
locked = 1;
SDL_LockAudio();
}
m_dont_fill = 0;
m_first_filled = 1;
for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) {
m_buffer_filled[ix] = 0;
}
m_buffer_offset_on = 0;
m_play_index = m_fill_index = 0;
m_audio_paused = 1;
m_resync_buffer = 0;
m_samples_loaded = 0;
if (locked)
SDL_UnlockAudio();
//player_debug_message("flushed decode");
}
void CSDLAudioSync::set_volume (int volume)
{
m_volume = (volume * SDL_MIX_MAXVOLUME)/100;
}
static void c_audio_config (void *ifptr, int freq,
int chans, int format, uint32_t max_buffer_size)
{
((CSDLAudioSync *)ifptr)->set_config(freq,
chans,
format,
max_buffer_size);
}
static uint8_t *c_get_audio_buffer (void *ifptr)
{
return ((CSDLAudioSync *)ifptr)->get_audio_buffer();
}
static void c_filled_audio_buffer (void *ifptr,
uint64_t ts,
int resync_req)
{
((CSDLAudioSync *)ifptr)->filled_audio_buffer(ts,
resync_req);
}
static uint32_t c_load_audio_buffer (void *ifptr,
uint8_t *from,
uint32_t bytes,
uint64_t ts,
int resync)
{
return ((CSDLAudioSync *)ifptr)->load_audio_buffer(from,
bytes,
ts,
resync);
}
static audio_vft_t audio_vft = {
message,
c_audio_config,
c_get_audio_buffer,
c_filled_audio_buffer,
c_load_audio_buffer
};
CAudioSync *create_audio_sync (CPlayerSession *psptr, int volume)
{
return new CSDLAudioSync(psptr, volume);
}
audio_vft_t *get_audio_vft (void)
{
return &audio_vft;
}
int do_we_have_audio (void)
{
char buffer[80];
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) < 0) {
return (0);
}
if (SDL_AudioDriverName(buffer, sizeof(buffer)) == NULL) {
return (0);
}
return (1);
}
/* end audio.cpp */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -