Skip to content

Commit 5d62f85

Browse files
authored
Merge pull request #628 from barry-ran/dev
sync
2 parents d5e915d + cfc1775 commit 5d62f85

30 files changed

+757
-257
lines changed

.github/workflows/ubuntu.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
runs-on: ${{ matrix.os }}
1919
strategy:
2020
matrix:
21-
os: [ubuntu-18.04]
21+
os: [ubuntu-22.04]
2222
qt-ver: [5.15.1]
2323
qt-arch-install: [gcc_64]
2424
gcc-arch: [x64]

QtScrcpy/CMakeLists.txt

+23-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ set(CMAKE_AUTOUIC ON)
7979
set(CMAKE_AUTOMOC ON)
8080
set(CMAKE_AUTORCC ON)
8181

82-
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network REQUIRED)
83-
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network REQUIRED)
82+
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network Multimedia REQUIRED)
83+
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network Multimedia REQUIRED)
8484
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
8585
find_package(QT NAMES Qt6 Qt5 COMPONENTS X11Extras REQUIRED)
8686
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS X11Extras REQUIRED)
@@ -108,6 +108,13 @@ set(QC_UIBASE_SOURCES
108108
)
109109
source_group(uibase FILES ${QC_UIBASE_SOURCES})
110110

111+
# audio
112+
set(QC_AUDIO_SOURCES
113+
audio/audiooutput.h
114+
audio/audiooutput.cpp
115+
)
116+
source_group(audio FILES ${QC_AUDIO_SOURCES})
117+
111118
# ui
112119
set(QC_UI_SOURCES
113120
ui/toolform.h
@@ -199,6 +206,7 @@ set(QC_PROJECT_SOURCES
199206
${QC_MAIN_SOURCES}
200207
${QC_GROUP_CONTROLLER}
201208
${QC_PLANTFORM_SOURCES}
209+
${QC_AUDIO_SOURCES}
202210
)
203211

204212
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
@@ -242,6 +250,11 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
242250
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
243251
get_target_property(QSC_BIN_OUTPUT_PATH ${PROJECT_NAME} RUNTIME_OUTPUT_DIRECTORY)
244252
set(QSC_DEPLOY_PATH ${QSC_BIN_OUTPUT_PATH})
253+
254+
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
255+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.bat" "${QSC_BIN_OUTPUT_PATH}"
256+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.apk" "${QSC_BIN_OUTPUT_PATH}"
257+
)
245258
endif()
246259

247260
# MacOS
@@ -255,6 +268,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
255268
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
256269
# config file copy to Contents/MacOS/config
257270
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/../config/config.ini" "${MACOS_BUNDLE_PATH}/MacOS/config/config.ini"
271+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.sh" "${MACOS_BUNDLE_PATH}/MacOS"
272+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.apk" "${MACOS_BUNDLE_PATH}/MacOS"
258273
)
259274

260275
# Step 2. ues MACOSX_PACKAGE_LOCATION copy icns to Resources
@@ -284,6 +299,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
284299
get_target_property(QSC_BIN_OUTPUT_PATH ${PROJECT_NAME} RUNTIME_OUTPUT_DIRECTORY)
285300
set(QSC_DEPLOY_PATH ${QSC_BIN_OUTPUT_PATH})
286301

302+
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
303+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.sh" "${QSC_BIN_OUTPUT_PATH}"
304+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.apk" "${QSC_BIN_OUTPUT_PATH}"
305+
)
306+
287307
set(THREADS_PREFER_PTHREAD_FLAG ON)
288308
find_package(Threads REQUIRED)
289309

@@ -309,5 +329,6 @@ add_subdirectory(QtScrcpyCore)
309329
target_link_libraries(${PROJECT_NAME} PRIVATE
310330
Qt${QT_VERSION_MAJOR}::Widgets
311331
Qt${QT_VERSION_MAJOR}::Network
332+
Qt${QT_VERSION_MAJOR}::Multimedia
312333
QtScrcpyCore
313334
)

QtScrcpy/QtScrcpyCore

Submodule QtScrcpyCore updated 108 files

QtScrcpy/audio/audiooutput.cpp

+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
#include <QTcpSocket>
2+
#include <QHostAddress>
3+
#include <QAudioOutput>
4+
#include <QTime>
5+
#include <QElapsedTimer>
6+
7+
#include "audiooutput.h"
8+
9+
AudioOutput::AudioOutput(QObject *parent)
10+
: QObject(parent)
11+
{
12+
connect(&m_sndcpy, &QProcess::readyReadStandardOutput, this, [this]() {
13+
qInfo() << QString("AudioOutput::") << QString(m_sndcpy.readAllStandardOutput());
14+
});
15+
connect(&m_sndcpy, &QProcess::readyReadStandardError, this, [this]() {
16+
qInfo() << QString("AudioOutput::") << QString(m_sndcpy.readAllStandardError());
17+
});
18+
}
19+
20+
AudioOutput::~AudioOutput()
21+
{
22+
if (QProcess::NotRunning != m_sndcpy.state()) {
23+
m_sndcpy.kill();
24+
}
25+
stop();
26+
}
27+
28+
bool AudioOutput::start(const QString& serial, int port)
29+
{
30+
if (m_running) {
31+
stop();
32+
}
33+
34+
QElapsedTimer timeConsumeCount;
35+
timeConsumeCount.start();
36+
bool ret = runSndcpyProcess(serial, port);
37+
qInfo() << "AudioOutput::run sndcpy cost:" << timeConsumeCount.elapsed() << "milliseconds";
38+
if (!ret) {
39+
return ret;
40+
}
41+
42+
startAudioOutput();
43+
startRecvData(port);
44+
45+
m_running = true;
46+
return true;
47+
}
48+
49+
void AudioOutput::stop()
50+
{
51+
if (!m_running) {
52+
return;
53+
}
54+
m_running = false;
55+
56+
stopRecvData();
57+
stopAudioOutput();
58+
}
59+
60+
void AudioOutput::installonly(const QString &serial, int port)
61+
{
62+
runSndcpyProcess(serial, port, false);
63+
}
64+
65+
bool AudioOutput::runSndcpyProcess(const QString &serial, int port, bool wait)
66+
{
67+
if (QProcess::NotRunning != m_sndcpy.state()) {
68+
m_sndcpy.kill();
69+
}
70+
71+
#ifdef Q_OS_WIN32
72+
QStringList params;
73+
params << serial;
74+
params << QString("%1").arg(port);
75+
m_sndcpy.start("sndcpy.bat", params);
76+
#else
77+
QStringList params;
78+
params << "sndcpy.sh";
79+
params << serial;
80+
params << QString("%1").arg(port);
81+
m_sndcpy.start("bash", params);
82+
#endif
83+
84+
if (!wait) {
85+
return true;
86+
}
87+
88+
if (!m_sndcpy.waitForStarted()) {
89+
qWarning() << "AudioOutput::start sndcpy.bat failed";
90+
return false;
91+
}
92+
if (!m_sndcpy.waitForFinished()) {
93+
qWarning() << "AudioOutput::sndcpy.bat crashed";
94+
return false;
95+
}
96+
97+
return true;
98+
}
99+
100+
void AudioOutput::startAudioOutput()
101+
{
102+
if (m_audioOutput) {
103+
return;
104+
}
105+
106+
QAudioFormat format;
107+
format.setSampleRate(48000);
108+
format.setChannelCount(2);
109+
format.setSampleSize(16);
110+
format.setCodec("audio/pcm");
111+
format.setByteOrder(QAudioFormat::LittleEndian);
112+
format.setSampleType(QAudioFormat::SignedInt);
113+
114+
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
115+
if (!info.isFormatSupported(format)) {
116+
qWarning() << "AudioOutput::audio format not supported, cannot play audio.";
117+
return;
118+
}
119+
120+
m_audioOutput = new QAudioOutput(format, this);
121+
connect(m_audioOutput, &QAudioOutput::stateChanged, this, [](QAudio::State state) {
122+
qInfo() << "AudioOutput::audio state changed:" << state;
123+
});
124+
m_audioOutput->setBufferSize(48000*2*15/1000 * 20);
125+
m_outputDevice = m_audioOutput->start();
126+
}
127+
128+
void AudioOutput::stopAudioOutput()
129+
{
130+
if (!m_audioOutput) {
131+
return;
132+
}
133+
134+
m_audioOutput->stop();
135+
delete m_audioOutput;
136+
m_audioOutput = nullptr;
137+
}
138+
139+
void AudioOutput::startRecvData(int port)
140+
{
141+
if (m_workerThread.isRunning()) {
142+
stopRecvData();
143+
}
144+
145+
auto audioSocket = new QTcpSocket();
146+
audioSocket->moveToThread(&m_workerThread);
147+
connect(&m_workerThread, &QThread::finished, audioSocket, &QObject::deleteLater);
148+
149+
connect(this, &AudioOutput::connectTo, audioSocket, [audioSocket](int port) {
150+
audioSocket->connectToHost(QHostAddress::LocalHost, port);
151+
if (!audioSocket->waitForConnected(500)) {
152+
qWarning("AudioOutput::audio socket connect failed");
153+
return;
154+
}
155+
qInfo("AudioOutput::audio socket connect success");
156+
});
157+
connect(audioSocket, &QIODevice::readyRead, audioSocket, [this, audioSocket]() {
158+
qint64 recv = audioSocket->bytesAvailable();
159+
//qDebug() << "AudioOutput::recv data:" << recv;
160+
161+
if (!m_outputDevice) {
162+
return;
163+
}
164+
if (m_buffer.capacity() < recv) {
165+
m_buffer.reserve(recv);
166+
}
167+
168+
qint64 count = audioSocket->read(m_buffer.data(), audioSocket->bytesAvailable());
169+
m_outputDevice->write(m_buffer.data(), count);
170+
});
171+
connect(audioSocket, &QTcpSocket::stateChanged, audioSocket, [](QAbstractSocket::SocketState state) {
172+
qInfo() << "AudioOutput::audio socket state changed:" << state;
173+
174+
});
175+
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
176+
connect(audioSocket, &QTcpSocket::errorOccurred, audioSocket, [](QAbstractSocket::SocketError error) {
177+
qInfo() << "AudioOutput::audio socket error occurred:" << error;
178+
});
179+
#else
180+
connect(audioSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), audioSocket, [](QAbstractSocket::SocketError error) {
181+
qInfo() << "AudioOutput::audio socket error occurred:" << error;
182+
});
183+
#endif
184+
185+
m_workerThread.start();
186+
emit connectTo(port);
187+
}
188+
189+
void AudioOutput::stopRecvData()
190+
{
191+
if (!m_workerThread.isRunning()) {
192+
return;
193+
}
194+
195+
m_workerThread.quit();
196+
m_workerThread.wait();
197+
}

