feat: support audio on win

This commit is contained in:
Barry 2022-06-14 15:57:32 +08:00
parent 055315f411
commit e70070c0d6
12 changed files with 384 additions and 3 deletions

View file

@ -79,8 +79,8 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network REQUIRED)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network Multimedia REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network Multimedia REQUIRED)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_package(QT NAMES Qt6 Qt5 COMPONENTS X11Extras REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS X11Extras REQUIRED)
@ -108,6 +108,13 @@ set(QC_UIBASE_SOURCES
)
source_group(uibase FILES ${QC_UIBASE_SOURCES})
# audio
set(QC_AUDIO_SOURCES
audio/audiooutput.h
audio/audiooutput.cpp
)
source_group(audio FILES ${QC_AUDIO_SOURCES})
# ui
set(QC_UI_SOURCES
ui/toolform.h
@ -199,6 +206,7 @@ set(QC_PROJECT_SOURCES
${QC_MAIN_SOURCES}
${QC_GROUP_CONTROLLER}
${QC_PLANTFORM_SOURCES}
${QC_AUDIO_SOURCES}
)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
@ -242,6 +250,11 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
get_target_property(QSC_BIN_OUTPUT_PATH ${PROJECT_NAME} RUNTIME_OUTPUT_DIRECTORY)
set(QSC_DEPLOY_PATH ${QSC_BIN_OUTPUT_PATH})
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.bat" "${QSC_BIN_OUTPUT_PATH}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.apk" "${QSC_BIN_OUTPUT_PATH}"
)
endif()
# MacOS
@ -309,5 +322,6 @@ add_subdirectory(QtScrcpyCore)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Multimedia
QtScrcpyCore
)

View file

@ -0,0 +1,178 @@
#include <QTcpSocket>
#include <QHostAddress>
#include <QAudioOutput>
#include <QTime>
#include "audiooutput.h"
AudioOutput::AudioOutput(QObject *parent)
: QObject(parent)
{
connect(&m_sndcpy, &QProcess::readyReadStandardOutput, this, [this]() {
qInfo() << QString("AudioOutput::") << QString(m_sndcpy.readAllStandardOutput());
});
connect(&m_sndcpy, &QProcess::readyReadStandardError, this, [this]() {
qInfo() << QString("AudioOutput::") << QString(m_sndcpy.readAllStandardError());
});
}
AudioOutput::~AudioOutput()
{
if (QProcess::NotRunning != m_sndcpy.state()) {
m_sndcpy.kill();
}
stop();
}
bool AudioOutput::start(const QString& serial, int port)
{
if (m_running) {
stop();
}
QElapsedTimer timeConsumeCount;
timeConsumeCount.start();
bool ret = runSndcpyProcess(serial, port);
qInfo() << "AudioOutput::run sndcpy cost:" << timeConsumeCount.elapsed() << "milliseconds";
if (!ret) {
return ret;
}
startAudioOutput();
startRecvData(port);
m_running = true;
return true;
}
void AudioOutput::stop()
{
if (!m_running) {
return;
}
m_running = false;
stopRecvData();
stopAudioOutput();
}
void AudioOutput::installonly(const QString &serial, int port)
{
runSndcpyProcess(serial, port);
}
bool AudioOutput::runSndcpyProcess(const QString &serial, int port)
{
if (QProcess::NotRunning != m_sndcpy.state()) {
m_sndcpy.kill();
}
QStringList params;
params << serial;
params << QString("%1").arg(port);
m_sndcpy.start("sndcpy.bat", params);
/*
if (!m_sndcpy.waitForStarted()) {
qWarning() << "AudioOutput::start sndcpy.bat failed";
return false;
}
if (!m_sndcpy.waitForFinished()) {
qWarning() << "AudioOutput::sndcpy.bat crashed";
return false;
}
*/
return true;
}
void AudioOutput::startAudioOutput()
{
if (m_audioOutput) {
return;
}
QAudioFormat format;
format.setSampleRate(48000);
format.setChannelCount(2);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(format)) {
qWarning() << "AudioOutput::audio format not supported, cannot play audio.";
return;
}
m_audioOutput = new QAudioOutput(format, this);
connect(m_audioOutput, &QAudioOutput::stateChanged, this, [](QAudio::State state) {
qInfo() << "AudioOutput::audio state changed:" << state;
});
m_audioOutput->setBufferSize(48000*2*15/1000 * 20);
m_outputDevice = m_audioOutput->start();
}
void AudioOutput::stopAudioOutput()
{
if (!m_audioOutput) {
return;
}
m_audioOutput->stop();
delete m_audioOutput;
m_audioOutput = nullptr;
}
void AudioOutput::startRecvData(int port)
{
if (m_workerThread.isRunning()) {
stopRecvData();
}
auto audioSocket = new QTcpSocket();
audioSocket->moveToThread(&m_workerThread);
connect(&m_workerThread, &QThread::finished, audioSocket, &QObject::deleteLater);
connect(this, &AudioOutput::connectTo, audioSocket, [audioSocket](int port) {
audioSocket->connectToHost(QHostAddress::LocalHost, port);
if (!audioSocket->waitForConnected(500)) {
qWarning("AudioOutput::audio socket connect failed");
return;
}
qInfo("AudioOutput::audio socket connect success");
});
connect(audioSocket, &QIODevice::readyRead, audioSocket, [this, audioSocket]() {
qint64 recv = audioSocket->bytesAvailable();
//qDebug() << "AudioOutput::recv data:" << recv;
if (!m_outputDevice) {
return;
}
if (m_buffer.capacity() < recv) {
m_buffer.reserve(recv);
}
qint64 count = audioSocket->read(m_buffer.data(), audioSocket->bytesAvailable());
m_outputDevice->write(m_buffer.data(), count);
});
connect(audioSocket, &QTcpSocket::stateChanged, audioSocket, [](QAbstractSocket::SocketState state) {
qInfo() << "AudioOutput::audio socket state changed:" << state;
});
connect(audioSocket, &QTcpSocket::errorOccurred, audioSocket, [](QAbstractSocket::SocketError error) {
qInfo() << "AudioOutput::audio socket error occurred:" << error;
});
m_workerThread.start();
emit connectTo(port);
}
void AudioOutput::stopRecvData()
{
if (!m_workerThread.isRunning()) {
return;
}
m_workerThread.quit();
m_workerThread.wait();
}

