diff options
Diffstat (limited to 'src/plugins/Input/mpeg/decoder_mpg123.cpp')
| -rw-r--r-- | src/plugins/Input/mpeg/decoder_mpg123.cpp | 199 |
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; +} |
