summaryrefslogtreecommitdiff
path: root/pulseplayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pulseplayer.cpp')
-rw-r--r--pulseplayer.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/pulseplayer.cpp b/pulseplayer.cpp
new file mode 100644
index 0000000..ae924a7
--- /dev/null
+++ b/pulseplayer.cpp
@@ -0,0 +1,347 @@
+#include "pulseplayer.h"
+
+//PulsePlayer::PulsePlayer(QString filePath, QString sinkName, QString sourceName, QObject *parent) :
+// QObject(parent)
+PulsePlayer::PulsePlayer(QObject *parent) :
+ QObject(parent), mPlayer(0), mWorker(0)
+{
+}
+
+void PulsePlayer::play()
+{
+ QMap<QString, QString> prop;
+ prop.insert("media.role", mRoleName);
+
+ if(!mPlayer) {
+ mPlayer = new InternalPlayer(mFileName, mSinkName, mSourceName, prop, this);
+ mWorker = new QThread;
+ mPlayer->moveToThread(mWorker);
+ mWorker->start();
+ connect(this,SIGNAL(doPlay(QString)),mPlayer,SLOT(play(QString)));
+ connect(this,SIGNAL(doStop(QString)),mPlayer,SLOT(stop(QString)));
+ connect(mPlayer,SIGNAL(playStateChanged()),this,SIGNAL(playStateChanged()));
+ }
+
+ mPlayer->mIsAlive = true;
+ while(pa_context_get_state(mPlayer->getContext()) != PA_CONTEXT_READY) {
+ //qDebug() << " Waiting for ready state";
+ }
+
+ emit doPlay(mPlayer->mSourceName);
+}
+
+void PulsePlayer::stop()
+{
+ qDebug() << "STOP : " << mPlayer->mSourceName;
+ mPlayer->mIsAlive = false;
+ emit doStop(mPlayer->mSourceName);
+}
+
+InternalPlayer::InternalPlayer(QString fileName, QString sinkName, QString sourceName, QMap<QString, QString> proplist, QObject *parent)
+ : QObject(parent)
+{
+
+ mIsPlaying = false;
+ mIsInitSuccess = false;
+ mBufferIndex = 0;
+ mBufferLength = 0;
+ mBuffer = 0;
+ mStream = 0;
+ mAttrBuff = (pa_buffer_attr*)malloc(sizeof(pa_buffer_attr));
+ mAttrBuff->maxlength = (uint32_t) -1;
+ mAttrBuff->prebuf = (uint32_t) -1;
+ mAttrBuff->tlength = (uint32_t) -1;
+ mAttrBuff->minreq = 8192;
+ mDevice = NULL;
+ mFileName = fileName;
+
+ mPropMap = proplist;
+ mPropList = pa_proplist_new();
+ QMap<QString, QString>::iterator i = mPropMap.begin();
+
+ for(;i != mPropMap.end(); i++) {
+ QString key = i.key();
+ QString value = i.value();
+ pa_proplist_sets(mPropList, key.toUtf8().constData(), value.toUtf8().constData());
+ qDebug() << "Prop List Set " << key.toUtf8().constData() << " / " << value.toUtf8().constData();
+ }
+
+ mFile = new QFile(QString(":/")+fileName);
+ qDebug() << "FILE NAME : " << fileName << " , Exists = " << mFile->exists();
+ bool opened = mFile->open(QIODevice::ReadOnly);
+
+ mSinkName = sinkName;
+ mSourceName = sourceName;
+ if (!opened) {
+ qDebug() << "Cannot open wave file";
+ return;
+ } else {
+ QByteArray content = mFile->readAll();
+ QDataStream header(&content, QIODevice::ReadOnly);
+ header.setByteOrder(QDataStream::LittleEndian);
+ char tempBuffer[1024];
+ short channel;
+ int sampleRate;
+
+ header.readRawData(tempBuffer,22);
+ header >> channel;
+ header >> sampleRate;
+
+ content.clear();
+ qDebug() << "CHANNEL : " << channel;
+ qDebug() << "sampleRate : " << sampleRate;
+
+ mPASampleSpec.channels = channel;
+ mPASampleSpec.format = PA_SAMPLE_S16LE;
+ mPASampleSpec.rate = sampleRate;
+ mFile->close();
+ }
+
+ setupPulseAudio();
+}
+
+bool InternalPlayer::setupPulseAudio()
+{
+ mMainloop = pa_threaded_mainloop_new();
+ if (pa_threaded_mainloop_start(mMainloop)) {
+ qDebug() << "Unable to start pulseaudio threaded mainloop";
+ pa_threaded_mainloop_free(mMainloop);
+ mMainloop= 0;
+ return false;
+ }
+
+ mApi = pa_threaded_mainloop_get_api(mMainloop);
+ mContext = pa_context_new(mApi, "AM Monitor Player");
+ pa_threaded_mainloop_lock(mMainloop);
+ pa_context_set_state_callback(mContext, &InternalPlayer::contextStateCallback,this);
+
+ if (pa_context_connect(mContext, NULL, (pa_context_flags_t)0, NULL)) {
+ qDebug() << " Cannot connect to context" << pa_strerror(pa_context_errno(mContext));
+ pa_context_unref(mContext);
+ pa_threaded_mainloop_free(mMainloop);
+ return false;
+ }
+
+ pa_threaded_mainloop_unlock(mMainloop);
+
+ return true;
+}
+
+void InternalPlayer::setupStream()
+{
+ while(pa_context_get_state(mContext) == PA_OPERATION_RUNNING)
+ pa_threaded_mainloop_wait(mMainloop);
+
+ // Connect or Reconnect the stream
+ if(mStream) {
+ qDebug() << "Reconnect the stream";
+ pa_stream_disconnect(mStream);
+ pa_stream_unref(mStream);
+ mStream = 0;
+ }
+
+ mStream = pa_stream_new_with_proplist(mContext, mFile->fileName().toUtf8().constData(), &mPASampleSpec,NULL, mPropList);
+ qDebug() << "Stream created";
+
+ pa_stream_connect_playback(mStream, mDevice, mAttrBuff, (pa_stream_flags_t)0, NULL, NULL);
+ pa_stream_set_state_callback(mStream, &InternalPlayer::streamStateCallback, this);
+}
+
+void InternalPlayer::waitForOperation(pa_operation *)
+{
+ while(pa_context_get_state(mContext) == PA_OPERATION_RUNNING)
+ pa_threaded_mainloop_wait(mMainloop);
+
+ pa_context_unref(mContext);
+}
+
+void InternalPlayer::streamStateCallback(pa_stream *stream, void *userdata)
+{
+ InternalPlayer *player = reinterpret_cast<InternalPlayer*>(userdata);
+ qDebug() << "stream State Callback " << QString::number(pa_stream_get_state(stream));
+ switch(pa_stream_get_state(stream)) {
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_TERMINATED:
+ case PA_STREAM_CREATING:
+ break;
+ case PA_STREAM_READY:
+ pa_stream_set_write_callback(stream, &InternalPlayer::streamWriteCallback, userdata);
+ qDebug() << "Start to play";
+ break;
+ case PA_STREAM_FAILED:
+ break;
+ }
+
+ pa_threaded_mainloop_signal(player->getMainloop(),0);
+}
+
+void InternalPlayer::streamDrainCallback(pa_stream *, int success, void *userdata)
+{
+ InternalPlayer *player = reinterpret_cast<InternalPlayer*>(userdata);
+ //pa_operation *o = NULL;
+ pa_context *context = player->getContext();
+ qDebug() << "stream Drain Callback";
+
+ if (!success) {
+ qDebug("Failed to drain stream : %s", pa_strerror(pa_context_errno(context)));
+ return;
+ }
+
+ /*
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
+ stream = NULL;
+ player->mIsAlive = false;
+
+ if (!(o = pa_context_drain(context,&InternalPlayer::contextDrainCallback,userdata))) {
+ qDebug("Failed to context drain");
+ pa_context_disconnect(context);
+ } else {
+ pa_operation_unref(o);
+ }
+
+ */
+ player->disconnectStream();
+ pa_threaded_mainloop_signal(player->getMainloop(),0);
+}
+
+void InternalPlayer::disconnectStream()
+{
+ pa_operation *o = NULL;
+ pa_stream_disconnect(mStream);
+ pa_stream_unref(mStream);
+ mStream = 0;
+ mIsAlive = false;
+
+ if (!(o = pa_context_drain(mContext,&InternalPlayer::contextDrainCallback, this))) {
+ qDebug("Failed to context drain");
+ pa_context_disconnect(mContext);
+ } else {
+ pa_operation_unref(o);
+ }
+}
+
+void InternalPlayer::streamWriteCallback(pa_stream *stream, size_t length, void *userdata)
+{
+ InternalPlayer *player = reinterpret_cast<InternalPlayer*>(userdata);
+ player->playInternal(stream, length, userdata);
+ pa_threaded_mainloop_signal(player->getMainloop(),0);
+}
+void InternalPlayer::contextDrainCallback(pa_context *, void *userdata)
+{
+ InternalPlayer *player = reinterpret_cast<InternalPlayer*>(userdata);
+
+ qDebug() << " Context Drain Callback";
+ pa_threaded_mainloop_signal(player->getMainloop(),0);
+}
+
+void InternalPlayer::playInternal(pa_stream *stream, size_t length, void *userdata)
+{
+ InternalPlayer *player = reinterpret_cast<InternalPlayer*>(userdata);
+ pa_operation *o = NULL;
+
+
+ mBufferLength = length;
+
+ //qDebug(" playInternal : %d, %d", mBufferLength, player->isPlaying());
+
+ while (mBufferLength >0 && player->isPlaying()) {
+
+ mBuffer = mFile->read(mBufferLength);
+ int l = mBuffer.size();
+
+ if (l <= 0) {
+ mIsPlaying = false;
+ emit playStateChanged();
+ pa_stream_set_write_callback(stream, NULL, NULL);
+ if (!(o = pa_stream_drain(stream, &InternalPlayer::streamDrainCallback, this))) {
+ qDebug("pa_stream_drain failed : %s", pa_strerror(pa_context_errno(mContext)));
+ }
+ if(o)
+ pa_operation_unref(o);
+ break;
+ }
+
+ if (pa_stream_write(stream, (uint8_t*)mBuffer.constData(), l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
+ qDebug("pa_stream_write() failed : %s", pa_strerror(pa_context_errno(mContext)));
+ break;
+ }
+
+ mBufferLength -= l;
+ mBufferIndex += l;
+ if (!mBufferLength) {
+ mBufferLength = mBufferIndex = 0;
+ }
+ }
+}
+
+void InternalPlayer::contextStateCallback(pa_context *context, void *userdata)
+{
+ InternalPlayer *player = reinterpret_cast<InternalPlayer*>(userdata);
+
+ pa_context_state_t state = pa_context_get_state(context);
+ qDebug() << "Context Received " << QString::number(state);
+ switch(state) {
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ case PA_CONTEXT_READY:
+ // Ready to play, Start setup the stream
+ break;
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ case PA_CONTEXT_UNCONNECTED:
+ break;
+ }
+
+ pa_threaded_mainloop_signal(player->getMainloop(),0);
+}
+
+bool InternalPlayer::openFile()
+{
+ if (!mFile->exists()) {
+ qDebug() << "File Not exists";
+ return false;
+ }
+
+ if (!mFile->isOpen()) {
+ mFile->open(QIODevice::ReadOnly);
+ }
+ mFile->seek(22);
+ return true;
+}
+
+void InternalPlayer::play(QString sourceName)
+{
+
+ mIsPlaying = true;
+ qDebug() << "Try to play";
+ if (!openFile()) {
+ qDebug() << "Error occured";
+ goto finish;
+ }
+
+ if (sourceName != mSourceName) {
+ qDebug() << "Different source name " << sourceName << " / " << mSourceName;
+ return;
+ }
+
+ setupStream();
+ emit playStateChanged();
+ return;
+finish:
+ mIsPlaying = false;
+ mIsAlive = false;
+ emit playStateChanged();
+}
+
+void InternalPlayer::stop(QString sourceName)
+{
+ qDebug() << "SOURCE NAME : " << sourceName << " / " << mSourceName;
+ if (sourceName == mSourceName) {
+ mIsPlaying = false;
+ disconnectStream();
+ emit playStateChanged();
+ }
+}