添加注释

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/glib)
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(${CMAKE_SOURCE_DIR}/include)
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/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(${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)
@ -73,7 +74,7 @@ message( ${AVFOUNDATION_LIBRARY})
#
link_directories(${CMAKE_SOURCE_DIR}/lib/arm_macos)
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)
add_subdirectory(src/client)

View File

@ -24,7 +24,7 @@ target_link_libraries(client
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}
${CORE_AUDIO_LIBRARY}

View File

@ -74,7 +74,9 @@ namespace {
static rtc::scoped_refptr<DummySetSessionDescriptionObserver> Create() {
return rtc::make_ref_counted<DummySetSessionDescriptionObserver>();
}
virtual void OnSuccess() { RTC_LOG(LS_INFO) << __FUNCTION__; }
virtual void OnFailure(webrtc::RTCError error) {
RTC_LOG(LS_INFO) << __FUNCTION__ << " " << ToString(error.type()) << ": "
<< error.message();
@ -96,7 +98,7 @@ namespace {
// int num_devices = info->NumberOfDevices();
// for (int i = 0; i < num_devices; ++i) {
capturer = absl::WrapUnique(
webrtc::VcmCapturer::Create(kWidth, kHeight, kFps , 0));
webrtc::VcmCapturer::Create(kWidth, kHeight, kFps, 0));
if (capturer) {
return rtc::make_ref_counted<CapturerTrackSource>(std::move(capturer));
}
@ -111,9 +113,10 @@ namespace {
: VideoTrackSource(/*remote=*/false), capturer_(std::move(capturer)) {}
private:
rtc::VideoSourceInterface<webrtc::VideoFrame>* source() override {
rtc::VideoSourceInterface<webrtc::VideoFrame> *source() override {
return capturer_.get();
}
std::unique_ptr<webrtc::VcmCapturer> capturer_;
};

View File

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

View File

@ -19,15 +19,15 @@
namespace {
// This is our magical hangup signal.
constexpr char kByeMessage[] = "BYE";
constexpr char kByeMessage[] = "BYE";
// Delay between server connection retries, in milliseconds
constexpr webrtc::TimeDelta kReconnectDelay = webrtc::TimeDelta::Seconds(2);
constexpr webrtc::TimeDelta kReconnectDelay = webrtc::TimeDelta::Seconds(2);
rtc::Socket* CreateClientSocket(int family) {
rtc::Thread* thread = rtc::Thread::Current();
rtc::Socket *CreateClientSocket(int family) {
rtc::Thread *thread = rtc::Thread::Current();
RTC_DCHECK(thread != NULL);
return thread->socketserver()->CreateSocket(family, SOCK_STREAM);
}
}
} // namespace
@ -59,68 +59,78 @@ bool PeerConnectionClient::is_connected() const {
return my_id_ != -1;
}
const Peers& PeerConnectionClient::peers() const {
const Peers &PeerConnectionClient::peers() const {
return peers_;
}
void PeerConnectionClient::RegisterObserver(
PeerConnectionClientObserver* callback) {
PeerConnectionClientObserver *callback) {
RTC_DCHECK(!callback_);
callback_ = callback;
}
void PeerConnectionClient::Connect(const std::string& server,
// 链接服务器
void PeerConnectionClient::Connect(const std::string &server,
int port,
const std::string& client_name) {
const std::string &client_name) {
RTC_DCHECK(!server.empty());
RTC_DCHECK(!client_name.empty());
if (state_ != NOT_CONNECTED) {
RTC_LOG(LS_WARNING)
// 在调用 Connect() 之前,客户端不得连接
<< "The client must not be connected before you can call Connect()";
callback_->OnServerConnectionFailure();
return;
}
if (server.empty() || client_name.empty()) {
callback_->OnServerConnectionFailure();
callback_->OnServerConnectionFailure(); // 链接失败
return;
}
if (port <= 0)
port = kDefaultServerPort;
port = kDefaultServerPort; // 默认值8888
// 设置地址端口客户端名称
server_address_.SetIP(server);
server_address_.SetPort(port);
client_name_ = client_name;
// ip 是否正确 未解析ip
if (server_address_.IsUnresolvedIP()) {
state_ = RESOLVING;
state_ = RESOLVING; // 状态解决?
resolver_ = new rtc::AsyncResolver();
// SignalDone 当地址解析过程完成时会触发此信号。
// 解析结果 类指针 成员函数指针
resolver_->SignalDone.connect(this, &PeerConnectionClient::OnResolveResult);
// AsyncResolver 将执行异步 DNS 解析,并在上发送结果信号
resolver_->Start(server_address_);
} else {
DoConnect();
}
}
void PeerConnectionClient::OnResolveResult(
rtc::AsyncResolverInterface* resolver) {
void PeerConnectionClient::OnResolveResult(rtc::AsyncResolverInterface *resolver) {
if (resolver_->GetError() != 0) {
callback_->OnServerConnectionFailure();
callback_->OnServerConnectionFailure(); // 链接失败
resolver_->Destroy(false);
resolver_ = NULL;
state_ = NOT_CONNECTED;
resolver_ = nullptr;
state_ = NOT_CONNECTED; // 状态未链接
} else {
// 这里有重复检验了一下 因为进入此函数的先决条件是为解析ip
server_address_ = resolver_->address();
DoConnect();
}
}
// 执行链接 链接时 做链接 进行链接 真正执行链接的函数
void PeerConnectionClient::DoConnect() {
control_socket_.reset(CreateClientSocket(server_address_.ipaddr().family()));
hanging_get_.reset(CreateClientSocket(server_address_.ipaddr().family()));
// 初始化套接字信号
InitSocketSignals();
char buffer[1024];
snprintf(buffer, sizeof(buffer), "GET /sign_in?%s HTTP/1.0\r\n\r\n",
client_name_.c_str());
@ -134,7 +144,7 @@ void PeerConnectionClient::DoConnect() {
}
}
bool PeerConnectionClient::SendToPeer(int peer_id, const std::string& message) {
bool PeerConnectionClient::SendToPeer(int peer_id, const std::string &message) {
if (state_ != CONNECTED)
return false;
@ -214,14 +224,14 @@ bool PeerConnectionClient::ConnectControlSocket() {
return true;
}
void PeerConnectionClient::OnConnect(rtc::Socket* socket) {
void PeerConnectionClient::OnConnect(rtc::Socket *socket) {
RTC_DCHECK(!onconnect_data_.empty());
size_t sent = socket->Send(onconnect_data_.c_str(), onconnect_data_.length());
RTC_DCHECK(sent == onconnect_data_.length());
onconnect_data_.clear();
}
void PeerConnectionClient::OnHangingGetConnect(rtc::Socket* socket) {
void PeerConnectionClient::OnHangingGetConnect(rtc::Socket *socket) {
char buffer[1024];
snprintf(buffer, sizeof(buffer), "GET /wait?peer_id=%i HTTP/1.0\r\n\r\n",
my_id_);
@ -231,7 +241,7 @@ void PeerConnectionClient::OnHangingGetConnect(rtc::Socket* socket) {
}
void PeerConnectionClient::OnMessageFromPeer(int peer_id,
const std::string& message) {
const std::string &message) {
if (message.length() == (sizeof(kByeMessage) - 1) &&
message.compare(kByeMessage) == 0) {
callback_->OnPeerDisconnected(peer_id);
@ -240,10 +250,10 @@ void PeerConnectionClient::OnMessageFromPeer(int peer_id,
}
}
bool PeerConnectionClient::GetHeaderValue(const std::string& data,
bool PeerConnectionClient::GetHeaderValue(const std::string &data,
size_t eoh,
const char* header_pattern,
size_t* value) {
const char *header_pattern,
size_t *value) {
RTC_DCHECK(value != NULL);
size_t found = data.find(header_pattern);
if (found != std::string::npos && found < eoh) {
@ -253,10 +263,10 @@ bool PeerConnectionClient::GetHeaderValue(const std::string& data,
return false;
}
bool PeerConnectionClient::GetHeaderValue(const std::string& data,
bool PeerConnectionClient::GetHeaderValue(const std::string &data,
size_t eoh,
const char* header_pattern,
std::string* value) {
const char *header_pattern,
std::string *value) {
RTC_DCHECK(value != NULL);
size_t found = data.find(header_pattern);
if (found != std::string::npos && found < eoh) {
@ -270,9 +280,9 @@ bool PeerConnectionClient::GetHeaderValue(const std::string& data,
return false;
}
bool PeerConnectionClient::ReadIntoBuffer(rtc::Socket* socket,
std::string* data,
size_t* content_length) {
bool PeerConnectionClient::ReadIntoBuffer(rtc::Socket *socket,
std::string *data,
size_t *content_length) {
char buffer[0xffff];
do {
int bytes = socket->Recv(buffer, sizeof(buffer), nullptr);
@ -308,7 +318,7 @@ bool PeerConnectionClient::ReadIntoBuffer(rtc::Socket* socket,
return ret;
}
void PeerConnectionClient::OnRead(rtc::Socket* socket) {
void PeerConnectionClient::OnRead(rtc::Socket *socket) {
size_t content_length = 0;
if (ReadIntoBuffer(socket, &control_data_, &content_length)) {
size_t peer_id = 0, eoh = 0;
@ -360,7 +370,7 @@ void PeerConnectionClient::OnRead(rtc::Socket* socket) {
}
}
void PeerConnectionClient::OnHangingGetRead(rtc::Socket* socket) {
void PeerConnectionClient::OnHangingGetRead(rtc::Socket *socket) {
RTC_LOG(LS_INFO) << __FUNCTION__;
size_t content_length = 0;
if (ReadIntoBuffer(socket, &notification_data_, &content_length)) {
@ -403,10 +413,10 @@ void PeerConnectionClient::OnHangingGetRead(rtc::Socket* socket) {
}
}
bool PeerConnectionClient::ParseEntry(const std::string& entry,
std::string* name,
int* id,
bool* connected) {
bool PeerConnectionClient::ParseEntry(const std::string &entry,
std::string *name,
int *id,
bool *connected) {
RTC_DCHECK(name != NULL);
RTC_DCHECK(id != NULL);
RTC_DCHECK(connected != NULL);
@ -425,7 +435,7 @@ bool PeerConnectionClient::ParseEntry(const std::string& entry,
return !name->empty();
}
int PeerConnectionClient::GetResponseStatus(const std::string& response) {
int PeerConnectionClient::GetResponseStatus(const std::string &response) {
int status = -1;
size_t pos = response.find(' ');
if (pos != std::string::npos)
@ -433,10 +443,10 @@ int PeerConnectionClient::GetResponseStatus(const std::string& response) {
return status;
}
bool PeerConnectionClient::ParseServerResponse(const std::string& response,
bool PeerConnectionClient::ParseServerResponse(const std::string &response,
size_t content_length,
size_t* peer_id,
size_t* eoh) {
size_t *peer_id,
size_t *eoh) {
int status = GetResponseStatus(response.c_str());
if (status != 200) {
RTC_LOG(LS_ERROR) << "Received error from server";
@ -458,7 +468,7 @@ bool PeerConnectionClient::ParseServerResponse(const std::string& response,
return true;
}
void PeerConnectionClient::OnClose(rtc::Socket* socket, int err) {
void PeerConnectionClient::OnClose(rtc::Socket *socket, int err) {
RTC_LOG(LS_INFO) << __FUNCTION__;
socket->Close();

View File

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

View File

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

View File

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

View File

@ -13,17 +13,22 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(WEBRTC_POSIX)
#include <unistd.h>
#endif
#include "utils.h"
#include "rtc_base/checks.h"
// 表头终止符
static const char kHeaderTerminator[] = "\r\n\r\n";
// 表头终止符 数组的长度
static const int kHeaderTerminatorLength = sizeof(kHeaderTerminator) - 1;
// static
// static 什么头 不知道???? 像是http头的格式
const char DataSocket::kCrossOriginAllowHeaders[] =
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Credentials: true\r\n"
@ -52,8 +57,8 @@ WinsockInitializer WinsockInitializer::singleton;
//
bool SocketBase::Create() {
RTC_DCHECK(!valid());
socket_ = ::socket(AF_INET, SOCK_STREAM, 0);
RTC_DCHECK(!valid()); // 检验本地监听socket是否未有效
socket_ = ::socket(AF_INET, SOCK_STREAM, 0); // 初始化后有效了
return valid();
}
@ -75,7 +80,7 @@ std::string DataSocket::request_arguments() const {
return "";
}
bool DataSocket::PathEquals(const char* path) const {
bool DataSocket::PathEquals(const char *path) const {
RTC_DCHECK(path);
size_t args = request_path_.find('?');
if (args != std::string::npos)
@ -83,7 +88,7 @@ bool DataSocket::PathEquals(const char* path) const {
return request_path_.compare(path) == 0;
}
bool DataSocket::OnDataAvailable(bool* close_socket) {
bool DataSocket::OnDataAvailable(bool *close_socket) {
RTC_DCHECK(valid());
char buffer[0xfff] = {0};
int bytes = recv(socket_, buffer, sizeof(buffer), 0);
@ -114,16 +119,16 @@ bool DataSocket::OnDataAvailable(bool* close_socket) {
return ret;
}
bool DataSocket::Send(const std::string& data) const {
bool DataSocket::Send(const std::string &data) const {
return send(socket_, data.data(), static_cast<int>(data.length()), 0) !=
SOCKET_ERROR;
}
bool DataSocket::Send(const std::string& status,
bool DataSocket::Send(const std::string &status,
bool connection_close,
const std::string& content_type,
const std::string& extra_headers,
const std::string& data) const {
const std::string &content_type,
const std::string &extra_headers,
const std::string &data) const {
RTC_DCHECK(valid());
RTC_DCHECK(!status.empty());
std::string buffer("HTTP/1.1 " + status + "\r\n");
@ -177,7 +182,7 @@ bool DataSocket::ParseHeaders() {
RTC_DCHECK(!request_path_.empty());
if (method_ == POST) {
const char* headers = request_headers_.data() + i + 2;
const char *headers = request_headers_.data() + i + 2;
size_t len = request_headers_.length() - i - 2;
if (!ParseContentLengthAndType(headers, len))
return false;
@ -186,9 +191,9 @@ bool DataSocket::ParseHeaders() {
return true;
}
bool DataSocket::ParseMethodAndPath(const char* begin, size_t len) {
bool DataSocket::ParseMethodAndPath(const char *begin, size_t len) {
struct {
const char* method_name;
const char *method_name;
size_t method_name_len;
RequestMethod id;
} supported_methods[] = {
@ -197,7 +202,7 @@ bool DataSocket::ParseMethodAndPath(const char* begin, size_t len) {
{"OPTIONS", 7, OPTIONS},
};
const char* path = NULL;
const char *path = NULL;
for (size_t i = 0; i < ARRAYSIZE(supported_methods); ++i) {
if (len > supported_methods[i].method_name_len &&
isspace(begin[supported_methods[i].method_name_len]) &&
@ -209,7 +214,7 @@ bool DataSocket::ParseMethodAndPath(const char* begin, size_t len) {
}
}
const char* end = begin + len;
const char *end = begin + len;
if (!path || path >= end)
return false;
@ -223,11 +228,11 @@ bool DataSocket::ParseMethodAndPath(const char* begin, size_t len) {
return true;
}
bool DataSocket::ParseContentLengthAndType(const char* headers, size_t length) {
bool DataSocket::ParseContentLengthAndType(const char *headers, size_t length) {
RTC_DCHECK_EQ(content_length_, 0);
RTC_DCHECK(content_type_.empty());
const char* end = headers + length;
const char *end = headers + length;
while (headers && headers < end) {
if (!isspace(headers[0])) {
static const char kContentLength[] = "Content-Length:";
@ -245,7 +250,7 @@ bool DataSocket::ParseContentLengthAndType(const char* headers, size_t length) {
headers += ARRAYSIZE(kContentType) - 1;
while (headers[0] == ' ')
++headers;
const char* type_end = strstr(headers, "\r\n");
const char *type_end = strstr(headers, "\r\n");
if (type_end == NULL)
type_end = end;
content_type_.assign(headers, type_end);
@ -268,9 +273,8 @@ bool DataSocket::ParseContentLengthAndType(const char* headers, size_t length) {
bool ListeningSocket::Listen(unsigned short port) {
RTC_DCHECK(valid());
int enabled = 1;
if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&enabled),
sizeof(enabled)) != 0) {
// SO_REUSEADDR 端口复用
if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&enabled), sizeof(enabled)) != 0) {
printf("setsockopt failed\n");
return false;
}
@ -278,7 +282,7 @@ bool ListeningSocket::Listen(unsigned short port) {
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if (bind(socket_, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) ==
if (bind(socket_, reinterpret_cast<const sockaddr *>(&addr), sizeof(addr)) ==
SOCKET_ERROR) {
printf("bind failed\n");
return false;
@ -286,12 +290,12 @@ bool ListeningSocket::Listen(unsigned short port) {
return listen(socket_, 5) != SOCKET_ERROR;
}
DataSocket* ListeningSocket::Accept() const {
DataSocket *ListeningSocket::Accept() const {
RTC_DCHECK(valid());
struct sockaddr_in addr = {0};
socklen_t size = sizeof(addr);
NativeSocket client =
accept(socket_, reinterpret_cast<sockaddr*>(&addr), &size);
accept(socket_, reinterpret_cast<sockaddr *>(&addr), &size);
if (client == INVALID_SOCKET)
return NULL;

View File

@ -16,9 +16,11 @@
typedef int socklen_t;
typedef SOCKET NativeSocket;
#else
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#define closesocket close
typedef int NativeSocket;
@ -34,26 +36,33 @@ typedef int NativeSocket;
#include <string>
class SocketBase {
public:
public:
SocketBase() : socket_(INVALID_SOCKET) {}
explicit SocketBase(NativeSocket socket) : socket_(socket) {}
SocketBase(SocketBase& other) = delete;
SocketBase& operator=(const SocketBase& other) = delete;
SocketBase(SocketBase &other) = delete;
SocketBase &operator=(const SocketBase &other) = delete;
~SocketBase() { Close(); }
NativeSocket socket() const { return socket_; }
bool valid() const { return socket_ != INVALID_SOCKET; }
bool Create();
void Close();
protected:
protected:
NativeSocket socket_;
};
// Represents an HTTP server socket.
// 表示 HTTP 服务器套接字。
class DataSocket : public SocketBase {
public:
public:
enum RequestMethod {
INVALID,
GET,
@ -68,36 +77,44 @@ class DataSocket : public SocketBase {
static const char kCrossOriginAllowHeaders[];
// 标头是否有效 收到的标头
bool headers_received() const { return method_ != INVALID; }
RequestMethod method() const { return method_; }
const std::string& request_path() const { return request_path_; }
// 获取请求uri /命令?参数=值
const std::string &request_path() const { return request_path_; }
// 请求参数 ?后面拼接的参数
std::string request_arguments() const;
const std::string& data() const { return data_; }
const std::string &data() const { return data_; }
const std::string& content_type() const { return content_type_; }
const std::string &content_type() const { return content_type_; }
size_t content_length() const { return content_length_; }
// 已收到请求?
bool request_received() const {
return headers_received() && (method_ != POST || data_received());
}
// 收到的数据
bool data_received() const {
return method_ != POST || data_.length() >= content_length_;
}
// 检查请求路径 的 命令 /xxx? ?前的uri
// 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.
// Returns false if an error occurred.
bool OnDataAvailable(bool* close_socket);
// 当我们从客户端收到一些数据时调用。
// 如果发生错误则返回 false。
bool OnDataAvailable(bool *close_socket);
// Send a raw buffer of bytes.
bool Send(const std::string& data) const;
bool Send(const std::string &data) const;
// Send an HTTP response. The `status` should start with a valid HTTP
// response code, followed by a string. E.g. "200 OK".
@ -108,29 +125,34 @@ class DataSocket : public SocketBase {
// header terminates with "\r\n".
// `data` is the body of the message. It's length will be specified via
// a "Content-Length" header.
bool Send(const std::string& status,
bool Send(const std::string &status,
bool connection_close,
const std::string& content_type,
const std::string& extra_headers,
const std::string& data) const;
const std::string &content_type,
const std::string &extra_headers,
const std::string &data) const;
// Clears all held state and prepares the socket for receiving a new request.
void Clear();
protected:
protected:
// A fairly relaxed HTTP header parser. Parses the method, path and
// content length (POST only) of a request.
// Returns true if a valid request was received and no errors occurred.
// 一个相当宽松的 HTTP 标头解析器。解析方法、路径和请求的内容长度(仅限 POST。如果收到有效请求并且没有发生错误则返回 true。
bool ParseHeaders();
// Figures out whether the request is a GET or POST and what path is
// being requested.
bool ParseMethodAndPath(const char* begin, size_t len);
// 判断请求是 GET 还是 POST 以及路径是什么,正在被请求。
bool ParseMethodAndPath(const char *begin, size_t len);
// Determines the length of the body and it's mime type.
bool ParseContentLengthAndType(const char* headers, size_t length);
// 确定正文的长度及其 mime 类型。
// MIME (Multipurpose Internet Mail Extensions) 是一种在互联网上标识文件类型的标准。
// 在 WebRTC 相关的开发中MIME 类型用于标识传输的数据的类型。
bool ParseContentLengthAndType(const char *headers, size_t length);
protected:
protected:
RequestMethod method_;
size_t content_length_;
std::string content_type_;
@ -141,12 +163,16 @@ class DataSocket : public SocketBase {
// The server socket. Accepts connections and generates DataSocket instances
// for each new connection.
//
// 服务器套接字。接受连接并生成 DataSocket 实例
// 对于每个新连接。
class ListeningSocket : public SocketBase {
public:
public:
ListeningSocket() {}
bool Listen(unsigned short port);
DataSocket* Accept() const;
DataSocket *Accept() const;
};
#endif // EXAMPLES_PEERCONNECTION_SERVER_DATA_SOCKET_H_

View File

@ -10,9 +10,13 @@
#include <stdio.h>
#include <stdlib.h>
#if defined(WEBRTC_POSIX)
#include <sys/select.h>
#endif
#include <time.h>
#include <string>
@ -40,11 +44,11 @@ ABSL_FLAG(int, port, 8888, "default: 8888");
static const size_t kMaxConnections = (FD_SETSIZE - 2);
void HandleBrowserRequest(DataSocket* ds, bool* quit) {
void HandleBrowserRequest(DataSocket *ds, bool *quit) {
RTC_DCHECK(ds && ds->valid());
RTC_DCHECK(quit);
const std::string& path = ds->request_path();
const std::string &path = ds->request_path();
*quit = (path.compare("/quit") == 0);
@ -65,7 +69,7 @@ void HandleBrowserRequest(DataSocket* ds, bool* quit) {
}
}
int main(int argc, char* argv[]) {
int main(int argc, char *argv[]) {
absl::SetProgramUsageMessage(
"Example usage: ./peerconnection_server --port=8888\n");
absl::ParseCommandLine(argc, argv);
@ -96,30 +100,34 @@ int main(int argc, char* argv[]) {
printf("Server listening on port %i\n", port);
PeerChannel clients;
typedef std::vector<DataSocket*> SocketArray;
typedef std::vector<DataSocket *> SocketArray;
SocketArray sockets;
bool quit = false;
while (!quit) {
fd_set socket_set;
FD_ZERO(&socket_set);
// 监听socket放入socket集合
if (listener.valid())
FD_SET(listener.socket(), &socket_set);
// 后面建立的socket链接 select 设置 fd 到 fd_array select 维护
for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i)
FD_SET((*i)->socket(), &socket_set);
// 超时时间
struct timeval timeout = {10, 0};
if (select(FD_SETSIZE, &socket_set, NULL, NULL, &timeout) == SOCKET_ERROR) {
printf("select failed\n");
break;
}
// 有sokcet链接就执行 客户端socket发送的信令
for (SocketArray::iterator i = sockets.begin(); i != sockets.end(); ++i) {
DataSocket* s = *i;
bool socket_done = true;
DataSocket *s = *i;
bool socket_done = true; // 套接字完成状态
// 查看是否维护了某个fd描述符
if (FD_ISSET(s->socket(), &socket_set)) {
// 数据可用 与 已接收到请求
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) {
if (s->PathEquals("/sign_in")) {
@ -129,12 +137,14 @@ int main(int argc, char* argv[]) {
s->Send("500 Error", true, "text/plain", "",
"Peer most likely gone.");
}
} else if (member->is_wait_request(s)) {
} else if (member->is_wait_request(s)) { // 等待
// no need to do anything.
socket_done = false;
} else {
ChannelMember* target = clients.IsTargetedRequest(s);
// 对端的 ChannelMember 连接到服务器的单个对等点。
ChannelMember *target = clients.IsTargetedRequest(s); // to= 后的目标客户端target并转发数据到对端。
if (target) {
// 将请求转发到对等点
member->ForwardRequestToPeer(s, target);
} else if (s->PathEquals("/sign_out")) {
s->Send("200 OK", true, "text/plain", "", "");
@ -159,6 +169,7 @@ int main(int argc, char* argv[]) {
socket_done = false;
}
// 响应完消息 断开 socket 从监听的队列中删除
if (socket_done) {
printf("Disconnecting socket\n");
clients.OnClosing(s);
@ -173,10 +184,11 @@ int main(int argc, char* argv[]) {
clients.CheckForTimeout();
// 将新连接的DataSocket加入到sockets数组中
if (FD_ISSET(listener.socket(), &socket_set)) {
DataSocket* s = listener.Accept();
DataSocket *s = listener.Accept();
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");
} else {
sockets.push_back(s);

View File

@ -32,7 +32,7 @@
// at this point it is not working correctly in some popular browsers.
static const char kPeerIdHeader[] = "Pragma: ";
static const char* kRequestPaths[] = {
static const char *kRequestPaths[] = {
"/wait",
"/sign_out",
"/message",
@ -52,7 +52,7 @@ const size_t kMaxNameLength = 512;
int ChannelMember::s_member_id_ = 0;
ChannelMember::ChannelMember(DataSocket* socket)
ChannelMember::ChannelMember(DataSocket *socket)
: waiting_socket_(NULL),
id_(++s_member_id_),
connected_(true),
@ -71,7 +71,7 @@ ChannelMember::ChannelMember(DataSocket* socket)
ChannelMember::~ChannelMember() {}
bool ChannelMember::is_wait_request(DataSocket* ds) const {
bool ChannelMember::is_wait_request(DataSocket *ds) const {
return ds && ds->PathEquals(kRequestPaths[kWait]);
}
@ -84,7 +84,7 @@ std::string ChannelMember::GetPeerIdHeader() const {
return ret;
}
bool ChannelMember::NotifyOfOtherMember(const ChannelMember& other) {
bool ChannelMember::NotifyOfOtherMember(const ChannelMember &other) {
RTC_DCHECK_NE(&other, this);
QueueResponse("200 OK", "text/plain", GetPeerIdHeader(), other.GetEntry());
return true;
@ -101,7 +101,7 @@ std::string ChannelMember::GetEntry() const {
return entry;
}
void ChannelMember::ForwardRequestToPeer(DataSocket* ds, ChannelMember* peer) {
void ChannelMember::ForwardRequestToPeer(DataSocket *ds, ChannelMember *peer) {
RTC_DCHECK(peer);
RTC_DCHECK(ds);
@ -117,17 +117,17 @@ void ChannelMember::ForwardRequestToPeer(DataSocket* ds, ChannelMember* peer) {
}
}
void ChannelMember::OnClosing(DataSocket* ds) {
void ChannelMember::OnClosing(DataSocket *ds) {
if (ds == waiting_socket_) {
waiting_socket_ = NULL;
timestamp_ = time(NULL);
}
}
void ChannelMember::QueueResponse(const std::string& status,
const std::string& content_type,
const std::string& extra_headers,
const std::string& data) {
void ChannelMember::QueueResponse(const std::string &status,
const std::string &content_type,
const std::string &extra_headers,
const std::string &data) {
if (waiting_socket_) {
RTC_DCHECK(queue_.empty());
RTC_DCHECK_EQ(waiting_socket_->method(), DataSocket::GET);
@ -148,11 +148,11 @@ void ChannelMember::QueueResponse(const std::string& status,
}
}
void ChannelMember::SetWaitingSocket(DataSocket* ds) {
void ChannelMember::SetWaitingSocket(DataSocket *ds) {
RTC_DCHECK_EQ(ds->method(), DataSocket::GET);
if (ds && !queue_.empty()) {
RTC_DCHECK(!waiting_socket_);
const QueuedResponse& response = queue_.front();
const QueuedResponse &response = queue_.front();
ds->Send(response.status, true, response.content_type,
response.extra_headers, response.data);
queue_.pop();
@ -166,13 +166,13 @@ void ChannelMember::SetWaitingSocket(DataSocket* ds) {
//
// static
bool PeerChannel::IsPeerConnection(const DataSocket* ds) {
bool PeerChannel::IsPeerConnection(const DataSocket *ds) {
RTC_DCHECK(ds);
return (ds->method() == DataSocket::POST && ds->content_length() > 0) ||
(ds->method() == DataSocket::GET && ds->PathEquals("/sign_in"));
}
ChannelMember* PeerChannel::Lookup(DataSocket* ds) const {
ChannelMember *PeerChannel::Lookup(DataSocket *ds) const {
RTC_DCHECK(ds);
if (ds->method() != DataSocket::GET && ds->method() != DataSocket::POST)
@ -208,11 +208,11 @@ ChannelMember* PeerChannel::Lookup(DataSocket* ds) const {
return NULL;
}
ChannelMember* PeerChannel::IsTargetedRequest(const DataSocket* ds) const {
ChannelMember *PeerChannel::IsTargetedRequest(const DataSocket *ds) const {
RTC_DCHECK(ds);
// Regardless of GET or POST, we look for the peer_id parameter
// only in the request_path.
const std::string& path = ds->request_path();
const std::string &path = ds->request_path();
size_t args = path.find('?');
if (args == std::string::npos)
return NULL;
@ -238,9 +238,9 @@ ChannelMember* PeerChannel::IsTargetedRequest(const DataSocket* ds) const {
return NULL;
}
bool PeerChannel::AddMember(DataSocket* ds) {
bool PeerChannel::AddMember(DataSocket *ds) {
RTC_DCHECK(IsPeerConnection(ds));
ChannelMember* new_guy = new ChannelMember(ds);
ChannelMember *new_guy = new ChannelMember(ds);
Members failures;
BroadcastChangedState(*new_guy, &failures);
HandleDeliveryFailures(&failures);
@ -265,9 +265,9 @@ void PeerChannel::CloseAll() {
DeleteAll();
}
void PeerChannel::OnClosing(DataSocket* ds) {
void PeerChannel::OnClosing(DataSocket *ds) {
for (Members::iterator i = members_.begin(); i != members_.end(); ++i) {
ChannelMember* m = (*i);
ChannelMember *m = (*i);
m->OnClosing(ds);
if (!m->connected()) {
i = members_.erase(i);
@ -284,7 +284,7 @@ void PeerChannel::OnClosing(DataSocket* ds) {
void PeerChannel::CheckForTimeout() {
for (Members::iterator i = members_.begin(); i != members_.end(); ++i) {
ChannelMember* m = (*i);
ChannelMember *m = (*i);
if (m->TimedOut()) {
printf("Timeout: %s\n", m->name().c_str());
m->set_disconnected();
@ -305,8 +305,8 @@ void PeerChannel::DeleteAll() {
members_.clear();
}
void PeerChannel::BroadcastChangedState(const ChannelMember& member,
Members* delivery_failures) {
void PeerChannel::BroadcastChangedState(const ChannelMember &member,
Members *delivery_failures) {
// This function should be called prior to DataSocket::Close().
RTC_DCHECK(delivery_failures);
@ -328,12 +328,12 @@ void PeerChannel::BroadcastChangedState(const ChannelMember& member,
}
}
void PeerChannel::HandleDeliveryFailures(Members* failures) {
void PeerChannel::HandleDeliveryFailures(Members *failures) {
RTC_DCHECK(failures);
while (!failures->empty()) {
Members::iterator i = failures->begin();
ChannelMember* member = *i;
ChannelMember *member = *i;
RTC_DCHECK(!member->connected());
failures->erase(i);
BroadcastChangedState(*member, failures);
@ -342,8 +342,8 @@ void PeerChannel::HandleDeliveryFailures(Members* failures) {
}
// Builds a simple list of "name,id\n" entries for each member.
std::string PeerChannel::BuildResponseForNewMember(const ChannelMember& member,
std::string* content_type) {
std::string PeerChannel::BuildResponseForNewMember(const ChannelMember &member,
std::string *content_type) {
RTC_DCHECK(content_type);
*content_type = "text/plain";

View File

@ -20,43 +20,49 @@
class DataSocket;
// Represents a single peer connected to the server.
// 表示连接到服务器的单个对等点。
class ChannelMember {
public:
explicit ChannelMember(DataSocket* socket);
public:
explicit ChannelMember(DataSocket *socket);
~ChannelMember();
bool connected() const { return connected_; }
int id() const { return id_; }
void set_disconnected() { connected_ = false; }
bool is_wait_request(DataSocket* ds) const;
const std::string& name() const { return name_; }
bool is_wait_request(DataSocket *ds) const;
const std::string &name() const { return name_; }
bool TimedOut();
std::string GetPeerIdHeader() const;
bool NotifyOfOtherMember(const ChannelMember& other);
bool NotifyOfOtherMember(const ChannelMember &other);
// Returns a string in the form "name,id\n".
std::string GetEntry() const;
void ForwardRequestToPeer(DataSocket* ds, ChannelMember* peer);
void ForwardRequestToPeer(DataSocket *ds, ChannelMember *peer);
void OnClosing(DataSocket* ds);
void OnClosing(DataSocket *ds);
void QueueResponse(const std::string& status,
const std::string& content_type,
const std::string& extra_headers,
const std::string& data);
void QueueResponse(const std::string &status,
const std::string &content_type,
const std::string &extra_headers,
const std::string &data);
void SetWaitingSocket(DataSocket* ds);
void SetWaitingSocket(DataSocket *ds);
protected:
protected:
struct QueuedResponse {
std::string status, content_type, extra_headers, data;
};
DataSocket* waiting_socket_;
DataSocket *waiting_socket_;
int id_;
bool connected_;
time_t timestamp_;
@ -66,30 +72,31 @@ class ChannelMember {
};
// Manages all currently connected peers.
// 管理所有当前连接的对等点。
class PeerChannel {
public:
typedef std::vector<ChannelMember*> Members;
public:
typedef std::vector<ChannelMember *> Members;
PeerChannel() {}
~PeerChannel() { DeleteAll(); }
const Members& members() const { return members_; }
const Members &members() const { return members_; }
// Returns true if the request should be treated as a new ChannelMember
// request. Otherwise the request is not peerconnection related.
static bool IsPeerConnection(const DataSocket* ds);
static bool IsPeerConnection(const DataSocket *ds);
// Finds a connected peer that's associated with the `ds` socket.
ChannelMember* Lookup(DataSocket* ds) const;
ChannelMember *Lookup(DataSocket *ds) const;
// Checks if the request has a "peer_id" parameter and if so, looks up the
// peer for which the request is targeted at.
ChannelMember* IsTargetedRequest(const DataSocket* ds) const;
ChannelMember *IsTargetedRequest(const DataSocket *ds) const;
// Adds a new ChannelMember instance to the list of connected peers and
// associates it with the socket.
bool AddMember(DataSocket* ds);
bool AddMember(DataSocket *ds);
// Closes all connections and sends a "shutting down" message to all
// connected peers.
@ -97,21 +104,23 @@ class PeerChannel {
// Called when a socket was determined to be closing by the peer (or if the
// connection went dead).
void OnClosing(DataSocket* ds);
void OnClosing(DataSocket *ds);
void CheckForTimeout();
protected:
protected:
void DeleteAll();
void BroadcastChangedState(const ChannelMember& member,
Members* delivery_failures);
void HandleDeliveryFailures(Members* failures);
void BroadcastChangedState(const ChannelMember &member,
Members *delivery_failures);
void HandleDeliveryFailures(Members *failures);
// Builds a simple list of "name,id\n" entries for each member.
std::string BuildResponseForNewMember(const ChannelMember& member,
std::string* content_type);
std::string BuildResponseForNewMember(const ChannelMember &member,
std::string *content_type);
protected:
protected:
Members members_;
};

View File

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