⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 audio_sdl.cpp

📁 jpeg and mpeg 编解码技术源代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
 * 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>
//#define DEBUG_SYNC 1
//#define DEBUG_AUDIO_FILL 1
//#define DEBUG_DELAY 1
#ifdef _WIN32
DEFINE_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 = 1;
  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);
}

/*
 * 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;
  } 
  
  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]);
}

uint32_t CSDLAudioSync::load_audio_buffer (uint8_t *from, 
					   uint32_t bytes, 
					   uint64_t ts, 
					   int resync)
{
  uint8_t *to;
  uint32_t copied;
  int64_t diff, calc;
#ifdef DEBUG_AUDIO_FILL
  audio_message(LOG_DEBUG, "fill %d bytes at "LLU", offset %d", 
		bytes, ts, m_buffer_offset_on);
#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;
    }
    m_buffer_ts = ts;
  } else {
    diff = ts - m_buffer_ts;
    calc = m_buffer_offset_on * M_LLU;
    calc /= m_bytes_per_sample;
    calc /= m_freq;
    if (diff > calc + 2) {
      audio_message(LOG_DEBUG, "potential resync at ts "LLU" diff is "LLD" calc is "LLD, 
		    ts, diff, calc);
      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;
    }
  }
  while ( bytes > 0) {
    to = get_audio_buffer();
    if (to == NULL) {
      return copied;
    }
    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 (copied);
}

    

  
/*
 * 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;
  }
  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;
  } 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)) {
	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 error
int CSDLAudioSync::initialize_audio (int have_video) 
{
  if (m_audio_initialized == 0) {
    if (m_config_set) {
      SDL_AudioSpec wanted;
      m_do_sync = have_video;
      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 _WINDOWS
      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;
      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

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -