parent
055315f411
commit
e70070c0d6
@ -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();
|
||||||
|
}
|
||||||
@ -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.
Binary file not shown.
Binary file not shown.
@ -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%
|
||||||
Loading…
Reference in new issue