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

📄 audio_sdl.cpp

📁 jpeg and mpeg 编解码技术源代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// 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 + -