QtScrcpy/audio/audiooutput.h

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#ifndef AUDIOOUTPUT_H
2+
#define AUDIOOUTPUT_H
3+
4+
#include <QThread>
5+
#include <QProcess>
6+
#include <QPointer>
7+
#include <QVector>
8+
9+
class QAudioOutput;
10+
class QIODevice;
11+
class AudioOutput : public QObject
12+
{
13+
Q_OBJECT
14+
public:
15+
explicit AudioOutput(QObject *parent = nullptr);
16+
~AudioOutput();
17+
18+
bool start(const QString& serial, int port);
19+
void stop();
20+
void installonly(const QString& serial, int port);
21+
22+
private:
23+
bool runSndcpyProcess(const QString& serial, int port, bool wait = true);
24+
void startAudioOutput();
25+
void stopAudioOutput();
26+
void startRecvData(int port);
27+
void stopRecvData();
28+
29+
signals:
30+
void connectTo(int port);
31+
32+
private:
33+
QAudioOutput* m_audioOutput = nullptr;
34+
QPointer<QIODevice> m_outputDevice;
35+
QThread m_workerThread;
36+
QProcess m_sndcpy;
37+
QVector<char> m_buffer;
38+
bool m_running = false;
39+
};
40+
41+
#endif // AUDIOOUTPUT_H

QtScrcpy/main.cpp

+9-6
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,24 @@ int main(int argc, char *argv[])
2222
{
2323
// set env
2424
#ifdef Q_OS_WIN32
25-
qputenv("QTSCRCPY_ADB_PATH", "D:/android/sdk/platform-tools/adb.exe");
25+
qputenv("QTSCRCPY_ADB_PATH", "../../../QtScrcpy/QtScrcpyCore/src/third_party/adb/win/adb.exe");
2626
qputenv("QTSCRCPY_SERVER_PATH", "../../../QtScrcpy/QtScrcpyCore/src/third_party/scrcpy-server");
2727
qputenv("QTSCRCPY_KEYMAP_PATH", "../../../keymap");
2828
qputenv("QTSCRCPY_CONFIG_PATH", "../../../config");
2929
#endif
3030

3131
#ifdef Q_OS_OSX
32-
qputenv("QTSCRCPY_KEYMAP_PATH", "../../../../../keymap");
32+
qputenv("QTSCRCPY_ADB_PATH", "../../../../../../QtScrcpy/QtScrcpyCore/src/third_party/adb/mac/adb");
33+
qputenv("QTSCRCPY_SERVER_PATH", "../../../../../../QtScrcpy/QtScrcpyCore/src/third_party/scrcpy-server");
34+
qputenv("QTSCRCPY_KEYMAP_PATH", "../../../../../../keymap");
35+
qputenv("QTSCRCPY_CONFIG_PATH", "../../../../../../config");
3336
#endif
3437

3538
#ifdef Q_OS_LINUX
36-
qputenv("QTSCRCPY_ADB_PATH", "../../QtScrcpy/QtScrcpyCore/src/third_party/adb/linux/adb");
37-
qputenv("QTSCRCPY_SERVER_PATH", "../../QtScrcpy/QtScrcpyCore/src/third_party/scrcpy-server");
38-
qputenv("QTSCRCPY_CONFIG_PATH", "../../config");
39-
qputenv("QTSCRCPY_KEYMAP_PATH", "../../keymap");
39+
qputenv("QTSCRCPY_ADB_PATH", "../../../QtScrcpy/QtScrcpyCore/src/third_party/adb/linux/adb");
40+
qputenv("QTSCRCPY_SERVER_PATH", "../../../QtScrcpy/QtScrcpyCore/src/third_party/scrcpy-server");
41+
qputenv("QTSCRCPY_KEYMAP_PATH", "../../../keymap");
42+
qputenv("QTSCRCPY_CONFIG_PATH", "../../../config");
4043
#endif
4144

4245
g_msgType = covertLogLevel(Config::getInstance().getLogLevel());

QtScrcpy/res/i18n/en_US.qm

346 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)