📄 audio_buffer.cpp
字号:
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2004. All Rights Reserved. * * Contributor(s): * Bill May wmay@cisco.com *//* * audio_buffer.cpp contains code to handle an audio ring buffer with a * generic set of rendering code. */#include "audio_buffer.h"#include "player_session.h"#include "player_util.h"#include "our_config_file.h"//#define DEBUG_AUDIO_FILL 1//#define DEBUG_AUDIO_CALLBACK 1//#define DEBUG_AUDIO_TIMING 1//#define DEBUG_AUDIO_TIMING_CHANGES 1#ifdef _WIN32DEFINE_MESSAGE_MACRO(audio_message, "audiobuf")#else#define audio_message(loglevel, fmt...) message(loglevel, "audiobuf", fmt)#endifCBufferAudioSync::CBufferAudioSync (CPlayerSession *psptr, int volume) : CAudioSync(psptr){ m_sample_buffer = NULL; m_local_buffer = NULL; m_fill_offset = 0; m_play_offset = 0; m_first_filled = false; m_dont_fill = false; m_audio_waiting_buffer = false; m_audio_waiting = SDL_CreateSemaphore(0); m_have_resync = false; m_waiting_on_resync = false; m_resync_offset = 0; m_resync_ts = 0; m_resync_freq_ts = 0; m_audio_configured = false; m_mutex = SDL_CreateMutex(); m_hardware_initialized = false; m_audio_paused = true; m_first_callback = true; m_restart_request = true; m_sync_samples_added = 0; m_sync_samples_removed = 0; m_total_samples_played = 0; m_have_jitter = false; m_jitter_msec = 0; m_jitter_msec_total = 0; m_last_add_samples = false;}CBufferAudioSync::~CBufferAudioSync (void){ CHECK_AND_FREE(m_sample_buffer); CHECK_AND_FREE(m_local_buffer); SDL_DestroySemaphore(m_audio_waiting); SDL_DestroyMutex(m_mutex); // display stats audio_message(LOG_INFO, "total samples played: "U64, m_total_samples_played); audio_message(LOG_INFO, "sync samples added : "U64, m_sync_samples_added); audio_message(LOG_INFO, "sync samples removed: "U64, m_sync_samples_removed); if (m_total_samples_played > 0) { uint64_t calc = m_total_samples_played + m_sync_samples_added - m_sync_samples_removed; calc *= m_freq; calc /= m_total_samples_played; audio_message(LOG_INFO, "calculated output frequency : "U64, calc); }}/**************************************************************************** * routines used when filling buffers ****************************************************************************//* * do_we_need_resync - look at the freq_ts given, and see if it matches * what we are expecting. * returns true if we need a resync, false if we're on or about the time * - silence_samples will contain the number of samples that freq_ts is * past the expected time */ bool CBufferAudioSync::do_we_need_resync (uint32_t freq_ts, uint32_t &silence_samples){ if (m_first_filled == false) return false; if (m_buffer_next_freq_ts == freq_ts) return false; int32_t sample_diff = freq_ts - m_buffer_next_freq_ts; int32_t compare_val = 4 * m_samples_per_frame; // note - the 2 is so that we handle rounding errors that occur when // converting timescales to frequency scales (ie: 90000 to 44100). if (sample_diff > 2 && sample_diff <= compare_val) { audio_message(LOG_DEBUG, "sample diff is %d %d", sample_diff, compare_val); audio_message(LOG_DEBUG, "freq_ts %u next %u", freq_ts, m_buffer_next_freq_ts); silence_samples = sample_diff; return false; } else if (sample_diff >= -1) { m_buffer_next_freq_ts = freq_ts; // rounding error, probably return false; } return true;}/* * wait_for_callback - will perform a wait until the callback routine * signals us */void CBufferAudioSync::wait_for_callback (void){ m_audio_waiting_buffer = true; SDL_SemWait(m_audio_waiting); m_audio_waiting_buffer = false;}/* * fill_silence will copy the correct number of silence bytes into * the output buffer */void CBufferAudioSync::fill_silence (uint32_t silence_bytes){ audio_message(LOG_INFO, "Inserting %u silence bytes at "U64" (%u)", silence_bytes, m_buffer_next_ts, m_buffer_next_freq_ts); bool lock = Lock(); while (silence_bytes > 0) { uint32_t diff = m_sample_buffer_size - m_fill_offset; uint32_t copied = MIN(diff, silence_bytes); memset(m_sample_buffer + m_fill_offset, m_silence, copied); m_fill_offset += copied; m_filled_bytes += copied; if (m_fill_offset >= m_sample_buffer_size) { m_fill_offset = 0; } silence_bytes -= copied; } if (lock) Unlock();}/* * check_for_bytes makes sure that there is the correct number of * bytes in the buffer for us to insert */bool CBufferAudioSync::check_for_bytes (uint32_t bytes, uint32_t silence_bytes){ bool locked; uint32_t diff; uint32_t to_insert; uint32_t count; to_insert = bytes + silence_bytes; count = 0; // this has changed a bit - we're going to wait a couple of // callbacks, in case the output sample size rate is < the number // of bytes to fill. do { if (m_dont_fill) return false; locked = Lock(); diff = m_sample_buffer_size - m_filled_bytes; if (locked) Unlock(); if (diff >= to_insert) return true; count++; wait_for_callback(); } while (count < 5); return false;}/************************************************************************** * APIs from plugins **************************************************************************//* * set_config - set up the audio stream configuration */void CBufferAudioSync::set_config (uint32_t freq, uint32_t chans, audio_format_t format, uint32_t samples_per_frame){ if (m_audio_configured == false) { audio_message(LOG_DEBUG, "audio configure"); m_freq = freq; m_decode_format = format; m_channels = chans; m_samples_per_frame = samples_per_frame; m_silence = 0; switch (format) { case AUDIO_FMT_U8: m_silence = 0x80; // fall through case AUDIO_FMT_S8: case AUDIO_FMT_HW_AC3: m_bytes_per_sample_input = sizeof(uint8_t); break; case AUDIO_FMT_U16LSB: case AUDIO_FMT_S16LSB: case AUDIO_FMT_U16MSB: case AUDIO_FMT_S16MSB: case AUDIO_FMT_U16: case AUDIO_FMT_S16: m_bytes_per_sample_input = sizeof(int16_t); break; case AUDIO_FMT_FLOAT: m_bytes_per_sample_input = sizeof(float); break; } // this can be used for converting directly from bytes to samples m_bytes_per_sample_input *= m_channels; if (samples_per_frame == 0) { if (config.get_config_value(CONFIG_LIMIT_AUDIO_SDL_BUFFER) > 1) { m_samples_per_frame = config.get_config_value(CONFIG_LIMIT_AUDIO_SDL_BUFFER); } else m_samples_per_frame = m_freq / 20; // estimate for inserting silence m_sample_buffer_size = m_freq; } else { m_sample_buffer_size = 32 * m_samples_per_frame; } // allocate so if plugin gives the number of samples, that we // never have wrap... m_sample_buffer_size *= m_bytes_per_sample_input; m_sample_buffer = (uint8_t *)malloc(m_sample_buffer_size); audio_message(LOG_DEBUG, "ring buffer size is %u", m_sample_buffer_size); m_bytes_per_frame = m_samples_per_frame * m_bytes_per_sample_input; m_msec_per_frame = (m_samples_per_frame * 1000); m_msec_per_frame /= m_freq; m_audio_configured = true; m_psptr->send_sync_thread_a_message(MSG_AUDIO_CONFIGURED); }}/* * get_audio_buffer - get a fixed size buffer - # of samples passed in when * config'ed. Paired with filled_audio_buffer to complete transaction. * because the get_audio_buffer and filled_audio_buffer do similiar things, * and we might want to inject a couple of bytes of silence, we're just * using a temp buffer to fill. */uint8_t *CBufferAudioSync::get_audio_buffer (uint32_t freq_ts, uint64_t ts){ if (m_dont_fill) { return NULL; } if (m_local_buffer == NULL) { m_local_buffer = (uint8_t *)malloc(m_bytes_per_frame); } m_got_buffer_ts = ts; m_got_buffer_freq_ts = freq_ts; return m_local_buffer;} /* * filled_audio_buffer - plugin is done writing frame into buffer */void CBufferAudioSync::filled_audio_buffer (void){ load_audio_buffer(m_local_buffer, m_bytes_per_frame, m_got_buffer_freq_ts, m_got_buffer_ts);}/* * load_audio_buffer - used by routines that don't know how many * samples/bytes they need to write at 1 time */void CBufferAudioSync::load_audio_buffer (const uint8_t *from, uint32_t bytes, uint32_t freq_ts, uint64_t ts){ uint32_t silence_bytes = 0; bool locked; bool need_resync; uint32_t samples; uint32_t write_bytes; if (m_dont_fill) return; if (m_decode_format == AUDIO_FMT_HW_AC3) { samples = 256 * 6; write_bytes = bytes + 2; } else { samples = bytes / m_bytes_per_sample_input; write_bytes = bytes; }#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "load samples %u ts "U64"(%u) - expect %u", bytes, ts, freq_ts, m_buffer_next_freq_ts);#endif need_resync = do_we_need_resync(freq_ts, silence_bytes); if (need_resync) { // we need to resync locked = Lock(); if (m_have_resync) { // we have another resync pending asdf if (locked) Unlock(); wait_for_callback(); } else { if (locked) Unlock(); } } // we may have a situation where we don't have enough for the // silence byte, but enough for the frame - this means resync - // for now, we're not going to allow silence_bytes *= m_bytes_per_sample_input; if (check_for_bytes(write_bytes, silence_bytes) == false) { // can't insert. If no silence bytes, return NULL if (silence_bytes == 0) { audio_message(LOG_ERR, "Couldn't insert encoded %u bytes at %u", write_bytes, freq_ts); return; } // See if we can insert this frame without the silence bytes - // this will require a resync, but at least we don't lose anything silence_bytes = 0; need_resync = true; if (check_for_bytes(write_bytes, 0) == false) { audio_message(LOG_ERR, "Couldn't put %u resync bytes at %u", write_bytes, freq_ts); return; } } if (silence_bytes > 0) { fill_silence(silence_bytes); } locked = Lock(); if (need_resync) { m_resync_offset = m_fill_offset; m_resync_ts = ts; m_resync_freq_ts = freq_ts; m_have_resync = need_resync; m_first_filled = false; // reset jitter calcs m_have_jitter = false; m_jitter_msec = 0; m_jitter_msec_total = 0; } else { if (m_first_filled) { int32_t diff = freq_ts - m_jitter_calc_freq_ts; int64_t calc_diff = (int64_t)diff; calc_diff *= TO_D64(1000); calc_diff /= (int64_t)m_freq; int64_t msec_diff = ts - m_jitter_calc_ts; int64_t add_diff; msec_diff -= m_jitter_msec_total; add_diff = msec_diff - calc_diff; if (add_diff != 0) {#if 0 static int64_t last_diff = 0; if (last_diff != msec_diff) { audio_message(LOG_DEBUG, "first diff "D64 " %u", msec_diff, m_filled_bytes); last_diff = msec_diff; }#endif if (add_diff > TO_D64(10) || add_diff < TO_D64(-10)) { audio_message(LOG_DEBUG, "jitter calc start %u now %u diff %d", m_jitter_calc_freq_ts, freq_ts, diff); audio_message(LOG_DEBUG, "ts start "U64" now "U64" diff "D64, ts, m_jitter_calc_ts, msec_diff); audio_message(LOG_DEBUG, "calc diff "D64" add "D64, calc_diff, add_diff); m_jitter_msec_total += add_diff; m_have_jitter = true; m_jitter_msec += add_diff; } } if (diff > (int32_t) m_freq || add_diff == 0) { m_jitter_calc_freq_ts = freq_ts; m_jitter_calc_ts = ts; } } } if (m_decode_format == AUDIO_FMT_HW_AC3) { // write header here m_sample_buffer[m_fill_offset] = bytes >> 8; m_fill_offset++; if (m_fill_offset >= m_sample_buffer_size) m_fill_offset = 0; m_sample_buffer[m_fill_offset] = bytes & 0xff; m_fill_offset++; if (m_fill_offset >= m_sample_buffer_size) m_fill_offset = 0; } // copy the bytes to the buffer - be aware of wrap at the end of // the buffer. while (bytes > 0) { uint32_t to_copy = MIN(bytes, m_sample_buffer_size - m_fill_offset); memcpy(m_sample_buffer + m_fill_offset, from, to_copy); from += to_copy; m_fill_offset += to_copy; if (m_fill_offset >= m_sample_buffer_size) { m_fill_offset = 0; } bytes -= to_copy; m_filled_bytes += to_copy; } if (need_resync) { audio_message(LOG_DEBUG, "resync - freq ts %u should be %u", freq_ts, m_buffer_next_freq_ts); } // record timestamp information for next frame. // note - if first_ts is used, we may need to move this inside the // mutex routines. if (m_first_filled == false) { m_first_ts = ts; m_first_freq_ts = freq_ts; m_first_filled = true; m_jitter_calc_freq_ts = freq_ts; m_jitter_calc_ts = ts; } /* * TODO note: we're going to need to calculate to see if there is * any jitter between the freq_ts and the ts */ m_buffer_next_freq_ts = freq_ts + samples; m_buffer_next_ts = ts + (samples * 1000) / m_freq; m_have_resync |= need_resync; if (locked) Unlock();#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "filled %u bytes at %u - total %u %u", write_bytes, freq_ts, m_filled_bytes, m_sample_buffer_size);#endif}/* * flush_decode_buffers - clear out the information so we can start * from scratch. */void CBufferAudioSync::flush_decode_buffers (void){ bool locked = Lock(); m_dont_fill = false; m_first_filled = false; m_fill_offset = 0; m_play_offset = 0; m_filled_bytes = 0; // m_have_resync = true; why ? m_audio_paused = true; m_have_jitter = false; m_jitter_msec = 0; m_jitter_msec_total = 0; if (locked) Unlock();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -