📄 audio_sdl.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. 2000, 2001. All Rights Reserved. * * Contributor(s): * Bill May wmay@cisco.com *//* * audio.cpp provides an interface (CAudioSync) between the codec and * the SDL audio APIs. */#include <stdlib.h>#include <string.h>#include "player_session.h"#include "audio_sdl.h"#include "player_util.h"#include <SDL_thread.h>#include "our_config_file.h"//#define DEBUG_SYNC 1//#define DEBUG_SYNC_CHANGES 1//#define DEBUG_AUDIO_FILL 1//#define DEBUG_DELAY 1#ifdef _WIN32DEFINE_MESSAGE_MACRO(audio_message, "audiosync")#else#define audio_message(loglevel, fmt...) message(loglevel, "audiosync", fmt)#endif/* * c routine to call into the AudioSync class callback */static void c_audio_callback (void *userdata, Uint8 *stream, int len){ CSDLAudioSync *a = (CSDLAudioSync *)userdata; a->audio_callback(stream, len);}/* * Create an CSDLAudioSync for a session. Don't alloc any buffers until * config is called by codec */CSDLAudioSync::CSDLAudioSync (CPlayerSession *psptr, int volume) : CAudioSync(psptr){ SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE); m_fill_index = m_play_index = 0; for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { m_buffer_filled[ix] = 0; m_sample_buffer[ix] = NULL; } m_buffer_size = 0; m_config_set = 0; m_audio_initialized = 0; m_audio_paused = 1; m_resync_required = 0; m_dont_fill = 0; m_consec_no_buffers = 0; //SDL_Init(SDL_INIT_AUDIO); m_audio_waiting_buffer = 0; m_audio_waiting = SDL_CreateSemaphore(0); //NULL; // will be set by decode thread m_skipped_buffers = 0; m_didnt_fill_buffers = 0; m_play_time = 0 ; m_buffer_latency = 0; m_volume = (volume * SDL_MIX_MAXVOLUME)/100; m_first_time = 1; m_first_filled = 1; m_buffer_offset_on = 0; m_buffer_ts = 0; m_load_audio_do_next_resync = 0;}/* * Close out audio sync - stop and disconnect from SDL */CSDLAudioSync::~CSDLAudioSync (void){ SDL_PauseAudio(1); SDL_CloseAudio(); for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { if (m_sample_buffer[ix] != NULL) free(m_sample_buffer[ix]); m_sample_buffer[ix] = NULL; } audio_message(LOG_NOTICE, "Audio sync skipped %u buffers", m_skipped_buffers); audio_message(LOG_NOTICE, "didn't fill %u buffers", m_didnt_fill_buffers); SDL_DestroySemaphore(m_audio_waiting);}/* * codec api - set up information about the stream */void CSDLAudioSync::set_config (int freq, int channels, int format, uint32_t sample_size) { if (m_config_set != 0) return; if (format == AUDIO_U8 || format == AUDIO_S8) m_bytes_per_sample = 1; else m_bytes_per_sample = 2; if (sample_size == 0) { int temp; temp = freq; while ((temp & 0x1) == 0) temp >>= 1; sample_size = temp; while (sample_size < 1024) sample_size *= 2; while (((sample_size * 1000) % freq) != 0) sample_size *= 2; } m_buffer_size = channels * sample_size * m_bytes_per_sample; for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) { m_buffer_filled[ix] = 0; // I'm not sure where the 2 * comes in... Check this out m_sample_buffer[ix] = (uint8_t *)malloc(2 * m_buffer_size); } m_freq = freq; m_channels = channels; m_format = format; m_config_set = 1; m_msec_per_frame = (sample_size * 1000) / m_freq; audio_message(LOG_DEBUG, "buffer size %d msec per frame %d", m_buffer_size, m_msec_per_frame);};/* * Codec api - get_audio_buffer - will wait if there are no available * buffers */uint8_t *CSDLAudioSync::get_audio_buffer (void){ int ret; int locked = 0; if (m_dont_fill == 1) {#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "first dont fill");#endif return (NULL); } if (m_audio_initialized != 0) { locked = 1; SDL_LockAudio(); } ret = m_buffer_filled[m_fill_index]; if (locked) SDL_UnlockAudio(); if (ret == 1) { m_audio_waiting_buffer = 1; SDL_SemWait(m_audio_waiting); m_audio_waiting_buffer = 0; if (m_dont_fill != 0) {#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "2nd don't fill");#endif return (NULL); } locked = 0; if (m_audio_initialized != 0) { SDL_LockAudio(); locked = 1; } ret = m_buffer_filled[m_fill_index]; if (locked) SDL_UnlockAudio(); if (ret == 1) {#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "no buff");#endif return (NULL); } } return (m_sample_buffer[m_fill_index]);}void CSDLAudioSync::load_audio_buffer (uint8_t *from, uint32_t bytes, uint64_t ts, int resync){ uint8_t *to; uint32_t copied;#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "fill %d bytes at "LLU", offset %d resync %d", bytes, ts, m_buffer_offset_on, resync);#endif copied = 0; if (m_buffer_offset_on == 0) { if (m_buffer_ts != 0 && m_buffer_ts != ts) { m_load_audio_do_next_resync = 1; audio_message(LOG_DEBUG, "timeslot doesn't match - %llu %llu", ts, m_buffer_ts); } m_buffer_ts = ts; } else { int64_t check; check = ts - m_loaded_next_ts; if (check > m_msec_per_frame) { audio_message(LOG_DEBUG, "potential resync at ts "LLU" should be ts "LLU, ts, m_loaded_next_ts); uint32_t left; left = m_buffer_size - m_buffer_offset_on; to = get_audio_buffer(); memset(to + m_buffer_offset_on, 0, left); filled_audio_buffer(m_buffer_ts, 0); m_buffer_offset_on = 0; m_load_audio_do_next_resync = 1; m_buffer_ts = ts; } } m_loaded_next_ts = bytes * M_LLU; m_loaded_next_ts /= m_bytes_per_sample; m_loaded_next_ts /= m_freq; m_loaded_next_ts += ts; while ( bytes > 0) { to = get_audio_buffer(); if (to == NULL) { return; } int copy; uint32_t left; left = m_buffer_size - m_buffer_offset_on; copy = MIN(left, bytes); memcpy(to + m_buffer_offset_on, from, copy); bytes -= copy; copied += copy; from += copy; m_buffer_offset_on += copy; if (m_buffer_offset_on >= m_buffer_size) { m_buffer_offset_on = 0; filled_audio_buffer(m_buffer_ts, resync | m_load_audio_do_next_resync); m_buffer_ts += m_msec_per_frame; resync = 0; m_load_audio_do_next_resync = 0; } } return;} /* * filled_audio_buffer - codec API - after codec fills the buffer from * get_audio_buffer, it will call here. */void CSDLAudioSync::filled_audio_buffer (uint64_t ts, int resync){ uint32_t fill_index; int locked; // m_dont_fill will be set when we have a pause if (m_dont_fill == 1) { return; } // resync = 0; fill_index = m_fill_index; m_fill_index++; m_fill_index %= DECODE_BUFFERS_MAX; locked = 0; if (m_audio_initialized != 0) { SDL_LockAudio(); locked = 1; } if (m_first_filled != 0) { m_first_filled = 0; resync = 0; m_resync_required = 0; } else { int64_t diff; diff = ts - m_last_fill_timestamp; if (diff - m_msec_per_frame > m_msec_per_frame) { // have a hole here - don't want to resync#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "Filling - last %llu new %llu", m_last_fill_timestamp, ts);#endif if (diff > ((m_msec_per_frame + 1) * 4)) {#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "resync required %lld", diff);#endif resync = 1; } else { // try to fill the holes m_last_fill_timestamp += m_msec_per_frame + 1; // fill plus extra if (locked) SDL_UnlockAudio(); int64_t ts_diff; do { uint8_t *retbuffer; // Get and swap buffers. retbuffer = get_audio_buffer(); if (retbuffer == NULL) { return; } if (retbuffer != m_sample_buffer[m_fill_index]) { audio_message(LOG_ERR, "retbuffer not fill index in audio sync"); return; } locked = 0; if (m_audio_initialized != 0) { SDL_LockAudio(); locked = 1; } m_sample_buffer[m_fill_index] = m_sample_buffer[fill_index]; m_sample_buffer[fill_index] = retbuffer; memset(retbuffer, m_obtained.silence, m_buffer_size); m_buffer_time[fill_index] = m_last_fill_timestamp; m_buffer_filled[fill_index] = 1; m_samples_loaded += m_buffer_size; fill_index++; fill_index %= DECODE_BUFFERS_MAX; m_fill_index++; m_fill_index %= DECODE_BUFFERS_MAX; if (locked) SDL_UnlockAudio(); audio_message(LOG_NOTICE, "Filling timestamp %llu with silence", m_last_fill_timestamp); m_last_fill_timestamp += m_msec_per_frame + 1; // fill plus extra ts_diff = ts - m_last_fill_timestamp; audio_message(LOG_DEBUG, "diff is %lld", ts_diff); } while (ts_diff > 0); locked = 0; if (m_audio_initialized != 0) { SDL_LockAudio(); locked = 1; } } } else { if (m_last_fill_timestamp == ts) { audio_message(LOG_NOTICE, "Repeat timestamp with audio %llu", ts); if (locked) SDL_UnlockAudio(); return; } } } m_last_fill_timestamp = ts; m_buffer_filled[fill_index] = 1; m_samples_loaded += m_buffer_size; m_buffer_time[fill_index] = ts; if (resync) { m_resync_required = 1; m_resync_buffer = fill_index;#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "Resync from filled_audio_buffer");#endif } if (locked) SDL_UnlockAudio(); // Check this - we might not want to do this unless we're resyncing if (resync) m_psptr->wake_sync_thread();#ifdef DEBUG_AUDIO_FILL audio_message(LOG_DEBUG, "Filling " LLU " %u %u", ts, fill_index, m_samples_loaded);#endif}void CSDLAudioSync::set_eof(void) { uint8_t *to; if (m_buffer_offset_on != 0) { to = get_audio_buffer(); if (to != NULL) { uint32_t left; left = m_buffer_size - m_buffer_offset_on; memset(to + m_buffer_offset_on, 0, left); m_buffer_offset_on = 0; filled_audio_buffer(m_buffer_ts, 0); m_buffer_ts += m_msec_per_frame; } } CAudioSync::set_eof();}// Sync task api - initialize the sucker.// May need to check out non-standard frequencies, see about conversion.// returns 0 for not yet, 1 for initialized, -1 for errorint CSDLAudioSync::initialize_audio (int have_video) { if (m_audio_initialized == 0) { if (m_config_set) { SDL_AudioSpec wanted; m_do_sync = have_video; memset(&wanted, 0, sizeof(wanted)); wanted.freq = m_freq; wanted.channels = m_channels; wanted.format = m_format; int sample_size; sample_size = m_buffer_size / (m_channels * m_bytes_per_sample);#ifndef _WIN32 uint32_t ix; for (ix = 2; ix <= 0x8000; ix <<= 1) { if ((sample_size & ~(ix - 1)) == 0) { break; } } ix >>= 1; audio_message(LOG_DEBUG, "Sample size is %d", ix); m_sample_size = ix;#else m_sample_size = 4096;#endif if ((m_do_sync == 0) && m_sample_size < 4096) m_sample_size = 4096; if (config.get_config_value(CONFIG_LIMIT_AUDIO_SDL_BUFFER) > 0 && m_sample_size > 4096) m_sample_size = 4096; wanted.samples = m_sample_size; wanted.callback = c_audio_callback; wanted.userdata = this;#if 1 audio_message(LOG_INFO, "requested f %d chan %d format %x samples %d", wanted.freq, wanted.channels, wanted.format, wanted.samples);#endif int ret = SDL_OpenAudio(&wanted, &m_obtained); if (ret < 0) { audio_message(LOG_CRIT, "Couldn't open audio, %s", SDL_GetError()); return (-1); }#if 1 audio_message(LOG_INFO, "got f %d chan %d format %x samples %d size %u", m_obtained.freq, m_obtained.channels, m_obtained.format, m_obtained.samples, m_obtained.size);#endif m_audio_initialized = 1; m_use_SDL_delay = SDL_HasAudioDelayMsec(); if (m_use_SDL_delay) audio_message(LOG_NOTICE, "Using delay measurement from SDL"); } else { return 0; // check again pretty soon... } } return (1);}// This is used by the sync thread to determine if a valid amount of// 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -