1. 程式人生 > >webrtc58 中的音訊採集

webrtc58 中的音訊採集

關於webrtc中的音訊採集,當然和是各個平臺採集具體實現,上層封裝統一介面;

從資料來源來看,音訊資料來自於class AudioDeviceModule;

然後檢視:

 virtual int32_t RegisterAudioCallback(AudioTransport* audioCallback) = 0;

class AudioTransport {
 public:
  virtual int32_t RecordedDataIsAvailable(const void* audioSamples,
                                          const size_t nSamples,
                                          const size_t nBytesPerSample,
                                          const size_t nChannels,
                                          const uint32_t samplesPerSec,
                                          const uint32_t totalDelayMS,
                                          const int32_t clockDrift,
                                          const uint32_t currentMicLevel,
                                          const bool keyPressed,
                                          uint32_t& newMicLevel) = 0;


  virtual int32_t NeedMorePlayData(const size_t nSamples,
                                   const size_t nBytesPerSample,
                                   const size_t nChannels,
                                   const uint32_t samplesPerSec,
                                   void* audioSamples,
                                   size_t& nSamplesOut,
                                   int64_t* elapsed_time_ms,
                                   int64_t* ntp_time_ms) = 0;


  // Method to push the captured audio data to the specific VoE channel.
  // The data will not undergo audio processing.
  // |voe_channel| is the id of the VoE channel which is the sink to the
  // capture data.
  // TODO(bugs.webrtc.org/8659): Remove this method once clients updated.
  RTC_DEPRECATED virtual void PushCaptureData(
      int voe_channel,
      const void* audio_data,
      int bits_per_sample,
      int sample_rate,
      size_t number_of_channels,
      size_t number_of_frames) {
    RTC_NOTREACHED();
  }


  // Method to pull mixed render audio data from all active VoE channels.
  // The data will not be passed as reference for audio processing internally.
  virtual void PullRenderData(int bits_per_sample,
                              int sample_rate,
                              size_t number_of_channels,
                              size_t number_of_frames,
                              void* audio_data,
                              int64_t* elapsed_time_ms,
                              int64_t* ntp_time_ms) = 0;


 protected:
  virtual ~AudioTransport() {}
};

嗯,就是這了;

這裡除錯跟蹤的是Windows端的程式碼: 從下往上看,


VoEBaseImpl::RecordedDataIsAvailable(const void * audioSamples, const unsigned __int64 nSamples, const unsigned __int64 nBytesPerSample, const unsigned __int64 nChannels, const unsigned int samplesPerSec, const unsigned int totalDelayMS, const int clockDrift, const unsigned int currentMicLevel, const bool keyPressed, unsigned int & newMicLevel) 
AudioTransportProxy::RecordedDataIsAvailable(const void * audioSamples, const unsigned __int64 nSamples, const unsigned __int64 nBytesPerSample, const unsigned __int64 nChannels, const unsigned int samplesPerSec, const unsigned int totalDelayMS, const int clockDrift, const unsigned int currentMicLevel, const bool keyPressed, unsigned int & newMicLevel) 
AudioDeviceBuffer::DeliverRecordedData() 
AudioDeviceWindowsCore::DoCaptureThread() 
AudioDeviceWindowsCore::WSAPICaptureThread(void * context) 

從上述程式碼流程可以看出,先從平臺相關實現獲取具體音訊PCM資料,然後經過傳送到VoEBaseImpl::RecordedDataIsAvailable


這裡的資料採集為什麼說道這裡呢?因為:在VoEBaseImpl::RecordedDataIsAvailable函式中還執行了相關的一些資料函式;

所以,要獲取音訊資料,最好在ProcessRecordedDataWithAPM函式之後獲取音訊; 

int32_t VoEBaseImpl::RecordedDataIsAvailable(const void* audioSamples,
                                             const size_t nSamples,
                                             const size_t nBytesPerSample,
                                             const size_t nChannels,
                                             const uint32_t samplesPerSec,
                                             const uint32_t totalDelayMS,
                                             const int32_t clockDrift,
                                             const uint32_t currentMicLevel,
                                             const bool keyPressed,
                                             uint32_t& newMicLevel) {
  newMicLevel = static_cast<uint32_t>(ProcessRecordedDataWithAPM

(
 nullptr, 0, audioSamples, samplesPerSec, nChannels, nSamples,
 totalDelayMS, clockDrift, currentMicLevel, keyPressed));

}

寫在後面: 根據webrtc58版本中的示例,可以看出,已經預留了了音訊獲取的資料回撥函式介面OnData;

