EricssonResearch / openwebrtc

A cross-platform WebRTC client framework based on GStreamer
http://www.openwebrtc.org
BSD 2-Clause "Simplified" License
1.8k stars 537 forks source link

All components ready but stream won't start #708

Open SirUriel87 opened 6 years ago

SirUriel87 commented 6 years ago

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;
}

`