WebRTC Native, AudioTrackSinkInterface added to track, but OnData is never called - c++

I've been working on a product that uses WebRTC to exchange audio between a browser and a native client, the native side being implemented in C++. Currently I've built the latest stable release of webRtc (branch: branch-heads/65).
So far, I'm able to get the connection peers to connect, audio is received and rendered correctly on the browser. However, the native client seems to never receive any data through it's audio track sink, despite the chrome debug tools suggesting that data is being sent from the browser to the native client.
The following code is definitely called, and the channel is being added as expected.
void Conductor::OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream)
{
webrtc::AudioTrackVector atracks = stream->GetAudioTracks();
for (auto track : atracks)
{
remote_audio.reset(new Native::AudioRenderer(this, track));
track->set_enabled(true);
}
}
// Audio renderer derived from webrtc::AudioTrackSinkInterface
// In the audio renderer constructor, AddSink is called on the track.
AudioRenderer::AudioRenderer(AudioCallback* callback, webrtc::AudioTrackInterface* track) : track_(track), callback_(callback)
{
// Can confirm this point is reached.
track_->AddSink(this);
}
AudioRenderer::~AudioRenderer()
{
track_->RemoveSink(this);
}
void AudioRenderer::OnData(const void* audio_data, int bits_per_sample, int sample_rate, size_t number_of_channels,
size_t number_of_frames)
{
// This is never hit, despite the connection starting and streams being added.
if (callback_ != nullptr)
{
callback_->OnAudioData(audio_data, bits_per_sample, sample_rate, number_of_channels, number_of_frames);
}
}
I can also confirm that both offers include the option to receive audio:
Browser client offer:
// Create offer
var offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 0
};
pc.createOffer(offerOptions)
.then(offerCreated);
Native client answer:
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions o;
{
o.voice_activity_detection = false;
o.offer_to_receive_audio = webrtc::PeerConnectionInterface::RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
o.offer_to_receive_video = webrtc::PeerConnectionInterface::RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
}
peer_connection_->CreateAnswer(this, o);
I'm unable to find anything recent regarding this issue, and it seems like a common use case of the framework to be able to use the received audio within the client application. Any ideas for where I might be making a mistake in listening for inbound audio, or strategies for how I might take to investigate why this is not working?
Many thanks

I've managed to find an alternative approach to getting audio data from WebRTC which allows one to work around this issue.
Implement a custom webrtc::AudioDeviceModule implementation. Look at the webrtc source code to see how one might do this.
Capture the audio transport in the RegisterAudioCallback method, which is invoked when the call is established.
Snippet:
int32_t AudioDevice::RegisterAudioCallback(webrtc::AudioTransport * transport)
{
transport_ = transport;
return 0;
}
Add a custom method to device class for extracting audio from the audio transport using the NeedMorePlayData method. (Note: this seems to work with ntp_time_ms being passed in as 0, seems to not be required).
Snippet:
int32_t AudioDevice::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) const
{
return transport_->NeedMorePlayData(nSamples,
nBytesPerSample,
nChannels,
samplesPerSec,
audioSamples,
nSamplesOut,
elapsed_time_ms,
ntp_time_ms);
}

Related

How to transfer IVectorView<InkStroke> in C++/CX

scenario: I would like to exchange InkStroke between devices running on the same LAN.
Step1: collect all the strokes drawn on the screen from the client:
// Send strokes to a remote server.
strokesToReplay = inkCanvas->InkPresenter->StrokeContainer->GetStrokes();
const int sz = sizeof(strokesToReplay);
char msgToSend[sz];
memcpy(msgToSend, &strokesToReplay, sz);
send(tcpClient.sockfd, msgToSend, sz, 0);
Step2: receive data from the server part:
// tcpServer is an instance of TCPServer, which contains
// a function that calls listen -> accept
// to establish a connection with the client
bytesRecv = tcpServer.recvFromClient();
// Received data was storaged in TCPServer::buffer (char buffer[16384])
What I would like to do is cast the data in the buffer into IVectorView.
So that it is possible to iterate InkStroke from it like:
for (InkStroke^ inkStroke : buffer) {
... to do
}
But here is the question: how can I cast char * to IVectorView?
I've tried memcpy() and static_cast.
But since there is no proper memory allocated in IVectorView, memcpy() will destroy the whole program.
static_cast() won't work with IVectorView.
Now I am thinking about copying the data into the clipboard, then calling the API provided by Microsoft that gets data from the clipboard and cast it into strokes automatically.
But I Do Not Know If This Works Or Not...
Is there any advice that you guys can give me?
Thank you.

