diff --git a/BUILD.gn b/BUILD.gn index 89f3b71d72..844d79645d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -3,6 +3,7 @@ import("../webrtc.gni") declare_args() { libwebrtc_intel_media_sdk = false libwebrtc_desktop_capture = true + libwebrtc_export_capi = true } if (is_android) { @@ -158,6 +159,20 @@ rtc_shared_library("libwebrtc") { "src/rtc_logging.cc", ] + if(libwebrtc_export_capi) { + sources += [ + "include/ref_counted_object_capi.h", + "include/rtc_types_capi.h", + "include/libwebrtc_capi.h", + "include/rtc_peerconnection_factory_capi.h", + + + "src/capi/libwebrtc_capi.cc", + "src/capi/ref_counted_object_capi.cc", + "src/capi/rtc_peerconnection_factory_capi.cc", + ] + } + # intel media sdk if (is_win && libwebrtc_intel_media_sdk) { sources += [ @@ -273,3 +288,19 @@ rtc_shared_library("libwebrtc") { } } } + + rtc_test("libwebrtc_unittests") { + testonly = true + + sources = [ + "test/peerconnection.test.cc", + "test/tests.cc", + ] + + deps = [ + ":libwebrtc", + "//testing/gtest", + "//test:test_support", + "//test:test_main", + ] + } \ No newline at end of file diff --git a/include/libwebrtc_capi.h b/include/libwebrtc_capi.h new file mode 100644 index 0000000000..6f795a0234 --- /dev/null +++ b/include/libwebrtc_capi.h @@ -0,0 +1,42 @@ +#ifndef LIB_WEBRTC_HXX_CAPI +#define LIB_WEBRTC_HXX_CAPI + +#include "rtc_peerconnection_factory_capi.h" +#include "rtc_types_capi.h" + +extern "C" { +/** + * @brief Initializes the WebRTC library. + * + * This function initializes the WebRTC library. It must be called before + * using any other WebRTC functions. + * + * @return rtcBoolean Returns kTrue if initialization is successful, kFalse + * otherwise. + */ +LIB_WEBRTC_CAPI rtcBoolean LibWebRTC_Initialize(); + +/** + * @brief Creates a new WebRTC PeerConnectionFactory. + * + * This function creates a new WebRTC PeerConnectionFactory. It must be + * called after initializing the WebRTC library using LibWebRTC_Initialize(). + * + * @return A pointer to the newly created RTCPeerConnectionFactory. + */ +LIB_WEBRTC_CAPI rtcPeerConnectionFactoryHandle +LibWebRTC_CreatePeerConnectionFactory(); + +/** + * @brief Terminates the WebRTC PeerConnectionFactory and threads. + * + * Terminates the WebRTC PeerConnectionFactory and threads. This method is + * thread-safe and can be called from any thread. It cleans up SSL and stops + * and destroys the three threads: worker_thread, signaling_thread and + * network_thread. + * + */ +LIB_WEBRTC_CAPI void LibWebRTC_Terminate(); +} + +#endif // LIB_WEBRTC_HXX_CAPI diff --git a/include/ref_counted_object_capi.h b/include/ref_counted_object_capi.h new file mode 100644 index 0000000000..96f2d71b6b --- /dev/null +++ b/include/ref_counted_object_capi.h @@ -0,0 +1,14 @@ +#ifndef LIB_WEBRTC_REF_COUNTED_OBJECT_CAPI +#define LIB_WEBRTC_REF_COUNTED_OBJECT_CAPI + +#include "rtc_types_capi.h" + +extern "C" { + +LIB_WEBRTC_CAPI int RTCRefCountedObject_AddRef(rtcRefCountedObjectHandle handle); + +LIB_WEBRTC_CAPI int RTCRefCountedObject_Release(rtcRefCountedObjectHandle handle); + +} // extern "C" + +#endif // LIB_WEBRTC_REF_COUNTED_OBJECT_CAPI \ No newline at end of file diff --git a/include/rtc_peerconnection_factory_capi.h b/include/rtc_peerconnection_factory_capi.h new file mode 100644 index 0000000000..12b177492b --- /dev/null +++ b/include/rtc_peerconnection_factory_capi.h @@ -0,0 +1,21 @@ +#ifndef LIB_WEBRTC_RTC_PEERCONNECTION_FACTORY_HXX_CAPI +#define LIB_WEBRTC_RTC_PEERCONNECTION_FACTORY_HXX_CAPI + +#include "rtc_types_capi.h" + +extern "C" { + +typedef rtcRefCountedObjectHandle rtcPeerConnectionFactoryHandle; + +LIB_WEBRTC_CAPI rtcBoolean +RTCPeerConnectionFactory_Initialize(rtcPeerConnectionFactoryHandle factory); + +LIB_WEBRTC_CAPI rtcBoolean +RTCPeerConnectionFactory_Terminate(rtcPeerConnectionFactoryHandle factory); + +LIB_WEBRTC_CAPI rtcResult +RTCPeerConnectionFactory_Create(rtcPeerConnectionFactoryHandle factory, + const void* configuration); +} + +#endif // LIB_WEBRTC_RTC_PEERCONNECTION_FACTORY_HXX_CAPI \ No newline at end of file diff --git a/include/rtc_types_capi.h b/include/rtc_types_capi.h new file mode 100644 index 0000000000..b89835ce77 --- /dev/null +++ b/include/rtc_types_capi.h @@ -0,0 +1,122 @@ +#ifndef LIB_WEBRTC_RTC_TYPES_HXX_CAPI +#define LIB_WEBRTC_RTC_TYPES_HXX_CAPI + +#ifdef LIB_WEBRTC_API_EXPORTS +#define LIB_WEBRTC_CAPI __declspec(dllexport) +#elif defined(LIB_WEBRTC_API_DLL) +#define LIB_WEBRTC_CAPI __declspec(dllimport) +#elif !defined(WIN32) +#define LIB_WEBRTC_CAPI __attribute__((visibility("default"))) +#else +#define LIB_WEBRTC_CAPI +#endif + +#include + +extern "C" { +typedef void* rtcRefCountedObjectHandle; + +typedef struct { + const char* message; +} rtcError; + +enum class rtcBoolean : int { kFalse = 0, kTrue = 1 }; + +enum class rtcResult : uint32_t { + kSuccess = 0, + kInvalidPointer = 1, +}; + +const int kMaxIceServerSize = 8; + +enum class MediaSecurityType : int { + kSRTP_None = 0, + kSDES_SRTP = 1, + kDTLS_SRTP = 2 +}; + +enum class RTCMediaType : int { + AUDIO = 0, + VIDEO = 1, + DATA = 2, + UNSUPPORTED = 3 +}; + +struct IceServer { + const char* uri = nullptr; + const char* username = nullptr; + const char* password = nullptr; +}; + +enum class IceTransportsType { kNone, kRelay, kNoHost, kAll }; + +enum class TcpCandidatePolicy { + kTcpCandidatePolicyEnabled, + kTcpCandidatePolicyDisabled +}; + +enum class CandidateNetworkPolicy { + kCandidateNetworkPolicyAll, + kCandidateNetworkPolicyLowCost +}; + +enum class RtcpMuxPolicy { + kRtcpMuxPolicyNegotiate, + kRtcpMuxPolicyRequire, +}; + +enum BundlePolicy { + kBundlePolicyBalanced, + kBundlePolicyMaxBundle, + kBundlePolicyMaxCompat +}; + +enum class SdpSemantics { kPlanB, kUnifiedPlan }; + +struct RTCConfiguration { + IceServer ice_servers[kMaxIceServerSize]; + IceTransportsType type = IceTransportsType::kAll; + BundlePolicy bundle_policy = BundlePolicy::kBundlePolicyBalanced; + RtcpMuxPolicy rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire; + CandidateNetworkPolicy candidate_network_policy = + CandidateNetworkPolicy::kCandidateNetworkPolicyAll; + TcpCandidatePolicy tcp_candidate_policy = + TcpCandidatePolicy::kTcpCandidatePolicyEnabled; + + int ice_candidate_pool_size = 0; + + MediaSecurityType srtp_type = MediaSecurityType::kDTLS_SRTP; + SdpSemantics sdp_semantics = SdpSemantics::kUnifiedPlan; + bool offer_to_receive_audio = true; + bool offer_to_receive_video = true; + + bool disable_ipv6 = false; + bool disable_ipv6_on_wifi = false; + int max_ipv6_networks = 5; + bool disable_link_local_networks = false; + int screencast_min_bitrate = -1; + + // private + bool use_rtp_mux = true; + uint32_t local_audio_bandwidth = 128; + uint32_t local_video_bandwidth = 512; +}; + +struct SdpParseError { + // The sdp line that causes the error. + const char* line = nullptr; + // Explains the error. + const char* description = nullptr; +}; + +enum DesktopType { kScreen, kWindow }; + +#define CHECK_POINTER_EX(p, r) \ + if ((p) == nullptr) { \ + return (r); \ + } + +#define CHECK_POINTER(p) CHECK_POINTER_EX(p, rtcResult::kInvalidPointer) +} + +#endif // LIB_WEBRTC_RTC_TYPES_HXX diff --git a/src/capi/libwebrtc_capi.cc b/src/capi/libwebrtc_capi.cc new file mode 100644 index 0000000000..ad7aa2deaf --- /dev/null +++ b/src/capi/libwebrtc_capi.cc @@ -0,0 +1,17 @@ +#include "include/libwebrtc_capi.h" + +#include "include/libwebrtc.h" + +using namespace libwebrtc; + +rtcBoolean LibWebRTC_Initialize() { + return LibWebRTC::Initialize() ? rtcBoolean::kTrue : rtcBoolean::kFalse; +} + +void LibWebRTC_Terminate() { LibWebRTC::Terminate(); } + +rtcPeerConnectionFactoryHandle LibWebRTC_CreatePeerConnectionFactory() { + scoped_refptr p = + LibWebRTC::CreateRTCPeerConnectionFactory(); + return static_cast(p.release()); +} diff --git a/src/capi/ref_counted_object_capi.cc b/src/capi/ref_counted_object_capi.cc new file mode 100644 index 0000000000..bc4b89b6f8 --- /dev/null +++ b/src/capi/ref_counted_object_capi.cc @@ -0,0 +1,24 @@ +#include "include/ref_counted_object_capi.h" + +#include "base/refcount.h" +#include "base/scoped_ref_ptr.h" + +using namespace libwebrtc; + +LIB_WEBRTC_CAPI int RTCRefCountedObject_AddRef( + rtcRefCountedObjectHandle handle) { + if (handle == nullptr) { + return -1; + } + RefCountInterface* p = static_cast(handle); + return p->AddRef(); +} + +LIB_WEBRTC_CAPI int RTCRefCountedObject_Release( + rtcRefCountedObjectHandle handle) { + if (handle == nullptr) { + return -1; + } + RefCountInterface* p = static_cast(handle); + return p->Release(); +} \ No newline at end of file diff --git a/src/capi/rtc_peerconnection_factory_capi.cc b/src/capi/rtc_peerconnection_factory_capi.cc new file mode 100644 index 0000000000..4885d289e7 --- /dev/null +++ b/src/capi/rtc_peerconnection_factory_capi.cc @@ -0,0 +1,26 @@ +#include "include/rtc_peerconnection_factory_capi.h" + +#include "include/rtc_peerconnection_factory.h" + +using namespace libwebrtc; + +rtcBoolean RTCPeerConnectionFactory_Initialize( + rtcPeerConnectionFactoryHandle factory) { + CHECK_POINTER_EX(factory, rtcBoolean::kFalse); + + scoped_refptr pFactory = + static_cast(factory); + return pFactory->Initialize() ? rtcBoolean::kTrue : rtcBoolean::kFalse; +} + +rtcBoolean RTCPeerConnectionFactory_Terminate( + rtcPeerConnectionFactoryHandle factory) { + CHECK_POINTER_EX(factory, rtcBoolean::kFalse); + + scoped_refptr pFactory = + static_cast(factory); + return pFactory->Terminate() ? rtcBoolean::kTrue : rtcBoolean::kFalse; +} + +LIB_WEBRTC_CAPI rtcResult RTCPeerConnectionFactory_Create( + rtcPeerConnectionFactoryHandle factory, const void* configuration); \ No newline at end of file diff --git a/src/libwebrtc.cc b/src/libwebrtc.cc index 522c506a3c..dd8dd5b943 100644 --- a/src/libwebrtc.cc +++ b/src/libwebrtc.cc @@ -15,15 +15,24 @@ bool LibWebRTC::Initialize() { if (!g_is_initialized) { webrtc::InitializeSSL(); g_is_initialized = true; +#ifdef WEBRTC_WIN + WSADATA data; + WSAStartup(MAKEWORD(1, 0), &data); +#endif } return g_is_initialized; } // Stops and cleans up the threads and SSL. void LibWebRTC::Terminate() { + if(!g_is_initialized) { + return; + } webrtc::ThreadManager::Instance()->SetCurrentThread(NULL); webrtc::CleanupSSL(); - +#ifdef WEBRTC_WIN + WSACleanup(); +#endif // Resets the static variable g_is_initialized to false. g_is_initialized = false; } @@ -34,7 +43,6 @@ LibWebRTC::CreateRTCPeerConnectionFactory() { scoped_refptr rtc_peerconnection_factory = scoped_refptr( new RefCountedObject()); - rtc_peerconnection_factory->Initialize(); return rtc_peerconnection_factory; } diff --git a/src/rtc_peerconnection_factory_impl.cc b/src/rtc_peerconnection_factory_impl.cc index 75d7fa9a42..3145a46c87 100644 --- a/src/rtc_peerconnection_factory_impl.cc +++ b/src/rtc_peerconnection_factory_impl.cc @@ -47,6 +47,9 @@ RTCPeerConnectionFactoryImpl::RTCPeerConnectionFactoryImpl() {} RTCPeerConnectionFactoryImpl::~RTCPeerConnectionFactoryImpl() {} bool RTCPeerConnectionFactoryImpl::Initialize() { + + task_queue_factory_ = webrtc::CreateDefaultTaskQueueFactory(); + worker_thread_ = webrtc::Thread::Create(); worker_thread_->SetName("worker_thread", nullptr); RTC_CHECK(worker_thread_->Start()) << "Failed to start thread"; @@ -59,7 +62,6 @@ bool RTCPeerConnectionFactoryImpl::Initialize() { network_thread_->SetName("network_thread", nullptr); RTC_CHECK(network_thread_->Start()) << "Failed to start thread"; if (!audio_device_module_) { - task_queue_factory_ = webrtc::CreateDefaultTaskQueueFactory(); worker_thread_->BlockingCall([&] { CreateAudioDeviceModule_w(); }); } @@ -105,11 +107,11 @@ bool RTCPeerConnectionFactoryImpl::Terminate() { video_device_impl_ = nullptr; audio_processing_impl_ = nullptr; }); - rtc_peerconnection_factory_ = NULL; + if (audio_device_module_) { worker_thread_->BlockingCall([this] { DestroyAudioDeviceModule_w(); }); } - + rtc_peerconnection_factory_ = nullptr; return true; } diff --git a/test/peerconnection.test.cc b/test/peerconnection.test.cc index e69de29bb2..ad5f7a06ce 100644 --- a/test/peerconnection.test.cc +++ b/test/peerconnection.test.cc @@ -0,0 +1,48 @@ +#include +#include + +#include "test/gmock.h" +#include "test/gtest.h" + +#include "rtc_base/logging.h" +#include "system_wrappers/include/sleep.h" + +#include "libwebrtc.h" +#include "libwebrtc_capi.h" +#include "ref_counted_object_capi.h" +#include "rtc_peerconnection_factory_capi.h" + +namespace libwebrtc { + +/* +TEST(LibWebRTC, ConstructDestruct) { + EXPECT_EQ(LibWebRTC::Initialize(), true); + auto peer_connection_factory = LibWebRTC::CreateRTCPeerConnectionFactory(); + EXPECT_NE(peer_connection_factory, nullptr); + peer_connection_factory = nullptr; + LibWebRTC::Terminate(); +}*/ + +TEST(LibWebRTC_CAPI, ConstructDestruct) { + EXPECT_EQ(LibWebRTC_Initialize(), rtcBoolean::kTrue); + + RTC_LOG(LS_INFO) << "LibWebRTC_Initialize() called"; + + auto peer_connection_factory = LibWebRTC_CreatePeerConnectionFactory(); + + EXPECT_NE(peer_connection_factory, nullptr); + + RTC_LOG(LS_INFO) << "LibWebRTC_CreatePeerConnectionFactory() called, peer_connection_factory " << peer_connection_factory; + + EXPECT_EQ(RTCPeerConnectionFactory_Initialize(peer_connection_factory), rtcBoolean::kTrue); + + EXPECT_EQ(RTCPeerConnectionFactory_Terminate(peer_connection_factory), rtcBoolean::kTrue); + + RTC_LOG(LS_INFO) << "RTCRefCountedObject_Release() called"; + RTCRefCountedObject_Release(peer_connection_factory); + + LibWebRTC_Terminate(); + RTC_LOG(LS_INFO) << "LibWebRTC_Terminate() called"; +} + +} // namespace libwebrtc diff --git a/test/tests.cc b/test/tests.cc index e69de29bb2..1b11f1bd27 100644 --- a/test/tests.cc +++ b/test/tests.cc @@ -0,0 +1,9 @@ +#include +#include + +#include "test/gmock.h" +#include "test/gtest.h" + +namespace libwebrtc { + +} // namespace libwebrtc \ No newline at end of file