I encountered a problem with android webrtc in version 1.22, can you help me solve it?
Which of those are you building libnice against, libnice doesn’t currently use the GIO abstraction. So it shouldn’t influence how the peer reflexive candidates are resolved there. Maybe this somehow breaks the DNS resolution? There are so many paramters, you’ll have to look at the logs of webrtcbin to see if it sets the right STUN server, and then look at the logs of libnice ot see whats up with the STUN server.
Thanks for your careful reply, I have a general understanding of the situation.
- The demo is currently developed with flutter, including the ios platform, and currently android/ios is developed with flutter ffi and c without native code.
c code to implement gstreamer webrtc flutter ffi then go to call.
Currently, gst1.22.7 on ios is normal, gst1.16.3 on android is normal, and gst1.22.7 to 1.22.10 on android is abnormal. You can be sure that stun is correctly configured. - If the Logcat logs are normal, how do I view the libnice logs?Or there is no high version of android gstreamer webrtc I refer to solve.
- The next reply comes with the source code.
1.22.10-Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := tutorial-5
LOCAL_SRC_FILES := ../../../../ios/Classes/app_rtc.c dummy.cpp
LOCAL_SHARED_LIBRARIES := gstreamer_android
LOCAL_LDLIBS := -llog -landroid
include $(BUILD_SHARED_LIBRARY)
GSTREAMER_ROOT_ANDROID := /Users/teihideharu/workspace/sdk/gstreamer-1.0
ifndef GSTREAMER_ROOT_ANDROID
$(error GSTREAMER_ROOT_ANDROID is not defined!)
endif
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
GSTREAMER_ROOT := $(GSTREAMER_ROOT_ANDROID)/arm64
else
$(error Target arch ABI not supported: $(TARGET_ARCH_ABI))
endif
GSTREAMER_NDK_BUILD_PATH := $(GSTREAMER_ROOT)/share/gst-android/ndk-build/
include $(GSTREAMER_NDK_BUILD_PATH)/plugins.mk
GSTREAMER_PLUGINS := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_SYS) $(GSTREAMER_PLUGINS_NET) $(GSTREAMER_PLUGINS_ENCODING) $(GSTREAMER_PLUGINS_PLAYBACK) $(GSTREAMER_PLUGINS_GES) $(GSTREAMER_PLUGINS_VIS) $(GSTREAMER_PLUGINS_CODECS_RESTRICTED) $(GSTREAMER_PLUGINS_EFFECTS)
GSTREAMER_EXTRA_DEPS := openssl gstreamer-1.0 gio-2.0 gstreamer-webrtc-1.0 gstreamer-sdp-1.0 gstreamer-video-1.0 libsoup-2.4 json-glib-1.0 glib-2.0 gstreamer-app-1.0 gstreamer-webrtc-nice-1.0
G_IO_MODULES := openssl
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk
1.16.3-Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := tutorial-5
LOCAL_SRC_FILES := ../../../../ios/Classes/app_rtc.c dummy.cpp
LOCAL_SHARED_LIBRARIES := gstreamer_android
LOCAL_LDLIBS := -llog -landroid
include $(BUILD_SHARED_LIBRARY)
GSTREAMER_ROOT_ANDROID := /Users/teihideharu/workspace/sdk/gstreamer-1.0
ifndef GSTREAMER_ROOT_ANDROID
$(error GSTREAMER_ROOT_ANDROID is not defined!)
endif
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
GSTREAMER_ROOT := $(GSTREAMER_ROOT_ANDROID)/arm64-1.16
else
$(error Target arch ABI not supported: $(TARGET_ARCH_ABI))
endif
GSTREAMER_NDK_BUILD_PATH := $(GSTREAMER_ROOT)/share/gst-android/ndk-build/
include $(GSTREAMER_NDK_BUILD_PATH)/plugins.mk
GSTREAMER_PLUGINS := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_SYS) $(GSTREAMER_PLUGINS_NET) $(GSTREAMER_PLUGINS_ENCODING) $(GSTREAMER_PLUGINS_PLAYBACK) $(GSTREAMER_PLUGINS_GES) $(GSTREAMER_PLUGINS_VIS) $(GSTREAMER_PLUGINS_CODECS_RESTRICTED) $(GSTREAMER_PLUGINS_EFFECTS)
GSTREAMER_EXTRA_DEPS := gstreamer-webrtc-1.0 gstreamer-sdp-1.0 gstreamer-video-1.0 libsoup-2.4 json-glib-1.0 glib-2.0 gstreamer-app-1.0
G_IO_MODULES := gnutls
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk
Core code:
static void add_fec_to_offer(GstElement* webrtc){
GstWebRTCRTPTransceiver *trans=NULL;
g_signal_emit_by_name(webrtc,"get-transceiver",0,&trans);
g_object_set(trans,"fec-type",GST_WEBRTC_FEC_TYPE_ULP_RED,
"fec-percentage",25,"do-nack",FALSE,NULL);
}
#define RTP_CAPS_OPUS "application/x-rtp,media=audio,encoding-name=OPUS,payload=97"
#define RTP_CAPS_VP8 "application/x-rtp,media=video,encoding-name=VP8,payload=96"
#define TURN_URL "turn-server=turns://gstreamer@webrtc.gstreamer.net "
//#define TURN_URL "turn-server=turn://user:123456@74.48.95.60:3478 "
static gboolean start_pipeline(AppRTC* apprtc){
GstStateChangeReturn ret;
GError *error=NULL;
GstPad *pad;
apprtc->pipe=gst_parse_launch(
"webrtcbin name=sendrecv bundle-policy=max-bundle " TURN_URL
"videotestsrc name=videosrc ! queue ! videoconvert ! "
"vp8enc ! rtpvp8pay "
" ! queue ! "RTP_CAPS_VP8" ! sendrecv.sink_0 "
//"audiotestsrc ! queue ! audioconvert ! audioresample ! audiorate ! opusenc ! rtpopuspay ! "
//""RTP_CAPS_OPUS" ! sendrecv.sink_1 "
,&error);
if(error){
g_printerr("Failed to parse launch %s\n",error->message);
g_error_free(error);
goto err;
}
apprtc->webrtcbin=gst_bin_get_by_name(GST_BIN(apprtc->pipe),"sendrecv");
g_assert(apprtc->webrtcbin!=NULL);
add_fec_to_offer(apprtc->webrtcbin);
g_object_set(apprtc->webrtcbin,
"stun-server", "stun://stun.l.google.com:19302",
NULL);
g_signal_connect(apprtc->webrtcbin,"on-negotiation-needed",
G_CALLBACK(on_negotiation_needed),apprtc);
g_signal_connect(apprtc->webrtcbin,"on-ice-candidate",G_CALLBACK(send_ice_candidate_message),apprtc);
g_signal_connect(apprtc->webrtcbin,"pad-added",G_CALLBACK(on_incoming_stream),apprtc);
g_signal_connect(apprtc->webrtcbin,"notify::ice-connection-state",G_CALLBACK(_on_ice_connection_state_notify),NULL);
gst_object_unref(apprtc->webrtcbin);
g_print("Staring pipeline\n");
ret=gst_element_set_state(GST_ELEMENT(apprtc->pipe),GST_STATE_PLAYING);
if(ret==GST_STATE_CHANGE_FAILURE)
goto err;
return TRUE;
err:
if(apprtc->pipe)
g_clear_object(&apprtc->pipe);
if(apprtc->webrtcbin)
apprtc->webrtcbin=NULL;
return FALSE;
}
Full code:
#include<string.h>
#include<stdint.h>
#include<gst/video/videooverlay.h>
#define GST_USE_UNSTABLE_API
#include<gst/webrtc/webrtc.h>
#include<libsoup/soup.h>
#include<gst/video/video.h>
#include<json-glib/json-glib.h>
#include <gst/app/app.h>
GST_DEBUG_CATEGORY_STATIC (debug_category);
#define GST_CAT_DEFAULT debug_category
//#define DEFAULT_SIGNALLING_SERVER "ws://192.168.3.95:8443"
#define DEFAULT_SIGNALLING_SERVER "wss://webrtc.nirbheek.in:8443"
typedef struct _AppRtc{
GstElement* pipe;
GThread* thread;
GMainLoop *loop;
GMutex *lock;
GCond cond;
int is_send;
char* offer;
SoupWebsocketConnection *ws_conn;
GstElement* webrtcbin,*video_sink;
GstElement* video_src,*audio_src;
} AppRTC;
typedef struct _VideoFrame{
guint8* data;
int size;
int width;
int height;
} VideoFrame;
AppRTC* APPRTC;
static VideoFrame* _video_frame=NULL;
static char* _message=NULL;
static void (* listener)(guint8*,int,int,int);
static void (* message_listener)(char*);
static void send_message_to_ui(char* message){
if(_message!=NULL){
g_free(_message);
_message=NULL;
}
_message=message;
g_print("send_message:%s\n",_message);
//while(_message!=NULL){
//}
}
static void send_video_frame_to_ui(VideoFrame* video_frame){
if(_video_frame!=NULL){
//g_free(video_frame->data);
g_free(_video_frame);
_video_frame=NULL;
}
g_free(video_frame);
//_video_frame=video_frame;
//while(_video_frame!=NULL){
//}
}
//ffi
void apprtc_loop(){
if(_video_frame!=NULL){
if(listener!=NULL){
listener(_video_frame->data,_video_frame->size,_video_frame->width,_video_frame->height);
}
g_free(_video_frame);
_video_frame=NULL;
}
if(_message!=NULL){
g_print("message:%s\n",_message);
if(message_listener!=NULL){
g_print("message_listener!=null\n");
message_listener(_message);
}
g_free(_message);
_message=NULL;
}
}
static gchar*
get_string_from_json_object (JsonObject * object)
{
JsonNode *root;
JsonGenerator *generator;
gchar *text;
/* Make it the root node */
root = json_node_init_object (json_node_alloc (), object);
generator = json_generator_new ();
json_generator_set_root (generator, root);
text = json_generator_to_data (generator, NULL);
/* Release everything */
g_object_unref (generator);
json_node_free (root);
return text;
}
static gboolean cleanup_and_quit_loop(AppRTC* apprtc,gchar* msg){
if(msg)
g_printerr("%s\n",msg);
if(apprtc->ws_conn){
if(soup_websocket_connection_get_state(apprtc->ws_conn)==SOUP_WEBSOCKET_STATE_OPEN)
soup_websocket_connection_close(apprtc->ws_conn,1000,"");
else
g_object_unref(apprtc->ws_conn);
}
if(apprtc->loop){
g_main_loop_quit(apprtc->loop);
apprtc->loop=NULL;
}
if(apprtc->pipe){
gst_element_set_state(apprtc->pipe,GST_STATE_NULL);
gst_object_unref(apprtc->pipe);
apprtc->pipe=NULL;
}
return G_SOURCE_REMOVE;
}
static void send_sdp(AppRTC* apprtc,GstWebRTCSessionDescription* answer,int is_offer){
//g_print("ssend_sdp\n");
gchar* text,*type;
JsonObject *msg,*sdp;
text= gst_sdp_message_as_text(answer->sdp);
if(is_offer==1)
type="offer";
else
type="answer";
sdp=json_object_new();
json_object_set_string_member(sdp,"sdp",text);
json_object_set_string_member(sdp,"type",type);
g_free(text);
msg=json_object_new();
json_object_set_object_member(msg,"sdp",sdp);
text=get_string_from_json_object(msg);
json_object_unref(msg);
g_print("send-answer:%s\n",text);
soup_websocket_connection_send_text(apprtc->ws_conn,text);
g_free(text);
}
static void on_answer_created(GstPromise* promise,void* rtc){
g_print("on_answer_created\n");
AppRTC* apprtc=(AppRTC*) rtc;
GstWebRTCSessionDescription* answer=NULL;
const GstStructure* reply;
gst_promise_wait(promise);
reply=gst_promise_get_reply(promise);
gst_structure_get(reply,"answer",GST_TYPE_WEBRTC_SESSION_DESCRIPTION,&answer,NULL);
gst_promise_unref(promise);
promise=gst_promise_new();
g_signal_emit_by_name(apprtc->webrtcbin,"set-local-description",answer,promise);
gst_promise_interrupt(promise);
gst_promise_unref(promise);
send_sdp(apprtc,answer,0);
gst_webrtc_session_description_free(answer);
}
static void on_offer_set(GstPromise* promise,void* rtc){
g_print("create-answer\n");
gst_promise_unref(promise);
AppRTC* apprtc=(AppRTC*) rtc;
promise=gst_promise_new_with_change_func(on_answer_created,apprtc,NULL);
g_signal_emit_by_name(apprtc->webrtcbin,"create-answer",NULL,promise);
//gst_promise_unref(promise);
}
static void set_offer_and_create_answer(AppRTC* apprtc){
if(apprtc->offer==NULL) return;
int ret;
GstSDPMessage* sdp;
ret=gst_sdp_message_new(&sdp);
g_assert(ret==GST_SDP_OK);
ret=gst_sdp_message_parse_buffer((guint8 *)apprtc->offer,strlen(apprtc->offer),sdp);
g_assert(ret==GST_SDP_OK);
GstWebRTCSessionDescription *offer;
offer=gst_webrtc_session_description_new(GST_WEBRTC_SDP_TYPE_OFFER,sdp);
g_assert(offer);
{
GstPromise* promise=gst_promise_new_with_change_func(on_offer_set,apprtc,NULL);
g_signal_emit_by_name(apprtc->webrtcbin,"set-remote-description",offer,promise);
//gst_promise_interrupt(promise);
//gst_promise_unref(promise);
}
gst_webrtc_session_description_free(offer);
g_free(apprtc->offer);
apprtc->offer=NULL;
}
static GstFlowReturn sink_on_sample(GstAppSink* appsink,gpointer data){
GstSample* sample=gst_app_sink_pull_sample(appsink);
if(sample){
GstCaps* caps=gst_sample_get_caps(sample);
GstStructure* info=gst_caps_get_structure(caps,0);
GstBuffer* buffer=gst_sample_get_buffer(sample);
GstMapInfo map_info;
gst_buffer_map(buffer,&map_info,(GstMapFlags)(GST_MAP_READ));
int width;
int height;
guint8* data=map_info.data;
gsize size=map_info.size;
gst_structure_get_int(info,"width",&width);
gst_structure_get_int(info,"height",&height);
VideoFrame* frame=malloc(sizeof(VideoFrame));
frame->data=data;
frame->size=size;
frame->width=width;
frame->height=height;
send_video_frame_to_ui(frame);
gst_buffer_unmap(buffer,&map_info);
gst_sample_unref(sample);
}
return GST_FLOW_OK;
}
static GstFlowReturn sink_on_preroll(GstAppSink* element,gpointer data){
g_printerr("player_on_preroll");
return GST_FLOW_OK;
}
static GstElement* handle_media_stream(GstPad* pad,GstElement* pipe,const char* convert_name,const char* sink_name){
GstPad* qpad;
GstElement *q,*conv,*sink;
GstPadLinkReturn ret;
q=gst_element_factory_make("queue",NULL);
g_assert(q);
conv=gst_element_factory_make(convert_name,NULL);
g_assert(conv);
sink=gst_element_factory_make(sink_name,NULL);
g_assert(sink);
if(g_strcmp0(convert_name,"audioconvert")==0){
GstElement* resample=gst_element_factory_make("audioresample",NULL);
g_assert_nonnull (resample);
gst_bin_add_many(GST_BIN(pipe),q,conv,resample,sink,NULL);
gst_element_sync_state_with_parent(q);
gst_element_sync_state_with_parent(conv);
gst_element_sync_state_with_parent(resample);
gst_element_sync_state_with_parent(sink);
gst_element_link_many(q,conv,resample,sink,NULL);
}else{
GstElement* filter;
GstCaps* caps;
filter=gst_element_factory_make("capsfilter","filter");
caps=gst_caps_new_simple("video/x-raw","format",G_TYPE_STRING,"RGBA",NULL);
g_object_set(G_OBJECT(filter),"caps",caps,NULL);
gst_caps_unref(caps);
gst_bin_add_many(GST_BIN(pipe),q,conv,filter,sink,NULL);
gst_element_sync_state_with_parent(q);
gst_element_sync_state_with_parent(conv);
gst_element_sync_state_with_parent(filter);
gst_element_sync_state_with_parent(sink);
gst_element_link_many(q,conv,filter,sink,NULL);
}
qpad=gst_element_get_static_pad(q,"sink");
ret=gst_pad_link(pad,qpad);
g_assert(ret==GST_PAD_LINK_OK);
gst_object_unref(qpad);
return sink;
}
static void on_incoming_decodebin_stream(GstElement* decodebin,GstPad* pad,AppRTC* apprtc){
GstCaps* caps;
const gchar* name;
if(!gst_pad_has_current_caps(pad)){
g_print("Pad %s has no caps\n",GST_PAD_NAME (pad));
return;
}
caps=gst_pad_get_current_caps(pad);
name=gst_structure_get_name(gst_caps_get_structure(caps,0));
g_print("on_incoming_decodebin_stream:%s\n",name);
if(g_str_has_prefix(name,"video")){
GstElement* sink=handle_media_stream(pad,apprtc->pipe,"videoconvert","appsink");
apprtc->video_sink=sink;
GstAppSinkCallbacks callbacks={NULL,sink_on_preroll,sink_on_sample};
gst_app_sink_set_callbacks (GST_APP_SINK(sink),&callbacks,NULL,NULL);
}else if(g_str_has_prefix(name,"audio")){
handle_media_stream(pad,apprtc->pipe,"audioconvert","autoaudiosink");
}else{
g_printerr ("Unknown pad %s, ignoring", GST_PAD_NAME (pad));
}
gst_caps_unref(caps);
}
static void on_incoming_stream(GstElement* webrtcbin,GstPad* pad,AppRTC* apprtc){
GstElement* decodebin;
if(GST_PAD_DIRECTION(pad)!=GST_PAD_SRC)
return;
decodebin=gst_element_factory_make("decodebin",NULL);
g_signal_connect(decodebin,"pad-added",
G_CALLBACK(on_incoming_decodebin_stream),apprtc);
gst_bin_add(GST_BIN(apprtc->pipe),decodebin);
gst_element_sync_state_with_parent(decodebin);
gst_element_link(webrtcbin,decodebin);
}
static void send_ice_candidate_message(GstElement* webrtcbin G_GNUC_UNUSED,guint mlineindex,gchar* candidate,AppRTC* apprtc){
gchar* text;
JsonObject* ice,*msg;
ice=json_object_new();
json_object_set_string_member(ice,"candidate",candidate);
json_object_set_int_member(ice,"sdpMLineIndex",mlineindex);
msg=json_object_new();
json_object_set_object_member(msg,"ice",ice);
text=get_string_from_json_object(msg);
json_object_unref(msg);
g_print("send_ice:%s\n",candidate);
soup_websocket_connection_send_text(apprtc->ws_conn,text);
g_free(text);
}
static void on_offer_created(GstPromise* promise,AppRTC* apprtc){
GstWebRTCSessionDescription* offer;
const GstStructure* reply;
g_assert(gst_promise_wait(promise)==GST_PROMISE_RESULT_REPLIED);
reply=gst_promise_get_reply(promise);
gst_structure_get(reply,"offer",GST_TYPE_WEBRTC_SESSION_DESCRIPTION,&offer,NULL);
gst_promise_unref(promise);
promise=gst_promise_new();
g_signal_emit_by_name(apprtc->webrtcbin,"set-local-description",offer,promise);
gst_promise_interrupt(promise);
gst_promise_unref(promise);
send_sdp(apprtc,offer,1);
gst_webrtc_session_description_free(offer);
}
static void on_negotiation_needed(GstElement* element,AppRTC* apprtc){
if(apprtc->is_send==1){
gchar *msg = g_strdup_printf ("OFFER_REQUEST");
soup_websocket_connection_send_text (apprtc->ws_conn, msg);
g_free (msg);
return;
}
GstPromise* promise;
promise=gst_promise_new_with_change_func(on_offer_created,apprtc,NULL);
g_signal_emit_by_name(apprtc->webrtcbin,"create-offer",NULL,promise);
}
static void _on_ice_connection_state_notify(GstElement* webrtcbin,GParamSpec* pspec,gpointer webrtc){
GstWebRTCICEConnectionState state;
g_object_get(APPRTC->webrtcbin,"ice-connection-state",&state,NULL);
switch(state){
case GST_WEBRTC_ICE_CONNECTION_STATE_NEW:
g_print("ice-connection-state:new\n");
break;
case GST_WEBRTC_ICE_CONNECTION_STATE_CHECKING:
g_print("ice-connection-state:checking\n");
break;
case GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED:
g_print("ice-connection-state:connected\n");
break;
case GST_WEBRTC_ICE_CONNECTION_STATE_COMPLETED:
g_print("ice-connection-state:COMPLETED\n");
break;
case GST_WEBRTC_ICE_CONNECTION_STATE_FAILED:
g_print("ice-connection-state:FAILED\n");
break;
case GST_WEBRTC_ICE_CONNECTION_STATE_DISCONNECTED:
g_print("ice-connection-state:DISCONNECTED\n");
break;
}
}
static void add_fec_to_offer(GstElement* webrtc){
GstWebRTCRTPTransceiver *trans=NULL;
g_signal_emit_by_name(webrtc,"get-transceiver",0,&trans);
g_object_set(trans,"fec-type",GST_WEBRTC_FEC_TYPE_ULP_RED,
"fec-percentage",25,"do-nack",FALSE,NULL);
}
#define RTP_CAPS_OPUS "application/x-rtp,media=audio,encoding-name=OPUS,payload=97"
#define RTP_CAPS_VP8 "application/x-rtp,media=video,encoding-name=VP8,payload=96"
#define TURN_URL "turn-server=turns://gstreamer@webrtc.gstreamer.net "
//#define TURN_URL "turn-server=turn://user:123456@74.48.95.60:3478 "
static gboolean start_pipeline(AppRTC* apprtc){
GstStateChangeReturn ret;
GError *error=NULL;
GstPad *pad;
apprtc->pipe=gst_parse_launch(
"webrtcbin name=sendrecv bundle-policy=max-bundle " TURN_URL
"videotestsrc name=videosrc ! queue ! videoconvert ! "
"vp8enc ! rtpvp8pay "
" ! queue ! "RTP_CAPS_VP8" ! sendrecv.sink_0 "
//"audiotestsrc ! queue ! audioconvert ! audioresample ! audiorate ! opusenc ! rtpopuspay ! "
//""RTP_CAPS_OPUS" ! sendrecv.sink_1 "
,&error);
if(error){
g_printerr("Failed to parse launch %s\n",error->message);
g_error_free(error);
goto err;
}
apprtc->webrtcbin=gst_bin_get_by_name(GST_BIN(apprtc->pipe),"sendrecv");
g_assert(apprtc->webrtcbin!=NULL);
add_fec_to_offer(apprtc->webrtcbin);
g_object_set(apprtc->webrtcbin,
"stun-server", "stun://stun.l.google.com:19302",
NULL);
g_signal_connect(apprtc->webrtcbin,"on-negotiation-needed",
G_CALLBACK(on_negotiation_needed),apprtc);
g_signal_connect(apprtc->webrtcbin,"on-ice-candidate",G_CALLBACK(send_ice_candidate_message),apprtc);
g_signal_connect(apprtc->webrtcbin,"pad-added",G_CALLBACK(on_incoming_stream),apprtc);
g_signal_connect(apprtc->webrtcbin,"notify::ice-connection-state",G_CALLBACK(_on_ice_connection_state_notify),NULL);
gst_object_unref(apprtc->webrtcbin);
g_print("Staring pipeline\n");
ret=gst_element_set_state(GST_ELEMENT(apprtc->pipe),GST_STATE_PLAYING);
if(ret==GST_STATE_CHANGE_FAILURE)
goto err;
return TRUE;
err:
if(apprtc->pipe)
g_clear_object(&apprtc->pipe);
if(apprtc->webrtcbin)
apprtc->webrtcbin=NULL;
return FALSE;
}
static gboolean register_with_server(AppRTC* apprtc){
gchar* hello;
gint32 our_id;
if(soup_websocket_connection_get_state(apprtc->ws_conn)!=SOUP_WEBSOCKET_STATE_OPEN){
return FALSE;
}
our_id=g_random_int_range(10,10000);
hello=g_strdup_printf("HELLO %i",our_id);
g_print("%s\n",hello);
send_message_to_ui(g_strdup_printf("id:%i",our_id));
soup_websocket_connection_send_text(apprtc->ws_conn,hello);
g_free(hello);
return TRUE;
}
static void on_server_close(SoupWebsocketConnection* conn G_GNUC_UNUSED,AppRTC* apprtc){
cleanup_and_quit_loop(apprtc,"websocket close");
}
static void on_server_message(SoupWebsocketConnection* conn,SoupWebsocketDataType type,
GBytes* message,AppRTC* apprtc){
g_print("on_server_message\n");
gsize size;
gchar *text,*data;
switch(type){
case SOUP_WEBSOCKET_DATA_BINARY:
g_print("Received unknown binary message, ignoring\n");
g_bytes_unref(message);
return;
case SOUP_WEBSOCKET_DATA_TEXT:
data=g_bytes_unref_to_data(message,&size);
text=g_strndup(data,size);
g_free(data);
break;
default:
g_assert_not_reached();
}
g_print("receive:%s\n",text);
if(g_strcmp0(text,"HELLO")==0){
}else if(g_strcmp0(text,"SESSION_OK")==0){
if(apprtc->pipe==NULL){
if(!start_pipeline(apprtc)){
cleanup_and_quit_loop(apprtc,"start pipeline false");
}
}
}else if(g_strcmp0(text,"OFFER_REQUEST")==0){
if(apprtc->pipe==NULL){
if(!start_pipeline(apprtc)){
cleanup_and_quit_loop(apprtc,"start pipeline false");
}
}
}else{
JsonNode *root;
JsonObject *object;
JsonParser *parser=json_parser_new();
if(!json_parser_load_from_data(parser,text,-1,NULL)){
g_print("json parser error:%s\n",text);
g_object_unref(parser);
goto out;
}
root=json_parser_get_root(parser);
if(!JSON_NODE_HOLDS_OBJECT(root)){
g_print("json holds error:%s\n",text);
g_object_unref(parser);
goto out;
}
object=json_node_get_object(root);
if(json_object_has_member(object,"sdp")){
int ret;
const char* text;
GstSDPMessage* sdp;
object=json_object_get_object_member(object,"sdp");
g_assert(json_object_has_member(object,"type"));
g_print("sdp_type %s\n",json_object_get_string_member(object,"type"));
char* type=json_object_get_string_member(object,"type");
text=json_object_get_string_member(object,"sdp");
if(g_strcmp0(type,"answer")==0){
ret=gst_sdp_message_new(&sdp);
g_assert(ret==GST_SDP_OK);
ret=gst_sdp_message_parse_buffer(text,strlen(text),sdp);
g_assert(ret==GST_SDP_OK);
GstWebRTCSessionDescription* answer;
answer=gst_webrtc_session_description_new(GST_WEBRTC_SDP_TYPE_ANSWER,sdp);
g_assert(answer);
{
GstPromise* promise=gst_promise_new();
g_signal_emit_by_name(apprtc->webrtcbin,"set-remote-description",answer,promise);
gst_promise_interrupt(promise);
gst_promise_unref(promise);
}
}else {
char* offer=malloc(strlen(text));
strcpy(offer,text);
apprtc->offer=offer;
set_offer_and_create_answer(apprtc);
}
}else if(json_object_has_member(object,"ice")){
JsonObject *ice;
const gchar* candidate;
gint sdpmlineindex;
ice=json_object_get_object_member(object,"ice");
candidate=json_object_get_string_member(ice,"candidate");
sdpmlineindex=json_object_get_int_member(ice,"sdpMLineIndex");
g_signal_emit_by_name(apprtc->webrtcbin,"add_ice_candidate",sdpmlineindex,candidate);
}else{
g_print("unknow json\n");
}
g_object_unref(parser);
}
out:
g_print("freeText:%s\n",text);
g_free(text);
}
static void on_server_connected(SoupSession* session,GAsyncResult* res,AppRTC* apprtc){
GError* error=NULL;
apprtc->ws_conn=soup_session_websocket_connect_finish(session,res,&error);
if(error){
cleanup_and_quit_loop(apprtc,error->message);
g_error_free(error);
return;
}
g_assert(apprtc->ws_conn!=NULL);
g_print("connect to signalling server \n");
g_signal_connect(apprtc->ws_conn,"close",G_CALLBACK(on_server_close),apprtc);
g_signal_connect(apprtc->ws_conn,"message",G_CALLBACK(on_server_message),apprtc);
register_with_server(apprtc);
}
static gboolean connect_to_websocket_server_async(AppRTC* apprtc){
SoupLogger *logger;
SoupMessage *message;
SoupSession *session;
const char* https_aliases[]={"wss",NULL};
const gchar* ca_certs;
ca_certs=g_getenv("CA_CERTIFICATES");
g_print("ca_cents:%s\n",ca_certs);
g_assert(ca_certs!=NULL);
session=soup_session_new_with_options(SOUP_SESSION_SSL_STRICT,FALSE,
SOUP_SESSION_SSL_CA_FILE,ca_certs,
SOUP_SESSION_HTTPS_ALIASES,https_aliases,NULL);
g_print("connect to server0000\n");
soup_logger_new(SOUP_LOGGER_LOG_BODY,-1);
g_print("connect to server111\n");
//soup_session_add_feature(session,SOUP_SESSION_FEATURE(logger));
g_print("connect to server1.5\n");
//g_object_unref(logger);
g_print("connect to server2222\n");
message=soup_message_new(SOUP_METHOD_GET,DEFAULT_SIGNALLING_SERVER);
g_print("connect to server\n");
soup_session_websocket_connect_async(session,message,NULL,NULL,NULL,(GAsyncReadyCallback) on_server_connected,apprtc);
g_print("connect to server_done\n");
return G_SOURCE_REMOVE;
}
void apprtc_end_call(){
AppRTC* apprtc=APPRTC;
if(!apprtc) return;
g_mutex_lock(&apprtc->lock);
if(apprtc->loop){
GThread* thread=apprtc->thread;
cleanup_and_quit_loop(apprtc,NULL);
apprtc->thread=NULL;
g_mutex_unlock(&apprtc->lock);
g_thread_join(thread);
}else{
g_mutex_unlock(&apprtc->lock);
}
}
static gboolean _unlock_mutex(GMutex* m){
g_mutex_unlock(m);
return G_SOURCE_REMOVE;
}
static gpointer _call_thread(AppRTC* apprtc){
GMainContext* context;
g_mutex_lock(&apprtc->lock);
context=g_main_context_new();
apprtc->loop=g_main_loop_new(context,FALSE);
g_main_context_invoke(context,(GSourceFunc)_unlock_mutex,&apprtc->lock);
g_main_context_invoke(context,(GSourceFunc)connect_to_websocket_server_async,apprtc);
g_main_context_push_thread_default(context);
g_cond_broadcast(&apprtc->cond);
g_main_loop_run(apprtc->loop);
g_main_context_pop_thread_default(context);
return NULL;
}
void apprtc_start_thread(AppRTC* apprtc){
if(!apprtc) return;
if(apprtc->thread)
apprtc_end_call();
apprtc->thread=g_thread_new("apprtc",(GThreadFunc)_call_thread,apprtc);
g_mutex_lock(&apprtc->lock);
while(!apprtc->loop)
g_cond_wait(&apprtc->cond,&apprtc->lock);
g_mutex_unlock(&apprtc->lock);
}
//ffi
void apprtc_connect_peer_id(char* id,int is_remote_offer){
AppRTC* apprtc=APPRTC;
g_print("apprtc_connect_peer_id\n");
//if(!apprtc) return;
apprtc->is_send=is_remote_offer;
gchar* message=g_strdup_printf("SESSION %s",id);
soup_websocket_connection_send_text(apprtc->ws_conn,message);
g_free(message);
g_print("apprtc_connect_peer_id_done\n");
}
//ffi
void apprtc_set_listener(void (* _listener)(guint8*,int,int,int),void (* _message_listener)(char*)){
listener=_listener;
message_listener=_message_listener;
}
//ffi
void apprtc_init(){
g_print("apprtc_init\n");
APPRTC=g_new0(AppRTC,1);
AppRTC* apprtc=APPRTC;
g_mutex_init(&apprtc->lock);
g_cond_init(&apprtc->cond);
apprtc_start_thread(apprtc);
}