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

📄 audio_alsa_source.cpp

📁 完整的RTP RTSP代码库
💻 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-2002.  All Rights Reserved. *  * Contributor(s):  *		Dave Mackie		dmackie@cisco.com *		Bill May 		wmay@cisco.com *		Sverker Abrahamsson	sverker@abrahamsson.com */#include "mp4live.h"#include "audio_alsa_source.h"#include "audio_encoder.h"//#define DEBUG_TIMESTAMPS 1#ifdef HAVE_ALSACALSAAudioSource::CALSAAudioSource(CLiveConfig *pConfig) : CMediaSource() {  SetConfig(pConfig);  m_prevTimestamp = 0;  m_timestampOverflowArray = NULL;  m_timestampOverflowArrayIndex = 0;  m_audioMaxBufferSize = 0;}int CALSAAudioSource::ThreadMain(void) {  debug_message("alsa start");  while (true) {    int rc;    if (m_source) {      rc = SDL_SemTryWait(m_myMsgQueueSemaphore);    } else {      rc = SDL_SemWait(m_myMsgQueueSemaphore);    }    // semaphore error    if (rc == -1) {      break;    }     // message pending    if (rc == 0) {      CMsg* pMsg = m_myMsgQueue.get_message();		      if (pMsg != NULL) {        switch (pMsg->get_value()) {        case MSG_NODE_STOP_THREAD:          DoStopCapture();	// ensure things get cleaned up          delete pMsg;          debug_message("alsa stop thread");          return 0;        case MSG_NODE_START:          DoStartCapture();          break;        case MSG_NODE_STOP:          DoStopCapture();          break;        }        delete pMsg;      }    }    if (m_source) {      try {        //debug_message("processaudio");        ProcessAudio();      }      catch (...) {        error_message("alsa stop capture");        DoStopCapture();	        break;      }    }  }  debug_message("alsa thread exit");  return -1;}void CALSAAudioSource::DoStartCapture(){  if (m_source) {    return;  }  if (!Init()) {    return;  }  m_source = true;}void CALSAAudioSource::DoStopCapture(){  if (!m_source) {    return;  }  //  CMediaSource::DoStopAudio();  snd_pcm_drop(m_pcmHandle);  snd_pcm_close(m_pcmHandle);  CHECK_AND_FREE(m_timestampOverflowArray);  m_source = false;}bool CALSAAudioSource::Init(void){  bool rc = InitAudio(true);  if (!rc) {    return false;  }  if (!InitDevice()) {    return false;  }  //#error we will have to remove this below - the sample rate will be  rc = SetAudioSrc(                   PCMAUDIOFRAME,                   m_channelsConfigured,                   m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE));  if (!rc) {    return false;  }  // for live capture we can match the source to the destination  //  m_audioSrcSamplesPerFrame = m_audioDstSamplesPerFrame;  // gets set   m_pcmFrameSize = m_audioSrcSamplesPerFrame * m_audioSrcChannels * sizeof(u_int16_t);  if (m_audioMaxBufferSize > 0) {    size_t array_size;    m_audioMaxBufferFrames = m_audioMaxBufferSize / m_pcmFrameSize;    if (m_audioMaxBufferFrames == 0) {      m_audioMaxBufferFrames = 1;    }    array_size = m_audioMaxBufferFrames * sizeof(*m_timestampOverflowArray);    m_timestampOverflowArray = (Timestamp *)Malloc(array_size);    memset(m_timestampOverflowArray, 0, array_size);  }      // maximum number of passes in ProcessAudio, approx 1 sec.  m_maxPasses = m_audioSrcSampleRate / m_audioSrcSamplesPerFrame;  return true;#if 0 init_failure:  debug_message("audio initialization failed");  snd_pcm_close(m_pcmHandle);  m_pcmHandle = -1;  return false;#endif}bool CALSAAudioSource::InitDevice(void){	int err;  // Capture stream  snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;  // This structure contains information about  // the hardware and can be used to specify the  // configuration to be used for the PCM stream.  snd_pcm_hw_params_t *hwparams;  // Name of the PCM device, like plughw:0,0  const char *pcm_name = m_pConfig->GetStringValue(CONFIG_AUDIO_SOURCE_NAME);  // Allocate the snd_pcm_hw_params_t structure on the stack.  snd_pcm_hw_params_alloca(&hwparams);  // Open PCM  if ((err = snd_pcm_open(&m_pcmHandle, pcm_name, stream, 0)) < 0) {    error_message("Failed to open %s: %s", pcm_name, snd_strerror(err));    return false;  }  // Init hwparams with full configuration space  if ((err = snd_pcm_hw_params_any(m_pcmHandle, hwparams)) < 0) {    error_message("Can not configure the PCM device %s: %s", pcm_name, snd_strerror(err));    return false;  }	// Enable resample#ifdef HAVE_SND_PCM_HW_PARAMS_SET_RATE_RESAMPLE  if ((err = snd_pcm_hw_params_set_rate_resample(m_pcmHandle, hwparams, 1)) < 0) {    error_message("Error enabling resample for PCM device %s: %s", pcm_name, snd_strerror(err));    return false;  }#endif  // Set access type to SND_PCM_ACCESS_RW_INTERLEAVED  if ((err = snd_pcm_hw_params_set_access(m_pcmHandle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {    error_message("Error setting access type for PCM device %s: %s", pcm_name, snd_strerror(err));    return false;  }  // Set sample format#ifdef WORDS_BIGENDIAN#define OUR_FORMAT SND_PCM_FORMAT_S16_BE#else#define OUR_FORMAT SND_PCM_FORMAT_S16_LE#endif  if ((err = snd_pcm_hw_params_set_format(m_pcmHandle, hwparams, OUR_FORMAT)) < 0) {    error_message("Couldn't set format for PCM device %s: %s", pcm_name, snd_strerror(err));    snd_pcm_close(m_pcmHandle);    return false;  }  // Set number of channels  m_channelsConfigured = m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS);  if ((err = snd_pcm_hw_params_set_channels(m_pcmHandle, hwparams, m_channelsConfigured)) < 0) {    error_message("Couldn't set audio channels for PCM device %s: %s", pcm_name, snd_strerror(err));    snd_pcm_close(m_pcmHandle);    return false;  }  // Set sample rate  u_int32_t samplingRate = m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE);  u_int32_t targetSamplingRate = samplingRate;  // TODO maybe use snd_pcm_hw_params_set_rate instead  if ((err = snd_pcm_hw_params_set_rate_near(m_pcmHandle, hwparams, &targetSamplingRate, 0)) < 0) {    error_message("Couldn't set sample rate for PCM device %s: %s", pcm_name, snd_strerror(err));    snd_pcm_close(m_pcmHandle);    return false;  }  if (samplingRate != targetSamplingRate) {    error_message("Couldn't set sample rate for PCM device %s", pcm_name);    snd_pcm_close(m_pcmHandle);    return false;  }  // Apply HW parameter settings to PCM device. This will also put it into PREPARED state  if ((err = snd_pcm_hw_params(m_pcmHandle, hwparams)) < 0) {    error_message("Couldn't set hw parameters PCM device %s: %s", pcm_name, snd_strerror(err));    snd_pcm_close(m_pcmHandle);    return false;  }  debug_message("alsa init done");  return true;}void CALSAAudioSource::ProcessAudio(void){	int err;	  if (m_audioSrcFrameNumber == 0) {  	// Start the device    if ((err = snd_pcm_start(m_pcmHandle)) < 0) {      error_message("Couldn't start the PCM device: %s", snd_strerror(err));    }  }	  snd_pcm_status_t *status;  snd_pcm_status_alloca(&status);  // for efficiency, process 1 second before returning to check for commands  for (int pass = 0; pass < m_maxPasses && m_stop_thread == false; pass++) {    u_int8_t*     pcmFrameBuffer;    pcmFrameBuffer = (u_int8_t*)malloc(m_pcmFrameSize);    // The alsa frames is not the same as the pcm frames used to feed the encoder    // Calculate how many alsa frames is neccesary to read to fill one pcm frame	  snd_pcm_uframes_t num_frames = m_pcmFrameSize / (m_audioSrcChannels * sizeof(u_int16_t));	  		// Check how many bytes there is to read in the buffer, it will be used to calculate timestamp    snd_pcm_status(m_pcmHandle, status);		unsigned long avail_bytes = snd_pcm_status_get_avail(status) * (m_audioSrcChannels * sizeof(u_int16_t));    Timestamp currentTime = GetTimestamp();    Timestamp timestamp;    // Read num_frames frames from the PCM device    // pointed to by pcm_handle to buffer capdata.    // Returns the number of frames actually read.    // TODO On certain alsa configurations, e.g. when using dsnoop with low sample rate, the period gets too small. What to do about that?    snd_pcm_sframes_t framesRead;    if((framesRead = snd_pcm_readi(m_pcmHandle, pcmFrameBuffer, num_frames)) == -EPIPE) {      snd_pcm_prepare(m_pcmHandle);      // Buffer Overrun. This means the audio buffer is full, and not capturing      // we want to make the timestamp based on the previous one      // When we hit this case, we start using the m_timestampOverflowArray      // This will give us a timestamp for when the array is full.      //       // In other words, if we have a full audio buffer (ie: it's not loading      // any more), we start storing the current timestamp into the array.      // This will let us "catch up", and have a somewhat accurate timestamp      // when we loop around      //       // wmay - I'm not convinced that this actually works - if the buffer      // cleans up, we'll ignore m_timestampOverflowArray      if (m_timestampOverflowArray != NULL && m_timestampOverflowArray[m_timestampOverflowArrayIndex] != 0) {        timestamp = m_timestampOverflowArray[m_timestampOverflowArrayIndex];      } else {        timestamp = m_prevTimestamp + SrcSamplesToTicks(avail_bytes);      }      if (m_timestampOverflowArray != NULL)        m_timestampOverflowArray[m_timestampOverflowArrayIndex] = currentTime;      debug_message("audio buffer full !");    } else {      if (framesRead < (snd_pcm_sframes_t) num_frames) {        error_message("Bad audio read. Expected %li frames, got %li", num_frames, framesRead);        free(pcmFrameBuffer);        continue;      }      // buffer is not full - so, we make the timestamp based on the number      // of bytes in the buffer that we read.      timestamp = currentTime - SrcSamplesToTicks(SrcBytesToSamples(avail_bytes));      if (m_timestampOverflowArray != NULL)        m_timestampOverflowArray[m_timestampOverflowArrayIndex] = 0;    }    //debug_message("alsa read");#ifdef DEBUG_TIMESTAMPS    debug_message("avail_bytes=%lu t="U64" timestamp="U64" delta="U64,                  avail_bytes, currentTime, timestamp, timestamp - m_prevTimestamp);#endif    m_prevTimestamp = timestamp;    if (m_timestampOverflowArray != NULL) {      m_timestampOverflowArrayIndex = (m_timestampOverflowArrayIndex + 1) % m_audioMaxBufferFrames;    }#ifdef DEBUG_TIMESTAMPS    debug_message("pcm forward "U64" %u", timestamp, m_pcmFrameSize);#endif    if (m_audioSrcFrameNumber == 0 && m_videoSource != NULL) {      m_videoSource->RequestKeyFrame(timestamp);    }    m_audioSrcFrameNumber++;    CMediaFrame *frame = new CMediaFrame(PCMAUDIOFRAME,					 pcmFrameBuffer, 					 m_pcmFrameSize, 					 timestamp);    ForwardFrame(frame);  }}bool CALSAAudioCapabilities::ProbeDevice(){  if (allSampleRateTableSize > NUM_ELEMENTS_IN_ARRAY(m_samplingRates)) {    error_message("Number of sample rates exceeds audio cap array");    return false;  }  // Open PCM  snd_pcm_t *pcm_handle;  snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;  snd_pcm_hw_params_t *hwparams;  snd_pcm_hw_params_alloca(&hwparams);	int err;  if ((err= snd_pcm_open(&pcm_handle, m_deviceName, stream, SND_PCM_NONBLOCK)) < 0) {    error_message("Failed to open %s: %s", m_deviceName, snd_strerror(err));    return false;  }  m_canOpen = true;  if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {    error_message("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));    return false;  }  // union of valid sampling rates for MP3 and AAC  // for all possible sampling rates  u_int8_t i;  for (i = 0; i < allSampleRateTableSize; i++) {    u_int32_t targetRate = allSampleRateTable[i];    if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, targetRate, 0) != 0) {      debug_message("audio device %s doesn't support sampling rate %u",                    m_deviceName, targetRate);      continue;    }    debug_message("sampling rate %u supported", targetRate);    // valid sampling rate    m_samplingRates[m_numSamplingRates++] = targetRate;  }  // zero out remaining sampling rate entries  for (i = m_numSamplingRates; i < allSampleRateTableSize; i++) {    m_samplingRates[i] = 0;  }  snd_pcm_close(pcm_handle);  return true;}void CALSAAudioCapabilities::Display (CLiveConfig *pConfig, 				  char *msg, 				  uint32_t max_len){  // might want to parse out CONFIG_AUDIO_INPUT_NAME a bit to make it  // more English like...  snprintf(msg, max_len,	   "%s, %s, %u Hz, %s", 	   pConfig->GetStringValue(CONFIG_AUDIO_SOURCE_TYPE),	   pConfig->GetStringValue(CONFIG_AUDIO_SOURCE_NAME),	   pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE),	   pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS) == 1 ? 	   "mono" : "stereo");}#endif

⌨️ 快捷键说明

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