只不過在webrtc58版本中沒有實現完成,所以在後去版本中,應該會實現。

ProcessRecordedDataWithAPM 函式中實現了音訊的處理,encode,rtp打包,rtp 傳送等;內容比較多;

本來想單獨寫一遍文章,但是為了內容的連續性,就在這類繼續寫了;

//直接獲取音訊PCM編碼後的資料

  encoded_info = encoder_stack_->Encode(
      rtp_timestamp, rtc::ArrayView<const int16_t>(
                         input_data.audio, input_data.audio_channel *
                                               input_data.length_per_channel),
      &encode_buffer_);

AudioCodingModuleImpl::Encode(const webrtc::`anonymous-namespace'::AudioCodingModuleImpl::InputData & input_data) 
AudioCodingModuleImpl::Add10MsData(const webrtc::AudioFrame & audio_frame) 
voe::Channel::EncodeAndSend() 
voe::TransmitMixer::EncodeAndSend() 
VoEBaseImpl::ProcessRecordedDataWithAPM(const int * voe_channels, unsigned __int64 number_of_voe_channels, const void * audio_data, unsigned int sample_rate, unsigned __int64 number_of_channels, unsigned __int64 number_of_frames, unsigned int audio_delay_milliseconds, int clock_drift, unsigned int volume, bool key_pressed) 

每次獲取10Ms的音訊資料,然後進行音訊資料的處理,然後進行編碼;

webrtc會將各類編碼具體實現,都具基礎實現 public AudioEncoder ;

如:

class AudioEncoderOpus final : public AudioEncoder 

class AudioEncoderG722 final : public AudioEncoder

PCM編碼完成後,傳送資料,其實,就是進入了:int32_t Channel::SendData

   if (packetization_callback_) {
      packetization_callback_->SendData(
          frame_type, encoded_info.payload_type, encoded_info.encoded_timestamp,
          encode_buffer_.data(), encode_buffer_.size(),
          my_fragmentation.fragmentationVectorSize > 0 ? &my_fragmentation
                                                       : nullptr);

然後:

  // Push data from ACM to RTP/RTCP-module to deliver audio frame for
  // packetization.
  // This call will trigger Transport::SendPacket() from the RTP/RTCP module.
  if (!_rtpRtcpModule->SendOutgoingData(
          (FrameType&)frameType, payloadType, timeStamp,
          // Leaving the time when this frame was
          // received from the capture device as
          // undefined for voice for now.
          -1, payloadData, payloadSize, fragmentation, nullptr, nullptr)) {
    _engineStatisticsPtr->SetLastError(
        VE_RTP_RTCP_MODULE_ERROR, kTraceWarning,
        "Channel::SendData() failed to send data to RTP/RTCP module");
    return -1;
  }

...

然後:

實現音訊資料的RTP打包,然後傳送,高優先順序;

bool RTPSenderAudio::SendAudio

  bool send_result = rtp_sender_->SendToNetwork(
      std::move(packet), kAllowRetransmission, RtpPacketSender::kHighPriority);

然後:

新增到傳送快取:

   paced_sender_->InsertPacket(priority, ssrc, seq_no, corrected_time_ms,
                                payload_length, false);

//void PacedSender::InsertPacket //執行到這裡;

//這個類實現了平滑傳送資料,同時根據位元速率控制傳送資料;

class PacedSender : public Module, public RtpPacketSender 

通過PacedSender::InsertPacket新增rtp packet, 新增到 packets_ 連結串列中;

通過 PacedSender::Process實現packets_ 連結串列中中的資料計算,傳送;通常這個函式會在一個單獨的執行緒中執行;

關於 PacedSender 可以檢視這篇文章:http://blog.csdn.net/qq_24283329/article/details/72899322  《傳送位元速率控制之PacedSender模組

資料的傳送

 ricket::UDPPort::SendTo 
 ricket::ProxyConnection::Send 
 ricket::P2PTransportChannel::SendPacket 
 ricket::DtlsTransport::SendPacket 
 ricket::BaseChannel::SendPacket 
 ricket::BaseChannel::OnMessage 
 ricket::VideoChannel::OnMessage 
 tc::MessageQueue::Dispatch 
 tc::Thread::ProcessMessages 
 tc::Thread::Run 
 tc::Thread::PreRun

UDPPort::SendTo實現了的資料的真正傳送;

UDPPort中包含了webrtc中封裝的UDP類;

一個 ProxyConnection 對應 一個 UDPPort;

ProxyConnection::ProxyConnection的實現函式

int ProxyConnection::Send(const void* data, size_t size,  const rtc::PacketOptions& options) {
  stats_.sent_total_packets++;
  int sent = port_->SendTo(data, size, remote_candidate_.address(),
                           options, true);
  if (sent <= 0) {
    RTC_DCHECK(sent < 0);
    error_ = port_->GetError();
    stats_.sent_discarded_packets++;
  } else {
    send_rate_tracker_.AddSamples(sent);
  }
  return sent;
}

可清楚的看到,port_通過設定遠端的地址,通過UDP傳送資料到指定地址;

UDPPort中的socket建立函式

bool UDPPort::Init() {
  stun_keepalive_lifetime_ = GetStunKeepaliveLifetime();
  if (!SharedSocket()) {
    RTC_DCHECK(socket_ == NULL);
    socket_ = socket_factory()->CreateUdpSocket(
        rtc::SocketAddress(ip(), 0), min_port(), max_port());
    if (!socket_) {
      LOG_J(LS_WARNING, this) << "UDP socket creation failed";
      return false;
    }
    socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket);
  }
  socket_->SignalSentPacket.connect(this, &UDPPort::OnSentPacket);
  socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend);
  socket_->SignalAddressReady.connect(this, &UDPPort::OnLocalAddressReady);
  requests_.SignalSendPacket.connect(this, &UDPPort::OnSendPacket);
  return true;
}

最近看見一個檔案:

audio_device_data_observer.h

// This interface will capture the raw PCM data of both the local captured as
// well as the mixed/rendered remote audio.
class AudioDeviceDataObserver {
 public:
  virtual void OnCaptureData(const void* audio_samples,
                             const size_t num_samples,
                             const size_t bytes_per_sample,
                             const size_t num_channels,
                             const uint32_t samples_per_sec) = 0;


  virtual void OnRenderData(const void* audio_samples,
                            const size_t num_samples,
                            const size_t bytes_per_sample,
                            const size_t num_channels,
                            const uint32_t samples_per_sec) = 0;


  AudioDeviceDataObserver() = default;
  virtual ~AudioDeviceDataObserver() = default;
};


// Creates an ADM instance with AudioDeviceDataObserver registered.
rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceWithDataObserver(
    const AudioDeviceModule::AudioLayer audio_layer,
    AudioDeviceDataObserver* observer);


// TODO(bugs.webrtc.org/7306): deprecated.
rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceWithDataObserver(
    const int32_t id,
    const AudioDeviceModule::AudioLayer audio_layer,
    AudioDeviceDataObserver* observer);


}  // namespace webrtc

看檔案的內容,應該可以更好的獲取音訊採集資料和播放資料;有時間在具體測試;

當然64版本已經有Ondata函式獲取本地採集資料了,可以簡單獲取採集資料;

還有ADMWrapper ,關於 ADMWrapper ,看我這篇文章:《webrtc中的音訊裝置 音訊採集 AudioDeviceModule

相關推薦

webrtc58 音訊採集

關於webrtc中的音訊採集,當然和是各個平臺採集具體實現,上層封裝統一介面;從資料來源來看,音訊資料來自於class AudioDeviceModule;然後檢視: virtual int32_t RegisterAudioCallback(AudioTransport*

學習音訊之androidAudioRecord採集音訊的引數說明

在android中採集音訊的api是android.media.AudioRecord類 其中構造器的幾個引數就是標準的聲音採集引數 以下是引數的含義解釋 public AudioRecord (int audioSource, int sampleRateInH

androidAudioRecord採集音訊的引數說明以及audioTrack的播放

在android中採集音訊的api是android.media.AudioRecord類 在android中播放音訊也是從api中的類分析 其中構造器的幾個引數就是標準的聲音採集引數 以下是引數的含義解釋 1. public AudioRecord (int audioSource, int sampleR

webRTC音訊相關的netEQ(五):DSP處理 webRTC音訊相關的netEQ(四):控制命令決策 webRTC音訊相關的netEQ(二):資料結構)

上篇(webRTC中音訊相關的netEQ(四):控制命令決策)講了MCU模組是怎麼根據網路延時、抖動緩衝延時和反饋報告等來決定給DSP模組發什麼控制命令的。DSP模組根據收到的命令進行相關處理,處理簡要流程圖如下。   從上圖看出如果有語音包從packet buffer裡取出來先要做解碼得到PC

Qt工程音訊資原始檔的路徑報錯

void Player::play(QString filePath) { player->setMedia(QUrl(filePath)); player->play(); } Player *player=new Player();

音訊採集-AudioUnit

AudioUnit簡介   AudioUnit這個名字取得還是比較形象的,它的主體就是一系列的unit,不同unit能夠實現不同的功能,將一個或多個unit新增到AUGraph(Audio Processing Graph)中,並建立unit之間的連線,音訊資料順次通過各個

webRTC音訊相關的netEQ(四):控制命令決策

上篇(webRTC中音訊相關的netEQ(三):存取包和延時計算)講了語音包的存取以及網路延時和抖動緩衝延時的計算,MCU也收到了DSP模組發來的反饋報告。本文講MCU模組如何根據網路延時、抖動緩衝延時和反饋報告等決定發給DSP模組的控制命令, 好讓DSP模組先對取出的語音包做解碼處理(如果有的話)以及根據這

pcm原始音訊採集率轉換

pcm介紹 pcm也被稱為 脈碼編碼調製,是音訊中沒經過壓縮的原始資料。 在聲音採集中經過抽樣,量化,最後編碼。 取樣:對聲音進行一定頻率的採集,頻率越高,間隔時間越小,聲音更接近真實。常用的取樣率有8khz,16khz,22.05KHz、44.1KHz、48KHz等。 量化就是對每個採

HI3521D外接audio codec轉I2S音訊採集

經過幾天的不斷的閱讀文件,問人,嘗試終於除錯成功。反過來一想,原來如此簡單。 1.硬體原理圖 a.外接codec部分 b.時鐘MCLK部分,由海思提供 c.海思對接codec部分 對原理圖的理解:外接codec通過由海思GPIO9_3/I2S2_

Windows下wave API 音訊採集

目的:Windows下wave API採集PCM 環境: 系統:Win10 環境:VS2015 64bit 操作步驟: 1. 匯入系統wave標頭檔案及庫 #include <mmsystem.h> #pragma com

C#音訊採集 (筆記)

using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Threading; using Microsoft.

alsa音訊採集和播放 (麥克風)

Alsa音訊採集 #include <stdio.h> #include <malloc.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include 

iOS 實時音訊採集與播放

前言 在iOS中有很多方法可以進行音視訊採集。如 AVCaptureDevice, AudioQueue以及Audio Unit。其中 Audio Unit是最底層的介面,它的優點是功能強大,延遲低; 而缺點是學習成本高,難度大。對於一般的iOS應用程式,AV

Android開發之PCM音訊採集

通過ijkplayer播放視訊,需求:移動端實現實時對講。即pcm音訊錄製實時推送到裝置端,本篇主要記錄pcm音訊採集 為保證語音連續性,bufferSize 1m在網不好的情況下可能出現語音時斷時續,需要調小bufferSize 到最合適的值 impo

Android音訊採集、壓縮、傳送

基本的實現流程:  1、從手機麥中採集音訊資料;2、將PCM音訊資料編碼壓縮;3、將壓縮好的音訊通過無線網路傳送出去;4、其他手機接收音訊資料並解碼;5、將音訊資料寫入到音軌中播放。專案雖然簡單,但其中的一些小問題也折騰了我不少時間。   首先我們建立一個執行緒用來採

瀏覽器音訊相容性問題(上)

參考文章:http://blog.csdn.net/www3300300/article/details/17709219 我參考了上面連結的文章,由於這篇文章寫於2013年,目前已經不能滿足需求了。 下面我將把我參考改進後的做法列出來: (由於我們公司只

淺談iOS音訊的開發

  我對音訊技術不是很精通,最近對這方面比較有興趣,公司以前的專案用到這方面的技術不多。我只是粗略的涉獵沒有深入研究。今天就研究一下寫一篇部落格。同時希望以後能做一個關於音訊的商業專案也期望能接觸到音

windows下簡單的音訊採集示例

最近需要在window下進行音訊採集,網上找了很久都沒找到win7下如何採集pcm資料的完整示例,經過一翻折騰後寫了一個很簡單的demo程式以供同行進行參考,如有不正確的地方請指正 本例是採用audio core進行音訊採集 程式碼塊 #include "

linux 音訊採集基礎知識普及

雖然目前Linux的優勢主要體現在網路服務方面,但事實上同樣也有著非常豐富的媒體功能,本文就是以多媒體應用中最基本的聲音為物件,介紹如何在Linux平臺下開發實際的音訊應用程式,同時還給出了一些常用的音訊程式設計框架。 一、數字音訊 音訊訊號是一種連

UE4 4.18關於視訊播放音訊播放的變動

這個問題是我請教一個UE4開發好友解決的(當然是他告訴我的,外掛結構發生了變動) 他的聯絡方式,QQ:10235336 , email: [email protected] 具體視訊播放的變動日期不知道,但很可能在4.18.3。 該變動中是視訊播放的Media S