I wrote two command line applications in C++ which use WebRTC:
The basic implementation works: They exchange SDP-Offer and -Answer, find their external IPs using ICE, a PeerConnection and a PeerConnectionFactory with the corresponding constraints are created, etc. I added a hook on the server side to RtpReceiverImpl::IncomingRtpPacket
which writes the received payload to a file. The file contains valid PCM audio. Therefore, I assume the client streams data successfully through the network to the server application.
On the server side, my callback PeerConnectionObserver::OnAddStream
is called and receives a MediaStreamInterface
. Furthermore, i can iterate with my DeviceManagerInterface::GetAudioOutputDevices
through my audio devices. So basically, everything is fine.
What is missing: I need some kind of glue to tell WebRTC to play my AudioStream on the corresponding device. I have seen that I can get an AudioSink, AudioRenderer and AudioTrack objects. Again: Unfortunatly, I do not see an interface to pass them to the audio device. Can anyone help me with that?
One important note: I want to avoid to debug real hardware. Therefore, I added -DWEBRTC_DUMMY_FILE_DEVICES when building my WebRTC modules. It should write audio to an output file but the file just contains 0x00. The input file is read successfully because as I mentioned earlier, audio is send via RTP.
Finally, I found the solution: First, I have to say that my Code uses a WebRTC from 2017. So, the following things may have been changed and/or are fixed already:
After debugging my code and the WebRTC library I saw: When a remote stream is added, playback should start automatically. There is no need on the developer side to call playout()
in the VoiceEngine
or something comparable. When the library recognizes a remote audio stream, playback is paused, the new source is added to the mixer, and playback is resumed. The only APIs to control playback are provided by webrtc::MediaStreamInterface
which is passed via the PeerConnectionObserver::OnAddStream
. Examples are SetVolume()
or set_enabled()
.
So, what went wrong in my case? I used the FileAudioDevice
class which should write raw audio data to an output file instead of speakers. My implementation contains two bugs:
FileAudioDevice::Playing()
returned true in any case. Due to this, the library added the remote stream, wanted to resume playout, called FileAudioDevice::Playing()
which returned true and aborted because it thought the AudioDevice was already in playout mode.FileAudioDevice::PlayThreadProcess()
via _outputFile.Write(_playoutBuffer, kPlayoutBufferSize)
onto disk. However, this does not work. Luckily, a plain C fwrite() as hacky bugfix does work.