View file

@ -0,0 +1,41 @@
#ifndef AUDIOOUTPUT_H
#define AUDIOOUTPUT_H
#include <QThread>
#include <QProcess>
#include <QPointer>
#include <QVector>
class QAudioOutput;
class QIODevice;
class AudioOutput : public QObject
{
Q_OBJECT
public:
explicit AudioOutput(QObject *parent = nullptr);
~AudioOutput();
bool start(const QString& serial, int port);
void stop();
void installonly(const QString& serial, int port);
private:
bool runSndcpyProcess(const QString& serial, int port);
void startAudioOutput();
void stopAudioOutput();
void startRecvData(int port);
void stopRecvData();
signals:
void connectTo(int port);
private:
QAudioOutput* m_audioOutput = nullptr;
QPointer<QIODevice> m_outputDevice;
QThread m_workerThread;
QProcess m_sndcpy;
QVector<char> m_buffer;
bool m_running = false;
};
#endif // AUDIOOUTPUT_H

Binary file not shown.

View file

@ -273,5 +273,17 @@
<source>refresh devices</source>
<translation>refresh devices</translation>
</message>
<message>
<source>install sndcpy</source>
<translation>install sndcpy</translation>
</message>
<message>
<source>start audio</source>
<translation>start audio</translation>
</message>
<message>
<source>stop audio</source>
<translation>stop audio</translation>
</message>
</context>
</TS>

Binary file not shown.

View file

@ -273,5 +273,17 @@
<source>refresh devices</source>
<translation></translation>
</message>
<message>
<source>install sndcpy</source>
<translation>sndcpy</translation>
</message>
<message>
<source>start audio</source>
<translation></translation>
</message>
<message>
<source>stop audio</source>
<translation></translation>
</message>
</context>
</TS>

