aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/Input/mpeg/decoder_mpg123.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/Input/mpeg/decoder_mpg123.cpp')
-rw-r--r--src/plugins/Input/mpeg/decoder_mpg123.cpp199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/plugins/Input/mpeg/decoder_mpg123.cpp b/src/plugins/Input/mpeg/decoder_mpg123.cpp
new file mode 100644
index 000000000..4451ff47b
--- /dev/null
+++ b/src/plugins/Input/mpeg/decoder_mpg123.cpp
@@ -0,0 +1,199 @@
+/***************************************************************************
+ * Copyright (C) 2011-2018 by Ilya Kotov *
+ * forkotov02@ya.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <taglib/id3v2header.h>
+#include <taglib/tbytevector.h>
+#include <qmmp/buffer.h>
+#include <qmmp/output.h>
+#include <math.h>
+#include <stdio.h>
+#include "tagextractor.h"
+#include "decoder_mpg123.h"
+
+ssize_t mpg123_read_cb (void *src, void *buf, size_t size)
+{
+ DecoderMPG123 *d = (DecoderMPG123 *) src;
+ return d->input()->read((char *)buf, size);
+}
+
+off_t mpg123_seek_cb(void *src, off_t offset, int whence)
+{
+ DecoderMPG123 *d = (DecoderMPG123 *) src;
+ if (d->input()->isSequential())
+ return -1;
+
+ long start = 0;
+ switch (whence)
+ {
+ case SEEK_END:
+ start = d->input()->size();
+ break;
+
+ case SEEK_CUR:
+ start = d->input()->pos();
+ break;
+
+ case SEEK_SET:
+ default:
+ start = 0;
+ }
+
+ if (!d->input()->seek(start + offset))
+ return -1;
+ return d->input()->pos();
+}
+
+DecoderMPG123::DecoderMPG123(QIODevice *i) : Decoder(i)
+{
+ m_totalTime = 0;
+ m_rate = 0;
+ m_frame_info.bitrate = 0;
+ m_mpg123_encoding = MPG123_ENC_SIGNED_16;
+ m_handle = 0;
+}
+
+DecoderMPG123::~DecoderMPG123()
+{
+ cleanup(m_handle);
+ m_handle = 0;
+}
+
+bool DecoderMPG123::initialize()
+{
+ if (input()->isSequential ()) //for streams only
+ {
+ TagExtractor extractor(input());
+ if(!extractor.id3v2tag().isEmpty())
+ addMetaData(extractor.id3v2tag());
+ }
+
+ int err = mpg123_init();
+ if(err != MPG123_OK)
+ {
+ qWarning("DecoderMPG123: basic setup goes wrong: %s", mpg123_plain_strerror(err));
+ return false;
+ }
+ int channels = 0;
+
+ if(!(m_handle = mpg123_new(0, &err)))
+ {
+ qWarning("DecoderMPG123: basic setup goes wrong: %s", mpg123_plain_strerror(err));
+ return false;
+ }
+
+ mpg123_param (m_handle, MPG123_ADD_FLAGS, MPG123_SEEKBUFFER | MPG123_FUZZY, 0);
+
+ if((err = mpg123_replace_reader_handle(m_handle, mpg123_read_cb, mpg123_seek_cb, 0)) != MPG123_OK)
+ {
+ qWarning("DecoderMPG123: mpg123 error: %s", mpg123_plain_strerror(err));
+ cleanup(m_handle);
+ m_handle = 0;
+ return false;
+ }
+ setMPG123Format(MPG123_ENC_FLOAT_32);
+
+ if((err = mpg123_open_handle(m_handle, this)) != MPG123_OK)
+ {
+ qWarning("DecoderMPG123: mpg123 error: %s", mpg123_plain_strerror(err));
+ cleanup(m_handle);
+ m_handle = 0;
+ return false;
+ }
+
+ if((err = mpg123_getformat(m_handle, &m_rate, &channels, &m_mpg123_encoding)) != MPG123_OK)
+ {
+ qWarning("DecoderMPG123: mpg123 error: %s", mpg123_plain_strerror(err));
+ cleanup(m_handle);
+ m_handle = 0;
+ return false;
+ }
+ //check format
+ if(m_mpg123_encoding != MPG123_ENC_FLOAT_32)
+ {
+ cleanup(m_handle);
+ qWarning("DecoderMPG123: bad encoding: 0x%x!\n", m_mpg123_encoding);
+ m_handle = 0;
+ return false;
+ }
+
+ if(!input()->isSequential())
+ {
+ if((err = mpg123_scan(m_handle)) != MPG123_OK)
+ qWarning("DecoderMPG123: mpg123 error: %s", mpg123_plain_strerror(err));
+ //duration
+ m_totalTime = (qint64) mpg123_length(m_handle) * 1000 / m_rate;
+ }
+ else
+ m_totalTime = 0;
+
+ configure(m_rate, channels, Qmmp::PCM_FLOAT);
+ return true;
+}
+
+qint64 DecoderMPG123::totalTime() const
+{
+ return m_totalTime;
+}
+
+int DecoderMPG123::bitrate() const
+{
+ return m_frame_info.bitrate;
+}
+
+qint64 DecoderMPG123::read(unsigned char *data, qint64 size)
+{
+ size_t done = 0;
+ int err = mpg123_read(m_handle, data, size, &done);
+ if(err != MPG123_DONE && err != MPG123_OK)
+ {
+ qWarning("DecoderMPG123: decoder error: %s", mpg123_plain_strerror(err));
+ return -1;
+ }
+ mpg123_info(m_handle, &m_frame_info);
+ return done;
+}
+
+void DecoderMPG123::seek(qint64 pos)
+{
+ if(m_totalTime > 0)
+ {
+ mpg123_seek(m_handle, pos * m_rate / 1000, SEEK_SET);
+ }
+}
+
+void DecoderMPG123::cleanup(mpg123_handle *handle)
+{
+ if(handle)
+ {
+ mpg123_close(handle);
+ mpg123_delete(handle);
+ }
+}
+
+void DecoderMPG123::setMPG123Format(int encoding)
+{
+ int sample_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 };
+
+ /* Ensure that this output format will not change (it could, when we allow it). */
+ mpg123_format_none(m_handle);
+ for(unsigned int i = 0; i < sizeof(sample_rates)/sizeof(int); ++i)
+ mpg123_format(m_handle, sample_rates[i], MPG123_MONO | MPG123_STEREO, encoding);
+ m_mpg123_encoding = encoding;
+}