Qt C++ 的电视远程投屏程序
功能需求分解
设备发现
使用局域网广播(UDP)发现支持投屏的电视设备。
广播消息内容为一个简单的字符串标识,如 “DISCOVER_TV_DEVICES”。
电视设备需响应此消息,并回传自己的 IP 地址、名称等信息。
设备连接
使用 TCP 连接选中的设备,并准备发送屏幕图像数据。
连接成功后,保持长连接,用于持续发送帧数据。
屏幕捕获
捕获当前主屏幕画面(可选部分屏幕),调整分辨率并压缩为合适的格式(如 JPEG)。
数据传输
编码后的数据通过 TCP Socket 发送到目标设备。
横屏/竖屏切换
通过旋转图像数据实现横屏与竖屏的调整。
更详细的代码实现
- 主程序框架
#include <QApplication>
#include <QMainWindow>
#include <QUdpSocket>
#include <QTcpSocket>
#include <QScreen>
#include <QImage>
#include <QTimer>
#include <QListWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QMessageBox>
class ScreenCaster : public QMainWindow {
Q_OBJECT
public:
explicit ScreenCaster(QWidget *parent = nullptr) : QMainWindow(parent) {
setupUI();
setupConnections();
}
private:
QUdpSocket *udpSocket; // 用于设备发现的 UDP Socket
QTcpSocket *tcpSocket; // 用于数据传输的 TCP Socket
QTimer *captureTimer; // 定时捕获屏幕的计时器
QListWidget *deviceList; // 用于显示发现的设备列表
QPushButton *discoverButton, *connectButton, *landscapeButton, *portraitButton;
QString targetIP; // 目标设备 IP 地址
int targetPort = 12345; // 目标设备端口号
bool isPortrait = false; // 当前是否为竖屏模式
void setupUI() {
// 创建主窗口 UI
QWidget *centralWidget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(centralWidget);
deviceList = new QListWidget(this);
discoverButton = new QPushButton("发现设备", this);
connectButton = new QPushButton("连接设备", this);
landscapeButton = new QPushButton("横屏模式", this);
portraitButton = new QPushButton("竖屏模式", this);
layout->addWidget(deviceList);
layout->addWidget(discoverButton);
layout->addWidget(connectButton);
layout->addWidget(landscapeButton);
layout->addWidget(portraitButton);
this->setCentralWidget(centralWidget);
this->setWindowTitle("电视远程投屏");
this->resize(400, 400);
}
void setupConnections() {
udpSocket = new QUdpSocket(this);
tcpSocket = new QTcpSocket(this);
captureTimer = new QTimer(this);
// 按钮事件连接
connect(discoverButton, &QPushButton::clicked, this, &ScreenCaster::discoverDevices);
connect(connectButton, &QPushButton::clicked, this, &ScreenCaster::connectToSelectedDevice);
connect(landscapeButton, &QPushButton::clicked, this, &ScreenCaster::switchToLandscape);
connect(portraitButton, &QPushButton::clicked, this, &ScreenCaster::switchToPortrait);
// 定时器连接
connect(captureTimer, &QTimer::timeout, this, &ScreenCaster::captureAndSendScreen);
// 处理 UDP 消息
connect(udpSocket, &QUdpSocket::readyRead, this, &ScreenCaster::processDeviceDiscovery);
}
void discoverDevices() {
// 广播设备发现请求
QByteArray data("DISCOVER_TV_DEVICES");
udpSocket->writeDatagram(data, QHostAddress::Broadcast, targetPort);
QMessageBox::information(this, "提示", "正在发现设备...");
}
void processDeviceDiscovery() {
// 处理设备返回的发现信息
while (udpSocket->hasPendingDatagrams()) {
QByteArray buffer;
buffer.resize(udpSocket->pendingDatagramSize());
QHostAddress senderAddress;
udpSocket->readDatagram(buffer.data(), buffer.size(), &senderAddress);
QString deviceInfo = QString("%1 (%2)").arg(QString(buffer), senderAddress.toString());
if (deviceList->findItems(deviceInfo, Qt::MatchExactly).isEmpty()) {
deviceList->addItem(deviceInfo);
}
}
}
void connectToSelectedDevice() {
// 获取选中的设备
QListWidgetItem *selectedItem = deviceList->currentItem();
if (!selectedItem) {
QMessageBox::warning(this, "警告", "请先选择一个设备!");
return;
}
QStringList parts = selectedItem->text().split(' ');
targetIP = parts.last().remove('(').remove(')');
tcpSocket->connectToHost(targetIP, targetPort);
if (tcpSocket->waitForConnected()) {
QMessageBox::information(this, "成功", "连接设备成功!");
captureTimer->start(1000 / 30); // 每秒捕获 30 帧
} else {
QMessageBox::critical(this, "失败", "连接设备失败!");
}
}
void captureAndSendScreen() {
// 捕获屏幕内容
QScreen *screen = QApplication::primaryScreen();
if (!screen) return;
QPixmap pixmap = screen->grabWindow(0);
QImage image = pixmap.toImage();
// 根据模式旋转图像
if (isPortrait) {
QTransform transform;
transform.rotate(90);
image = image.transformed(transform);
}
image = image.scaled(1280, 720, Qt::KeepAspectRatio);
// 压缩并发送
QByteArray buffer;
QBuffer imageBuffer(&buffer);
imageBuffer.open(QIODevice::WriteOnly);
image.save(&imageBuffer, "JPEG", 50); // JPEG 压缩质量 50
if (tcpSocket->state() == QAbstractSocket::ConnectedState) {
tcpSocket->write(buffer);
}
}
void switchToLandscape() {
isPortrait = false;
QMessageBox::information(this, "模式切换", "切换为横屏模式!");
}
void switchToPortrait() {
isPortrait = true;
QMessageBox::information(this, "模式切换", "切换为竖屏模式!");
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ScreenCaster caster;
caster.show();
return app.exec();
}
补充功能和注意事项
电视端支持
电视端需要运行一个简单的服务器程序,用于接收并解码图像数据,并实时显示。
优化和扩展
压缩格式:支持 H.264 等高效编码格式。
多设备支持:支持同时向多个设备投屏。
帧率优化:根据网络状况调整帧率。
错误处理
添加超时机制,处理网络不稳定或设备掉线的情况。
处理设备返回错误时的 UI 提示。
此实现是一个基础版本,可根据需求逐步扩展,例如添加更多协议支持(如 DLNA、Miracast)。
原文地址:https://blog.csdn.net/llhllq2015/article/details/143934068
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!