BIN
QtScrcpy/sndcpy/sndcpy.apk Normal file

Binary file not shown.

View file

@ -0,0 +1,53 @@
@echo off
echo Begin Runing...
set SNDCPY_PORT=28200
set SNDCPY_APK=sndcpy.apk
set ADB=adb.exe
if not "%1"=="" (
set serial=-s %1
)
if not "%2"=="" (
set SNDCPY_PORT=%2
)
echo Waiting for device %1...
%ADB% %serial% wait-for-device || goto :error
echo Find device %1
for /f "delims=" %%i in ('%ADB% %serial% shell pm path com.rom1v.sndcpy') do set sndcpy_installed=%%i
if "%sndcpy_installed%"=="" (
echo Install %SNDCPY_APK%...
%ADB% %serial% uninstall com.rom1v.sndcpy || goto :error
%ADB% %serial% install -t -r -g %SNDCPY_APK% || goto :error
echo Install %SNDCPY_APK% success
)
echo Request PROJECT_MEDIA permission...
%ADB% %serial% shell appops set com.rom1v.sndcpy PROJECT_MEDIA allow
echo Forward port %SNDCPY_PORT%...
%ADB% %serial% forward tcp:%SNDCPY_PORT% localabstract:sndcpy || goto :error
echo Start %SNDCPY_APK%...
%ADB% %serial% shell am start com.rom1v.sndcpy/.MainActivity || goto :error
:check_start
echo Waiting %SNDCPY_APK% start...
::timeout /T 1 /NOBREAK > nul
%ADB% %serial% shell sleep 0.1
for /f "delims=" %%i in ("%ADB% shell 'ps | grep com.rom1v.sndcpy'") do set sndcpy_started=%%i
if "%sndcpy_started%"=="" (
goto :check_start
)
echo %SNDCPY_APK% started...
echo Ready playing...
::vlc.exe -Idummy --demux rawaud --network-caching=0 --play-and-exit tcp://localhost:%SNDCPY_PORT%
::ffplay.exe -nodisp -autoexit -probesize 32 -sync ext -f s16le -ar 48k -ac 2 tcp://localhost:%SNDCPY_PORT%
goto :EOF
:error
echo Failed with error #%errorlevel%.
exit /b %errorlevel%

View file

@ -687,3 +687,27 @@ const QString &Dialog::getServerPath()
}
return serverPath;
}
void Dialog::on_startAudioBtn_clicked()
{
if (ui->serialBox->count() == 0) {
qWarning() << "No device is connected!";
return;
}
m_audioOutput.start(ui->serialBox->currentText(), 28200);
}
void Dialog::on_stopAudioBtn_clicked()
{
m_audioOutput.stop();
}
void Dialog::on_installSndcpyBtn_clicked()
{
if (ui->serialBox->count() == 0) {
qWarning() << "No device is connected!";
return;
}
m_audioOutput.installonly(ui->serialBox->currentText(), 28200);
}

View file

@ -11,6 +11,7 @@
#include "adbprocess.h"
#include "../QtScrcpyCore/include/QtScrcpyCore.h"
#include "audio/audiooutput.h"
namespace Ui
{
@ -57,6 +58,12 @@ private slots:
void on_useSingleModeCheck_clicked();
void on_serialBox_currentIndexChanged(const QString &arg1);
void on_startAudioBtn_clicked();
void on_stopAudioBtn_clicked();
void on_installSndcpyBtn_clicked();
private:
bool checkAdbRun();
void initUI();
@ -79,6 +86,7 @@ private:
QMenu *m_menu;
QAction *m_showWindow;
QAction *m_quit;
AudioOutput m_audioOutput;
};
#endif // DIALOG_H

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>1293</width>
<height>419</height>
<height>454</height>
</rect>
</property>
<property name="windowTitle">
@ -941,6 +941,45 @@
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="usbWidget3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="installSndcpyBtn">
<property name="text">
<string>install sndcpy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="startAudioBtn">
<property name="text">
<string>start audio</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopAudioBtn">
<property name="text">
<string>stop audio</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>