Hello,
I'm trying to build a simple sender and receiver using c language and your openwebrtc libraries.
The sender and the receiver exchanges candidates correctly and my video session goes into "ready" state, but my stream won't start. Or at least, i see nothing on the screen where the receiver is running. It seems that no fatal errors occurs judging from libnice-debug output and from bus messages sent by transport_agent and media_session. Can someone help me?
Here is the sender.c code:
`
int write_code(int sock, guint code){
char *conv = (char *)&code;
int len = sizeof(guint);
return write(sock, conv, len);
}
void exchange_candidates(OwrSession *session){
int sock, ret;
gint code;
struct sockaddr_in receiver;
gchar *address, *baseAddress, *foundation, *password, *ufrag, *msg;
guint basePort, port, priority, compType, transportType, type;
GList *candidates, *elem;
// Create client socket for signaling
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0){
printf("Error in creating socket: [%d]:[%s]", errno, strerror(errno));
return;
}
receiver.sin_family = AF_INET;
receiver.sin_port = htons(RCV_SOCK_PORT);
receiver.sin_addr.s_addr = inet_addr(remote_addr);
if (connect(sock, (struct sockaddr *) &receiver, sizeof(struct sockaddr_in)) < 0 ){
g_warning("Error while trying to connect to receiver: [%d]:[%s]", errno, strerror(errno));
return;
}
msg = malloc(sizeof(char) * MSG_MAX_LEN);
// Wait until receiver has accepted our connection
if (read(sock, &code, sizeof(guint)) < 0){
g_warning("Error while reading from socket: [%d]:[%s]", errno, strerror(errno));
return;
}
if (code != ACCEPTED){
printf("Socket: Wrong message sequence!\n");
return;
}
printf("Connection accepted\n");
ret = 1;
candidates = g_object_steal_data(G_OBJECT(session), "local-candidates");
for (elem = candidates; elem; elem = elem->next){
int len;
OwrCandidate *cand = elem->data;
print_candidate_info(cand, "CAND:\n");
g_object_get(G_OBJECT(cand), "address", &address, "base-address", &baseAddress,
"base-port", &basePort, "component-type", &compType, "foundation", &foundation,
"password", &password, "port", &port, "priority", &priority, "transport-type", &transportType,
"type", &type, "ufrag", &ufrag, NULL);
// using write/gets functions, so need to be careful about buffers size
/** every candidate information message begin with a number indicating in wich field we want to store
* the value that follows. when reading, we proceed to the next candidate when we encounter the
* "END" value integer. if a field is a string, we first send the buffer size, then the string.
*
* here we duplicate some code for the purpose of a more readable one.
* if at least one of the reads returns -1 (error), ret will be 0.
* Send Component type and CandidateType because they are needed to initialize the candidate
**/
ret = ret * 1+write_code(sock, COMPONENT_TYPE);
ret = ret * 1+write_code(sock, compType);
ret = ret * 1+write_code(sock, TYPE);
ret = ret * 1+write_code(sock, type);
ret = ret * 1+write_code(sock, ADDRESS);
len = strlen(address) * sizeof(char) + 1;
ret = ret * 1+write_code(sock, len);
ret = ret * 1+write(sock, address, len);
ret = ret * 1+write_code(sock, BASE_ADDRESS);
len = strlen(baseAddress) * sizeof(char) + 1;
ret = ret * 1+write_code(sock, len);
ret = ret * 1+write(sock, baseAddress, len);
ret = ret * 1+write_code(sock, BASE_PORT);
ret = ret * 1+write(sock, &basePort, sizeof(guint));
ret = ret * 1+write_code(sock, FOUNDATION);
len = strlen(foundation) * sizeof(char) + 1;
ret = ret * 1+write_code(sock, len);
ret = ret * 1+write(sock, foundation, len);
ret = ret * 1+write_code(sock, PASSWORD);
len = strlen(password) * sizeof(char) + 1;
ret = ret * 1+write_code(sock, len);
ret = ret * 1+write(sock, password, len);
ret = ret * 1+write_code(sock, PORT);
ret = ret * 1+write_code(sock, port);
ret = ret * 1+write_code(sock, PRIORITY);
ret = ret * 1+write_code(sock, priority);
ret = ret * 1+write_code(sock, TRANSPORT_TYPE);
ret = ret * 1+write_code(sock, transportType);
ret = ret * 1+write_code(sock, UFRAG);
len = strlen(ufrag) * sizeof(char) + 1;
ret = ret * 1+write_code(sock, len);
ret = ret * 1+write(sock, ufrag, len);
ret = ret * 1+write_code(sock, END);
}
ret = ret * 1+write_code(sock, END_OF_CANDIDATES);
printf("End of candidates\n");
if (ret <= 0){
printf("Error while writing on socket: [%d]:[%s]", errno, strerror(errno));
return;
}
ret = 1;
// Read receiver's candidates
ret = ret * 1+read(sock, &code, sizeof(guint));
while(code != END_OF_CANDIDATES){
// initialize candidate with default values. eventually those values will be replaced
OwrCandidate *cand = NULL;
guint len;
gboolean hasCT = FALSE, hasT = FALSE;
OwrCandidateType t;
OwrComponentType ct;
OwrTransportType tt;
// wait until we have component_type and candidate_type for initializing candidate.
while (!cand){
if (code == COMPONENT_TYPE){
ret = ret * 1+read(sock, &compType, sizeof(guint));
ct = compType;
hasCT = TRUE;
}
if (code == TYPE){
ret = ret * 1+read(sock, &type, sizeof(guint));
t = type;
hasT = TRUE;
}
if (hasCT & hasT)
cand = owr_candidate_new(t, ct);
// read next code
ret = ret * 1+read(sock, &code, sizeof(guint));
}
while (code != END){
switch (code){
case ADDRESS:
ret = ret * 1+read(sock, &len, sizeof(guint));
ret = ret * 1+read(sock, address, len);
g_object_set(cand, "address", address, NULL);
break;
case BASE_ADDRESS:
ret = ret * 1+read(sock, &len, sizeof(guint));
ret = ret * 1+read(sock, baseAddress, len);
g_object_set(cand, "base-address", baseAddress, NULL);
break;
case BASE_PORT:
ret = ret * 1+read(sock, &basePort, sizeof(guint));
g_object_set(cand, "base-port", basePort, NULL);
break;
case FOUNDATION:
ret = ret * 1+read(sock, &len, sizeof(guint));
ret = ret * 1+read(sock, foundation, len);
g_object_set(cand, "foundation", foundation, NULL);
break;
case PASSWORD:
ret = ret * 1+read(sock, &len, sizeof(guint));
ret = ret * 1+read(sock, password, len);
g_object_set(cand, "password", password, NULL);
break;
case PORT:
ret = ret * 1+read(sock, &port, sizeof(guint));
g_object_set(cand, "port", port, NULL);
break;
case PRIORITY:
ret = ret * 1+read(sock, &priority, sizeof(guint));
g_object_set(cand, "priority", priority, NULL);
break;
case TRANSPORT_TYPE:
ret = ret * 1+read(sock, &transportType, sizeof(guint));
tt = transportType;
g_object_set(cand, "transport-type", tt, NULL);
break;
case UFRAG:
ret = ret * 1+read(sock, &len, sizeof(guint));
ret = ret * 1+read(sock, ufrag, len);
g_object_set(cand, "ufrag", ufrag, NULL);
break;
}
// read next code
ret = ret * 1+read(sock, &code, sizeof(guint));
}// End when code == END
owr_session_add_remote_candidate(session, cand);
print_candidate_info(cand, "RECEIVED CANDIDATE:\n");
// read code of the next candidate (or end of stream)
ret = ret * 1+read(sock, &code, sizeof(guint));
}// End when code == END_OF_CANDIDATES
if (ret <= 0){
printf("Error while writing on socket: [%d]:[%s]", errno, strerror(errno));
return;
}
printf("All candidates received successfully\n");
// TODO: rendere un po' piu' "guardabile" il modo in cui si gestiscono gli errori tramite la variabile ret
close(sock);
free(msg);
return;
}
static void got_candidate(OwrMediaSession *sessionA, OwrCandidate *candidate, gpointer data){
GList *localCandidates;
//print_candidate_info(candidate, "Got candidate");
//force_candidate_port(candidate);
localCandidates = g_object_get_data(G_OBJECT(sessionA), "local-candidates");
localCandidates = g_list_append(localCandidates, candidate);
g_object_set_data(G_OBJECT(sessionA), "local-candidates", localCandidates);
}
static void gathering_done(OwrSession *session, gpointer data){
g_object_set_data(G_OBJECT(session), "gathering-done", GUINT_TO_POINTER(1));
exchange_candidates(session);
}
void print_source_info(OwrMediaSource *src){
OwrMediaType mediaType;
gchar *name = NULL;
OwrSourceType sourceType;
g_object_get(src, "name", &name, "type", &sourceType, "media-type", &mediaType, NULL);
g_print("[%s/%s] %s\n", mediaType == OWR_MEDIA_TYPE_AUDIO ? "audio" : "video",
sourceType == OWR_SOURCE_TYPE_CAPTURE ? "capture" : sourceType == OWR_SOURCE_TYPE_TEST ? "test" : "unknown",
name);
}
/**
void force_candidates(OwrSession *mediaSession){
GList *candidates;
OwrCandidate *remote_rtp, *remote_rtcp;
// Setting up RTP and RTCP candidates
remote_rtp = owr_candidate_new(OWR_CANDIDATE_TYPE_HOST, OWR_COMPONENT_TYPE_RTP);
g_object_set(remote_rtp, "address", remote_addr, "password", stun_pass,
"port", RCV_PORT_RTP, "ufrag", remote_addr, NULL);
remote_rtcp = owr_candidate_new(OWR_CANDIDATE_TYPE_HOST, OWR_COMPONENT_TYPE_RTCP);
g_object_set(remote_rtcp, "address", remote_addr, "password", stun_pass,
"port", RCV_PORT_RTCP, "ufrag", remote_addr, NULL);
owr_session_add_remote_candidate(mediaSession, remote_rtp);
owr_session_add_remote_candidate(mediaSession, remote_rtcp);
// Forcing candidates pairs
for (candidates = g_object_get_data(G_OBJECT(mediaSession), "local-candidates"); candidates;
candidates = g_list_next(candidates)){
OwrCandidate *localCnd = candidates->data, *remoteCnd;
OwrComponentType ctype;
guint port = 0, rport = 0;
gchar *r_addr;
g_object_get(localCnd, "port", &port, "component-type", &ctype, NULL);
// +++++ printing pairing info
if (ctype == OWR_COMPONENT_TYPE_RTP) type = "RTP";
else if (ctype == OWR_COMPONENT_TYPE_RTCP) type = "RTCP";
else type = "Unknown";
g_object_get(G_OBJECT(localCnd), "address", &l_addr, NULL);
g_object_get(G_OBJECT(remote_rtp), "address", &r_addr, "port", &rport, NULL);
g_object_get(G_OBJECT(remote_rtcp), "address", &r_addrc, "port", &rcport, NULL);
printf("trying to pair:\nlocal %s = %s:%d\nremoteRTP= %s:%d\nremoteRTCP= %s:%d\n\n",
type, l_addr, port, r_addr, rport, r_addrc, rcport);
// ----- finished printing pairing info
if (port == SEND_PORT_RTP && ctype == OWR_COMPONENT_TYPE_RTP)
remoteCnd = remote_rtp;
else if (port == SEND_PORT_RTCP && ctype == OWR_COMPONENT_TYPE_RTCP)
remoteCnd = remote_rtcp;
else continue; //TODO: trovare il modo di fare a meno di questo continue
g_object_get(G_OBJECT(remoteCnd), "address", &r_addr, "port", &rport, NULL);
owr_session_force_candidate_pair(mediaSession, ctype, localCnd, remoteCnd);
}
}
**/
static void got_sources(GList *sourcesList, gpointer data){
OwrMediaSource *src;
OwrVideoRenderer *renderer;
gboolean haveVideo = FALSE;
g_assert(sourcesList);
while (sourcesList && (src = sourcesList->data)){
OwrMediaType mediaType;
g_assert(OWR_IS_MEDIA_SOURCE(src));
print_source_info(src);
g_object_get(src, "media-type", &mediaType, NULL);
if (!haveVideo && mediaType == OWR_MEDIA_TYPE_VIDEO){
haveVideo = TRUE;
// preparing the payload for sending
pl = owr_video_payload_new(codec_type, 103, 8000, TRUE, FALSE);
g_object_set(pl, "width", WIDTH, "height", HEIGHT, "framerate", FRATE, NULL);
// Setting up session and transport agent
owr_media_session_set_send_payload(videoSession, pl);
owr_media_session_set_send_source(videoSession, src);
//owr_transport_agent_add_session(sendingAgent, OWR_SESSION(videoSession));
// show in self view
renderer = owr_video_renderer_new(NULL);
g_assert(renderer);
g_object_set(renderer, "width", WIDTH, "height", HEIGHT, "max-framerate", FRATE, NULL);
owr_media_renderer_set_source(OWR_MEDIA_RENDERER(renderer), src);
//videoRenderer = OWR_MEDIA_RENDERER(renderer);
// If a reference to the video source is needed,uncomment this(and declare variable videoSource)
//videoSource = g_object_ref(src);
}
sourcesList = sourcesList->next;
}
}
int main(int argc, char **argv){
GList *sessionList;
owr_init(NULL);
// creating the bus for message handling (not used for now)
bus = owr_bus_new();
owr_bus_set_message_callback(bus, (OwrBusMessageCallback)bus_message_cb, translate_message_origin, NULL);
// Setting up Sending Transport Agent
sendingAgent = owr_transport_agent_new(TRUE);
g_assert(OWR_IS_TRANSPORT_AGENT(sendingAgent));
//owr_transport_agent_set_local_port_range(sendingAgent, PORT_MIN, PORT_MAX);
owr_transport_agent_add_local_address(sendingAgent, local_addr);
owr_transport_agent_add_helper_server(sendingAgent, OWR_HELPER_SERVER_TYPE_STUN,
stunServer, stunPort, NULL, NULL);
//Uncomment if you want a TURN server as well
owr_transport_agent_add_helper_server(sendingAgent, OWR_HELPER_SERVER_TYPE_TURN_UDP,
"192.158.29.39", 3478, "28224511:1379330808", "JZEOEt2V3Qb0y27GRntt2u2PAYA=");
// Creating video session
videoSession = owr_media_session_new(USE_DTLS);
sessionList = g_object_get_data(G_OBJECT(videoSession), "media-sessions");
sessionList = g_list_append(sessionList, videoSession);
g_object_set_data(G_OBJECT(videoSession), "media-sessions", sessionList);
owr_transport_agent_add_session(sendingAgent, OWR_SESSION(videoSession));
//force_candidates();
// adding message origins to bus
owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(sendingAgent));
owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(videoSession));
// Candidate gathering
g_signal_connect(videoSession, "on-new-candidate", G_CALLBACK(got_candidate), NULL);
g_signal_connect(videoSession, "on-candidate-gathering-done", G_CALLBACK(gathering_done), NULL);
// Capturing video source with got_sources Callback
owr_get_capture_sources(OWR_MEDIA_TYPE_VIDEO, got_sources, NULL);
owr_run();
g_free(bus);
g_free(sendingAgent);
g_free(videoSession);
return 0;
}
Hello, I'm trying to build a simple sender and receiver using c language and your openwebrtc libraries. The sender and the receiver exchanges candidates correctly and my video session goes into "ready" state, but my stream won't start. Or at least, i see nothing on
the
screen where the receiver is running. It seems that no fatal errors occurs judging from libnice-debug output and from bus messages sent by transport_agent and media_session. Can someone help me?Here is the sender.c code: `
`