Halting an OSC UDP listener function. Xcode iOS application

I'm developing an application that sends OSC data via UDP between a host program and an iPad. Sending from the iPad to the host program was reasonably straight forward. However, receiving data from host program on the iPad has been more troublesome.
I have got to a point where the iPad is RECEIVING the data, but becomes stuck in the .run() packet listener function. It was my understanding that a .break() would interrupt the function and return to my main program, but it doesn't seem to work.
I am more experienced working with objective-c in an iOS environment, so it may be my rudimentary understanding of C++ that is letting me down.
Code below: (I have been using the oscPack library to produce this code. Available Here: http://www.rossbencina.com/code/oscpack)
in my main function (Where the break() points don't appear to be working):
ExamplePacketListener listener;
UdpListeningReceiveSocket s(
IpEndpointName( IpEndpointName::ANY_ADDRESS, RECEIVEPORT ),
&listener );
s.RunUntilSigInt();
//s.Run();
s.Break();
s.AsynchronousBreak();
My packet listener class, very similar to the oscPack example. With some argument modifications. This was another area I felt could be causing a problem. If my osc messages aren't padded correctly, could it stop the run() function from being returned?
class ExamplePacketListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
(void) remoteEndpoint; // suppress unused parameter warning
try{
// example of parsing single messages. osc::OsckPacketListener
// handles the bundle traversal.
if( std::strcmp( m.AddressPattern(), "/oscMasterSend" ) == 0 ){
// example #1 -- argument stream interface
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
bool a1;
args >> a1 >> osc::EndMessage;
NSLog(#"%d",a1);
}
}
}
};
Any advice would be greatly appreciated.
Thanks,
Tom.

Best way to send data using LibEvent

I have a multi-threaded C++ app using lib event. The receive all happens in the libevent process and then a flag is set so that the data received is processed later. On the send tho, things go wrong. I am in a "main thread" and the data to send is assembled and then the following function is invoked.
int SocketWrapper :: SendData( const U8* buffer, int length )
{
if( m_useLibeventToSend )
{
bufferevent* bev = GetBufferEvent();
struct evbuffer* outputBuffer = bufferevent_get_output( bev );
evbuffer_lock( outputBuffer );
int result = evbuffer_add( outputBuffer, buffer, length );
evbuffer_unlock( outputBuffer );
return result;
}
return send( m_socketId, (const char* )buffer, length, 0 );
}
This function crashes on occasion at the point of the evbuffer_add invocation but 99.9% of the time it works fine. This smells like a concurrency bug and it may be related to clients crashing or coming-and-going. I made sure that during the initial creation of the socket by libevent, I did the following:
struct evbuffer* outputBuffer = bufferevent_get_output( GetBufferEvent() );
evbuffer_enable_locking( outputBuffer, NULL );
Do you have any notion of some other special initialization I should be doing? Should I not invoke "SendData" from my main thread and instead send an event to the bufferevent so that the send should happen in the same process as libevent?
All design ideas are open. So far, my workaround is to not use libevent for the send, but to write directly to the socket.
This crash happens in both release and debug, VS 2008, libevent 2.0. It's deep in the library so I will be resorting to including the c files in my project to try and track down my problem, but maybe someone here knows instantly what's wrong. :-)

Sending data with libevent works just sometimes

