添加注释

This commit is contained in:
dongl 2024-02-21 23:09:39 +08:00
parent 2861cdee5f
commit dcc9180221
14 changed files with 1422 additions and 1258 deletions

View File

@ -48,14 +48,15 @@ include_directories(/opt/homebrew/include/pango-1.0)
include_directories(/opt/homebrew/include/glib-2.0) include_directories(/opt/homebrew/include/glib-2.0)
include_directories(/opt/homebrew/include/glib-2.0/glib) include_directories(/opt/homebrew/include/glib-2.0/glib)
include_directories(/opt/homebrew/include/atk-1.0) include_directories(/opt/homebrew/include/atk-1.0)
include_directories(${JSONCPP_INCLUDE_DIRS}) #include_directories(/opt/homebrew/Cellar/ffmpeg/6.1.1_3/include)
#include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(${GTK3_INCLUDE_DIRS}) include_directories(${GTK3_INCLUDE_DIRS})
# #
include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_SOURCE_DIR}/include)
include_directories(${CMAKE_SOURCE_DIR}/include/absl) include_directories(${CMAKE_SOURCE_DIR}/include/absl)
include_directories(${CMAKE_SOURCE_DIR}/include/webrtc_cxx_sdk) include_directories(${CMAKE_SOURCE_DIR}/include/webrtc_cxx_sdk)
include_directories(${CMAKE_SOURCE_DIR}/include/webrtc_cxx_sdk/third_party/ffmpeg) #include_directories(${CMAKE_SOURCE_DIR}/include/webrtc_cxx_sdk/third_party/ffmpeg)
#include_directories(/Users/dongl/tools/ffmpeg/ffmpeg_build/include) #include_directories(/Users/dongl/tools/ffmpeg/ffmpeg_build/include)
include_directories(${CMAKE_SOURCE_DIR}/include/webrtc_cxx_sdk/third_party/harfbuzz-ng/src/src) include_directories(${CMAKE_SOURCE_DIR}/include/webrtc_cxx_sdk/third_party/harfbuzz-ng/src/src)
include_directories(${CMAKE_SOURCE_DIR}/include/webrtc_cxx_sdk/third_party/libyuv/include) include_directories(${CMAKE_SOURCE_DIR}/include/webrtc_cxx_sdk/third_party/libyuv/include)
@ -73,7 +74,7 @@ message( ${AVFOUNDATION_LIBRARY})
# #
link_directories(${CMAKE_SOURCE_DIR}/lib/arm_macos) link_directories(${CMAKE_SOURCE_DIR}/lib/arm_macos)
link_directories(/opt/homebrew/lib) link_directories(/opt/homebrew/lib)
link_directories(/opt/homebrew/Cellar/ffmpeg/6.0_1/lib) #link_directories(/opt/homebrew/Cellar/ffmpeg/6.1.1_3/lib)
#link_directories(/Users/dongl/tools/ffmpeg/ffmpeg_build/lib) #link_directories(/Users/dongl/tools/ffmpeg/ffmpeg_build/lib)
add_subdirectory(src/client) add_subdirectory(src/client)

View File

