前沿
之前开发了一个云真机软件,参考的是QtScrcpy这个工程来开发。
开发的时间比较旧了,当时参考的这个工程还是一年多前了,现在回头再看看,源码解析一下这个工程,顺便也重新学习一下。
QtScrcpy地址
解析的版本是v2.1.2
自己工程结构
因为是云真机,所以走的是服务器方式,并不是本地直接连接
服务器代码是java代码,因为之前主要开发安卓,对java比较熟悉,采用的是spring-boot,通过netty框架进行通讯
QtScrcpy视频流处理
关键代码
该代码在QtScrcpyCore中,该仓库类似于一个SDK服务
QtScrcpyCore地址
// 启动scrcpy服务
AdbProcess::execute(const QString &serial, const QStringList &args)
// server.cpp中,被动监听视频流
connect(&m_serverSocket, &QTcpServer::newConnection, this, [this]() {
// 省略
// 校验成功后通知连接成功
emit serverStarted(true);
// 省略
});
// device.cpp
connect(m_server, &Server::serverStarted, this, [this](bool success, const QString &deviceName, const QSize &size) {
// 省略
// 初始化socket并且开始解码
m_stream->installVideoSocket(m_server->removeVideoSocket());
m_stream->startDecode();
// 省略
});
// demuxer.cpp
// 该类是一个线程,视频流在线程中不停读取解码
bool Demuxer::startDecode()
{
if (!m_videoSocket) {
return false;
}
start();
return true;
}
// 具体实现在run中
void Demuxer::run()
{
// 省略
for (;;) {
// 获取视频流包
bool ok = recvPacket(packet);
if (!ok) {
// end of stream
break;
}
// 将解码好的视频流包推送到ui层
ok = pushPacket(packet);
av_packet_unref(packet);
if (!ok) {
// cannot process packet (error already logged)
break;
}
}
// 省略
}
bool Demuxer::recvPacket(AVPacket *packet)
{
quint8 header[HEADER_SIZE];
// 读取socket数据
qint32 r = recvData(header, HEADER_SIZE);
if (r < HEADER_SIZE) {
return false;
}
quint64 ptsFlags = bufferRead64be(header);
quint32 len = bufferRead32be(&header[8]);
if (av_new_packet(packet, static_cast<int>(len))) {
qCritical("Could not allocate packet");
return false;
}
// 读取socket数据
r = recvData(packet->data, static_cast<qint32>(len));
if (r < 0 || static_cast<quint32>(r) < len) {
av_packet_unref(packet);
return false;
}
if (ptsFlags & SC_PACKET_FLAG_CONFIG) {
packet->pts = AV_NOPTS_VALUE;
} else {
packet->pts = ptsFlags & SC_PACKET_PTS_MASK;
}
if (ptsFlags & SC_PACKET_FLAG_KEY_FRAME) {
packet->flags |= AV_PKT_FLAG_KEY;
}
packet->dts = packet->pts;
return true;
}
qint32 Demuxer::recvData(quint8 *buf, qint32 bufSize)
{
if (!buf || !m_videoSocket) {
return 0;
}
// 实际走了socket的read函数
qint32 len = m_videoSocket->subThreadRecvData(buf, bufSize);
return len;
}
bool Demuxer::pushPacket(AVPacket *packet)
{
// 省略
bool ok = parse(packet);
// 省略
}
bool Demuxer::parse(AVPacket *packet)
{
// 省略
bool ok = processFrame(packet);
// 省略
}
bool Demuxer::processFrame(AVPacket *packet)
{
packet->dts = packet->pts;
// 将处理好的帧数据抛出去
// 最终会到decoder.cpp中的onNewFrame方法中
// registerDeviceObserver将帧传递到ui层
emit getFrame(packet);
return true;
}