While developing it's very common that things work or they don't. When sending data from my client to my server it does not work everytime but in most cases it does. I am guessing that probably the kernel don't send the buffer it has stored. Anyways, there have to be a method to work arround this behaviour.
My client have a GUI and it have to communitcate with a server. Because threads don't work as I want them to, I decided to use event_base_loop so that it just blocks until one package is processed. After that it can do GUI stuff so that the window won't freeze.
I am very certain that my sending fails and NOT my reading because my server does not call the my callback for reading ("readcb").
The attached function i am calling from the main function like this:
int main(int argc, char **argv)
{
// init stuff
// connnect to server
sendPacket(bev);
}
I researched a lot about this, but I don't find anything. For example bufferevent_flush(bev, EV_WRITE, BEV_FLUSH) don't works with sockets (i tried it even out).
My current function for writing (in short form, simplified for one package):
void sendPacket(bufferevent * bev)
{
// just data:
const unsigned int msg_ = 12;
char msg[msg_] = "01234567891";
// send that packet:
uint16_t packet_id = 1
bufferevent_write(bev, &packet_id, 2);
bufferevent_write(bev, msg, msg_);
//and this part SHOULD make that data really send but it does not every time:
while (evbuffer_get_length(bufferevent_get_output(bev)) > 0)
{
event_base_loop(bufferevent_get_base(bev), EVLOOP_ONCE);
};
//this last one, only to be really sure (that's why i use the second option):
event_base_loop(bufferevent_get_base(bev), EVLOOP_NONBLOCK | EVLOOP_ONCE);
}
Thanks for your time, I would be lost without your help.

winsock, message oriented networking, and type-casting the buffer from recv

Okay, I actually don't have code as of yet because i'm just picking out a framework for the time being, but i'm still a little baffled about how i wish to go about this :.
Server side, i wish to have a class where each instance has a socket and various information identifying each connection. each object will have it's own thread for receiving data. I understand how i'll be implementing most of that, but my confusion starts just as i get to the actual transfer of data between server and client. I'll want to have a bunch of different message structs for specific cases, (for example CONNECT_MSG , DISCONNECT_MSG, POSTTEXT_MSG, etc) and then all i have to do is have a char * point at that struct and then pass it via the send() function.
But as i think on it, it gets a little complicated at that point. Any of those different message types could be sent, and on the receiving end, you will have no idea what you should cast the incoming buffer as. What i was hoping to do is, in the thread of each connection object, have it block until it receives a packet with a message, then dump it into a single queue object managed by the server(mutexes will prevent greediness) and then the server will process each message in FIFO order independent of the connection objects.
I havn't written anything yet, but let me write a little something to illustrate my setup.
#define CONNECT 1000
struct GENERIC_MESSAGE
{
int id;
}
struct CONNECT_MESSAGE : public GENERIC_MESSAGE
{
m_username;
}
void Connection::Thread()
{
while(1)
{
char buffer[MAX_BUFFER_SIZE]; // some constant(probably 2048)
recv(m_socket, buffer, MAX_BUFFER_SIZE, 0);
MESSAGE_GENERIC * msg = reinterpret_cast<MESSAGE_GENERIC *> (buffer);
server->queueMessage(msg);
}
}
void Server::QueueMessage(MESSAGE_GENERIC * msg)
{
messageQueue.push(msg);
}
void Server::Thread()
{
while(1)
{
if(!messageQueue.empty())
ProcessMessages();
else
Sleep(1);
}
}
void Server::ProcessMessages()
{
for(int i = 0; i < messageQueue.size(); i++)
{
switch(messageQueue.front()->id)
{
case CONNECT:
{
// the part i REALLY don't like
CONNECT_MESSAGE * msg = static_cast<CONNECT_MESSAGE *>(messageQueue.front() );
// do the rest of the processing on connect
break;
}
// other cases for the other message types
}
messageQueue.pop();
}
}
Now if you've been following up until now, you realize just how STUPID and fragile this is. it casts to the base class, passes that pointer to a queue, and then just assumes that the pointer is still valid from the other thread, and even then whether or not the remaining buffer after the pointer for the rest of the derived class will always be valid afterward for casting, but i have yet to find a correct way of doing this. I am wide open for ANY suggestions, either making this work, or an entirely different messaging design.
Before you write even a line of code, design the protocol that will be used on the wired. Decide what a message will consist of at the byte level. Decide who sends first, whether messages are acknowledged, how receivers identify message boundaries, and so on. Decide how the connection will be kept active (if it will be), which side will close first, and so on. Then write the code around the specification.
Do not tightly associate how you store things in memory with how you send things on the wire. These are two very different things with two very different sets of requirements.
Of course, feel free to adjust the protocol specification as you write the code.