@ -24,7 +24,7 @@ target_link_libraries(client
PkgConfig::FFMPEG PkgConfig::FFMPEG
gtk-3 gdk-3 pangocairo-1.0 pango-1.0 harfbuzz atk-1.0 cairo-gobject cairo gdk_pixbuf-2.0 gio-2.0 gobject-2.0 glib-2.0 gtk-3 gdk-3 cairo gobject-2.0 glib-2.0
${COCOA_LIBRARY} ${COCOA_LIBRARY}
${CORE_AUDIO_LIBRARY} ${CORE_AUDIO_LIBRARY}

View File

@ -74,7 +74,9 @@ namespace {
static rtc::scoped_refptr<DummySetSessionDescriptionObserver> Create() { static rtc::scoped_refptr<DummySetSessionDescriptionObserver> Create() {
return rtc::make_ref_counted<DummySetSessionDescriptionObserver>(); return rtc::make_ref_counted<DummySetSessionDescriptionObserver>();
} }
virtual void OnSuccess() { RTC_LOG(LS_INFO) << __FUNCTION__; } virtual void OnSuccess() { RTC_LOG(LS_INFO) << __FUNCTION__; }
virtual void OnFailure(webrtc::RTCError error) { virtual void OnFailure(webrtc::RTCError error) {
RTC_LOG(LS_INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": " RTC_LOG(LS_INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": "
<< error.message(); << error.message();
@ -114,6 +116,7 @@ namespace {
rtc::VideoSourceInterface<webrtc::VideoFrame> *source() override { rtc::VideoSourceInterface<webrtc::VideoFrame> *source() override {
return capturer_.get(); return capturer_.get();
} }
std::unique_ptr<webrtc::VcmCapturer> capturer_; std::unique_ptr<webrtc::VcmCapturer> capturer_;
}; };

View File

@ -25,6 +25,7 @@
#include "rtc_base/win32.h" #include "rtc_base/win32.h"
#endif // WEBRTC_WIN #endif // WEBRTC_WIN
// 抽象回调
class MainWndCallback { class MainWndCallback {
public: public:
// 通知登陆信令服务器 // 通知登陆信令服务器
@ -37,7 +38,7 @@ public:
virtual void DisconnectFromCurrentPeer() = 0; virtual void DisconnectFromCurrentPeer() = 0;
// 自定义消息处理函数 // 自定义消息处理函数
virtual void UIThreadCallback(int msg_id, void *data) = 0; virtual void UIThreadCallback(int msg_id, void *data) = 0;
// 关闭conductor // 关闭
virtual void Close() = 0; virtual void Close() = 0;
protected: protected:
@ -45,41 +46,32 @@ protected:
}; };
// Pure virtual interface for the main window. // Pure virtual interface for the main window.
// 主窗口基类
class MainWindow { class MainWindow {
public: public:
virtual ~MainWindow() {} virtual ~MainWindow() {}
// 当前的UI界面
enum UI { enum UI {
CONNECT_TO_SERVER, CONNECT_TO_SERVER, // 链接至服务器页面
LIST_PEERS, LIST_PEERS, // PEER双端通讯列表的页面
STREAMING, STREAMING, // 流媒体对话的页面
}; };
// 注册抽象回调函数集结构体
virtual void RegisterObserver(MainWndCallback *callback) = 0; virtual void RegisterObserver(MainWndCallback *callback) = 0;
// 窗口是否有效?
virtual bool IsWindow() = 0; virtual bool IsWindow() = 0;
virtual void MessageBox(const char *caption, virtual void MessageBox(const char *caption,const char *text,bool is_error) = 0;
const char *text,
bool is_error) = 0;
virtual UI current_ui() = 0; virtual UI current_ui() = 0;
virtual void SwitchToConnectUI() = 0; virtual void SwitchToConnectUI() = 0;
virtual void SwitchToPeerList(const Peers &peers) = 0; virtual void SwitchToPeerList(const Peers &peers) = 0;
virtual void SwitchToStreamingUI() = 0; virtual void SwitchToStreamingUI() = 0;
virtual void StartLocalRenderer(webrtc::VideoTrackInterface *local_video) = 0; virtual void StartLocalRenderer(webrtc::VideoTrackInterface *local_video) = 0;
virtual void StopLocalRenderer() = 0; virtual void StopLocalRenderer() = 0;
virtual void StartRemoteRenderer(webrtc::VideoTrackInterface *remote_video) = 0;
virtual void StartRemoteRenderer(
webrtc::VideoTrackInterface *remote_video) = 0;
virtual void StopRemoteRenderer() = 0; virtual void StopRemoteRenderer() = 0;
virtual void QueueUIThreadCallback(int msg_id, void *data) = 0; virtual void QueueUIThreadCallback(int msg_id, void *data) = 0;
}; };

View File

@ -69,6 +69,7 @@ void PeerConnectionClient::RegisterObserver(
callback_ = callback; callback_ = callback;
} }
// 链接服务器
void PeerConnectionClient::Connect(const std::string &server, void PeerConnectionClient::Connect(const std::string &server,
int port, int port,
const std::string &client_name) { const std::string &client_name) {
@ -77,50 +78,59 @@ void PeerConnectionClient::Connect(const std::string& server,
if (state_ != NOT_CONNECTED) { if (state_ != NOT_CONNECTED) {
RTC_LOG(LS_WARNING) RTC_LOG(LS_WARNING)
// 在调用 Connect() 之前,客户端不得连接
<< "The client must not be connected before you can call Connect()"; << "The client must not be connected before you can call Connect()";
callback_->OnServerConnectionFailure(); callback_->OnServerConnectionFailure();
return; return;
} }
if (server.empty() || client_name.empty()) { if (server.empty() || client_name.empty()) {
callback_->OnServerConnectionFailure(); callback_->OnServerConnectionFailure(); // 链接失败
return; return;
} }
if (port <= 0) if (port <= 0)
port = kDefaultServerPort; port = kDefaultServerPort; // 默认值8888
// 设置地址端口客户端名称
server_address_.SetIP(server); server_address_.SetIP(server);
server_address_.SetPort(port); server_address_.SetPort(port);
client_name_ = client_name; client_name_ = client_name;
// ip 是否正确 未解析ip
if (server_address_.IsUnresolvedIP()) { if (server_address_.IsUnresolvedIP()) {
state_ = RESOLVING; state_ = RESOLVING; // 状态解决?
resolver_ = new rtc::AsyncResolver(); resolver_ = new rtc::AsyncResolver();
// SignalDone 当地址解析过程完成时会触发此信号。
// 解析结果 类指针 成员函数指针
resolver_->SignalDone.connect(this, &PeerConnectionClient::OnResolveResult); resolver_->SignalDone.connect(this, &PeerConnectionClient::OnResolveResult);
// AsyncResolver 将执行异步 DNS 解析,并在上发送结果信号
resolver_->Start(server_address_); resolver_->Start(server_address_);
} else { } else {
DoConnect(); DoConnect();
} }
} }
void PeerConnectionClient::OnResolveResult( void PeerConnectionClient::OnResolveResult(rtc::AsyncResolverInterface *resolver) {
rtc::AsyncResolverInterface* resolver) {
if (resolver_->GetError() != 0) { if (resolver_->GetError() != 0) {
callback_->OnServerConnectionFailure(); callback_->OnServerConnectionFailure(); // 链接失败
resolver_->Destroy(false); resolver_->Destroy(false);
resolver_ = NULL; resolver_ = nullptr;
state_ = NOT_CONNECTED; state_ = NOT_CONNECTED; // 状态未链接
} else { } else {
// 这里有重复检验了一下 因为进入此函数的先决条件是为解析ip
server_address_ = resolver_->address(); server_address_ = resolver_->address();
DoConnect(); DoConnect();
} }
} }
// 执行链接 链接时 做链接 进行链接 真正执行链接的函数
void PeerConnectionClient::DoConnect() { void PeerConnectionClient::DoConnect() {
control_socket_.reset(CreateClientSocket(server_address_.ipaddr().family())); control_socket_.reset(CreateClientSocket(server_address_.ipaddr().family()));
hanging_get_.reset(CreateClientSocket(server_address_.ipaddr().family())); hanging_get_.reset(CreateClientSocket(server_address_.ipaddr().family()));
// 初始化套接字信号
InitSocketSignals(); InitSocketSignals();
char buffer[1024]; char buffer[1024];
snprintf(buffer, sizeof(buffer), "GET /sign_in?%s HTTP/1.0\r\n\r\n", snprintf(buffer, sizeof(buffer), "GET /sign_in?%s HTTP/1.0\r\n\r\n",
client_name_.c_str()); client_name_.c_str());

View File

@ -38,12 +38,12 @@ struct PeerConnectionClientObserver {
class PeerConnectionClient : public sigslot::has_slots<> { class PeerConnectionClient : public sigslot::has_slots<> {
public: public:
enum State { enum State {
NOT_CONNECTED, NOT_CONNECTED, // 未连接
RESOLVING, RESOLVING, // 解决
SIGNING_IN, SIGNING_IN, // 登录中
CONNECTED, CONNECTED, // 连接的
SIGNING_OUT_WAITING, SIGNING_OUT_WAITING, // 退出等待
SIGNING_OUT, SIGNING_OUT, // 退出
}; };
PeerConnectionClient(); PeerConnectionClient();

View File

@ -17,10 +17,10 @@
#include <glib.h> #include <glib.h>
#include <gobject/gclosure.h> #include <gobject/gclosure.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <stddef.h> #include <cstddef>
#include <stdio.h> #include <cstdio>
#include <stdlib.h> #include <cstdlib>
#include <string.h> #include <cstring>
#include <cstdint> #include <cstdint>
#include <map> #include <map>
@ -42,9 +42,7 @@ namespace {
// GtkMainWnd instance. // GtkMainWnd instance.
// //
gboolean OnDestroyedCallback(GtkWidget *widget, gboolean OnDestroyedCallback(GtkWidget *widget, GdkEvent *event, gpointer data) {
GdkEvent *event,
gpointer data) {
reinterpret_cast<GtkMainWnd *>(data)->OnDestroyed(widget, event); reinterpret_cast<GtkMainWnd *>(data)->OnDestroyed(widget, event);
return FALSE; return FALSE;
} }
@ -54,6 +52,9 @@ namespace {
} }
gboolean SimulateButtonClick(gpointer button) { gboolean SimulateButtonClick(gpointer button) {
// g_signal_emit_by_name 是 GLib 库中用于通过信号名称发出信号的函数。
// 在 GTK 编程中,通过 g_signal_emit_by_name 可以模拟发出特定信号,触发与之关联的信号处理函数。
// 模拟触发模拟按钮点击
g_signal_emit_by_name(button, "clicked"); g_signal_emit_by_name(button, "clicked");
return false; return false;
} }
@ -77,7 +78,7 @@ namespace {
GtkTreeModel *model = gtk_tree_view_get_model(tree_view); GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
// "if iter is NULL, then the number of toplevel nodes is returned." // "if iter is NULL, then the number of toplevel nodes is returned."
int rows = gtk_tree_model_iter_n_children(model, NULL); int rows = gtk_tree_model_iter_n_children(model, nullptr);
GtkTreePath *lastpath = gtk_tree_path_new_from_indices(rows - 1, -1); GtkTreePath *lastpath = gtk_tree_path_new_from_indices(rows - 1, -1);
// Select the last item in the list // Select the last item in the list
@ -94,6 +95,7 @@ namespace {
} }
// Creates a tree view, that we use to display the list of peers. // Creates a tree view, that we use to display the list of peers.
// 创建一个树视图,我们用它来显示对等点列表。
void InitializeList(GtkWidget *list) { void InitializeList(GtkWidget *list) {
GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes( GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
@ -125,17 +127,20 @@ namespace {
gboolean HandleUIThreadCallback(gpointer data) { gboolean HandleUIThreadCallback(gpointer data) {
UIThreadCallbackData *cb_data = reinterpret_cast<UIThreadCallbackData *>(data); UIThreadCallbackData *cb_data = reinterpret_cast<UIThreadCallbackData *>(data);
// 自定义的消息处理纯虚函数
cb_data->callback->UIThreadCallback(cb_data->msg_id, cb_data->data); cb_data->callback->UIThreadCallback(cb_data->msg_id, cb_data->data);
delete cb_data; delete cb_data;
return false; return false;
} }
// 重新绘制
gboolean Redraw(gpointer data) { gboolean Redraw(gpointer data) {
GtkMainWnd *wnd = reinterpret_cast<GtkMainWnd *>(data); GtkMainWnd *wnd = reinterpret_cast<GtkMainWnd *>(data);
wnd->OnRedraw(); wnd->OnRedraw();
return false; return false;
} }
// 绘制
gboolean Draw(GtkWidget *widget, cairo_t *cr, gpointer data) { gboolean Draw(GtkWidget *widget, cairo_t *cr, gpointer data) {
GtkMainWnd *wnd = reinterpret_cast<GtkMainWnd *>(data); GtkMainWnd *wnd = reinterpret_cast<GtkMainWnd *>(data);
wnd->Draw(widget, cr); wnd->Draw(widget, cr);
@ -152,16 +157,17 @@ GtkMainWnd::GtkMainWnd(const char *server,
int port, int port,
bool autoconnect, bool autoconnect,
bool autocall) bool autocall)
: window_(NULL), : window_(nullptr),
draw_area_(NULL), draw_area_(nullptr),
vbox_(NULL), vbox_(nullptr),
server_edit_(NULL), server_edit_(nullptr),
port_edit_(NULL), port_edit_(nullptr),
peer_list_(NULL), peer_list_(nullptr),
callback_(NULL), callback_(nullptr),
server_(server), server_(server),
autoconnect_(autoconnect), autoconnect_(autoconnect),
autocall_(autocall) { autocall_(autocall) {
char buffer[10]; char buffer[10];
snprintf(buffer, sizeof(buffer), "%i", port); snprintf(buffer, sizeof(buffer), "%i", port);
port_ = buffer; port_ = buffer;
@ -176,70 +182,92 @@ void GtkMainWnd::RegisterObserver(MainWndCallback *callback) {
} }
bool GtkMainWnd::IsWindow() { bool GtkMainWnd::IsWindow() {
return window_ != NULL && GTK_IS_WINDOW(window_); return window_ != nullptr && GTK_IS_WINDOW(window_);
} }
void GtkMainWnd::MessageBox(const char *caption, void GtkMainWnd::MessageBox(const char *caption, const char *text, bool is_error) {
const char *text,
bool is_error) {
GtkWidget *dialog = gtk_message_dialog_new( GtkWidget *dialog = gtk_message_dialog_new(
GTK_WINDOW(window_), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_WINDOW(window_), GTK_DIALOG_DESTROY_WITH_PARENT,
is_error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", is_error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s",
text); text);
// "hhhhhhhh");
gtk_window_set_title(GTK_WINDOW(dialog), caption); gtk_window_set_title(GTK_WINDOW(dialog), caption);
gtk_dialog_run(GTK_DIALOG(dialog)); gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog); gtk_widget_destroy(dialog);
} }
// 查看当前的界面
MainWindow::UI GtkMainWnd::current_ui() { MainWindow::UI GtkMainWnd::current_ui() {
if (vbox_) if (vbox_)
return CONNECT_TO_SERVER; return UI::CONNECT_TO_SERVER;
if (peer_list_) if (peer_list_)
return LIST_PEERS; return UI::LIST_PEERS;
return STREAMING; return UI::STREAMING;
} }
// 开始本地绘制
// VideoTrackInterface 是 WebRTC 中的一个接口,它表示一个视频轨道。在实时通信中,可以使用该接口来传输视频数据。
void GtkMainWnd::StartLocalRenderer(webrtc::VideoTrackInterface *local_video) { void GtkMainWnd::StartLocalRenderer(webrtc::VideoTrackInterface *local_video) {
// 重新初始化 或者是赋值 一个渲染绘制类
local_renderer_.reset(new VideoRenderer(this, local_video)); local_renderer_.reset(new VideoRenderer(this, local_video));
} }
// 通知本地绘制
void GtkMainWnd::StopLocalRenderer() { void GtkMainWnd::StopLocalRenderer() {
local_renderer_.reset(); local_renderer_.reset();
} }
// 开始远程绘制
void GtkMainWnd::StartRemoteRenderer( void GtkMainWnd::StartRemoteRenderer(
webrtc::VideoTrackInterface *remote_video) { webrtc::VideoTrackInterface *remote_video) {
remote_renderer_.reset(new VideoRenderer(this, remote_video)); remote_renderer_.reset(new VideoRenderer(this, remote_video));
} }
// 停止远程绘制
void GtkMainWnd::StopRemoteRenderer() { void GtkMainWnd::StopRemoteRenderer() {
remote_renderer_.reset(); remote_renderer_.reset();
} }
// 队列形式的UI线程内的回调
void GtkMainWnd::QueueUIThreadCallback(int msg_id, void *data) { void GtkMainWnd::QueueUIThreadCallback(int msg_id, void *data) {
g_idle_add(HandleUIThreadCallback, // g_idle_add 是 GLib 库中的一个函数用于在主事件循环main loop中添加一个空闲回调函数。
new UIThreadCallbackData(callback_, msg_id, data)); // 当调用 g_idle_add 函数时它会创建一个空闲源idle source并将空闲回调函数注册到主事件循环中。
// 当主事件循环处于空闲状态时,空闲源会触发并调用注册的回调函数。
// 使用 g_idle_add 函数可以方便地在主事件循环的空闲时执行任务或更新界面,以提高程序的响应性。
// 只声明 定义不在这里
g_idle_add( // 回调处理函数
HandleUIThreadCallback,
// 回调类 封装了一层 添加了消息id 与 数据
new UIThreadCallbackData(callback_, msg_id, data)
);
} }
// 创建GUI窗口/窗体/GTK部件
bool GtkMainWnd::Create() { bool GtkMainWnd::Create() {
RTC_DCHECK(window_ == NULL); RTC_DCHECK(window_ == nullptr);
// GTK_WINDOW_TOPLEVEL 类型,表示创建一个顶层窗口。顶层窗口是一个独立的窗口框架,通常作为应用程序的主窗口。
window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
if (window_) { if (window_) {
// 设置窗口位置
gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER); gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER);
// 默认大小
gtk_window_set_default_size(GTK_WINDOW(window_), 640, 480); gtk_window_set_default_size(GTK_WINDOW(window_), 640, 480);
// 窗口标题
gtk_window_set_title(GTK_WINDOW(window_), "PeerConnection client"); gtk_window_set_title(GTK_WINDOW(window_), "PeerConnection client");
g_signal_connect(G_OBJECT(window_), "delete-event", // 可以将特定信号与回调函数连接起来,从而实现事件驱动的编程模型
G_CALLBACK(&OnDestroyedCallback), this); g_signal_connect(G_OBJECT(window_), "delete-event", // 要连接信号的对象实例、信号名称
G_CALLBACK(&OnDestroyedCallback), this); // 回调函数,即信号发出时要执行的函数、传递给回调函数的数据
g_signal_connect(window_, "key-press-event", G_CALLBACK(OnKeyPressCallback), g_signal_connect(window_, "key-press-event", G_CALLBACK(OnKeyPressCallback),
this); this);
// 切换到连接用户界面
SwitchToConnectUI(); SwitchToConnectUI();
} }
return window_ != NULL; return window_ != nullptr;
} }
bool GtkMainWnd::Destroy() { bool GtkMainWnd::Destroy() {
@ -248,83 +276,118 @@ bool GtkMainWnd::Destroy() {
// 销毁释放 一个 GTK+ 窗口部件widget及其所有子部件。 // 销毁释放 一个 GTK+ 窗口部件widget及其所有子部件。
gtk_widget_destroy(window_); gtk_widget_destroy(window_);
window_ = NULL; window_ = nullptr;
return true; return true;
} }
// 切换到连接用户界面
void GtkMainWnd::SwitchToConnectUI() { void GtkMainWnd::SwitchToConnectUI() {
RTC_LOG(LS_INFO) << __FUNCTION__; RTC_LOG(LS_INFO) << __FUNCTION__;
RTC_DCHECK(IsWindow()); RTC_DCHECK(IsWindow());
RTC_DCHECK(vbox_ == NULL); RTC_DCHECK(vbox_ == nullptr);
// 设置 GTK 容器(如窗口、框架等)的边框宽度
gtk_container_set_border_width(GTK_CONTAINER(window_), 10); gtk_container_set_border_width(GTK_CONTAINER(window_), 10);
// peer_list 待通话页面
if (peer_list_) { if (peer_list_) {
// 销毁一个小部件。当一个小部件被销毁时,它在其他对象上持有的所有引用都将被释放:
gtk_widget_destroy(peer_list_); gtk_widget_destroy(peer_list_);
peer_list_ = NULL; peer_list_ = nullptr;
} }
// 这行代码创建了一个垂直方向的 GTK 容器box并设置了 5 像素的间距。
vbox_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); vbox_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
// 这个和上一行好像都是控件容器 最终依次添加到window_窗口中
// 应该是布局管理容器 在布局中的位置的管理
GtkWidget *valign = gtk_alignment_new(0, 1, 0, 0); GtkWidget *valign = gtk_alignment_new(0, 1, 0, 0);
gtk_container_add(GTK_CONTAINER(vbox_), valign); gtk_container_add(GTK_CONTAINER(vbox_), valign);
gtk_container_add(GTK_CONTAINER(window_), vbox_); gtk_container_add(GTK_CONTAINER(window_), vbox_);
// 创建了一个水平容器
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
// 这回才是独立的子控件 一个label文字标签
GtkWidget *label = gtk_label_new("Server"); GtkWidget *label = gtk_label_new("Server");
gtk_container_add(GTK_CONTAINER(hbox), label); gtk_container_add(GTK_CONTAINER(hbox), label);
// 文本输入控件 服务器地址
server_edit_ = gtk_entry_new(); server_edit_ = gtk_entry_new();
// 设置输入框的初始值 这个初始值在构造本类GtkMainWnd时就初始化了
gtk_entry_set_text(GTK_ENTRY(server_edit_), server_.c_str()); gtk_entry_set_text(GTK_ENTRY(server_edit_), server_.c_str());
// 设置控件最小尺寸的函数
gtk_widget_set_size_request(server_edit_, 400, 30); gtk_widget_set_size_request(server_edit_, 400, 30);
gtk_container_add(GTK_CONTAINER(hbox), server_edit_); gtk_container_add(GTK_CONTAINER(hbox), server_edit_);
// 文本输入控件 端口号
port_edit_ = gtk_entry_new(); port_edit_ = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(port_edit_), port_.c_str()); gtk_entry_set_text(GTK_ENTRY(port_edit_), port_.c_str());
gtk_widget_set_size_request(port_edit_, 70, 30); gtk_widget_set_size_request(port_edit_, 70, 30);
gtk_container_add(GTK_CONTAINER(hbox), port_edit_); gtk_container_add(GTK_CONTAINER(hbox), port_edit_);
// 创建一个带有包含给定文本的子部件的小GtkButton部件 按钮部件
GtkWidget *button = gtk_button_new_with_label("Connect"); GtkWidget *button = gtk_button_new_with_label("Connect");
gtk_widget_set_size_request(button, 70, 30); gtk_widget_set_size_request(button, 70, 30);
// 设置绑定事件
// GLib 库中用于连接信号和信号处理函数的函数。在 GTK 编程中,
// 通过 g_signal_connect 可以将特定的信号与相应的信号处理函数关联起来,以实现用户交互和事件处理。
g_signal_connect(button, "clicked", G_CALLBACK(OnClickedCallback), this); g_signal_connect(button, "clicked", G_CALLBACK(OnClickedCallback), this);
gtk_container_add(GTK_CONTAINER(hbox), button); gtk_container_add(GTK_CONTAINER(hbox), button);
// end 至此水平容器子控件全部添加完毕了
//
GtkWidget *halign = gtk_alignment_new(1, 0, 0, 0); GtkWidget *halign = gtk_alignment_new(1, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), hbox); gtk_container_add(GTK_CONTAINER(halign), hbox);
// 最终添加到这个垂直容器
gtk_box_pack_start(GTK_BOX(vbox_), halign, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox_), halign, FALSE, FALSE, 0);
// 显示窗口及其所有子控件
gtk_widget_show_all(window_); gtk_widget_show_all(window_);
// autoconnect_ 构造时初始化的字段
if (autoconnect_) if (autoconnect_)
// g_idle_add 是 GLib 库中用于注册空闲处理函数的函数。在 GTK 编程中,通过 g_idle_add 可以注册一个空闲处理函数,
// 当主循环处于空闲状态时该函数将被调用。g_idle_add 是 GLib 库中用于注册空闲处理函数的函数。
// 在 GTK 编程中,通过 g_idle_add 可以注册一个空闲处理函数,当主循环处于空闲状态时,该函数将被调用。
// 待触发的函数 模拟按钮点击
g_idle_add(SimulateButtonClick, button); g_idle_add(SimulateButtonClick, button);
} }
// 切换到对等列表界面 待通话界面
void GtkMainWnd::SwitchToPeerList(const Peers &peers) { void GtkMainWnd::SwitchToPeerList(const Peers &peers) {
RTC_LOG(LS_INFO) << __FUNCTION__; RTC_LOG(LS_INFO) << __FUNCTION__;
// peer_list gtk 界面是否有效
if (!peer_list_) { if (!peer_list_) {
// 设置边框宽度0
gtk_container_set_border_width(GTK_CONTAINER(window_), 0); gtk_container_set_border_width(GTK_CONTAINER(window_), 0);
if (vbox_) { if (vbox_) {
// 消逝了垂直容器还有其内的所有子控件
gtk_widget_destroy(vbox_); gtk_widget_destroy(vbox_);
vbox_ = NULL; vbox_ = nullptr;
server_edit_ = NULL; server_edit_ = nullptr;
port_edit_ = NULL; port_edit_ = nullptr;
} else if (draw_area_) { } else if (draw_area_) {
// 用于渲染视频流的绘图表面界面
gtk_widget_destroy(draw_area_); gtk_widget_destroy(draw_area_);
draw_area_ = NULL; draw_area_ = nullptr;
draw_buffer_.reset(); draw_buffer_.reset();
} }
// 初始化待通话界面控件 树形视图控件
peer_list_ = gtk_tree_view_new(); peer_list_ = gtk_tree_view_new();
g_signal_connect(peer_list_, "row-activated", // 绑定信号事件 行激活?事件
G_CALLBACK(OnRowActivatedCallback), this); g_signal_connect(peer_list_, "row-activated", G_CALLBACK(OnRowActivatedCallback), this);
// 用于设置树形视图控件 GtkTreeView 是否显示列标题
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(peer_list_), FALSE); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(peer_list_), FALSE);
// 创建一个树视图,我们用它来显示对等点列表。
InitializeList(peer_list_); InitializeList(peer_list_);
gtk_container_add(GTK_CONTAINER(window_), peer_list_); gtk_container_add(GTK_CONTAINER(window_), peer_list_);
gtk_widget_show_all(window_); gtk_widget_show_all(window_);
} else { } else {
//
GtkListStore *store = GtkListStore *store =
GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(peer_list_))); GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(peer_list_)));
gtk_list_store_clear(store); gtk_list_store_clear(store);
@ -338,42 +401,54 @@ void GtkMainWnd::SwitchToPeerList(const Peers &peers) {
g_idle_add(SimulateLastRowActivated, peer_list_); g_idle_add(SimulateLastRowActivated, peer_list_);
} }
// 切换到流媒体用户通话界面
void GtkMainWnd::SwitchToStreamingUI() { void GtkMainWnd::SwitchToStreamingUI() {
RTC_LOG(LS_INFO) << __FUNCTION__; RTC_LOG(LS_INFO) << __FUNCTION__;
RTC_DCHECK(draw_area_ == NULL); RTC_DCHECK(draw_area_ == nullptr);
// 设置边框
gtk_container_set_border_width(GTK_CONTAINER(window_), 0); gtk_container_set_border_width(GTK_CONTAINER(window_), 0);
// 消逝待通话界面
if (peer_list_) { if (peer_list_) {
gtk_widget_destroy(peer_list_); gtk_widget_destroy(peer_list_);
peer_list_ = NULL; peer_list_ = nullptr;
} }
// 用于创建一个空白的绘图区域控件 GtkDrawingArea。
// GtkDrawingArea 控件通常用于显示自定义绘图,比如绘制图形、图表、图像等。
draw_area_ = gtk_drawing_area_new(); draw_area_ = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window_), draw_area_); gtk_container_add(GTK_CONTAINER(window_), draw_area_);
// 绑定事件开始绘制
g_signal_connect(G_OBJECT(draw_area_), "draw", G_CALLBACK(&::Draw), this); g_signal_connect(G_OBJECT(draw_area_), "draw", G_CALLBACK(&::Draw), this);
gtk_widget_show_all(window_); gtk_widget_show_all(window_);
} }
// 窗口整体消逝
void GtkMainWnd::OnDestroyed(GtkWidget *widget, GdkEvent *event) { void GtkMainWnd::OnDestroyed(GtkWidget *widget, GdkEvent *event) {
callback_->Close(); callback_->Close();
window_ = NULL; window_ = nullptr;
draw_area_ = NULL; draw_area_ = nullptr;
vbox_ = NULL; vbox_ = nullptr;
server_edit_ = NULL; server_edit_ = nullptr;
port_edit_ = NULL; port_edit_ = nullptr;
peer_list_ = NULL; peer_list_ = nullptr;
} }
//
void GtkMainWnd::OnClicked(GtkWidget *widget) { void GtkMainWnd::OnClicked(GtkWidget *widget) {
// Make the connect button insensitive, so that it cannot be clicked more than // Make the connect button insensitive, so that it cannot be clicked more than
// once. Now that the connection includes auto-retry, it should not be // once. Now that the connection includes auto-retry, it should not be
// necessary to click it more than once. // necessary to click it more than once.
// 使连接按钮不敏感,使其不能被点击超过一次。 现在连接包括自动重试,不应该需要多次点击它。
// 用于设置一个控件是否处于可用状态(即是否响应用户输入)。当控件处于可用状态时,用户可以与其交互;当控件不可用时,用户无法与其交互。
gtk_widget_set_sensitive(widget, false); gtk_widget_set_sensitive(widget, false);
server_ = gtk_entry_get_text(GTK_ENTRY(server_edit_)); server_ = gtk_entry_get_text(GTK_ENTRY(server_edit_));
port_ = gtk_entry_get_text(GTK_ENTRY(port_edit_)); port_ = gtk_entry_get_text(GTK_ENTRY(port_edit_));
int port = port_.length() ? atoi(port_.c_str()) : 0; int port = port_.length() ? atoi(port_.c_str()) : 0;
// 回调 登录
callback_->StartLogin(server_, port); callback_->StartLogin(server_, port);
} }

View File

@ -11,7 +11,7 @@
#ifndef EXAMPLES_PEERCONNECTION_CLIENT_LINUX_MAIN_WND_H_ #ifndef EXAMPLES_PEERCONNECTION_CLIENT_LINUX_MAIN_WND_H_
#define EXAMPLES_PEERCONNECTION_CLIENT_LINUX_MAIN_WND_H_ #define EXAMPLES_PEERCONNECTION_CLIENT_LINUX_MAIN_WND_H_
#include <stdint.h> #include <cstdint>
#include <memory> #include <memory>
#include <string> #include <string>
@ -23,6 +23,8 @@
#include "../main_wnd.h" #include "../main_wnd.h"
#include "../peer_connection_client.h" #include "../peer_connection_client.h"
// 主窗口的GTK实现
// Forward declarations. // Forward declarations.
typedef struct _GtkWidget GtkWidget; typedef struct _GtkWidget GtkWidget;
typedef union _GdkEvent GdkEvent; typedef union _GdkEvent GdkEvent;
@ -38,55 +40,82 @@ typedef struct _cairo cairo_t;
class GtkMainWnd : public MainWindow { class GtkMainWnd : public MainWindow {
public: public:
GtkMainWnd(const char *server, int port, bool autoconnect, bool autocall); GtkMainWnd(const char *server, int port, bool autoconnect, bool autocall);
~GtkMainWnd();
virtual void RegisterObserver(MainWndCallback* callback); ~GtkMainWnd() override;
virtual bool IsWindow();
virtual void SwitchToConnectUI();
virtual void SwitchToPeerList(const Peers& peers);
virtual void SwitchToStreamingUI();
virtual void MessageBox(const char* caption, const char* text, bool is_error);
virtual MainWindow::UI current_ui();
virtual void StartLocalRenderer(webrtc::VideoTrackInterface* local_video);
virtual void StopLocalRenderer();
virtual void StartRemoteRenderer(webrtc::VideoTrackInterface* remote_video);
virtual void StopRemoteRenderer();
virtual void QueueUIThreadCallback(int msg_id, void* data); // 继承的纯虚接口
/*virtual */void RegisterObserver(MainWndCallback *callback) override;
/*virtual */bool IsWindow() override;
/*virtual */void SwitchToConnectUI() override;
/*virtual */void SwitchToPeerList(const Peers &peers) override;
/*virtual */void SwitchToStreamingUI() override;
/*virtual */void MessageBox(const char *caption, const char *text, bool is_error) override;
/*virtual */MainWindow::UI current_ui() override;
/*virtual */void StartLocalRenderer(webrtc::VideoTrackInterface *local_video) override;
/*virtual */void StopLocalRenderer() override;
/*virtual */void StartRemoteRenderer(webrtc::VideoTrackInterface *remote_video) override;
/*virtual */void StopRemoteRenderer() override;
/*virtual*/ void QueueUIThreadCallback(int msg_id, void *data) override;
/********************************************************************************************/
/** 接下来的UI操作的函数 **/
/********************************************************************************************/
// Creates and shows the main window with the |Connect UI| enabled. // Creates and shows the main window with the |Connect UI| enabled.
// 创建主窗口 (链接窗口)
bool Create(); bool Create();
// Destroys the window. When the window is destroyed, it ends the // Destroys the window. When the window is destroyed, it ends the main message loop.
// main message loop. // 销毁窗口
bool Destroy(); bool Destroy();
// Callback for when the main window is destroyed. // Callback for when the main window is destroyed.
// 主窗口被销毁时的回调。
void OnDestroyed(GtkWidget *widget, GdkEvent *event); void OnDestroyed(GtkWidget *widget, GdkEvent *event);
// Callback for when the user clicks the "Connect" button. // Callback for when the user clicks the "Connect" button.
// 当用户点击“连接”按钮时的回调。 链接信令服务器的回调
void OnClicked(GtkWidget *widget); void OnClicked(GtkWidget *widget);
// Callback for keystrokes. Used to capture Esc and Return. // Callback for keystrokes. Used to capture Esc and Return.
// 按键事件回调。 用于捕获 Esc 和 Return。
void OnKeyPress(GtkWidget *widget, GdkEventKey *key); void OnKeyPress(GtkWidget *widget, GdkEventKey *key);
// Callback when the user double clicks a peer in order to initiate a // Callback when the user double clicks a peer in order to initiate a connection.
// connection. // 当用户双击对等点以发起连接时的回调。 点击待通信列表后webrtc对话的回调
void OnRowActivated(GtkTreeView* tree_view, void OnRowActivated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column);
GtkTreePath* path,
GtkTreeViewColumn* column);
// 重画时?
void OnRedraw(); void OnRedraw();
// 绘制
void Draw(GtkWidget *widget, cairo_t *cr); void Draw(GtkWidget *widget, cairo_t *cr);
/// 内部类
protected: protected:
// 视频渲染绘制 继承webrtc的sink接收组件
// 一般来说VideoSinkInterface 接口定义了一组方法或函数,用于接收、处理和显示视频数据。
class VideoRenderer : public rtc::VideoSinkInterface<webrtc::VideoFrame> { class VideoRenderer : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
public: public:
VideoRenderer(GtkMainWnd *main_wnd, webrtc::VideoTrackInterface *track_to_render); VideoRenderer(GtkMainWnd *main_wnd, webrtc::VideoTrackInterface *track_to_render);
virtual ~VideoRenderer(); virtual ~VideoRenderer();
// VideoSinkInterface implementation // VideoSinkInterface implementation
// 基类 VideoSinkInterface 视频接收器接口实现
void OnFrame(const webrtc::VideoFrame &frame) override; void OnFrame(const webrtc::VideoFrame &frame) override;
const uint8_t *image() const { return image_.get(); } const uint8_t *image() const { return image_.get(); }
@ -96,32 +125,35 @@ class GtkMainWnd : public MainWindow {
int height() const { return height_; } int height() const { return height_; }
protected: protected:
// 设置渲染大小
void SetSize(int width, int height); void SetSize(int width, int height);
std::unique_ptr<uint8_t[]> image_;
int width_; std::unique_ptr<uint8_t[]> image_; // 某一帧?
int height_; int width_; // 渲染的宽
GtkMainWnd* main_wnd_; int height_; // 渲染的高
rtc::scoped_refptr<webrtc::VideoTrackInterface> rendered_track_; GtkMainWnd *main_wnd_; // 自定义的主窗口抽象类
rtc::scoped_refptr<webrtc::VideoTrackInterface> rendered_track_; // 渲染轨道
}; };
protected: protected:
GtkWidget* window_; // Our main window. /// GtkWidget 可能是部件/窗口/控件
GtkWidget* draw_area_; // The drawing surface for rendering video streams. GtkWidget *window_; // Our main window. // GTK的 主窗口部件
GtkWidget* vbox_; // Container for the Connect UI. GtkWidget *draw_area_; // The drawing surface for rendering video streams. // 用于渲染视频流的绘图表面。
GtkWidget* server_edit_; GtkWidget *vbox_; // Container for the Connect UI. // Connect UI 的容器。
GtkWidget* port_edit_; GtkWidget *server_edit_; // 链接信令服务器地址的输入框控件
GtkWidget* peer_list_; // The list of peers. GtkWidget *port_edit_; // 链接信令服务器端口的输入框控件
MainWndCallback* callback_; GtkWidget *peer_list_; // The list of peers. // Peer 链接的列表 待通话的列表 此时双方已经互相交换了SDP了 协商完了已经
std::string server_; MainWndCallback *callback_; // 回调函数集抽象类 自定义的各种操作的回调
std::string port_; std::string server_; // 链接的地址
bool autoconnect_; std::string port_; // 链接的端口
bool autocall_; bool autoconnect_; // 是否链接?
std::unique_ptr<VideoRenderer> local_renderer_; bool autocall_; // 是否通话?
std::unique_ptr<VideoRenderer> remote_renderer_; std::unique_ptr<GtkMainWnd::VideoRenderer> local_renderer_; // 本地渲染
int width_; std::unique_ptr<GtkMainWnd::VideoRenderer> remote_renderer_; // 远程渲染
int height_; int width_; //
std::unique_ptr<uint8_t[]> draw_buffer_; int height_; //
int draw_buffer_size_; std::unique_ptr<uint8_t[]> draw_buffer_; // 绘制缓存
int draw_buffer_size_; // 绘制缓存大小
}; };
#endif // EXAMPLES_PEERCONNECTION_CLIENT_LINUX_MAIN_WND_H_ #endif // EXAMPLES_PEERCONNECTION_CLIENT_LINUX_MAIN_WND_H_

View File

@ -13,17 +13,22 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#if defined(WEBRTC_POSIX) #if defined(WEBRTC_POSIX)
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "utils.h" #include "utils.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
// 表头终止符
static const char kHeaderTerminator[] = "\r\n\r\n"; static const char kHeaderTerminator[] = "\r\n\r\n";
// 表头终止符 数组的长度
static const int kHeaderTerminatorLength = sizeof(kHeaderTerminator) - 1; static const int kHeaderTerminatorLength = sizeof(kHeaderTerminator) - 1;
// static // static 什么头 不知道???? 像是http头的格式
const char DataSocket::kCrossOriginAllowHeaders[] = const char DataSocket::kCrossOriginAllowHeaders[] =
"Access-Control-Allow-Origin: *\r\n" "Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Credentials: true\r\n" "Access-Control-Allow-Credentials: true\r\n"
@ -52,8 +57,8 @@ WinsockInitializer WinsockInitializer::singleton;
// //
bool SocketBase::Create() { bool SocketBase::Create() {
RTC_DCHECK(!valid()); RTC_DCHECK(!valid()); // 检验本地监听socket是否未有效
socket_ = ::socket(AF_INET, SOCK_STREAM, 0); socket_ = ::socket(AF_INET, SOCK_STREAM, 0); // 初始化后有效了
return valid(); return valid();
} }
@ -268,9 +273,8 @@ bool DataSocket::ParseContentLengthAndType(const char* headers, size_t length) {
bool ListeningSocket::Listen(unsigned short port) { bool ListeningSocket::Listen(unsigned short port) {
RTC_DCHECK(valid()); RTC_DCHECK(valid());
int enabled = 1; int enabled = 1;
if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, // SO_REUSEADDR 端口复用
reinterpret_cast<const char*>(&enabled), if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&enabled), sizeof(enabled)) != 0) {
sizeof(enabled)) != 0) {
printf("setsockopt failed\n"); printf("setsockopt failed\n");
return false; return false;
} }

View File

@ -16,9 +16,11 @@
typedef int socklen_t; typedef int socklen_t;
typedef SOCKET NativeSocket; typedef SOCKET NativeSocket;
#else #else
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/socket.h> #include <sys/socket.h>
#define closesocket close #define closesocket close
typedef int NativeSocket; typedef int NativeSocket;
@ -36,15 +38,21 @@ typedef int NativeSocket;
class SocketBase { class SocketBase {
public: public:
SocketBase() : socket_(INVALID_SOCKET) {} SocketBase() : socket_(INVALID_SOCKET) {}
explicit SocketBase(NativeSocket socket) : socket_(socket) {} explicit SocketBase(NativeSocket socket) : socket_(socket) {}
SocketBase(SocketBase &other) = delete; SocketBase(SocketBase &other) = delete;
SocketBase &operator=(const SocketBase &other) = delete; SocketBase &operator=(const SocketBase &other) = delete;
~SocketBase() { Close(); } ~SocketBase() { Close(); }
NativeSocket socket() const { return socket_; } NativeSocket socket() const { return socket_; }
bool valid() const { return socket_ != INVALID_SOCKET; } bool valid() const { return socket_ != INVALID_SOCKET; }
bool Create(); bool Create();
void Close(); void Close();
protected: protected:
@ -52,6 +60,7 @@ class SocketBase {
}; };
// Represents an HTTP server socket. // Represents an HTTP server socket.
// 表示 HTTP 服务器套接字。
class DataSocket : public SocketBase { class DataSocket : public SocketBase {
public: public:
enum RequestMethod { enum RequestMethod {
@ -68,11 +77,15 @@ class DataSocket : public SocketBase {
static const char kCrossOriginAllowHeaders[]; static const char kCrossOriginAllowHeaders[];
// 标头是否有效 收到的标头
bool headers_received() const { return method_ != INVALID; } bool headers_received() const { return method_ != INVALID; }
RequestMethod method() const { return method_; } RequestMethod method() const { return method_; }
// 获取请求uri /命令?参数=值
const std::string &request_path() const { return request_path_; } const std::string &request_path() const { return request_path_; }
// 请求参数 ?后面拼接的参数
std::string request_arguments() const; std::string request_arguments() const;
const std::string &data() const { return data_; } const std::string &data() const { return data_; }
@ -81,19 +94,23 @@ class DataSocket : public SocketBase {
size_t content_length() const { return content_length_; } size_t content_length() const { return content_length_; }
// 已收到请求?
bool request_received() const { bool request_received() const {
return headers_received() && (method_ != POST || data_received()); return headers_received() && (method_ != POST || data_received());
} }
// 收到的数据
bool data_received() const { bool data_received() const {
return method_ != POST || data_.length() >= content_length_; return method_ != POST || data_.length() >= content_length_;
} }
// 检查请求路径 的 命令 /xxx? ?前的uri
// Checks if the request path (minus arguments) matches a given path. // Checks if the request path (minus arguments) matches a given path.
bool PathEquals(const char *path) const; bool PathEquals(const char *path) const;
// Called when we have received some data from clients. // Called when we have received some data from clients.
// Returns false if an error occurred. // Returns false if an error occurred.
// 当我们从客户端收到一些数据时调用。
// 如果发生错误则返回 false。
bool OnDataAvailable(bool *close_socket); bool OnDataAvailable(bool *close_socket);
// Send a raw buffer of bytes. // Send a raw buffer of bytes.
@ -121,13 +138,18 @@ class DataSocket : public SocketBase {
// A fairly relaxed HTTP header parser. Parses the method, path and // A fairly relaxed HTTP header parser. Parses the method, path and
// content length (POST only) of a request. // content length (POST only) of a request.
// Returns true if a valid request was received and no errors occurred. // Returns true if a valid request was received and no errors occurred.
// 一个相当宽松的 HTTP 标头解析器。解析方法、路径和请求的内容长度(仅限 POST。如果收到有效请求并且没有发生错误则返回 true。
bool ParseHeaders(); bool ParseHeaders();
// Figures out whether the request is a GET or POST and what path is // Figures out whether the request is a GET or POST and what path is
// being requested. // being requested.
// 判断请求是 GET 还是 POST 以及路径是什么,正在被请求。
bool ParseMethodAndPath(const char *begin, size_t len); bool ParseMethodAndPath(const char *begin, size_t len);
// Determines the length of the body and it's mime type. // Determines the length of the body and it's mime type.
// 确定正文的长度及其 mime 类型。
// MIME (Multipurpose Internet Mail Extensions) 是一种在互联网上标识文件类型的标准。
// 在 WebRTC 相关的开发中MIME 类型用于标识传输的数据的类型。
bool ParseContentLengthAndType(const char *headers, size_t length); bool ParseContentLengthAndType(const char *headers, size_t length);
protected: protected:
@ -141,11 +163,15 @@ class DataSocket : public SocketBase {
// The server socket. Accepts connections and generates DataSocket instances // The server socket. Accepts connections and generates DataSocket instances
// for each new connection. // for each new connection.
//
// 服务器套接字。接受连接并生成 DataSocket 实例
// 对于每个新连接。
class ListeningSocket : public SocketBase { class ListeningSocket : public SocketBase {
public: public:
ListeningSocket() {} ListeningSocket() {}
bool Listen(unsigned short port); bool Listen(unsigned short port);
DataSocket *Accept() const; DataSocket *Accept() const;
}; };

View File

@ -10,9 +10,13 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#if defined(WEBRTC_POSIX) #if defined(WEBRTC_POSIX)
#include <sys/select.h> #include <sys/select.h>
#endif #endif
#include <time.h> #include <time.h>
#include <string> #include <string>
@ -102,24 +106,28 @@ int main(int argc, char* argv[]) {
while (!quit) { while (!quit) {
fd_set socket_set; fd_set socket_set;
FD_ZERO(&socket_set); FD_ZERO(&socket_set);
// 监听socket放入socket集合
if (listener.valid()) if (listener.valid())
FD_SET(listener.socket(), &socket_set); FD_SET(listener.socket(), &socket_set);
// 后面建立的socket链接 select 设置 fd 到 fd_array select 维护
for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i)
FD_SET((*i)->socket(), &socket_set); FD_SET((*i)->socket(), &socket_set);
// 超时时间
struct timeval timeout = {10, 0}; struct timeval timeout = {10, 0};
if (select(FD_SETSIZE, &socket_set, NULL, NULL, &timeout) == SOCKET_ERROR) { if (select(FD_SETSIZE, &socket_set, NULL, NULL, &timeout) == SOCKET_ERROR) {
printf("select failed\n"); printf("select failed\n");
break; break;
} }
// 有sokcet链接就执行 客户端socket发送的信令
for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) { for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) {
DataSocket *s = *i; DataSocket *s = *i;
bool socket_done = true; bool socket_done = true; // 套接字完成状态
// 查看是否维护了某个fd描述符
if (FD_ISSET(s->socket(), &socket_set)) { if (FD_ISSET(s->socket(), &socket_set)) {
// 数据可用 与 已接收到请求
if (s->OnDataAvailable(&socket_done) && s->request_received()) { if (s->OnDataAvailable(&socket_done) && s->request_received()) {
ChannelMember* member = clients.Lookup(s); ChannelMember *member = clients.Lookup(s); // 查找是否已经包含当前DataSocket 则该用户是第一次发送信令
if (member || PeerChannel::IsPeerConnection(s)) { if (member || PeerChannel::IsPeerConnection(s)) {
if (!member) { if (!member) {
if (s->PathEquals("/sign_in")) { if (s->PathEquals("/sign_in")) {
@ -129,12 +137,14 @@ int main(int argc, char* argv[]) {
s->Send("500 Error", true, "text/plain", "", s->Send("500 Error", true, "text/plain", "",
"Peer most likely gone."); "Peer most likely gone.");
} }
} else if (member->is_wait_request(s)) { } else if (member->is_wait_request(s)) { // 等待
// no need to do anything. // no need to do anything.
socket_done = false; socket_done = false;
} else { } else {
ChannelMember* target = clients.IsTargetedRequest(s); // 对端的 ChannelMember 连接到服务器的单个对等点。
ChannelMember *target = clients.IsTargetedRequest(s); // to= 后的目标客户端target并转发数据到对端。
if (target) { if (target) {
// 将请求转发到对等点
member->ForwardRequestToPeer(s, target); member->ForwardRequestToPeer(s, target);
} else if (s->PathEquals("/sign_out")) { } else if (s->PathEquals("/sign_out")) {
s->Send("200 OK", true, "text/plain", "", ""); s->Send("200 OK", true, "text/plain", "", "");
@ -159,6 +169,7 @@ int main(int argc, char* argv[]) {
socket_done = false; socket_done = false;
} }
// 响应完消息 断开 socket 从监听的队列中删除
if (socket_done) { if (socket_done) {
printf("Disconnecting socket\n"); printf("Disconnecting socket\n");
clients.OnClosing(s); clients.OnClosing(s);
@ -173,10 +184,11 @@ int main(int argc, char* argv[]) {
clients.CheckForTimeout(); clients.CheckForTimeout();
// 将新连接的DataSocket加入到sockets数组中
if (FD_ISSET(listener.socket(), &socket_set)) { if (FD_ISSET(listener.socket(), &socket_set)) {
DataSocket *s = listener.Accept(); DataSocket *s = listener.Accept();
if (sockets.size() >= kMaxConnections) { if (sockets.size() >= kMaxConnections) {
delete s; // sorry, that's all we can take. delete s; // sorry, that's all we can take. 超过链接最大限制了 拒绝掉了
printf("Connection limit reached\n"); printf("Connection limit reached\n");
} else { } else {
sockets.push_back(s); sockets.push_back(s);

View File

@ -20,15 +20,21 @@
class DataSocket; class DataSocket;
// Represents a single peer connected to the server. // Represents a single peer connected to the server.
// 表示连接到服务器的单个对等点。
class ChannelMember { class ChannelMember {
public: public:
explicit ChannelMember(DataSocket *socket); explicit ChannelMember(DataSocket *socket);
~ChannelMember(); ~ChannelMember();
bool connected() const { return connected_; } bool connected() const { return connected_; }
int id() const { return id_; } int id() const { return id_; }
void set_disconnected() { connected_ = false; } void set_disconnected() { connected_ = false; }
bool is_wait_request(DataSocket *ds) const; bool is_wait_request(DataSocket *ds) const;
const std::string &name() const { return name_; } const std::string &name() const { return name_; }
bool TimedOut(); bool TimedOut();
@ -66,6 +72,7 @@ class ChannelMember {
}; };
// Manages all currently connected peers. // Manages all currently connected peers.
// 管理所有当前连接的对等点。
class PeerChannel { class PeerChannel {
public: public:
typedef std::vector<ChannelMember *> Members; typedef std::vector<ChannelMember *> Members;
@ -103,8 +110,10 @@ class PeerChannel {
protected: protected:
void DeleteAll(); void DeleteAll();
void BroadcastChangedState(const ChannelMember &member, void BroadcastChangedState(const ChannelMember &member,
Members *delivery_failures); Members *delivery_failures);
void HandleDeliveryFailures(Members *failures); void HandleDeliveryFailures(Members *failures);
// Builds a simple list of "name,id\n" entries for each member. // Builds a simple list of "name,id\n" entries for each member.

View File

@ -1,16 +1,16 @@
project(test) project(test)
include_directories(/opt/homebrew/Cellar/sdl2_image/2.8.1/include) #include_directories(/opt/homebrew/Cellar/sdl2_image/2.8.1/include)
find_library(SDL2Image SDL2_image REQUIRED) #find_library(SDL2Image SDL2_image REQUIRED)
find_library(SDL2 SDL2 REQUIRED) #find_library(SDL2 SDL2 REQUIRED)
#
message(${SDL2Image}) #message(${SDL2Image})
message(${SDL2}) #message(${SDL2})
add_executable(test test_main.cc) add_executable(test test_main.cc)
target_link_libraries(test PUBLIC #target_link_libraries(test PUBLIC
${SDL2Image} # ${SDL2Image}
${SDL2} # ${SDL2}
PkgConfig::FFMPEG # PkgConfig::FFMPEG
) #)