在 QtScrcpy 项目中,DeviceObserver 是 QtScrcpy 项目中的核心接口类,作为连接底层设备操作和上层 UI 交互的关键桥梁。其作为核心观察者接口,在 QtScrcpyCore
(核心层)和 QtScrcpy
(UI层)之间扮演着 桥梁 角色,其设计体现了清晰的分层架构和观察者模式的应用。
DeviceObserver类角色定位
1.1 设计模式
-
观察者模式:作为抽象观察者接口,允许具体观察者(如 VideoForm)订阅设备事件
-
桥接模式:连接设备核心功能模块与UI表示层
1.2 架构层次
QtScrcpy (UI层) │ ├── VideoForm ├── Controller │ QtScrcpyCore (核心层) │ ├── DeviceObserver (接口) ├── Device (被观察者) └── InputManager
QtScrcpyCore(核心层)
-
职责:处理底层设备连接、视频流解码、输入控制等核心逻辑。
-
关键类:
-
Device
:代表物理设备,管理ADB连接和原始数据流。 -
InputManager
:负责将输入事件转换为ADB命令。 -
DeviceObserver
:作为抽象接口,定义核心层向UI层通知的事件(如视频帧、输入事件等)。
-
QtScrcpy(UI层)
-
职责:实现用户界面、渲染视频、处理用户交互。
-
关键类:
-
VideoForm
:继承自DeviceObserver
,接收视频帧并渲染到界面。 -
Controller
:处理用户输入(鼠标/键盘),通过核心层转发到设备。
-
接口功能分类
DeviceObserver
定义了 两类关键接口,分别用于 数据上行 和 控制下行:
数据上行(Core → UI)
核心层通过这些接口向UI层推送实时数据:
// 视频帧回调(YUV格式)
virtual void onFrame(int width, int height, uint8_t* dataY, uint8_t* dataU, uint8_t* dataV, ...);
// 帧率更新
virtual void updateFPS(quint32 fps);
// 设备剪贴板内容回调
virtual void onDeviceClipboardUpdated(const QString& text);
控制下行(UI → Core)
UI层通过实现这些接口向核心层发送控制命令:
// 输入事件(UI层捕获用户操作后调用)
virtual void mouseEvent(const QMouseEvent* event, const QSize& frameSize);
virtual void keyEvent(const QKeyEvent* event);
// 系统命令(如返回键、音量控制)
virtual void postGoBack();
virtual void postVolumeUp();
2.1 视频数据通道
virtual void onFrame(int width, int height,
uint8_t* dataY, uint8_t* dataU, uint8_t* dataV,
int linesizeY, int linesizeU, int linesizeV);
-
参数说明:
-
YUV420格式视频帧数据
-
linesize包含内存对齐的步长信息
-
-
调用频率:每帧调用,典型60fps设备约16ms调用一次
2.2 输入事件通道
virtual void mouseEvent(const QMouseEvent* from,
const QSize& frameSize,
const QSize& showSize);
-
坐标转换:
-
frameSize
:设备物理分辨率(如1080x1920) -
showSize
:实际显示区域(可能缩放后)
-
-
事件映射:将Qt事件转换为Android输入协议
2.3 系统控制通道
virtual void postGoBack(); // KEYCODE_BACK
virtual void postPower(); // KEYCODE_POWER
virtual void setScreenPowerMode(bool open); // 屏幕唤醒/休眠
典型工作流程
3.1 视频流处理流程
-
核心层:通过ADB获取设备视频流,解码为YUV格式。
-
通知UI:调用
VideoForm::onFrame()
,传递YUV数据。 -
UI层:在
QYUVOpenGLWidget
中渲染帧数据。
3.2 输入事件流程
-
UI层:
VideoForm
捕获鼠标点击事件。 -
转发到核心层:调用
mouseEvent()
,核心层通过InputManager
将其转换为ADB触摸事件。 -
设备执行:ADB命令发送到设备,触发实际点击。
UI层中使用
针对DeviceObserver继承具体实现类和注册观察者
UI层VideoForm 类
class ToolForm;
class FileHandler;
class QYUVOpenGLWidget;
class QLabel;
class VideoForm : public QWidget, public qsc::DeviceObserver
{
Q_OBJECT
public:
explicit VideoForm(bool framelessWindow = false, bool skin = true, QWidget *parent = 0);
~VideoForm();
void staysOnTop(bool top = true);
void updateShowSize(const QSize &newSize);
void updateRender(int width, int height, uint8_t* dataY, uint8_t* dataU, uint8_t* dataV, int linesizeY, int linesizeU, int linesizeV);
void setSerial(const QString& serial);
QRect getGrabCursorRect();
const QSize &frameSize();
void resizeSquare();
void removeBlackRect();
void showFPS(bool show);
void switchFullScreen();
bool isHost();
private:
void onFrame(int width, int height, uint8_t* dataY, uint8_t* dataU, uint8_t* dataV,
int linesizeY, int linesizeU, int linesizeV) override;
void updateFPS(quint32 fps) override;
void grabCursor(bool grab) override;
void updateStyleSheet(bool vertical);
QMargins getMargins(bool vertical);
void initUI();
void showToolForm(bool show = true);
void moveCenter();
void installShortcut();
QRect getScreenRect();
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void paintEvent(QPaintEvent *) override;
void showEvent(QShowEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void closeEvent(QCloseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dropEvent(QDropEvent *event) override;
private:
// ui
Ui::videoForm *ui;
QPointer<ToolForm> m_toolForm;
QPointer<QWidget> m_loadingWidget;
QPointer<QYUVOpenGLWidget> m_videoWidget;
QPointer<QLabel> m_fpsLabel;
//inside member
QSize m_frameSize;
QSize m_normalSize;
QPoint m_dragPosition;
float m_widthHeightRatio = 0.5f;
bool m_skin = true;
QPoint m_fullScreenBeforePos;
QString m_serial;
};
VideoForm 是 QtScrcpy 中负责视频显示和设备交互的核心窗口类,继承自 QWidget 和 qsc::DeviceObserver。
继承关系
-
继承 QWidget:作为可视化窗口组件
-
继承 qsc::DeviceObserver:实现设备观察者接口
主要职责
-
视频帧的渲染显示
-
用户输入事件处理
-
设备控制界面管理
-
窗口样式和行为控制
UI层GroupController类
#include "QtScrcpyCore.h"
class GroupController : public QObject, public qsc::DeviceObserver
{
Q_OBJECT
public:
static GroupController& instance();
void updateDeviceState(const QString& serial);
void addDevice(const QString& serial);
void removeDevice(const QString& serial);
private:
// DeviceObserver
void mouseEvent(const QMouseEvent *from, const QSize &frameSize, const QSize &showSize) override;
void wheelEvent(const QWheelEvent *from, const QSize &frameSize, const QSize &showSize) override;
void keyEvent(const QKeyEvent *from, const QSize &frameSize, const QSize &showSize) override;
void postGoBack() override;
void postGoHome() override;
void postGoMenu() override;
void postAppSwitch() override;
void postPower() override;
void postVolumeUp() override;
void postVolumeDown() override;
void postCopy() override;
void postCut() override;
void setScreenPowerMode(bool open) override;
void expandNotificationPanel() override;
void collapsePanel() override;
void postBackOrScreenOn(bool down) override;
void postTextInput(QString &text) override;
void requestDeviceClipboard() override;
void setDeviceClipboard(bool pause = true) override;
void clipboardPaste() override;
void pushFileRequest(const QString &file, const QString &devicePath = "") override;
void installApkRequest(const QString &apkFile) override;
void screenshot() override;
void showTouch(bool show) override;
private:
explicit GroupController(QObject *parent = nullptr);
bool isHost(const QString& serial);
QSize getFrameSize(const QString& serial);
private:
QVector<QString> m_devices;
};
GroupController 是一个实现设备群控功能的类,继承自 QObject 和 qsc::DeviceObserver,用于管理多个 Android 设备并同步控制它们。
继承关系
-
继承 QObject:获得 Qt 对象系统的支持(信号槽、父子对象管理等)
-
继承 qsc::DeviceObserver:实现设备观察者接口,处理设备输入输出
设计模式
-
单例模式:通过
instance()
静态方法获取唯一实例 -
观察者模式:继承 DeviceObserver 实现对设备事件的观察
线程模型分析
4.1 关键线程
-
视频解码线程:调用onFrame()
-
UI主线程:处理渲染和用户输入
-
ADB I/O线程:处理设备通信
4.2 线程安全实践
// 视频线程到UI线程的帧数据传递
void VideoForm::onFrame(...) {
QMetaObject::invokeMethod(this, [=](){
// 实际渲染操作
}, Qt::QueuedConnection);
}
总结
-
解耦核心与UI
-
核心层不依赖具体UI实现,只需调用
DeviceObserver
接口。 -
UI层自由选择如何渲染视频或处理输入(如支持OpenGL或软件渲染)。
-
-
多平台扩展性
通过实现DeviceObserver
,可轻松适配不同UI框架(如Qt/WxWidgets)或平台(Windows/Linux)。 -
事件驱动的异步模型
核心层通过回调通知UI层,避免轮询,提高效率。 -
单一职责原则
-
QtScrcpyCore
专注设备通信和数据流。 -
QtScrcpy
专注用户交互和渲染。
-