diff options
Diffstat (limited to 'src/decoder')
-rw-r--r-- | src/decoder/DecoderAPI.cxx | 597 | ||||
-rw-r--r-- | src/decoder/DecoderAPI.hxx | 213 | ||||
-rw-r--r-- | src/decoder/DecoderBuffer.cxx | 168 | ||||
-rw-r--r-- | src/decoder/DecoderBuffer.hxx | 111 | ||||
-rw-r--r-- | src/decoder/DecoderCommand.hxx | 32 | ||||
-rw-r--r-- | src/decoder/DecoderControl.cxx | 140 | ||||
-rw-r--r-- | src/decoder/DecoderControl.hxx | 395 | ||||
-rw-r--r-- | src/decoder/DecoderError.cxx | 23 | ||||
-rw-r--r-- | src/decoder/DecoderError.hxx | 25 | ||||
-rw-r--r-- | src/decoder/DecoderInternal.cxx | 101 | ||||
-rw-r--r-- | src/decoder/DecoderInternal.hxx | 125 | ||||
-rw-r--r-- | src/decoder/DecoderList.cxx | 161 | ||||
-rw-r--r-- | src/decoder/DecoderList.hxx | 89 | ||||
-rw-r--r-- | src/decoder/DecoderPlugin.cxx | 42 | ||||
-rw-r--r-- | src/decoder/DecoderPlugin.hxx | 186 | ||||
-rw-r--r-- | src/decoder/DecoderPrint.cxx | 55 | ||||
-rw-r--r-- | src/decoder/DecoderPrint.hxx | 28 | ||||
-rw-r--r-- | src/decoder/DecoderThread.cxx | 481 | ||||
-rw-r--r-- | src/decoder/DecoderThread.hxx | 28 | ||||
-rw-r--r-- | src/decoder/plugins/AdPlugDecoderPlugin.cxx (renamed from src/decoder/AdPlugDecoderPlugin.cxx) | 13 | ||||
-rw-r--r-- | src/decoder/plugins/AdPlugDecoderPlugin.h (renamed from src/decoder/AdPlugDecoderPlugin.h) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/AudiofileDecoderPlugin.cxx (renamed from src/decoder/AudiofileDecoderPlugin.cxx) | 19 | ||||
-rw-r--r-- | src/decoder/plugins/AudiofileDecoderPlugin.hxx (renamed from src/decoder/AudiofileDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/DsdLib.cxx (renamed from src/decoder/DsdLib.cxx) | 8 | ||||
-rw-r--r-- | src/decoder/plugins/DsdLib.hxx (renamed from src/decoder/DsdLib.hxx) | 4 | ||||
-rw-r--r-- | src/decoder/plugins/DsdiffDecoderPlugin.cxx (renamed from src/decoder/DsdiffDecoderPlugin.cxx) | 9 | ||||
-rw-r--r-- | src/decoder/plugins/DsdiffDecoderPlugin.hxx (renamed from src/decoder/DsdiffDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/DsfDecoderPlugin.cxx (renamed from src/decoder/DsfDecoderPlugin.cxx) | 9 | ||||
-rw-r--r-- | src/decoder/plugins/DsfDecoderPlugin.hxx (renamed from src/decoder/DsfDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FaadDecoderPlugin.cxx (renamed from src/decoder/FaadDecoderPlugin.cxx) | 177 | ||||
-rw-r--r-- | src/decoder/plugins/FaadDecoderPlugin.hxx (renamed from src/decoder/FaadDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FfmpegDecoderPlugin.cxx (renamed from src/decoder/FfmpegDecoderPlugin.cxx) | 17 | ||||
-rw-r--r-- | src/decoder/plugins/FfmpegDecoderPlugin.hxx (renamed from src/decoder/FfmpegDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FfmpegMetaData.cxx (renamed from src/decoder/FfmpegMetaData.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FfmpegMetaData.hxx (renamed from src/decoder/FfmpegMetaData.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FlacCommon.cxx (renamed from src/decoder/FlacCommon.cxx) | 10 | ||||
-rw-r--r-- | src/decoder/plugins/FlacCommon.hxx (renamed from src/decoder/FlacCommon.hxx) | 5 | ||||
-rw-r--r-- | src/decoder/plugins/FlacDecoderPlugin.cxx (renamed from src/decoder/FlacDecoderPlugin.cxx) | 15 | ||||
-rw-r--r-- | src/decoder/plugins/FlacDecoderPlugin.h (renamed from src/decoder/FlacDecoderPlugin.h) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FlacDomain.cxx (renamed from src/decoder/FlacDomain.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FlacDomain.hxx (renamed from src/decoder/FlacDomain.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FlacIOHandle.cxx (renamed from src/decoder/FlacIOHandle.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FlacIOHandle.hxx (renamed from src/decoder/FlacIOHandle.hxx) | 4 | ||||
-rw-r--r-- | src/decoder/plugins/FlacInput.cxx (renamed from src/decoder/FlacInput.cxx) | 6 | ||||
-rw-r--r-- | src/decoder/plugins/FlacInput.hxx (renamed from src/decoder/FlacInput.hxx) | 4 | ||||
-rw-r--r-- | src/decoder/plugins/FlacMetadata.cxx (renamed from src/decoder/FlacMetadata.cxx) | 106 | ||||
-rw-r--r-- | src/decoder/plugins/FlacMetadata.hxx (renamed from src/decoder/FlacMetadata.hxx) | 13 | ||||
-rw-r--r-- | src/decoder/plugins/FlacPcm.cxx (renamed from src/decoder/FlacPcm.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FlacPcm.hxx (renamed from src/decoder/FlacPcm.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/FluidsynthDecoderPlugin.cxx (renamed from src/decoder/FluidsynthDecoderPlugin.cxx) | 13 | ||||
-rw-r--r-- | src/decoder/plugins/FluidsynthDecoderPlugin.hxx (renamed from src/decoder/FluidsynthDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/GmeDecoderPlugin.cxx (renamed from src/decoder/GmeDecoderPlugin.cxx) | 35 | ||||
-rw-r--r-- | src/decoder/plugins/GmeDecoderPlugin.hxx (renamed from src/decoder/GmeDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/MadDecoderPlugin.cxx (renamed from src/decoder/MadDecoderPlugin.cxx) | 61 | ||||
-rw-r--r-- | src/decoder/plugins/MadDecoderPlugin.hxx (renamed from src/decoder/MadDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/MikmodDecoderPlugin.cxx (renamed from src/decoder/MikmodDecoderPlugin.cxx) | 19 | ||||
-rw-r--r-- | src/decoder/plugins/MikmodDecoderPlugin.hxx (renamed from src/decoder/MikmodDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/ModplugDecoderPlugin.cxx (renamed from src/decoder/ModplugDecoderPlugin.cxx) | 6 | ||||
-rw-r--r-- | src/decoder/plugins/ModplugDecoderPlugin.hxx (renamed from src/decoder/ModplugDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/MpcdecDecoderPlugin.cxx (renamed from src/decoder/MpcdecDecoderPlugin.cxx) | 8 | ||||
-rw-r--r-- | src/decoder/plugins/MpcdecDecoderPlugin.hxx (renamed from src/decoder/MpcdecDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/Mpg123DecoderPlugin.cxx (renamed from src/decoder/Mpg123DecoderPlugin.cxx) | 16 | ||||
-rw-r--r-- | src/decoder/plugins/Mpg123DecoderPlugin.hxx (renamed from src/decoder/Mpg123DecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OggCodec.cxx (renamed from src/decoder/OggCodec.cxx) | 3 | ||||
-rw-r--r-- | src/decoder/plugins/OggCodec.hxx (renamed from src/decoder/OggCodec.hxx) | 5 | ||||
-rw-r--r-- | src/decoder/plugins/OggFind.cxx (renamed from src/decoder/OggFind.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OggFind.hxx (renamed from src/decoder/OggFind.hxx) | 5 | ||||
-rw-r--r-- | src/decoder/plugins/OggSyncState.hxx (renamed from src/decoder/OggSyncState.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OggUtil.cxx (renamed from src/decoder/OggUtil.cxx) | 4 | ||||
-rw-r--r-- | src/decoder/plugins/OggUtil.hxx (renamed from src/decoder/OggUtil.hxx) | 4 | ||||
-rw-r--r-- | src/decoder/plugins/OpusDecoderPlugin.cxx (renamed from src/decoder/OpusDecoderPlugin.cxx) | 19 | ||||
-rw-r--r-- | src/decoder/plugins/OpusDecoderPlugin.h (renamed from src/decoder/OpusDecoderPlugin.h) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OpusDomain.cxx (renamed from src/decoder/OpusDomain.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OpusDomain.hxx (renamed from src/decoder/OpusDomain.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OpusHead.cxx (renamed from src/decoder/OpusHead.cxx) | 3 | ||||
-rw-r--r-- | src/decoder/plugins/OpusHead.hxx (renamed from src/decoder/OpusHead.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OpusReader.hxx (renamed from src/decoder/OpusReader.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OpusTags.cxx (renamed from src/decoder/OpusTags.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/OpusTags.hxx (renamed from src/decoder/OpusTags.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/PcmDecoderPlugin.cxx (renamed from src/decoder/PcmDecoderPlugin.cxx) | 10 | ||||
-rw-r--r-- | src/decoder/plugins/PcmDecoderPlugin.hxx (renamed from src/decoder/PcmDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/SidplayDecoderPlugin.cxx (renamed from src/decoder/SidplayDecoderPlugin.cxx) | 36 | ||||
-rw-r--r-- | src/decoder/plugins/SidplayDecoderPlugin.hxx (renamed from src/decoder/SidplayDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/SndfileDecoderPlugin.cxx (renamed from src/decoder/SndfileDecoderPlugin.cxx) | 13 | ||||
-rw-r--r-- | src/decoder/plugins/SndfileDecoderPlugin.hxx (renamed from src/decoder/SndfileDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/VorbisComments.cxx (renamed from src/decoder/VorbisComments.cxx) | 22 | ||||
-rw-r--r-- | src/decoder/plugins/VorbisComments.hxx (renamed from src/decoder/VorbisComments.hxx) | 4 | ||||
-rw-r--r-- | src/decoder/plugins/VorbisDecoderPlugin.cxx (renamed from src/decoder/VorbisDecoderPlugin.cxx) | 9 | ||||
-rw-r--r-- | src/decoder/plugins/VorbisDecoderPlugin.h (renamed from src/decoder/VorbisDecoderPlugin.h) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/VorbisDomain.cxx (renamed from src/decoder/VorbisDomain.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/VorbisDomain.hxx (renamed from src/decoder/VorbisDomain.hxx) | 6 | ||||
-rw-r--r-- | src/decoder/plugins/WavpackDecoderPlugin.cxx (renamed from src/decoder/WavpackDecoderPlugin.cxx) | 26 | ||||
-rw-r--r-- | src/decoder/plugins/WavpackDecoderPlugin.hxx (renamed from src/decoder/WavpackDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/WildmidiDecoderPlugin.cxx (renamed from src/decoder/WildmidiDecoderPlugin.cxx) | 13 | ||||
-rw-r--r-- | src/decoder/plugins/WildmidiDecoderPlugin.hxx (renamed from src/decoder/WildmidiDecoderPlugin.hxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/XiphTags.cxx (renamed from src/decoder/XiphTags.cxx) | 2 | ||||
-rw-r--r-- | src/decoder/plugins/XiphTags.hxx (renamed from src/decoder/XiphTags.hxx) | 2 |
97 files changed, 3369 insertions, 468 deletions
diff --git a/src/decoder/DecoderAPI.cxx b/src/decoder/DecoderAPI.cxx new file mode 100644 index 000000000..555e5c89b --- /dev/null +++ b/src/decoder/DecoderAPI.cxx @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "config.h" +#include "DecoderAPI.hxx" +#include "DecoderError.hxx" +#include "pcm/PcmConvert.hxx" +#include "AudioConfig.hxx" +#include "ReplayGainConfig.hxx" +#include "MusicChunk.hxx" +#include "MusicBuffer.hxx" +#include "MusicPipe.hxx" +#include "DecoderControl.hxx" +#include "DecoderInternal.hxx" +#include "DetachedSong.hxx" +#include "input/InputStream.hxx" +#include "util/Error.hxx" +#include "Log.hxx" + +#include <assert.h> +#include <string.h> +#include <math.h> + +void +decoder_initialized(Decoder &decoder, + const AudioFormat audio_format, + bool seekable, float total_time) +{ + DecoderControl &dc = decoder.dc; + struct audio_format_string af_string; + + assert(dc.state == DecoderState::START); + assert(dc.pipe != nullptr); + assert(decoder.convert == nullptr); + assert(decoder.stream_tag == nullptr); + assert(decoder.decoder_tag == nullptr); + assert(!decoder.seeking); + assert(audio_format.IsDefined()); + assert(audio_format.IsValid()); + + dc.in_audio_format = audio_format; + dc.out_audio_format = getOutputAudioFormat(audio_format); + + dc.seekable = seekable; + dc.total_time = total_time; + + FormatDebug(decoder_domain, "audio_format=%s, seekable=%s", + audio_format_to_string(dc.in_audio_format, &af_string), + seekable ? "true" : "false"); + + if (dc.in_audio_format != dc.out_audio_format) { + FormatDebug(decoder_domain, "converting to %s", + audio_format_to_string(dc.out_audio_format, + &af_string)); + + decoder.convert = new PcmConvert(); + + Error error; + if (!decoder.convert->Open(dc.in_audio_format, + dc.out_audio_format, + error)) + decoder.error = std::move(error); + } + + dc.Lock(); + dc.state = DecoderState::DECODE; + dc.client_cond.signal(); + dc.Unlock(); +} + +/** + * Checks if we need an "initial seek". If so, then the initial seek + * is prepared, and the function returns true. + */ +gcc_pure +static bool +decoder_prepare_initial_seek(Decoder &decoder) +{ + const DecoderControl &dc = decoder.dc; + assert(dc.pipe != nullptr); + + if (dc.state != DecoderState::DECODE) + /* wait until the decoder has finished initialisation + (reading file headers etc.) before emitting the + virtual "SEEK" command */ + return false; + + if (decoder.initial_seek_running) + /* initial seek has already begun - override any other + command */ + return true; + + if (decoder.initial_seek_pending) { + if (!dc.seekable) { + /* seeking is not possible */ + decoder.initial_seek_pending = false; + return false; + } + + if (dc.command == DecoderCommand::NONE) { + /* begin initial seek */ + + decoder.initial_seek_pending = false; + decoder.initial_seek_running = true; + return true; + } + + /* skip initial seek when there's another command + (e.g. STOP) */ + + decoder.initial_seek_pending = false; + } + + return false; +} + +/** + * Returns the current decoder command. May return a "virtual" + * synthesized command, e.g. to seek to the beginning of the CUE + * track. + */ +gcc_pure +static DecoderCommand +decoder_get_virtual_command(Decoder &decoder) +{ + if (decoder.error.IsDefined()) + /* an error has occurred: stop the decoder plugin */ + return DecoderCommand::STOP; + + const DecoderControl &dc = decoder.dc; + assert(dc.pipe != nullptr); + + if (decoder_prepare_initial_seek(decoder)) + return DecoderCommand::SEEK; + + return dc.command; +} + +DecoderCommand +decoder_get_command(Decoder &decoder) +{ + return decoder_get_virtual_command(decoder); +} + +void +decoder_command_finished(Decoder &decoder) +{ + DecoderControl &dc = decoder.dc; + + dc.Lock(); + + assert(dc.command != DecoderCommand::NONE || + decoder.initial_seek_running); + assert(dc.command != DecoderCommand::SEEK || + decoder.initial_seek_running || + dc.seek_error || decoder.seeking); + assert(dc.pipe != nullptr); + + if (decoder.initial_seek_running) { + assert(!decoder.seeking); + assert(decoder.chunk == nullptr); + assert(dc.pipe->IsEmpty()); + + decoder.initial_seek_running = false; + decoder.timestamp = dc.start_ms / 1000.; + dc.Unlock(); + return; + } + + if (decoder.seeking) { + decoder.seeking = false; + + /* delete frames from the old song position */ + + if (decoder.chunk != nullptr) { + dc.buffer->Return(decoder.chunk); + decoder.chunk = nullptr; + } + + dc.pipe->Clear(*dc.buffer); + + decoder.timestamp = dc.seek_where; + } + + dc.command = DecoderCommand::NONE; + dc.client_cond.signal(); + dc.Unlock(); +} + +double decoder_seek_where(gcc_unused Decoder & decoder) +{ + const DecoderControl &dc = decoder.dc; + + assert(dc.pipe != nullptr); + + if (decoder.initial_seek_running) + return dc.start_ms / 1000.; + + assert(dc.command == DecoderCommand::SEEK); + + decoder.seeking = true; + + return dc.seek_where; +} + +void decoder_seek_error(Decoder & decoder) +{ + DecoderControl &dc = decoder.dc; + + assert(dc.pipe != nullptr); + + if (decoder.initial_seek_running) { + /* d'oh, we can't seek to the sub-song start position, + what now? - no idea, ignoring the problem for now. */ + decoder.initial_seek_running = false; + return; + } + + assert(dc.command == DecoderCommand::SEEK); + + dc.seek_error = true; + decoder.seeking = false; + + decoder_command_finished(decoder); +} + +/** + * Should be read operation be cancelled? That is the case when the + * player thread has sent a command such as "STOP". + */ +gcc_pure +static inline bool +decoder_check_cancel_read(const Decoder *decoder) +{ + if (decoder == nullptr) + return false; + + const DecoderControl &dc = decoder->dc; + if (dc.command == DecoderCommand::NONE) + return false; + + /* ignore the SEEK command during initialization, the plugin + should handle that after it has initialized successfully */ + if (dc.command == DecoderCommand::SEEK && + (dc.state == DecoderState::START || decoder->seeking)) + return false; + + return true; +} + +size_t +decoder_read(Decoder *decoder, + InputStream &is, + void *buffer, size_t length) +{ + /* XXX don't allow decoder==nullptr */ + + assert(decoder == nullptr || + decoder->dc.state == DecoderState::START || + decoder->dc.state == DecoderState::DECODE); + assert(buffer != nullptr); + + if (length == 0) + return 0; + + is.Lock(); + + while (true) { + if (decoder_check_cancel_read(decoder)) { + is.Unlock(); + return 0; + } + + if (is.IsAvailable()) + break; + + is.cond.wait(is.mutex); + } + + Error error; + size_t nbytes = is.Read(buffer, length, error); + assert(nbytes == 0 || !error.IsDefined()); + assert(nbytes > 0 || error.IsDefined() || is.IsEOF()); + + is.Unlock(); + + if (gcc_unlikely(nbytes == 0 && error.IsDefined())) + LogError(error); + + return nbytes; +} + +bool +decoder_read_full(Decoder *decoder, InputStream &is, + void *_buffer, size_t size) +{ + uint8_t *buffer = (uint8_t *)_buffer; + + while (size > 0) { + size_t nbytes = decoder_read(decoder, is, buffer, size); + if (nbytes == 0) + return false; + + buffer += nbytes; + size -= nbytes; + } + + return true; +} + +bool +decoder_skip(Decoder *decoder, InputStream &is, size_t size) +{ + while (size > 0) { + char buffer[1024]; + size_t nbytes = decoder_read(decoder, is, buffer, + std::min(sizeof(buffer), size)); + if (nbytes == 0) + return false; + + size -= nbytes; + } + + return true; +} + +void +decoder_timestamp(Decoder &decoder, double t) +{ + assert(t >= 0); + + decoder.timestamp = t; +} + +/** + * Sends a #tag as-is to the music pipe. Flushes the current chunk + * (decoder.chunk) if there is one. + */ +static DecoderCommand +do_send_tag(Decoder &decoder, const Tag &tag) +{ + struct music_chunk *chunk; + + if (decoder.chunk != nullptr) { + /* there is a partial chunk - flush it, we want the + tag in a new chunk */ + decoder.FlushChunk(); + } + + assert(decoder.chunk == nullptr); + + chunk = decoder.GetChunk(); + if (chunk == nullptr) { + assert(decoder.dc.command != DecoderCommand::NONE); + return decoder.dc.command; + } + + chunk->tag = new Tag(tag); + return DecoderCommand::NONE; +} + +static bool +update_stream_tag(Decoder &decoder, InputStream *is) +{ + Tag *tag; + + tag = is != nullptr + ? is->LockReadTag() + : nullptr; + if (tag == nullptr) { + tag = decoder.song_tag; + if (tag == nullptr) + return false; + + /* no stream tag present - submit the song tag + instead */ + decoder.song_tag = nullptr; + } + + delete decoder.stream_tag; + decoder.stream_tag = tag; + return true; +} + +DecoderCommand +decoder_data(Decoder &decoder, + InputStream *is, + const void *data, size_t length, + uint16_t kbit_rate) +{ + DecoderControl &dc = decoder.dc; + DecoderCommand cmd; + + assert(dc.state == DecoderState::DECODE); + assert(dc.pipe != nullptr); + assert(length % dc.in_audio_format.GetFrameSize() == 0); + + dc.Lock(); + cmd = decoder_get_virtual_command(decoder); + dc.Unlock(); + + if (cmd == DecoderCommand::STOP || cmd == DecoderCommand::SEEK || + length == 0) + return cmd; + + /* send stream tags */ + + if (update_stream_tag(decoder, is)) { + if (decoder.decoder_tag != nullptr) { + /* merge with tag from decoder plugin */ + Tag *tag = Tag::Merge(*decoder.decoder_tag, + *decoder.stream_tag); + cmd = do_send_tag(decoder, *tag); + delete tag; + } else + /* send only the stream tag */ + cmd = do_send_tag(decoder, *decoder.stream_tag); + + if (cmd != DecoderCommand::NONE) + return cmd; + } + + if (decoder.convert != nullptr) { + assert(dc.in_audio_format != dc.out_audio_format); + + Error error; + data = decoder.convert->Convert(data, length, + &length, + error); + if (data == nullptr) { + /* the PCM conversion has failed - stop + playback, since we have no better way to + bail out */ + LogError(error); + return DecoderCommand::STOP; + } + } else { + assert(dc.in_audio_format == dc.out_audio_format); + } + + while (length > 0) { + struct music_chunk *chunk; + bool full; + + chunk = decoder.GetChunk(); + if (chunk == nullptr) { + assert(dc.command != DecoderCommand::NONE); + return dc.command; + } + + const auto dest = + chunk->Write(dc.out_audio_format, + decoder.timestamp - + dc.song->GetStartMS() / 1000.0, + kbit_rate); + if (dest.IsNull()) { + /* the chunk is full, flush it */ + decoder.FlushChunk(); + continue; + } + + size_t nbytes = dest.size; + assert(nbytes > 0); + + if (nbytes > length) + nbytes = length; + + /* copy the buffer */ + + memcpy(dest.data, data, nbytes); + + /* expand the music pipe chunk */ + + full = chunk->Expand(dc.out_audio_format, nbytes); + if (full) { + /* the chunk is full, flush it */ + decoder.FlushChunk(); + } + + data = (const uint8_t *)data + nbytes; + length -= nbytes; + + decoder.timestamp += (double)nbytes / + dc.out_audio_format.GetTimeToSize(); + + if (dc.end_ms > 0 && + decoder.timestamp >= dc.end_ms / 1000.0) + /* the end of this range has been reached: + stop decoding */ + return DecoderCommand::STOP; + } + + return DecoderCommand::NONE; +} + +DecoderCommand +decoder_tag(Decoder &decoder, InputStream *is, + Tag &&tag) +{ + gcc_unused const DecoderControl &dc = decoder.dc; + DecoderCommand cmd; + + assert(dc.state == DecoderState::DECODE); + assert(dc.pipe != nullptr); + + /* save the tag */ + + delete decoder.decoder_tag; + decoder.decoder_tag = new Tag(tag); + + /* check for a new stream tag */ + + update_stream_tag(decoder, is); + + /* check if we're seeking */ + + if (decoder_prepare_initial_seek(decoder)) + /* during initial seek, no music chunk must be created + until seeking is finished; skip the rest of the + function here */ + return DecoderCommand::SEEK; + + /* send tag to music pipe */ + + if (decoder.stream_tag != nullptr) { + /* merge with tag from input stream */ + Tag *merged; + + merged = Tag::Merge(*decoder.stream_tag, + *decoder.decoder_tag); + cmd = do_send_tag(decoder, *merged); + delete merged; + } else + /* send only the decoder tag */ + cmd = do_send_tag(decoder, *decoder.decoder_tag); + + return cmd; +} + +void +decoder_replay_gain(Decoder &decoder, + const ReplayGainInfo *replay_gain_info) +{ + if (replay_gain_info != nullptr) { + static unsigned serial; + if (++serial == 0) + serial = 1; + + if (REPLAY_GAIN_OFF != replay_gain_mode) { + ReplayGainMode rgm = replay_gain_mode; + if (rgm != REPLAY_GAIN_ALBUM) + rgm = REPLAY_GAIN_TRACK; + + const auto &tuple = replay_gain_info->tuples[rgm]; + const auto scale = + tuple.CalculateScale(replay_gain_preamp, + replay_gain_missing_preamp, + replay_gain_limit); + decoder.dc.replay_gain_db = 20.0 * log10f(scale); + } + + decoder.replay_gain_info = *replay_gain_info; + decoder.replay_gain_serial = serial; + + if (decoder.chunk != nullptr) { + /* flush the current chunk because the new + replay gain values affect the following + samples */ + decoder.FlushChunk(); + } + } else + decoder.replay_gain_serial = 0; +} + +void +decoder_mixramp(Decoder &decoder, MixRampInfo &&mix_ramp) +{ + DecoderControl &dc = decoder.dc; + + dc.SetMixRamp(std::move(mix_ramp)); +} diff --git a/src/decoder/DecoderAPI.hxx b/src/decoder/DecoderAPI.hxx new file mode 100644 index 000000000..0a7b84371 --- /dev/null +++ b/src/decoder/DecoderAPI.hxx @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +/*! \file + * \brief The MPD Decoder API + * + * This is the public API which is used by decoder plugins to + * communicate with the mpd core. + */ + +#ifndef MPD_DECODER_API_HXX +#define MPD_DECODER_API_HXX + +// IWYU pragma: begin_exports + +#include "check.h" +#include "DecoderCommand.hxx" +#include "DecoderPlugin.hxx" +#include "ReplayGainInfo.hxx" +#include "tag/Tag.hxx" +#include "AudioFormat.hxx" +#include "MixRampInfo.hxx" +#include "config/ConfigData.hxx" + +// IWYU pragma: end_exports + +/** + * Notify the player thread that it has finished initialization and + * that it has read the song's meta data. + * + * @param decoder the decoder object + * @param audio_format the audio format which is going to be sent to + * decoder_data() + * @param seekable true if the song is seekable + * @param total_time the total number of seconds in this song; -1 if unknown + */ +void +decoder_initialized(Decoder &decoder, + AudioFormat audio_format, + bool seekable, float total_time); + +/** + * Determines the pending decoder command. + * + * @param decoder the decoder object + * @return the current command, or DecoderCommand::NONE if there is no + * command pending + */ +gcc_pure +DecoderCommand +decoder_get_command(Decoder &decoder); + +/** + * Called by the decoder when it has performed the requested command + * (dc->command). This function resets dc->command and wakes up the + * player thread. + * + * @param decoder the decoder object + */ +void +decoder_command_finished(Decoder &decoder); + +/** + * Call this when you have received the DecoderCommand::SEEK command. + * + * @param decoder the decoder object + * @return the destination position for the week + */ +gcc_pure +double +decoder_seek_where(Decoder &decoder); + +/** + * Call this instead of decoder_command_finished() when seeking has + * failed. + * + * @param decoder the decoder object + */ +void +decoder_seek_error(Decoder &decoder); + +/** + * Blocking read from the input stream. + * + * @param decoder the decoder object + * @param is the input stream to read from + * @param buffer the destination buffer + * @param length the maximum number of bytes to read + * @return the number of bytes read, or 0 if one of the following + * occurs: end of file; error; command (like SEEK or STOP). + */ +size_t +decoder_read(Decoder *decoder, InputStream &is, + void *buffer, size_t length); + +static inline size_t +decoder_read(Decoder &decoder, InputStream &is, + void *buffer, size_t length) +{ + return decoder_read(&decoder, is, buffer, length); +} + +/** + * Blocking read from the input stream. Attempts to fill the buffer + * completely; there is no partial result. + * + * @return true on success, false on error or command or not enough + * data + */ +bool +decoder_read_full(Decoder *decoder, InputStream &is, + void *buffer, size_t size); + +/** + * Skip data on the #InputStream. + * + * @return true on success, false on error or command + */ +bool +decoder_skip(Decoder *decoder, InputStream &is, size_t size); + +/** + * Sets the time stamp for the next data chunk [seconds]. The MPD + * core automatically counts it up, and a decoder plugin only needs to + * use this function if it thinks that adding to the time stamp based + * on the buffer size won't work. + */ +void +decoder_timestamp(Decoder &decoder, double t); + +/** + * This function is called by the decoder plugin when it has + * successfully decoded block of input data. + * + * @param decoder the decoder object + * @param is an input stream which is buffering while we are waiting + * for the player + * @param data the source buffer + * @param length the number of bytes in the buffer + * @return the current command, or DecoderCommand::NONE if there is no + * command pending + */ +DecoderCommand +decoder_data(Decoder &decoder, InputStream *is, + const void *data, size_t length, + uint16_t kbit_rate); + +static inline DecoderCommand +decoder_data(Decoder &decoder, InputStream &is, + const void *data, size_t length, + uint16_t kbit_rate) +{ + return decoder_data(decoder, &is, data, length, kbit_rate); +} + +/** + * This function is called by the decoder plugin when it has + * successfully decoded a tag. + * + * @param decoder the decoder object + * @param is an input stream which is buffering while we are waiting + * for the player + * @param tag the tag to send + * @return the current command, or DecoderCommand::NONE if there is no + * command pending + */ +DecoderCommand +decoder_tag(Decoder &decoder, InputStream *is, Tag &&tag); + +static inline DecoderCommand +decoder_tag(Decoder &decoder, InputStream &is, Tag &&tag) +{ + return decoder_tag(decoder, &is, std::move(tag)); +} + +/** + * Set replay gain values for the following chunks. + * + * @param decoder the decoder object + * @param rgi the replay_gain_info object; may be nullptr to invalidate + * the previous replay gain values + */ +void +decoder_replay_gain(Decoder &decoder, + const ReplayGainInfo *replay_gain_info); + +/** + * Store MixRamp tags. + * + * @param decoder the decoder object + * @param mixramp_start the mixramp_start tag; may be nullptr to invalidate + * @param mixramp_end the mixramp_end tag; may be nullptr to invalidate + */ +void +decoder_mixramp(Decoder &decoder, MixRampInfo &&mix_ramp); + +#endif diff --git a/src/decoder/DecoderBuffer.cxx b/src/decoder/DecoderBuffer.cxx new file mode 100644 index 000000000..47671513e --- /dev/null +++ b/src/decoder/DecoderBuffer.cxx @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "config.h" +#include "DecoderBuffer.hxx" +#include "DecoderAPI.hxx" +#include "util/ConstBuffer.hxx" +#include "util/VarSize.hxx" + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +struct DecoderBuffer { + Decoder *decoder; + InputStream *is; + + /** the allocated size of the buffer */ + size_t size; + + /** the current length of the buffer */ + size_t length; + + /** number of bytes already consumed at the beginning of the + buffer */ + size_t consumed; + + /** the actual buffer (dynamic size) */ + unsigned char data[sizeof(size_t)]; + + DecoderBuffer(Decoder *_decoder, InputStream &_is, + size_t _size) + :decoder(_decoder), is(&_is), + size(_size), length(0), consumed(0) {} +}; + +DecoderBuffer * +decoder_buffer_new(Decoder *decoder, InputStream &is, + size_t size) +{ + assert(size > 0); + + return NewVarSize<DecoderBuffer>(sizeof(DecoderBuffer::data), + size, + decoder, is, size); +} + +void +decoder_buffer_free(DecoderBuffer *buffer) +{ + assert(buffer != nullptr); + + DeleteVarSize(buffer); +} + +bool +decoder_buffer_is_empty(const DecoderBuffer *buffer) +{ + return buffer->consumed == buffer->length; +} + +bool +decoder_buffer_is_full(const DecoderBuffer *buffer) +{ + return buffer->consumed == 0 && buffer->length == buffer->size; +} + +void +decoder_buffer_clear(DecoderBuffer *buffer) +{ + buffer->length = buffer->consumed = 0; +} + +static void +decoder_buffer_shift(DecoderBuffer *buffer) +{ + assert(buffer->consumed > 0); + + buffer->length -= buffer->consumed; + memmove(buffer->data, buffer->data + buffer->consumed, buffer->length); + buffer->consumed = 0; +} + +bool +decoder_buffer_fill(DecoderBuffer *buffer) +{ + size_t nbytes; + + if (buffer->consumed > 0) + decoder_buffer_shift(buffer); + + if (buffer->length >= buffer->size) + /* buffer is full */ + return false; + + nbytes = decoder_read(buffer->decoder, *buffer->is, + buffer->data + buffer->length, + buffer->size - buffer->length); + if (nbytes == 0) + /* end of file, I/O error or decoder command + received */ + return false; + + buffer->length += nbytes; + assert(buffer->length <= buffer->size); + + return true; +} + +ConstBuffer<void> +decoder_buffer_read(const DecoderBuffer *buffer) +{ + return { + buffer->data + buffer->consumed, + buffer->length - buffer->consumed + }; +} + +void +decoder_buffer_consume(DecoderBuffer *buffer, size_t nbytes) +{ + /* just move the "consumed" pointer - decoder_buffer_shift() + will do the real work later (called by + decoder_buffer_fill()) */ + buffer->consumed += nbytes; + + assert(buffer->consumed <= buffer->length); +} + +bool +decoder_buffer_skip(DecoderBuffer *buffer, size_t nbytes) +{ + bool success; + + /* this could probably be optimized by seeking */ + + while (true) { + auto data = decoder_buffer_read(buffer); + if (!data.IsEmpty()) { + if (data.size > nbytes) + data.size = nbytes; + decoder_buffer_consume(buffer, data.size); + nbytes -= data.size; + if (nbytes == 0) + return true; + } + + success = decoder_buffer_fill(buffer); + if (!success) + return false; + } +} diff --git a/src/decoder/DecoderBuffer.hxx b/src/decoder/DecoderBuffer.hxx new file mode 100644 index 000000000..d6f303c36 --- /dev/null +++ b/src/decoder/DecoderBuffer.hxx @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_BUFFER_HXX +#define MPD_DECODER_BUFFER_HXX + +#include "Compiler.h" + +#include <stddef.h> + +/** + * This objects handles buffered reads in decoder plugins easily. You + * create a buffer object, and use its high-level methods to fill and + * read it. It will automatically handle shifting the buffer. + */ +struct DecoderBuffer; + +struct Decoder; +class InputStream; + +template<typename T> struct ConstBuffer; + +/** + * Creates a new buffer. + * + * @param decoder the decoder object, used for decoder_read(), may be nullptr + * @param is the input stream object where we should read from + * @param size the maximum size of the buffer + * @return the new decoder_buffer object + */ +DecoderBuffer * +decoder_buffer_new(Decoder *decoder, InputStream &is, + size_t size); + +/** + * Frees resources used by the decoder_buffer object. + */ +void +decoder_buffer_free(DecoderBuffer *buffer); + +gcc_pure +bool +decoder_buffer_is_empty(const DecoderBuffer *buffer); + +gcc_pure +bool +decoder_buffer_is_full(const DecoderBuffer *buffer); + +void +decoder_buffer_clear(DecoderBuffer *buffer); + +/** + * Read data from the input_stream and append it to the buffer. + * + * @return true if data was appended; false if there is no data + * available (yet), end of file, I/O error or a decoder command was + * received + */ +bool +decoder_buffer_fill(DecoderBuffer *buffer); + +/** + * Reads data from the buffer. This data is not yet consumed, you + * have to call decoder_buffer_consume() to do that. The returned + * buffer becomes invalid after a decoder_buffer_fill() or a + * decoder_buffer_consume() call. + * + * @param buffer the decoder_buffer object + */ +gcc_pure +ConstBuffer<void> +decoder_buffer_read(const DecoderBuffer *buffer); + +/** + * Consume (delete, invalidate) a part of the buffer. The "nbytes" + * parameter must not be larger than the length returned by + * decoder_buffer_read(). + * + * @param buffer the decoder_buffer object + * @param nbytes the number of bytes to consume + */ +void +decoder_buffer_consume(DecoderBuffer *buffer, size_t nbytes); + +/** + * Skips the specified number of bytes, discarding its data. + * + * @param buffer the decoder_buffer object + * @param nbytes the number of bytes to skip + * @return true on success, false on error + */ +bool +decoder_buffer_skip(DecoderBuffer *buffer, size_t nbytes); + +#endif diff --git a/src/decoder/DecoderCommand.hxx b/src/decoder/DecoderCommand.hxx new file mode 100644 index 000000000..a00519644 --- /dev/null +++ b/src/decoder/DecoderCommand.hxx @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_COMMAND_HXX +#define MPD_DECODER_COMMAND_HXX + +#include <stdint.h> + +enum class DecoderCommand : uint8_t { + NONE = 0, + START, + STOP, + SEEK +}; + +#endif diff --git a/src/decoder/DecoderControl.cxx b/src/decoder/DecoderControl.cxx new file mode 100644 index 000000000..d78fc66c9 --- /dev/null +++ b/src/decoder/DecoderControl.cxx @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "config.h" +#include "DecoderControl.hxx" +#include "MusicPipe.hxx" +#include "DetachedSong.hxx" + +#include <assert.h> + +DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond) + :mutex(_mutex), client_cond(_client_cond), + state(DecoderState::STOP), + command(DecoderCommand::NONE), + client_is_waiting(false), + song(nullptr), + replay_gain_db(0), replay_gain_prev_db(0) {} + +DecoderControl::~DecoderControl() +{ + ClearError(); + + delete song; +} + +void +DecoderControl::WaitForDecoder() +{ + assert(!client_is_waiting); + client_is_waiting = true; + + client_cond.wait(mutex); + + assert(client_is_waiting); + client_is_waiting = false; +} + +bool +DecoderControl::IsCurrentSong(const DetachedSong &_song) const +{ + switch (state) { + case DecoderState::STOP: + case DecoderState::ERROR: + return false; + + case DecoderState::START: + case DecoderState::DECODE: + return song->IsSame(_song); + } + + assert(false); + gcc_unreachable(); +} + +void +DecoderControl::Start(DetachedSong *_song, + unsigned _start_ms, unsigned _end_ms, + MusicBuffer &_buffer, MusicPipe &_pipe) +{ + assert(_song != nullptr); + assert(_pipe.IsEmpty()); + + delete song; + song = _song; + start_ms = _start_ms; + end_ms = _end_ms; + buffer = &_buffer; + pipe = &_pipe; + + LockSynchronousCommand(DecoderCommand::START); +} + +void +DecoderControl::Stop() +{ + Lock(); + + if (command != DecoderCommand::NONE) + /* Attempt to cancel the current command. If it's too + late and the decoder thread is already executing + the old command, we'll call STOP again in this + function (see below). */ + SynchronousCommandLocked(DecoderCommand::STOP); + + if (state != DecoderState::STOP && state != DecoderState::ERROR) + SynchronousCommandLocked(DecoderCommand::STOP); + + Unlock(); +} + +bool +DecoderControl::Seek(double where) +{ + assert(state != DecoderState::START); + assert(where >= 0.0); + + if (state == DecoderState::STOP || + state == DecoderState::ERROR || !seekable) + return false; + + seek_where = where; + seek_error = false; + LockSynchronousCommand(DecoderCommand::SEEK); + + return !seek_error; +} + +void +DecoderControl::Quit() +{ + assert(thread.IsDefined()); + + quit = true; + LockAsynchronousCommand(DecoderCommand::STOP); + + thread.Join(); +} + +void +DecoderControl::CycleMixRamp() +{ + previous_mix_ramp = std::move(mix_ramp); + mix_ramp.Clear(); +} diff --git a/src/decoder/DecoderControl.hxx b/src/decoder/DecoderControl.hxx new file mode 100644 index 000000000..4e5c43b5a --- /dev/null +++ b/src/decoder/DecoderControl.hxx @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_CONTROL_HXX +#define MPD_DECODER_CONTROL_HXX + +#include "DecoderCommand.hxx" +#include "AudioFormat.hxx" +#include "MixRampInfo.hxx" +#include "thread/Mutex.hxx" +#include "thread/Cond.hxx" +#include "thread/Thread.hxx" +#include "util/Error.hxx" + +#include <assert.h> +#include <stdint.h> + +/* damn you, windows.h! */ +#ifdef ERROR +#undef ERROR +#endif + +class DetachedSong; +class MusicBuffer; +class MusicPipe; + +enum class DecoderState : uint8_t { + STOP = 0, + START, + DECODE, + + /** + * The last "START" command failed, because there was an I/O + * error or because no decoder was able to decode the file. + * This state will only come after START; once the state has + * turned to DECODE, by definition no such error can occur. + */ + ERROR, +}; + +struct DecoderControl { + /** + * The handle of the decoder thread. + */ + Thread thread; + + /** + * This lock protects #state and #command. + * + * This is usually a reference to PlayerControl::mutex, so + * that both player thread and decoder thread share a mutex. + * This simplifies synchronization with #cond and + * #client_cond. + */ + Mutex &mutex; + + /** + * Trigger this object after you have modified #command. This + * is also used by the decoder thread to notify the caller + * when it has finished a command. + */ + Cond cond; + + /** + * The trigger of this object's client. It is signalled + * whenever an event occurs. + * + * This is usually a reference to PlayerControl::cond. + */ + Cond &client_cond; + + DecoderState state; + DecoderCommand command; + + /** + * The error that occurred in the decoder thread. This + * attribute is only valid if #state is #DecoderState::ERROR. + * The object must be freed when this object transitions to + * any other state (usually #DecoderState::START). + */ + Error error; + + bool quit; + + /** + * Is the client currently waiting for the DecoderThread? If + * false, the DecoderThread may omit invoking Cond::signal(), + * reducing the number of system calls. + */ + bool client_is_waiting; + + bool seek_error; + bool seekable; + double seek_where; + + /** the format of the song file */ + AudioFormat in_audio_format; + + /** the format being sent to the music pipe */ + AudioFormat out_audio_format; + + /** + * The song currently being decoded. This attribute is set by + * the player thread, when it sends the #DecoderCommand::START + * command. + * + * This is a duplicate, and must be freed when this attribute + * is cleared. + */ + DetachedSong *song; + + /** + * The initial seek position (in milliseconds), e.g. to the + * start of a sub-track described by a CUE file. + * + * This attribute is set by dc_start(). + */ + unsigned start_ms; + + /** + * The decoder will stop when it reaches this position (in + * milliseconds). 0 means don't stop before the end of the + * file. + * + * This attribute is set by dc_start(). + */ + unsigned end_ms; + + float total_time; + + /** the #music_chunk allocator */ + MusicBuffer *buffer; + + /** + * The destination pipe for decoded chunks. The caller thread + * owns this object, and is responsible for freeing it. + */ + MusicPipe *pipe; + + float replay_gain_db; + float replay_gain_prev_db; + + MixRampInfo mix_ramp, previous_mix_ramp; + + /** + * @param _mutex see #mutex + * @param _client_cond see #client_cond + */ + DecoderControl(Mutex &_mutex, Cond &_client_cond); + ~DecoderControl(); + + /** + * Locks the object. + */ + void Lock() const { + mutex.lock(); + } + + /** + * Unlocks the object. + */ + void Unlock() const { + mutex.unlock(); + } + + /** + * Signals the object. This function is only valid in the + * player thread. The object should be locked prior to + * calling this function. + */ + void Signal() { + cond.signal(); + } + + /** + * Waits for a signal on the #DecoderControl object. This function + * is only valid in the decoder thread. The object must be locked + * prior to calling this function. + */ + void Wait() { + cond.wait(mutex); + } + + /** + * Waits for a signal from the decoder thread. This object + * must be locked prior to calling this function. This method + * is only valid in the player thread. + * + * Caller must hold the lock. + */ + void WaitForDecoder(); + + bool IsIdle() const { + return state == DecoderState::STOP || + state == DecoderState::ERROR; + } + + gcc_pure + bool LockIsIdle() const { + Lock(); + bool result = IsIdle(); + Unlock(); + return result; + } + + bool IsStarting() const { + return state == DecoderState::START; + } + + gcc_pure + bool LockIsStarting() const { + Lock(); + bool result = IsStarting(); + Unlock(); + return result; + } + + bool HasFailed() const { + assert(command == DecoderCommand::NONE); + + return state == DecoderState::ERROR; + } + + gcc_pure + bool LockHasFailed() const { + Lock(); + bool result = HasFailed(); + Unlock(); + return result; + } + + /** + * Checks whether an error has occurred, and if so, returns a + * copy of the #Error object. + * + * Caller must lock the object. + */ + gcc_pure + Error GetError() const { + assert(command == DecoderCommand::NONE); + assert(state != DecoderState::ERROR || error.IsDefined()); + + Error result; + if (state == DecoderState::ERROR) + result.Set(error); + return result; + } + + /** + * Like dc_get_error(), but locks and unlocks the object. + */ + gcc_pure + Error LockGetError() const { + Lock(); + Error result = GetError(); + Unlock(); + return result; + } + + /** + * Clear the error condition and free the #Error object (if any). + * + * Caller must lock the object. + */ + void ClearError() { + if (state == DecoderState::ERROR) { + error.Clear(); + state = DecoderState::STOP; + } + } + + /** + * Check if the specified song is currently being decoded. If the + * decoder is not running currently (or being started), then this + * function returns false in any case. + * + * Caller must lock the object. + */ + gcc_pure + bool IsCurrentSong(const DetachedSong &_song) const; + + gcc_pure + bool LockIsCurrentSong(const DetachedSong &_song) const { + Lock(); + const bool result = IsCurrentSong(_song); + Unlock(); + return result; + } + +private: + /** + * Wait for the command to be finished by the decoder thread. + * + * To be called from the client thread. Caller must lock the + * object. + */ + void WaitCommandLocked() { + while (command != DecoderCommand::NONE) + WaitForDecoder(); + } + + /** + * Send a command to the decoder thread and synchronously wait + * for it to finish. + * + * To be called from the client thread. Caller must lock the + * object. + */ + void SynchronousCommandLocked(DecoderCommand cmd) { + command = cmd; + Signal(); + WaitCommandLocked(); + } + + /** + * Send a command to the decoder thread and synchronously wait + * for it to finish. + * + * To be called from the client thread. This method locks the + * object. + */ + void LockSynchronousCommand(DecoderCommand cmd) { + Lock(); + ClearError(); + SynchronousCommandLocked(cmd); + Unlock(); + } + + void LockAsynchronousCommand(DecoderCommand cmd) { + Lock(); + command = cmd; + Signal(); + Unlock(); + } + +public: + /** + * Start the decoder. + * + * @param song the song to be decoded; the given instance will be + * owned and freed by the decoder + * @param start_ms see #DecoderControl + * @param end_ms see #DecoderControl + * @param pipe the pipe which receives the decoded chunks (owned by + * the caller) + */ + void Start(DetachedSong *song, unsigned start_ms, unsigned end_ms, + MusicBuffer &buffer, MusicPipe &pipe); + + void Stop(); + + bool Seek(double where); + + void Quit(); + + const char *GetMixRampStart() const { + return mix_ramp.GetStart(); + } + + const char *GetMixRampEnd() const { + return mix_ramp.GetEnd(); + } + + const char *GetMixRampPreviousEnd() const { + return previous_mix_ramp.GetEnd(); + } + + void SetMixRamp(MixRampInfo &&new_value) { + mix_ramp = std::move(new_value); + } + + /** + * Move mixramp_end to mixramp_prev_end and clear + * mixramp_start/mixramp_end. + */ + void CycleMixRamp(); +}; + +#endif diff --git a/src/decoder/DecoderError.cxx b/src/decoder/DecoderError.cxx new file mode 100644 index 000000000..bd3842837 --- /dev/null +++ b/src/decoder/DecoderError.cxx @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "DecoderError.hxx" +#include "util/Domain.hxx" + +const Domain decoder_domain("decoder"); diff --git a/src/decoder/DecoderError.hxx b/src/decoder/DecoderError.hxx new file mode 100644 index 000000000..83cf98204 --- /dev/null +++ b/src/decoder/DecoderError.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_ERROR_HXX +#define MPD_DECODER_ERROR_HXX + +extern const class Domain decoder_domain; + +#endif diff --git a/src/decoder/DecoderInternal.cxx b/src/decoder/DecoderInternal.cxx new file mode 100644 index 000000000..b50fee185 --- /dev/null +++ b/src/decoder/DecoderInternal.cxx @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "config.h" +#include "DecoderInternal.hxx" +#include "DecoderControl.hxx" +#include "pcm/PcmConvert.hxx" +#include "MusicPipe.hxx" +#include "MusicBuffer.hxx" +#include "MusicChunk.hxx" +#include "tag/Tag.hxx" + +#include <assert.h> + +Decoder::~Decoder() +{ + /* caller must flush the chunk */ + assert(chunk == nullptr); + + if (convert != nullptr) { + convert->Close(); + delete convert; + } + + delete song_tag; + delete stream_tag; + delete decoder_tag; +} + +/** + * All chunks are full of decoded data; wait for the player to free + * one. + */ +static DecoderCommand +need_chunks(DecoderControl &dc) +{ + if (dc.command == DecoderCommand::NONE) + dc.Wait(); + + return dc.command; +} + +struct music_chunk * +Decoder::GetChunk() +{ + DecoderCommand cmd; + + if (chunk != nullptr) + return chunk; + + do { + chunk = dc.buffer->Allocate(); + if (chunk != nullptr) { + chunk->replay_gain_serial = replay_gain_serial; + if (replay_gain_serial != 0) + chunk->replay_gain_info = replay_gain_info; + + return chunk; + } + + dc.Lock(); + cmd = need_chunks(dc); + dc.Unlock(); + } while (cmd == DecoderCommand::NONE); + + return nullptr; +} + +void +Decoder::FlushChunk() +{ + assert(chunk != nullptr); + + if (chunk->IsEmpty()) + dc.buffer->Return(chunk); + else + dc.pipe->Push(chunk); + + chunk = nullptr; + + dc.Lock(); + if (dc.client_is_waiting) + dc.client_cond.signal(); + dc.Unlock(); +} diff --git a/src/decoder/DecoderInternal.hxx b/src/decoder/DecoderInternal.hxx new file mode 100644 index 000000000..bef6f6c13 --- /dev/null +++ b/src/decoder/DecoderInternal.hxx @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_INTERNAL_HXX +#define MPD_DECODER_INTERNAL_HXX + +#include "ReplayGainInfo.hxx" +#include "util/Error.hxx" + +class PcmConvert; +struct DecoderControl; +struct Tag; + +struct Decoder { + DecoderControl &dc; + + /** + * For converting input data to the configured audio format. + * nullptr means no conversion necessary. + */ + PcmConvert *convert; + + /** + * The time stamp of the next data chunk, in seconds. + */ + double timestamp; + + /** + * Is the initial seek (to the start position of the sub-song) + * pending, or has it been performed already? + */ + bool initial_seek_pending; + + /** + * Is the initial seek currently running? During this time, + * the decoder command is SEEK. This flag is set by + * decoder_get_virtual_command(), when the virtual SEEK + * command is generated for the first time. + */ + bool initial_seek_running; + + /** + * This flag is set by decoder_seek_where(), and checked by + * decoder_command_finished(). It is used to clean up after + * seeking. + */ + bool seeking; + + /** + * The tag from the song object. This is only used for local + * files, because we expect the stream server to send us a new + * tag each time we play it. + */ + Tag *song_tag; + + /** the last tag received from the stream */ + Tag *stream_tag; + + /** the last tag received from the decoder plugin */ + Tag *decoder_tag; + + /** the chunk currently being written to */ + struct music_chunk *chunk; + + ReplayGainInfo replay_gain_info; + + /** + * A positive serial number for checking if replay gain info + * has changed since the last check. + */ + unsigned replay_gain_serial; + + /** + * An error has occurred (in DecoderAPI.cxx), and the plugin + * will be asked to stop. + */ + Error error; + + Decoder(DecoderControl &_dc, bool _initial_seek_pending, Tag *_tag) + :dc(_dc), + convert(nullptr), + timestamp(0), + initial_seek_pending(_initial_seek_pending), + initial_seek_running(false), + seeking(false), + song_tag(_tag), stream_tag(nullptr), decoder_tag(nullptr), + chunk(nullptr), + replay_gain_serial(0) { + } + + ~Decoder(); + + /** + * Returns the current chunk the decoder writes to, or allocates a new + * chunk if there is none. + * + * @return the chunk, or NULL if we have received a decoder command + */ + music_chunk *GetChunk(); + + /** + * Flushes the current chunk. + * + * Caller must not lock the #DecoderControl object. + */ + void FlushChunk(); +}; + +#endif diff --git a/src/decoder/DecoderList.cxx b/src/decoder/DecoderList.cxx new file mode 100644 index 000000000..5d9d44d34 --- /dev/null +++ b/src/decoder/DecoderList.cxx @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "config.h" +#include "DecoderList.hxx" +#include "DecoderPlugin.hxx" +#include "config/ConfigGlobal.hxx" +#include "config/ConfigData.hxx" +#include "plugins/AudiofileDecoderPlugin.hxx" +#include "plugins/PcmDecoderPlugin.hxx" +#include "plugins/DsdiffDecoderPlugin.hxx" +#include "plugins/DsfDecoderPlugin.hxx" +#include "plugins/FlacDecoderPlugin.h" +#include "plugins/OpusDecoderPlugin.h" +#include "plugins/VorbisDecoderPlugin.h" +#include "plugins/AdPlugDecoderPlugin.h" +#include "plugins/WavpackDecoderPlugin.hxx" +#include "plugins/FfmpegDecoderPlugin.hxx" +#include "plugins/GmeDecoderPlugin.hxx" +#include "plugins/FaadDecoderPlugin.hxx" +#include "plugins/MadDecoderPlugin.hxx" +#include "plugins/SndfileDecoderPlugin.hxx" +#include "plugins/Mpg123DecoderPlugin.hxx" +#include "plugins/WildmidiDecoderPlugin.hxx" +#include "plugins/MikmodDecoderPlugin.hxx" +#include "plugins/ModplugDecoderPlugin.hxx" +#include "plugins/MpcdecDecoderPlugin.hxx" +#include "plugins/FluidsynthDecoderPlugin.hxx" +#include "plugins/SidplayDecoderPlugin.hxx" +#include "util/Macros.hxx" + +#include <string.h> + +const struct DecoderPlugin *const decoder_plugins[] = { +#ifdef HAVE_MAD + &mad_decoder_plugin, +#endif +#ifdef HAVE_MPG123 + &mpg123_decoder_plugin, +#endif +#ifdef ENABLE_VORBIS_DECODER + &vorbis_decoder_plugin, +#endif +#if defined(HAVE_FLAC) + &oggflac_decoder_plugin, +#endif +#ifdef HAVE_FLAC + &flac_decoder_plugin, +#endif +#ifdef HAVE_OPUS + &opus_decoder_plugin, +#endif +#ifdef ENABLE_SNDFILE + &sndfile_decoder_plugin, +#endif +#ifdef HAVE_AUDIOFILE + &audiofile_decoder_plugin, +#endif + &dsdiff_decoder_plugin, + &dsf_decoder_plugin, +#ifdef HAVE_FAAD + &faad_decoder_plugin, +#endif +#ifdef HAVE_MPCDEC + &mpcdec_decoder_plugin, +#endif +#ifdef HAVE_WAVPACK + &wavpack_decoder_plugin, +#endif +#ifdef HAVE_MODPLUG + &modplug_decoder_plugin, +#endif +#ifdef ENABLE_MIKMOD_DECODER + &mikmod_decoder_plugin, +#endif +#ifdef ENABLE_SIDPLAY + &sidplay_decoder_plugin, +#endif +#ifdef ENABLE_WILDMIDI + &wildmidi_decoder_plugin, +#endif +#ifdef ENABLE_FLUIDSYNTH + &fluidsynth_decoder_plugin, +#endif +#ifdef HAVE_ADPLUG + &adplug_decoder_plugin, +#endif +#ifdef HAVE_FFMPEG + &ffmpeg_decoder_plugin, +#endif +#ifdef HAVE_GME + &gme_decoder_plugin, +#endif + &pcm_decoder_plugin, + nullptr +}; + +static constexpr unsigned num_decoder_plugins = + ARRAY_SIZE(decoder_plugins) - 1; + +/** which plugins have been initialized successfully? */ +bool decoder_plugins_enabled[num_decoder_plugins]; + +const struct DecoderPlugin * +decoder_plugin_from_name(const char *name) +{ + return decoder_plugins_find([=](const DecoderPlugin &plugin){ + return strcmp(plugin.name, name) == 0; + }); +} + +void decoder_plugin_init_all(void) +{ + struct config_param empty; + + for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i) { + const DecoderPlugin &plugin = *decoder_plugins[i]; + const struct config_param *param = + config_find_block(CONF_DECODER, "plugin", plugin.name); + + if (param == nullptr) + param = ∅ + else if (!param->GetBlockValue("enabled", true)) + /* the plugin is disabled in mpd.conf */ + continue; + + if (plugin.Init(*param)) + decoder_plugins_enabled[i] = true; + } +} + +void decoder_plugin_deinit_all(void) +{ + decoder_plugins_for_each_enabled([=](const DecoderPlugin &plugin){ + plugin.Finish(); + }); +} + +bool +decoder_plugins_supports_suffix(const char *suffix) +{ + return decoder_plugins_try([suffix](const DecoderPlugin &plugin){ + return plugin.SupportsSuffix(suffix); + }); +} diff --git a/src/decoder/DecoderList.hxx b/src/decoder/DecoderList.hxx new file mode 100644 index 000000000..47085d4ae --- /dev/null +++ b/src/decoder/DecoderList.hxx @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_LIST_HXX +#define MPD_DECODER_LIST_HXX + +#include "Compiler.h" + +struct DecoderPlugin; + +extern const struct DecoderPlugin *const decoder_plugins[]; +extern bool decoder_plugins_enabled[]; + +/* interface for using plugins */ + +gcc_pure +const struct DecoderPlugin * +decoder_plugin_from_name(const char *name); + +/* this is where we "load" all the "plugins" ;-) */ +void decoder_plugin_init_all(void); + +/* this is where we "unload" all the "plugins" */ +void decoder_plugin_deinit_all(void); + +template<typename F> +static inline const DecoderPlugin * +decoder_plugins_find(F f) +{ + for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i) + if (decoder_plugins_enabled[i] && f(*decoder_plugins[i])) + return decoder_plugins[i]; + + return nullptr; +} + +template<typename F> +static inline bool +decoder_plugins_try(F f) +{ + for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i) + if (decoder_plugins_enabled[i] && f(*decoder_plugins[i])) + return true; + + return false; +} + +template<typename F> +static inline void +decoder_plugins_for_each(F f) +{ + for (auto i = decoder_plugins; *i != nullptr; ++i) + f(**i); +} + +template<typename F> +static inline void +decoder_plugins_for_each_enabled(F f) +{ + for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i) + if (decoder_plugins_enabled[i]) + f(*decoder_plugins[i]); +} + +/** + * Is there at least once #DecoderPlugin that supports the specified + * file name suffix? + */ +gcc_pure gcc_nonnull_all +bool +decoder_plugins_supports_suffix(const char *suffix); + +#endif diff --git a/src/decoder/DecoderPlugin.cxx b/src/decoder/DecoderPlugin.cxx new file mode 100644 index 000000000..3be812c3b --- /dev/null +++ b/src/decoder/DecoderPlugin.cxx @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "config.h" +#include "DecoderPlugin.hxx" +#include "util/StringUtil.hxx" + +#include <assert.h> + +bool +DecoderPlugin::SupportsSuffix(const char *suffix) const +{ + assert(suffix != nullptr); + + return suffixes != nullptr && string_array_contains(suffixes, suffix); + +} + +bool +DecoderPlugin::SupportsMimeType(const char *mime_type) const +{ + assert(mime_type != nullptr); + + return mime_types != nullptr && + string_array_contains(mime_types, mime_type); +} diff --git a/src/decoder/DecoderPlugin.hxx b/src/decoder/DecoderPlugin.hxx new file mode 100644 index 000000000..dbf3db9aa --- /dev/null +++ b/src/decoder/DecoderPlugin.hxx @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_PLUGIN_HXX +#define MPD_DECODER_PLUGIN_HXX + +#include "Compiler.h" + +struct config_param; +class InputStream; +struct tag_handler; +class Path; + +/** + * Opaque handle which the decoder plugin passes to the functions in + * this header. + */ +struct Decoder; + +struct DecoderPlugin { + const char *name; + + /** + * Initialize the decoder plugin. Optional method. + * + * @param param a configuration block for this plugin, or nullptr + * if none is configured + * @return true if the plugin was initialized successfully, + * false if the plugin is not available + */ + bool (*init)(const config_param ¶m); + + /** + * Deinitialize a decoder plugin which was initialized + * successfully. Optional method. + */ + void (*finish)(void); + + /** + * Decode a stream (data read from an #input_stream object). + * + * Either implement this method or file_decode(). If + * possible, it is recommended to implement this method, + * because it is more versatile. + */ + void (*stream_decode)(Decoder &decoder, InputStream &is); + + /** + * Decode a local file. + * + * Either implement this method or stream_decode(). + */ + void (*file_decode)(Decoder &decoder, Path path_fs); + + /** + * Scan metadata of a file. + * + * @return false if the operation has failed + */ + bool (*scan_file)(Path path_fs, + const struct tag_handler *handler, + void *handler_ctx); + + /** + * Scan metadata of a file. + * + * @return false if the operation has failed + */ + bool (*scan_stream)(InputStream &is, + const struct tag_handler *handler, + void *handler_ctx); + + /** + * @brief Return a "virtual" filename for subtracks in + * container formats like flac + * @param const char* pathname full pathname for the file on fs + * @param const unsigned int tnum track number + * + * @return nullptr if there are no multiple files + * a filename for every single track according to tnum (param 2) + * do not include full pathname here, just the "virtual" file + * + * Free the return value with delete[]. + */ + char* (*container_scan)(Path path_fs, const unsigned int tnum); + + /* last element in these arrays must always be a nullptr: */ + const char *const*suffixes; + const char *const*mime_types; + + /** + * Initialize a decoder plugin. + * + * @param param a configuration block for this plugin, or nullptr if none + * is configured + * @return true if the plugin was initialized successfully, false if + * the plugin is not available + */ + bool Init(const config_param ¶m) const { + return init != nullptr + ? init(param) + : true; + } + + /** + * Deinitialize a decoder plugin which was initialized successfully. + */ + void Finish() const { + if (finish != nullptr) + finish(); + } + + /** + * Decode a stream. + */ + void StreamDecode(Decoder &decoder, InputStream &is) const { + stream_decode(decoder, is); + } + + /** + * Decode a file. + */ + template<typename P> + void FileDecode(Decoder &decoder, P path_fs) const { + file_decode(decoder, path_fs); + } + + /** + * Read the tag of a file. + */ + template<typename P> + bool ScanFile(P path_fs, + const tag_handler &handler, void *handler_ctx) const { + return scan_file != nullptr + ? scan_file(path_fs, &handler, handler_ctx) + : false; + } + + /** + * Read the tag of a stream. + */ + bool ScanStream(InputStream &is, + const tag_handler &handler, void *handler_ctx) const { + return scan_stream != nullptr + ? scan_stream(is, &handler, handler_ctx) + : false; + } + + /** + * return "virtual" tracks in a container + */ + template<typename P> + char *ContainerScan(P path, const unsigned int tnum) const { + return container_scan(path, tnum); + } + + /** + * Does the plugin announce the specified file name suffix? + */ + gcc_pure gcc_nonnull_all + bool SupportsSuffix(const char *suffix) const; + + /** + * Does the plugin announce the specified MIME type? + */ + gcc_pure gcc_nonnull_all + bool SupportsMimeType(const char *mime_type) const; +}; + +#endif diff --git a/src/decoder/DecoderPrint.cxx b/src/decoder/DecoderPrint.cxx new file mode 100644 index 000000000..54b89c36c --- /dev/null +++ b/src/decoder/DecoderPrint.cxx @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "config.h" +#include "DecoderPrint.hxx" +#include "DecoderList.hxx" +#include "DecoderPlugin.hxx" +#include "client/Client.hxx" + +#include <functional> + +#include <assert.h> + +static void +decoder_plugin_print(Client &client, + const DecoderPlugin &plugin) +{ + const char *const*p; + + assert(plugin.name != nullptr); + + client_printf(client, "plugin: %s\n", plugin.name); + + if (plugin.suffixes != nullptr) + for (p = plugin.suffixes; *p != nullptr; ++p) + client_printf(client, "suffix: %s\n", *p); + + if (plugin.mime_types != nullptr) + for (p = plugin.mime_types; *p != nullptr; ++p) + client_printf(client, "mime_type: %s\n", *p); +} + +void +decoder_list_print(Client &client) +{ + using namespace std::placeholders; + const auto f = std::bind(decoder_plugin_print, std::ref(client), _1); + decoder_plugins_for_each_enabled(f); +} diff --git a/src/decoder/DecoderPrint.hxx b/src/decoder/DecoderPrint.hxx new file mode 100644 index 000000000..695bd099d --- /dev/null +++ b/src/decoder/DecoderPrint.hxx @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_PRINT_HXX +#define MPD_DECODER_PRINT_HXX + +class Client; + +void +decoder_list_print(Client &client); + +#endif diff --git a/src/decoder/DecoderThread.cxx b/src/decoder/DecoderThread.cxx new file mode 100644 index 000000000..06735de83 --- /dev/null +++ b/src/decoder/DecoderThread.cxx @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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 "config.h" +#include "DecoderThread.hxx" +#include "DecoderControl.hxx" +#include "DecoderInternal.hxx" +#include "DecoderError.hxx" +#include "DecoderPlugin.hxx" +#include "DetachedSong.hxx" +#include "system/FatalError.hxx" +#include "fs/Traits.hxx" +#include "fs/AllocatedPath.hxx" +#include "DecoderAPI.hxx" +#include "input/InputStream.hxx" +#include "DecoderList.hxx" +#include "util/UriUtil.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "thread/Name.hxx" +#include "tag/ApeReplayGain.hxx" +#include "Log.hxx" + +#include <functional> + +static constexpr Domain decoder_thread_domain("decoder_thread"); + +/** + * Marks the current decoder command as "finished" and notifies the + * player thread. + * + * @param dc the #DecoderControl object; must be locked + */ +static void +decoder_command_finished_locked(DecoderControl &dc) +{ + assert(dc.command != DecoderCommand::NONE); + + dc.command = DecoderCommand::NONE; + + dc.client_cond.signal(); +} + +/** + * Opens the input stream with input_stream::Open(), and waits until + * the stream gets ready. If a decoder STOP command is received + * during that, it cancels the operation (but does not close the + * stream). + * + * Unlock the decoder before calling this function. + * + * @return an input_stream on success or if #DecoderCommand::STOP is + * received, nullptr on error + */ +static InputStream * +decoder_input_stream_open(DecoderControl &dc, const char *uri) +{ + Error error; + + InputStream *is = InputStream::Open(uri, dc.mutex, dc.cond, error); + if (is == nullptr) { + if (error.IsDefined()) + LogError(error); + + return nullptr; + } + + /* wait for the input stream to become ready; its metadata + will be available then */ + + dc.Lock(); + + is->Update(); + while (!is->IsReady() && + dc.command != DecoderCommand::STOP) { + dc.Wait(); + + is->Update(); + } + + if (!is->Check(error)) { + dc.Unlock(); + + LogError(error); + return nullptr; + } + + dc.Unlock(); + + return is; +} + +static bool +decoder_stream_decode(const DecoderPlugin &plugin, + Decoder &decoder, + InputStream &input_stream) +{ + assert(plugin.stream_decode != nullptr); + assert(decoder.stream_tag == nullptr); + assert(decoder.decoder_tag == nullptr); + assert(input_stream.IsReady()); + assert(decoder.dc.state == DecoderState::START); + + FormatDebug(decoder_thread_domain, "probing plugin %s", plugin.name); + + if (decoder.dc.command == DecoderCommand::STOP) + return true; + + /* rewind the stream, so each plugin gets a fresh start */ + input_stream.Rewind(IgnoreError()); + + decoder.dc.Unlock(); + + FormatThreadName("decoder:%s", plugin.name); + + plugin.StreamDecode(decoder, input_stream); + + SetThreadName("decoder"); + + decoder.dc.Lock(); + + assert(decoder.dc.state == DecoderState::START || + decoder.dc.state == DecoderState::DECODE); + + return decoder.dc.state != DecoderState::START; +} + +static bool +decoder_file_decode(const DecoderPlugin &plugin, + Decoder &decoder, Path path) +{ + assert(plugin.file_decode != nullptr); + assert(decoder.stream_tag == nullptr); + assert(decoder.decoder_tag == nullptr); + assert(!path.IsNull()); + assert(path.IsAbsolute()); + assert(decoder.dc.state == DecoderState::START); + + FormatDebug(decoder_thread_domain, "probing plugin %s", plugin.name); + + if (decoder.dc.command == DecoderCommand::STOP) + return true; + + decoder.dc.Unlock(); + + FormatThreadName("decoder:%s", plugin.name); + + plugin.FileDecode(decoder, path); + + SetThreadName("decoder"); + + decoder.dc.Lock(); + + assert(decoder.dc.state == DecoderState::START || + decoder.dc.state == DecoderState::DECODE); + + return decoder.dc.state != DecoderState::START; +} + +gcc_pure +static bool +decoder_check_plugin_mime(const DecoderPlugin &plugin, const InputStream &is) +{ + assert(plugin.stream_decode != nullptr); + + const char *mime_type = is.GetMimeType(); + return mime_type != nullptr && plugin.SupportsMimeType(mime_type); +} + +gcc_pure +static bool +decoder_check_plugin_suffix(const DecoderPlugin &plugin, const char *suffix) +{ + assert(plugin.stream_decode != nullptr); + + return suffix != nullptr && plugin.SupportsSuffix(suffix); +} + +gcc_pure +static bool +decoder_check_plugin(const DecoderPlugin &plugin, const InputStream &is, + const char *suffix) +{ + return plugin.stream_decode != nullptr && + (decoder_check_plugin_mime(plugin, is) || + decoder_check_plugin_suffix(plugin, suffix)); +} + +static bool +decoder_run_stream_plugin(Decoder &decoder, InputStream &is, + const char *suffix, + const DecoderPlugin &plugin, + bool &tried_r) +{ + if (!decoder_check_plugin(plugin, is, suffix)) + return false; + + tried_r = true; + return decoder_stream_decode(plugin, decoder, is); +} + +static bool +decoder_run_stream_locked(Decoder &decoder, InputStream &is, + const char *uri, bool &tried_r) +{ + const char *const suffix = uri_get_suffix(uri); + + using namespace std::placeholders; + const auto f = std::bind(decoder_run_stream_plugin, + std::ref(decoder), std::ref(is), suffix, + _1, std::ref(tried_r)); + return decoder_plugins_try(f); +} + +/** + * Try decoding a stream, using the fallback plugin. + */ +static bool +decoder_run_stream_fallback(Decoder &decoder, InputStream &is) +{ + const struct DecoderPlugin *plugin; + + plugin = decoder_plugin_from_name("mad"); + return plugin != nullptr && plugin->stream_decode != nullptr && + decoder_stream_decode(*plugin, decoder, is); +} + +/** + * Try decoding a stream. + */ +static bool +decoder_run_stream(Decoder &decoder, const char *uri) +{ + DecoderControl &dc = decoder.dc; + InputStream *input_stream; + bool success; + + dc.Unlock(); + + input_stream = decoder_input_stream_open(dc, uri); + if (input_stream == nullptr) { + dc.Lock(); + return false; + } + + dc.Lock(); + + bool tried = false; + success = dc.command == DecoderCommand::STOP || + decoder_run_stream_locked(decoder, *input_stream, uri, + tried) || + /* fallback to mp3: this is needed for bastard streams + that don't have a suffix or set the mimeType */ + (!tried && + decoder_run_stream_fallback(decoder, *input_stream)); + + dc.Unlock(); + delete input_stream; + dc.Lock(); + + return success; +} + +/** + * Attempt to load replay gain data, and pass it to + * decoder_replay_gain(). + */ +static void +decoder_load_replay_gain(Decoder &decoder, Path path_fs) +{ + ReplayGainInfo info; + if (replay_gain_ape_read(path_fs, info)) + decoder_replay_gain(decoder, &info); +} + +static bool +TryDecoderFile(Decoder &decoder, Path path_fs, const char *suffix, + const DecoderPlugin &plugin) +{ + if (!plugin.SupportsSuffix(suffix)) + return false; + + DecoderControl &dc = decoder.dc; + + if (plugin.file_decode != nullptr) { + dc.Lock(); + + if (decoder_file_decode(plugin, decoder, path_fs)) + return true; + + dc.Unlock(); + } else if (plugin.stream_decode != nullptr) { + InputStream *input_stream = + decoder_input_stream_open(dc, path_fs.c_str()); + if (input_stream == nullptr) + return false; + + dc.Lock(); + + bool success = decoder_stream_decode(plugin, decoder, + *input_stream); + + dc.Unlock(); + + delete input_stream; + + if (success) { + dc.Lock(); + return true; + } + } + + return false; +} + +/** + * Try decoding a file. + */ +static bool +decoder_run_file(Decoder &decoder, const char *uri_utf8, Path path_fs) +{ + const char *suffix = uri_get_suffix(uri_utf8); + if (suffix == nullptr) + return false; + + DecoderControl &dc = decoder.dc; + dc.Unlock(); + + decoder_load_replay_gain(decoder, path_fs); + + if (decoder_plugins_try([&decoder, path_fs, + suffix](const DecoderPlugin &plugin){ + return TryDecoderFile(decoder, + path_fs, suffix, + plugin); + })) + return true; + + dc.Lock(); + return false; +} + +static void +decoder_run_song(DecoderControl &dc, + const DetachedSong &song, const char *uri, Path path_fs) +{ + Decoder decoder(dc, dc.start_ms > 0, + new Tag(song.GetTag())); + int ret; + + dc.state = DecoderState::START; + + decoder_command_finished_locked(dc); + + ret = !path_fs.IsNull() + ? decoder_run_file(decoder, uri, path_fs) + : decoder_run_stream(decoder, uri); + + dc.Unlock(); + + /* flush the last chunk */ + + if (decoder.chunk != nullptr) + decoder.FlushChunk(); + + dc.Lock(); + + if (decoder.error.IsDefined()) { + /* copy the Error from sruct Decoder to + DecoderControl */ + dc.state = DecoderState::ERROR; + dc.error = std::move(decoder.error); + } else if (ret) + dc.state = DecoderState::STOP; + else { + dc.state = DecoderState::ERROR; + + const char *error_uri = song.GetURI(); + const std::string allocated = uri_remove_auth(error_uri); + if (!allocated.empty()) + error_uri = allocated.c_str(); + + dc.error.Format(decoder_domain, + "Failed to decode %s", error_uri); + } + + dc.client_cond.signal(); +} + +static void +decoder_run(DecoderControl &dc) +{ + dc.ClearError(); + + assert(dc.song != nullptr); + const DetachedSong &song = *dc.song; + + const char *const uri_utf8 = song.GetRealURI(); + + Path path_fs = Path::Null(); + AllocatedPath path_buffer = AllocatedPath::Null(); + if (PathTraitsUTF8::IsAbsolute(uri_utf8)) { + path_buffer = AllocatedPath::FromUTF8(uri_utf8, dc.error); + if (path_buffer.IsNull()) { + dc.state = DecoderState::ERROR; + decoder_command_finished_locked(dc); + return; + } + + path_fs = path_buffer; + } + + decoder_run_song(dc, song, uri_utf8, path_fs); + +} + +static void +decoder_task(void *arg) +{ + DecoderControl &dc = *(DecoderControl *)arg; + + SetThreadName("decoder"); + + dc.Lock(); + + do { + assert(dc.state == DecoderState::STOP || + dc.state == DecoderState::ERROR); + + switch (dc.command) { + case DecoderCommand::START: + dc.CycleMixRamp(); + dc.replay_gain_prev_db = dc.replay_gain_db; + dc.replay_gain_db = 0; + + /* fall through */ + + case DecoderCommand::SEEK: + decoder_run(dc); + break; + + case DecoderCommand::STOP: + decoder_command_finished_locked(dc); + break; + + case DecoderCommand::NONE: + dc.Wait(); + break; + } + } while (dc.command != DecoderCommand::NONE || !dc.quit); + + dc.Unlock(); +} + +void +decoder_thread_start(DecoderControl &dc) +{ + assert(!dc.thread.IsDefined()); + + dc.quit = false; + + Error error; + if (!dc.thread.Start(decoder_task, &dc, error)) + FatalError(error); +} diff --git a/src/decoder/DecoderThread.hxx b/src/decoder/DecoderThread.hxx new file mode 100644 index 000000000..d5fde281c --- /dev/null +++ b/src/decoder/DecoderThread.hxx @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + */ + +#ifndef MPD_DECODER_THREAD_HXX +#define MPD_DECODER_THREAD_HXX + +struct DecoderControl; + +void +decoder_thread_start(DecoderControl &dc); + +#endif diff --git a/src/decoder/AdPlugDecoderPlugin.cxx b/src/decoder/plugins/AdPlugDecoderPlugin.cxx index c79fca5f9..32a2432f4 100644 --- a/src/decoder/AdPlugDecoderPlugin.cxx +++ b/src/decoder/plugins/AdPlugDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,8 +20,9 @@ #include "config.h" #include "AdPlugDecoderPlugin.h" #include "tag/TagHandler.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "CheckAudioFormat.hxx" +#include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Macros.hxx" #include "Log.hxx" @@ -48,12 +49,12 @@ adplug_init(const config_param ¶m) } static void -adplug_file_decode(Decoder &decoder, const char *path_fs) +adplug_file_decode(Decoder &decoder, Path path_fs) { CEmuopl opl(sample_rate, true, true); opl.init(); - CPlayer *player = CAdPlug::factory(path_fs, &opl); + CPlayer *player = CAdPlug::factory(path_fs.c_str(), &opl); if (player == nullptr) return; @@ -90,13 +91,13 @@ adplug_scan_tag(TagType type, const std::string &value, } static bool -adplug_scan_file(const char *path_fs, +adplug_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { CEmuopl opl(sample_rate, true, true); opl.init(); - CPlayer *player = CAdPlug::factory(path_fs, &opl); + CPlayer *player = CAdPlug::factory(path_fs.c_str(), &opl); if (player == nullptr) return false; diff --git a/src/decoder/AdPlugDecoderPlugin.h b/src/decoder/plugins/AdPlugDecoderPlugin.h index a827fdc7d..539dbbf0a 100644 --- a/src/decoder/AdPlugDecoderPlugin.h +++ b/src/decoder/plugins/AdPlugDecoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/AudiofileDecoderPlugin.cxx b/src/decoder/plugins/AudiofileDecoderPlugin.cxx index ab3557e52..262c4a5bf 100644 --- a/src/decoder/AudiofileDecoderPlugin.cxx +++ b/src/decoder/plugins/AudiofileDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,10 +19,11 @@ #include "config.h" #include "AudiofileDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" +#include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" @@ -37,10 +38,12 @@ static constexpr Domain audiofile_domain("audiofile"); -static int audiofile_get_duration(const char *file) +gcc_pure +static int +audiofile_get_duration(Path path_fs) { int total_time; - AFfilehandle af_fp = afOpenFile(file, "r", nullptr); + AFfilehandle af_fp = afOpenFile(path_fs.c_str(), "r", nullptr); if (af_fp == AF_NULL_FILEHANDLE) { return -1; } @@ -227,15 +230,15 @@ audiofile_stream_decode(Decoder &decoder, InputStream &is) } static bool -audiofile_scan_file(const char *file, +audiofile_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { - int total_time = audiofile_get_duration(file); + int total_time = audiofile_get_duration(path_fs); if (total_time < 0) { FormatWarning(audiofile_domain, "Failed to get total song time from: %s", - file); + path_fs.c_str()); return false; } diff --git a/src/decoder/AudiofileDecoderPlugin.hxx b/src/decoder/plugins/AudiofileDecoderPlugin.hxx index 5a17281b0..61129076d 100644 --- a/src/decoder/AudiofileDecoderPlugin.hxx +++ b/src/decoder/plugins/AudiofileDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DsdLib.cxx b/src/decoder/plugins/DsdLib.cxx index 67cc7e945..f77d8f59a 100644 --- a/src/decoder/DsdLib.cxx +++ b/src/decoder/plugins/DsdLib.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,10 +25,8 @@ #include "config.h" #include "DsdLib.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" -#include "util/bit_reverse.h" -#include "tag/TagHandler.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "tag/TagId3.hxx" #include "util/Error.hxx" diff --git a/src/decoder/DsdLib.hxx b/src/decoder/plugins/DsdLib.hxx index 53160cf1e..88a76d72a 100644 --- a/src/decoder/DsdLib.hxx +++ b/src/decoder/plugins/DsdLib.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ #include <stdint.h> struct Decoder; -struct InputStream; +class InputStream; struct DsdId { char value[4]; diff --git a/src/decoder/DsdiffDecoderPlugin.cxx b/src/decoder/plugins/DsdiffDecoderPlugin.cxx index a3c0149b9..92f7ecdaa 100644 --- a/src/decoder/DsdiffDecoderPlugin.cxx +++ b/src/decoder/plugins/DsdiffDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,8 +28,8 @@ #include "config.h" #include "DsdiffDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" #include "util/bit_reverse.h" #include "util/Error.hxx" @@ -38,9 +38,6 @@ #include "DsdLib.hxx" #include "Log.hxx" -#include <unistd.h> -#include <stdio.h> /* for SEEK_SET, SEEK_CUR */ - struct DsdiffHeader { DsdId id; DffDsdUint64 size; diff --git a/src/decoder/DsdiffDecoderPlugin.hxx b/src/decoder/plugins/DsdiffDecoderPlugin.hxx index be14fc9cd..7aa36752b 100644 --- a/src/decoder/DsdiffDecoderPlugin.hxx +++ b/src/decoder/plugins/DsdiffDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DsfDecoderPlugin.cxx b/src/decoder/plugins/DsfDecoderPlugin.cxx index 5ef94e647..6fbaf6bf6 100644 --- a/src/decoder/DsfDecoderPlugin.cxx +++ b/src/decoder/plugins/DsfDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,8 +29,8 @@ #include "config.h" #include "DsfDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" #include "util/bit_reverse.h" #include "util/Error.hxx" @@ -39,9 +39,6 @@ #include "tag/TagHandler.hxx" #include "Log.hxx" -#include <unistd.h> -#include <stdio.h> /* for SEEK_SET, SEEK_CUR */ - struct DsfMetaData { unsigned sample_rate, channels; bool bitreverse; diff --git a/src/decoder/DsfDecoderPlugin.hxx b/src/decoder/plugins/DsfDecoderPlugin.hxx index 921c94698..02bea0b5c 100644 --- a/src/decoder/DsfDecoderPlugin.hxx +++ b/src/decoder/plugins/DsfDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FaadDecoderPlugin.cxx b/src/decoder/plugins/FaadDecoderPlugin.cxx index 9fd20167d..e80665d24 100644 --- a/src/decoder/FaadDecoderPlugin.cxx +++ b/src/decoder/plugins/FaadDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,11 +19,12 @@ #include "config.h" #include "FaadDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "DecoderBuffer.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "../DecoderBuffer.hxx" +#include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" +#include "util/ConstBuffer.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" @@ -66,16 +67,11 @@ adts_check_frame(const unsigned char *data) static size_t adts_find_frame(DecoderBuffer *buffer) { - size_t length, frame_length; - bool ret; - while (true) { - const uint8_t *data = (const uint8_t *) - decoder_buffer_read(buffer, &length); - if (data == nullptr || length < 8) { + auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_read(buffer)); + if (data.size < 8) { /* not enough data yet */ - ret = decoder_buffer_fill(buffer); - if (!ret) + if (!decoder_buffer_fill(buffer)) /* failed */ return 0; @@ -83,21 +79,22 @@ adts_find_frame(DecoderBuffer *buffer) } /* find the 0xff marker */ - const uint8_t *p = (const uint8_t *)memchr(data, 0xff, length); + const uint8_t *p = (const uint8_t *) + memchr(data.data, 0xff, data.size); if (p == nullptr) { /* no marker - discard the buffer */ - decoder_buffer_consume(buffer, length); + decoder_buffer_clear(buffer); continue; } - if (p > data) { + if (p > data.data) { /* discard data before 0xff */ - decoder_buffer_consume(buffer, p - data); + decoder_buffer_consume(buffer, p - data.data); continue; } /* is it a frame? */ - frame_length = adts_check_frame(data); + size_t frame_length = adts_check_frame(data.data); if (frame_length == 0) { /* it's just some random 0xff byte; discard it and continue searching */ @@ -105,19 +102,15 @@ adts_find_frame(DecoderBuffer *buffer) continue; } - if (length < frame_length) { + if (data.size < frame_length) { /* available buffer size is smaller than the frame will be - attempt to read more data */ - ret = decoder_buffer_fill(buffer); - if (!ret) { + if (!decoder_buffer_fill(buffer)) { /* not enough data; discard this frame to prevent a possible buffer overflow */ - data = (const uint8_t *) - decoder_buffer_read(buffer, &length); - if (data != nullptr) - decoder_buffer_consume(buffer, length); + decoder_buffer_clear(buffer); } continue; @@ -131,31 +124,27 @@ adts_find_frame(DecoderBuffer *buffer) static float adts_song_duration(DecoderBuffer *buffer) { - unsigned int frames, frame_length; unsigned sample_rate = 0; - float frames_per_second; /* Read all frames to ensure correct time and bitrate */ - for (frames = 0;; frames++) { - frame_length = adts_find_frame(buffer); + unsigned frames = 0; + for (;; frames++) { + unsigned frame_length = adts_find_frame(buffer); if (frame_length == 0) break; - if (frames == 0) { - size_t buffer_length; - const uint8_t *data = (const uint8_t *) - decoder_buffer_read(buffer, &buffer_length); - assert(data != nullptr); - assert(frame_length <= buffer_length); + auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_read(buffer)); + assert(!data.IsEmpty()); + assert(frame_length <= data.size); - sample_rate = adts_sample_rates[(data[2] & 0x3c) >> 2]; + sample_rate = adts_sample_rates[(data.data[2] & 0x3c) >> 2]; } decoder_buffer_consume(buffer, frame_length); } - frames_per_second = (float)sample_rate / 1024.0; + float frames_per_second = (float)sample_rate / 1024.0; if (frames_per_second <= 0) return -1; @@ -165,66 +154,58 @@ adts_song_duration(DecoderBuffer *buffer) static float faad_song_duration(DecoderBuffer *buffer, InputStream &is) { - size_t fileread; - size_t tagsize; - size_t length; - bool success; - const auto size = is.GetSize(); - fileread = size >= 0 ? size : 0; + const size_t fileread = size >= 0 ? size : 0; decoder_buffer_fill(buffer); - const uint8_t *data = (const uint8_t *) - decoder_buffer_read(buffer, &length); - if (data == nullptr) + auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_read(buffer)); + if (data.IsEmpty()) return -1; - tagsize = 0; - if (length >= 10 && !memcmp(data, "ID3", 3)) { + size_t tagsize = 0; + if (data.size >= 10 && !memcmp(data.data, "ID3", 3)) { /* skip the ID3 tag */ - tagsize = (data[6] << 21) | (data[7] << 14) | - (data[8] << 7) | (data[9] << 0); + tagsize = (data.data[6] << 21) | (data.data[7] << 14) | + (data.data[8] << 7) | (data.data[9] << 0); tagsize += 10; - success = decoder_buffer_skip(buffer, tagsize) && + bool success = decoder_buffer_skip(buffer, tagsize) && decoder_buffer_fill(buffer); if (!success) return -1; - data = (const uint8_t *)decoder_buffer_read(buffer, &length); - if (data == nullptr) + data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_read(buffer)); + if (data.IsEmpty()) return -1; } - if (is.IsSeekable() && length >= 2 && - data[0] == 0xFF && ((data[1] & 0xF6) == 0xF0)) { + if (is.IsSeekable() && data.size >= 2 && + data.data[0] == 0xFF && ((data.data[1] & 0xF6) == 0xF0)) { /* obtain the duration from the ADTS header */ float song_length = adts_song_duration(buffer); is.LockSeek(tagsize, SEEK_SET, IgnoreError()); - data = (const uint8_t *)decoder_buffer_read(buffer, &length); - if (data != nullptr) - decoder_buffer_consume(buffer, length); + decoder_buffer_clear(buffer); decoder_buffer_fill(buffer); return song_length; - } else if (length >= 5 && memcmp(data, "ADIF", 4) == 0) { + } else if (data.size >= 5 && memcmp(data.data, "ADIF", 4) == 0) { /* obtain the duration from the ADIF header */ unsigned bit_rate; - size_t skip_size = (data[4] & 0x80) ? 9 : 0; + size_t skip_size = (data.data[4] & 0x80) ? 9 : 0; - if (8 + skip_size > length) + if (8 + skip_size > data.size) /* not enough data yet; skip parsing this header */ return -1; - bit_rate = ((data[4 + skip_size] & 0x0F) << 19) | - (data[5 + skip_size] << 11) | - (data[6 + skip_size] << 3) | - (data[7 + skip_size] & 0xE0); + bit_rate = ((data.data[4 + skip_size] & 0x0F) << 19) | + (data.data[5 + skip_size] << 11) | + (data.data[6 + skip_size] << 3) | + (data.data[7 + skip_size] & 0xE0); if (fileread != 0 && bit_rate != 0) return fileread * 8.0 / bit_rate; @@ -242,9 +223,7 @@ static bool faad_decoder_init(NeAACDecHandle decoder, DecoderBuffer *buffer, AudioFormat &audio_format, Error &error) { - int32_t nbytes; uint32_t sample_rate; - uint8_t channels; #ifdef HAVE_FAAD_LONG /* neaacdec.h declares all arguments as "unsigned long", but internally expects uint32_t pointers. To avoid gcc @@ -254,19 +233,18 @@ faad_decoder_init(NeAACDecHandle decoder, DecoderBuffer *buffer, uint32_t *sample_rate_p = &sample_rate; #endif - size_t length; - const unsigned char *data = (const unsigned char *) - decoder_buffer_read(buffer, &length); - if (data == nullptr) { + auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_read(buffer)); + if (data.IsEmpty()) { error.Set(faad_decoder_domain, "Empty file"); return false; } - nbytes = NeAACDecInit(decoder, - /* deconst hack, libfaad requires this */ - const_cast<unsigned char *>(data), - length, - sample_rate_p, &channels); + uint8_t channels; + int32_t nbytes = NeAACDecInit(decoder, + /* deconst hack, libfaad requires this */ + const_cast<uint8_t *>(data.data), + data.size, + sample_rate_p, &channels); if (nbytes < 0) { error.Set(faad_decoder_domain, "Not an AAC stream"); return false; @@ -286,16 +264,14 @@ static const void * faad_decoder_decode(NeAACDecHandle decoder, DecoderBuffer *buffer, NeAACDecFrameInfo *frame_info) { - size_t length; - const unsigned char *data = (const unsigned char *) - decoder_buffer_read(buffer, &length); - if (data == nullptr) + auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_read(buffer)); + if (data.IsEmpty()) return nullptr; return NeAACDecDecode(decoder, frame_info, /* deconst hack, libfaad requires this */ - const_cast<unsigned char *>(data), - length); + const_cast<uint8_t *>(data.data), + data.size); } /** @@ -306,17 +282,12 @@ faad_decoder_decode(NeAACDecHandle decoder, DecoderBuffer *buffer, static float faad_get_file_time_float(InputStream &is) { - DecoderBuffer *buffer; - float length; - - buffer = decoder_buffer_new(nullptr, is, - FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS); - length = faad_song_duration(buffer, is); + DecoderBuffer *buffer = + decoder_buffer_new(nullptr, is, + FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS); + float length = faad_song_duration(buffer, is); if (length < 0) { - bool ret; - AudioFormat audio_format; - NeAACDecHandle decoder = NeAACDecOpen(); NeAACDecConfigurationPtr config = @@ -326,9 +297,9 @@ faad_get_file_time_float(InputStream &is) decoder_buffer_fill(buffer); - ret = faad_decoder_init(decoder, buffer, audio_format, - IgnoreError()); - if (ret) + AudioFormat audio_format; + if (faad_decoder_init(decoder, buffer, audio_format, + IgnoreError())) length = 0; NeAACDecClose(decoder); @@ -359,15 +330,10 @@ faad_get_file_time(InputStream &is) static void faad_stream_decode(Decoder &mpd_decoder, InputStream &is) { - float total_time = 0; - AudioFormat audio_format; - bool ret; - uint16_t bit_rate = 0; - DecoderBuffer *buffer; - - buffer = decoder_buffer_new(&mpd_decoder, is, - FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS); - total_time = faad_song_duration(buffer, is); + DecoderBuffer *buffer = + decoder_buffer_new(&mpd_decoder, is, + FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS); + const float total_time = faad_song_duration(buffer, is); /* create the libfaad decoder */ @@ -389,8 +355,8 @@ faad_stream_decode(Decoder &mpd_decoder, InputStream &is) /* initialize it */ Error error; - ret = faad_decoder_init(decoder, buffer, audio_format, error); - if (!ret) { + AudioFormat audio_format; + if (!faad_decoder_init(decoder, buffer, audio_format, error)) { LogError(error); NeAACDecClose(decoder); decoder_buffer_free(buffer); @@ -404,6 +370,7 @@ faad_stream_decode(Decoder &mpd_decoder, InputStream &is) /* the decoder loop */ DecoderCommand cmd; + unsigned bit_rate = 0; do { size_t frame_size; const void *decoded; @@ -483,7 +450,7 @@ static const char *const faad_mime_types[] = { "audio/aac", "audio/aacp", nullptr }; -const struct DecoderPlugin faad_decoder_plugin = { +const DecoderPlugin faad_decoder_plugin = { "faad", nullptr, nullptr, diff --git a/src/decoder/FaadDecoderPlugin.hxx b/src/decoder/plugins/FaadDecoderPlugin.hxx index 817927d5e..968433e9b 100644 --- a/src/decoder/FaadDecoderPlugin.hxx +++ b/src/decoder/plugins/FaadDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx index bcb1ae3c9..3a0fa7389 100644 --- a/src/decoder/FfmpegDecoderPlugin.cxx +++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,10 +22,10 @@ #include "config.h" #include "FfmpegDecoderPlugin.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "FfmpegMetaData.hxx" #include "tag/TagHandler.hxx" -#include "InputStream.hxx" +#include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -385,7 +385,7 @@ ffmpeg_probe(Decoder *decoder, InputStream &is) AVProbeData avpd; avpd.buf = buffer; avpd.buf_size = nbytes; - avpd.filename = is.uri.c_str(); + avpd.filename = is.GetURI(); return av_probe_input_format(&avpd, true); } @@ -409,7 +409,7 @@ ffmpeg_decode(Decoder &decoder, InputStream &input) //ffmpeg works with ours "fileops" helper AVFormatContext *format_context = nullptr; if (mpd_ffmpeg_open_input(&format_context, stream.io, - input.uri.c_str(), + input.GetURI(), input_format) != 0) { LogError(ffmpeg_domain, "Open failed"); return; @@ -447,8 +447,11 @@ ffmpeg_decode(Decoder &decoder, InputStream &input) const SampleFormat sample_format = ffmpeg_sample_format(codec_context->sample_fmt); - if (sample_format == SampleFormat::UNDEFINED) + if (sample_format == SampleFormat::UNDEFINED) { + // (error message already done by ffmpeg_sample_format()) + avformat_close_input(&format_context); return; + } Error error; AudioFormat audio_format; @@ -555,7 +558,7 @@ ffmpeg_scan_stream(InputStream &is, return false; AVFormatContext *f = nullptr; - if (mpd_ffmpeg_open_input(&f, stream.io, is.uri.c_str(), + if (mpd_ffmpeg_open_input(&f, stream.io, is.GetURI(), input_format) != 0) return false; diff --git a/src/decoder/FfmpegDecoderPlugin.hxx b/src/decoder/plugins/FfmpegDecoderPlugin.hxx index 23bf74fce..0a3e78e4b 100644 --- a/src/decoder/FfmpegDecoderPlugin.hxx +++ b/src/decoder/plugins/FfmpegDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FfmpegMetaData.cxx b/src/decoder/plugins/FfmpegMetaData.cxx index 6e92b4a13..a39466945 100644 --- a/src/decoder/FfmpegMetaData.cxx +++ b/src/decoder/plugins/FfmpegMetaData.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FfmpegMetaData.hxx b/src/decoder/plugins/FfmpegMetaData.hxx index 998cdf5a8..5eb41db68 100644 --- a/src/decoder/FfmpegMetaData.hxx +++ b/src/decoder/plugins/FfmpegMetaData.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FlacCommon.cxx b/src/decoder/plugins/FlacCommon.cxx index e4b906c12..7b67585a0 100644 --- a/src/decoder/FlacCommon.cxx +++ b/src/decoder/plugins/FlacCommon.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,14 +25,10 @@ #include "FlacCommon.hxx" #include "FlacMetadata.hxx" #include "FlacPcm.hxx" -#include "MixRampInfo.hxx" #include "CheckAudioFormat.hxx" #include "util/Error.hxx" -#include "util/Domain.hxx" #include "Log.hxx" -#include <assert.h> - flac_data::flac_data(Decoder &_decoder, InputStream &_input_stream) :FlacInput(_input_stream, &_decoder), @@ -107,8 +103,8 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block, decoder_mixramp(data->decoder, flac_parse_mixramp(block)); - flac_vorbis_comments_to_tag(data->tag, - &block->data.vorbis_comment); + data->tag = flac_vorbis_comments_to_tag(&block->data.vorbis_comment); + break; default: break; diff --git a/src/decoder/FlacCommon.hxx b/src/decoder/plugins/FlacCommon.hxx index de000dfa1..34ce0a3fc 100644 --- a/src/decoder/FlacCommon.hxx +++ b/src/decoder/plugins/FlacCommon.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,11 +25,10 @@ #define MPD_FLAC_COMMON_HXX #include "FlacInput.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "pcm/PcmBuffer.hxx" #include <FLAC/stream_decoder.h> -#include <FLAC/metadata.h> struct flac_data : public FlacInput { PcmBuffer buffer; diff --git a/src/decoder/FlacDecoderPlugin.cxx b/src/decoder/plugins/FlacDecoderPlugin.cxx index 1b5734434..c39aeadd9 100644 --- a/src/decoder/FlacDecoderPlugin.cxx +++ b/src/decoder/plugins/FlacDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,13 +23,10 @@ #include "FlacCommon.hxx" #include "FlacMetadata.hxx" #include "OggCodec.hxx" +#include "fs/Path.hxx" #include "util/Error.hxx" #include "Log.hxx" -#include <glib.h> - -#include <assert.h> - #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 #error libFLAC is too old #endif @@ -83,11 +80,11 @@ flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame, } static bool -flac_scan_file(const char *file, +flac_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { FlacMetadataChain chain; - if (!chain.Read(file)) { + if (!chain.Read(path_fs.c_str())) { FormatDebug(flac_domain, "Failed to read FLAC tags: %s", chain.GetStatusString()); @@ -297,11 +294,11 @@ oggflac_init(gcc_unused const config_param ¶m) } static bool -oggflac_scan_file(const char *file, +oggflac_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { FlacMetadataChain chain; - if (!chain.ReadOgg(file)) { + if (!chain.ReadOgg(path_fs.c_str())) { FormatDebug(flac_domain, "Failed to read OggFLAC tags: %s", chain.GetStatusString()); diff --git a/src/decoder/FlacDecoderPlugin.h b/src/decoder/plugins/FlacDecoderPlugin.h index 936423fbf..fcdecf869 100644 --- a/src/decoder/FlacDecoderPlugin.h +++ b/src/decoder/plugins/FlacDecoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FlacDomain.cxx b/src/decoder/plugins/FlacDomain.cxx index 5858004de..fc5cc5498 100644 --- a/src/decoder/FlacDomain.cxx +++ b/src/decoder/plugins/FlacDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FlacDomain.hxx b/src/decoder/plugins/FlacDomain.hxx index cf357332f..a06c6c6b4 100644 --- a/src/decoder/FlacDomain.hxx +++ b/src/decoder/plugins/FlacDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FlacIOHandle.cxx b/src/decoder/plugins/FlacIOHandle.cxx index b471ecf64..aecee638b 100644 --- a/src/decoder/FlacIOHandle.cxx +++ b/src/decoder/plugins/FlacIOHandle.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FlacIOHandle.hxx b/src/decoder/plugins/FlacIOHandle.hxx index b6e563fa3..a4c3ab86d 100644 --- a/src/decoder/FlacIOHandle.hxx +++ b/src/decoder/plugins/FlacIOHandle.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ #define MPD_FLAC_IO_HANDLE_HXX #include "Compiler.h" -#include "InputStream.hxx" +#include "input/InputStream.hxx" #include <FLAC/callback.h> diff --git a/src/decoder/FlacInput.cxx b/src/decoder/plugins/FlacInput.cxx index ce193101d..720b115f7 100644 --- a/src/decoder/FlacInput.cxx +++ b/src/decoder/plugins/FlacInput.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,8 +20,8 @@ #include "config.h" #include "FlacInput.hxx" #include "FlacDomain.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "util/Error.hxx" #include "Log.hxx" #include "Compiler.h" diff --git a/src/decoder/FlacInput.hxx b/src/decoder/plugins/FlacInput.hxx index ddd5649f8..427abccb4 100644 --- a/src/decoder/FlacInput.hxx +++ b/src/decoder/plugins/FlacInput.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,7 +23,7 @@ #include <FLAC/stream_decoder.h> struct Decoder; -struct InputStream; +class InputStream; /** * This class wraps an #InputStream in libFLAC stream decoder diff --git a/src/decoder/FlacMetadata.cxx b/src/decoder/plugins/FlacMetadata.cxx index 17cc4cd8d..b921e8481 100644 --- a/src/decoder/FlacMetadata.cxx +++ b/src/decoder/plugins/FlacMetadata.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,43 +21,45 @@ #include "FlacMetadata.hxx" #include "XiphTags.hxx" #include "MixRampInfo.hxx" -#include "tag/Tag.hxx" #include "tag/TagHandler.hxx" #include "tag/TagTable.hxx" #include "tag/TagBuilder.hxx" +#include "tag/Tag.hxx" #include "ReplayGainInfo.hxx" #include "util/ASCII.hxx" +#include "util/SplitString.hxx" -#include <glib.h> - -#include <assert.h> #include <string.h> +static const char * +vorbis_comment_value(const FLAC__StreamMetadata *block, + const char *name) +{ + int offset = + FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, + name); + if (offset < 0) + return nullptr; + + size_t name_length = strlen(name); + + const FLAC__StreamMetadata_VorbisComment_Entry &vc = + block->data.vorbis_comment.comments[offset]; + const char *comment = (const char *)vc.entry; + + /* 1 is for '=' */ + return comment + name_length + 1; +} + static bool flac_find_float_comment(const FLAC__StreamMetadata *block, const char *cmnt, float *fl) { - int offset; - size_t pos; - int len; - unsigned char tmp, *p; - - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, - cmnt); - if (offset < 0) - return false; - - pos = strlen(cmnt) + 1; /* 1 is for '=' */ - len = block->data.vorbis_comment.comments[offset].length - pos; - if (len <= 0) + const char *value = vorbis_comment_value(block, cmnt); + if (value == nullptr) return false; - p = &block->data.vorbis_comment.comments[offset].entry[pos]; - tmp = p[len]; - p[len] = '\0'; - *fl = (float)atof((char *)p); - p[len] = tmp; - + *fl = (float)atof(value); return true; } @@ -88,23 +90,11 @@ gcc_pure static std::string flac_find_string_comment(const FLAC__StreamMetadata *block, const char *cmnt) { - int offset; - size_t pos; - int len; - const unsigned char *p; - - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, - cmnt); - if (offset < 0) + const char *value = vorbis_comment_value(block, cmnt); + if (value == nullptr) return std::string(); - pos = strlen(cmnt) + 1; /* 1 is for '=' */ - len = block->data.vorbis_comment.comments[offset].length - pos; - if (len <= 0) - return std::string(); - - p = &block->data.vorbis_comment.comments[offset].entry[pos]; - return std::string((const char *)p, len); + return std::string(value); } MixRampInfo @@ -118,21 +108,19 @@ flac_parse_mixramp(const FLAC__StreamMetadata *block) /** * Checks if the specified name matches the entry's name, and if yes, - * returns the comment value (not null-temrinated). + * returns the comment value; */ static const char * flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, - const char *name, size_t *length_r) + const char *name) { size_t name_length = strlen(name); const char *comment = (const char*)entry->entry; - if (entry->length <= name_length || - !StringEqualsCaseASCII(comment, name, name_length)) + if (!StringEqualsCaseASCII(comment, name, name_length)) return nullptr; if (comment[name_length] == '=') { - *length_r = entry->length - name_length - 1; return comment + name_length + 1; } @@ -148,14 +136,9 @@ flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *name, TagType tag_type, const struct tag_handler *handler, void *handler_ctx) { - const char *value; - size_t value_length; - - value = flac_comment_value(entry, name, &value_length); + const char *value = flac_comment_value(entry, name); if (value != nullptr) { - char *p = g_strndup(value, value_length); - tag_handler_invoke_tag(handler, handler_ctx, tag_type, p); - g_free(p); + tag_handler_invoke_tag(handler, handler_ctx, tag_type, value); return true; } @@ -167,16 +150,12 @@ flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, const struct tag_handler *handler, void *handler_ctx) { if (handler->pair != nullptr) { - char *name = g_strdup((const char*)entry->entry); - char *value = strchr(name, '='); - - if (value != nullptr && value > name) { - *value++ = 0; + const char *comment = (const char *)entry->entry; + const SplitString split(comment, '='); + if (split.IsDefined() && !split.IsEmpty()) tag_handler_invoke_pair(handler, handler_ctx, - name, value); - } - - g_free(name); + split.GetFirst(), + split.GetSecond()); } for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i) @@ -221,13 +200,12 @@ flac_scan_metadata(const FLAC__StreamMetadata *block, } } -void -flac_vorbis_comments_to_tag(Tag &tag, - const FLAC__StreamMetadata_VorbisComment *comment) +Tag +flac_vorbis_comments_to_tag(const FLAC__StreamMetadata_VorbisComment *comment) { TagBuilder tag_builder; flac_scan_comments(comment, &add_tag_handler, &tag_builder); - tag_builder.Commit(tag); + return tag_builder.Commit(); } void diff --git a/src/decoder/FlacMetadata.hxx b/src/decoder/plugins/FlacMetadata.hxx index 96c61b8e6..e0449b2a2 100644 --- a/src/decoder/FlacMetadata.hxx +++ b/src/decoder/plugins/FlacMetadata.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include <assert.h> +struct tag_handler; class MixRampInfo; class FlacMetadataChain { @@ -81,7 +82,7 @@ public: return FLAC__Metadata_ChainStatusString[GetStatus()]; } - void Scan(const struct tag_handler *handler, void *handler_ctx); + void Scan(const tag_handler *handler, void *handler_ctx); }; class FLACMetadataIterator { @@ -110,7 +111,6 @@ public: } }; -struct tag_handler; struct Tag; struct ReplayGainInfo; @@ -130,12 +130,11 @@ flac_parse_replay_gain(ReplayGainInfo &rgi, MixRampInfo flac_parse_mixramp(const FLAC__StreamMetadata *block); -void -flac_vorbis_comments_to_tag(Tag &tag, - const FLAC__StreamMetadata_VorbisComment *comment); +Tag +flac_vorbis_comments_to_tag(const FLAC__StreamMetadata_VorbisComment *comment); void flac_scan_metadata(const FLAC__StreamMetadata *block, - const struct tag_handler *handler, void *handler_ctx); + const tag_handler *handler, void *handler_ctx); #endif diff --git a/src/decoder/FlacPcm.cxx b/src/decoder/plugins/FlacPcm.cxx index 569879371..311500f26 100644 --- a/src/decoder/FlacPcm.cxx +++ b/src/decoder/plugins/FlacPcm.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FlacPcm.hxx b/src/decoder/plugins/FlacPcm.hxx index 6cb2d5062..30c318725 100644 --- a/src/decoder/FlacPcm.hxx +++ b/src/decoder/plugins/FlacPcm.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/FluidsynthDecoderPlugin.cxx b/src/decoder/plugins/FluidsynthDecoderPlugin.cxx index fa946f219..bdf30baea 100644 --- a/src/decoder/FluidsynthDecoderPlugin.cxx +++ b/src/decoder/plugins/FluidsynthDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,8 +19,9 @@ #include "config.h" #include "FluidsynthDecoderPlugin.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "CheckAudioFormat.hxx" +#include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "util/Macros.hxx" @@ -92,7 +93,7 @@ fluidsynth_init(const config_param ¶m) } static void -fluidsynth_file_decode(Decoder &decoder, const char *path_fs) +fluidsynth_file_decode(Decoder &decoder, Path path_fs) { char setting_sample_rate[] = "synth.sample-rate"; /* @@ -141,7 +142,7 @@ fluidsynth_file_decode(Decoder &decoder, const char *path_fs) return; } - ret = fluid_player_add(player, path_fs); + ret = fluid_player_add(player, path_fs.c_str()); if (ret != 0) { LogWarning(fluidsynth_domain, "fluid_player_add() failed"); delete_fluid_player(player); @@ -198,11 +199,11 @@ fluidsynth_file_decode(Decoder &decoder, const char *path_fs) } static bool -fluidsynth_scan_file(const char *file, +fluidsynth_scan_file(Path path_fs, gcc_unused const struct tag_handler *handler, gcc_unused void *handler_ctx) { - return fluid_is_midifile(file); + return fluid_is_midifile(path_fs.c_str()); } static const char *const fluidsynth_suffixes[] = { diff --git a/src/decoder/FluidsynthDecoderPlugin.hxx b/src/decoder/plugins/FluidsynthDecoderPlugin.hxx index 9771898a5..cd8ec2d62 100644 --- a/src/decoder/FluidsynthDecoderPlugin.hxx +++ b/src/decoder/plugins/FluidsynthDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx index d67ee4b42..469da2540 100644 --- a/src/decoder/GmeDecoderPlugin.cxx +++ b/src/decoder/plugins/GmeDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,9 +19,11 @@ #include "config.h" #include "GmeDecoderPlugin.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" +#include "fs/Path.hxx" +#include "util/Alloc.hxx" #include "util/FormatString.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" @@ -50,10 +52,10 @@ static constexpr unsigned GME_BUFFER_SAMPLES = * suffix */ static char * -get_container_name(const char *path_fs) +get_container_name(Path path_fs) { - const char *subtune_suffix = uri_get_suffix(path_fs); - char *path_container = g_strdup(path_fs); + const char *subtune_suffix = uri_get_suffix(path_fs.c_str()); + char *path_container = xstrdup(path_fs.c_str()); char pat[64]; snprintf(pat, sizeof(pat), "%s%s", @@ -79,9 +81,9 @@ get_container_name(const char *path_fs) * is appended. */ static int -get_song_num(const char *path_fs) +get_song_num(Path path_fs) { - const char *subtune_suffix = uri_get_suffix(path_fs); + const char *subtune_suffix = uri_get_suffix(path_fs.c_str()); char pat[64]; snprintf(pat, sizeof(pat), "%s%s", @@ -90,8 +92,8 @@ get_song_num(const char *path_fs) GPatternSpec *path_with_subtune = g_pattern_spec_new(pat); if (g_pattern_match(path_with_subtune, - strlen(path_fs), path_fs, nullptr)) { - char *sub = g_strrstr(path_fs, "/" SUBTUNE_PREFIX); + path_fs.length(), path_fs.data(), nullptr)) { + char *sub = g_strrstr(path_fs.c_str(), "/" SUBTUNE_PREFIX); g_pattern_spec_free(path_with_subtune); if (!sub) return 0; @@ -107,10 +109,11 @@ get_song_num(const char *path_fs) } static char * -gme_container_scan(const char *path_fs, const unsigned int tnum) +gme_container_scan(Path path_fs, const unsigned int tnum) { Music_Emu *emu; - const char *gme_err = gme_open_file(path_fs, &emu, GME_SAMPLE_RATE); + const char *gme_err = gme_open_file(path_fs.c_str(), &emu, + GME_SAMPLE_RATE); if (gme_err != nullptr) { LogWarning(gme_domain, gme_err); return nullptr; @@ -122,7 +125,7 @@ gme_container_scan(const char *path_fs, const unsigned int tnum) if (num_songs < 2) return nullptr; - const char *subtune_suffix = uri_get_suffix(path_fs); + const char *subtune_suffix = uri_get_suffix(path_fs.c_str()); if (tnum <= num_songs){ return FormatNew(SUBTUNE_PREFIX "%03u.%s", tnum, subtune_suffix); @@ -131,14 +134,14 @@ gme_container_scan(const char *path_fs, const unsigned int tnum) } static void -gme_file_decode(Decoder &decoder, const char *path_fs) +gme_file_decode(Decoder &decoder, Path path_fs) { char *path_container = get_container_name(path_fs); Music_Emu *emu; const char *gme_err = gme_open_file(path_container, &emu, GME_SAMPLE_RATE); - g_free(path_container); + free(path_container); if (gme_err != nullptr) { LogWarning(gme_domain, gme_err); return; @@ -207,7 +210,7 @@ gme_file_decode(Decoder &decoder, const char *path_fs) } static bool -gme_scan_file(const char *path_fs, +gme_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { char *path_container = get_container_name(path_fs); @@ -215,7 +218,7 @@ gme_scan_file(const char *path_fs, Music_Emu *emu; const char *gme_err = gme_open_file(path_container, &emu, GME_SAMPLE_RATE); - g_free(path_container); + free(path_container); if (gme_err != nullptr) { LogWarning(gme_domain, gme_err); return false; diff --git a/src/decoder/GmeDecoderPlugin.hxx b/src/decoder/plugins/GmeDecoderPlugin.hxx index e46dc766a..f4885b6e4 100644 --- a/src/decoder/GmeDecoderPlugin.hxx +++ b/src/decoder/plugins/GmeDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/MadDecoderPlugin.cxx b/src/decoder/plugins/MadDecoderPlugin.cxx index 9dd86c55f..ffd63a2ac 100644 --- a/src/decoder/MadDecoderPlugin.cxx +++ b/src/decoder/plugins/MadDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,30 +19,30 @@ #include "config.h" #include "MadDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" -#include "ConfigGlobal.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" +#include "config/ConfigGlobal.hxx" #include "tag/TagId3.hxx" #include "tag/TagRva2.hxx" #include "tag/TagHandler.hxx" #include "CheckAudioFormat.hxx" +#include "util/StringUtil.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" -#include <assert.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <glib.h> #include <mad.h> #ifdef HAVE_ID3TAG #include <id3tag.h> #endif +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + #define FRAMES_CUSHION 2000 #define READ_BUFFER_SIZE 40960 @@ -349,24 +349,14 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) id3_data = stream.this_frame; mad_stream_skip(&(stream), tagsize); } else { - allocated = (id3_byte_t *)g_malloc(tagsize); + allocated = new id3_byte_t[tagsize]; memcpy(allocated, stream.this_frame, count); mad_stream_skip(&(stream), count); - while (count < tagsize) { - size_t len; - - len = decoder_read(decoder, input_stream, - allocated + count, tagsize - count); - if (len == 0) - break; - else - count += len; - } - - if (count != tagsize) { + if (!decoder_read_full(decoder, input_stream, + allocated + count, tagsize - count)) { LogDebug(mad_domain, "error parsing ID3 tag"); - g_free(allocated); + delete[] allocated; return; } @@ -375,7 +365,7 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) id3_tag = id3_tag_parse(id3_data, tagsize); if (id3_tag == nullptr) { - g_free(allocated); + delete[] allocated; return; } @@ -400,7 +390,7 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) id3_tag_delete(id3_tag); - g_free(allocated); + delete[] allocated; #else /* !HAVE_ID3TAG */ (void)mpd_tag; @@ -413,20 +403,7 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) mad_stream_skip(&stream, tagsize); } else { mad_stream_skip(&stream, count); - - while (count < tagsize) { - size_t len = tagsize - count; - char ignored[1024]; - if (len > sizeof(ignored)) - len = sizeof(ignored); - - len = decoder_read(decoder, input_stream, - ignored, len); - if (len == 0) - break; - else - count += len; - } + decoder_skip(decoder, input_stream, tagsize - count); } #endif } @@ -690,7 +667,7 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) /* This is technically incorrect, since the encoder might not be lame. * But there's no other way to determine if this is a lame tag, and we * wouldn't want to go reading a tag that's not there. */ - if (!g_str_has_prefix(lame->encoder, "LAME")) + if (!StringStartsWith(lame->encoder, "LAME")) return false; if (sscanf(lame->encoder+4, "%u.%u", @@ -1119,7 +1096,7 @@ mp3_decode(Decoder &decoder, InputStream &input_stream) if (decoder_get_command(decoder) == DecoderCommand::NONE) LogError(mad_domain, - "Input does not appear to be a mp3 bit stream"); + "input/Input does not appear to be a mp3 bit stream"); return; } diff --git a/src/decoder/MadDecoderPlugin.hxx b/src/decoder/plugins/MadDecoderPlugin.hxx index 450323670..eb2a10d6f 100644 --- a/src/decoder/MadDecoderPlugin.hxx +++ b/src/decoder/plugins/MadDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/MikmodDecoderPlugin.cxx b/src/decoder/plugins/MikmodDecoderPlugin.cxx index 34381aafa..a1938617d 100644 --- a/src/decoder/MikmodDecoderPlugin.cxx +++ b/src/decoder/plugins/MikmodDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,14 +19,15 @@ #include "config.h" #include "MikmodDecoderPlugin.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "tag/TagHandler.hxx" #include "system/FatalError.hxx" +#include "fs/Path.hxx" #include "util/Domain.hxx" #include "Log.hxx" -#include <glib.h> #include <mikmod.h> + #include <assert.h> static constexpr Domain mikmod_domain("mikmod"); @@ -146,11 +147,11 @@ mikmod_decoder_finish(void) } static void -mikmod_decoder_file_decode(Decoder &decoder, const char *path_fs) +mikmod_decoder_file_decode(Decoder &decoder, Path path_fs) { /* deconstify the path because libmikmod wants a non-const string pointer */ - char *const path2 = const_cast<char *>(path_fs); + char *const path2 = const_cast<char *>(path_fs.c_str()); MODULE *handle; int ret; @@ -160,7 +161,7 @@ mikmod_decoder_file_decode(Decoder &decoder, const char *path_fs) if (handle == nullptr) { FormatError(mikmod_domain, - "failed to open mod: %s", path_fs); + "failed to open mod: %s", path_fs.c_str()); return; } @@ -184,18 +185,18 @@ mikmod_decoder_file_decode(Decoder &decoder, const char *path_fs) } static bool -mikmod_decoder_scan_file(const char *path_fs, +mikmod_decoder_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { /* deconstify the path because libmikmod wants a non-const string pointer */ - char *const path2 = const_cast<char *>(path_fs); + char *const path2 = const_cast<char *>(path_fs.c_str()); MODULE *handle = Player_Load(path2, 128, 0); if (handle == nullptr) { FormatDebug(mikmod_domain, - "Failed to open file: %s", path_fs); + "Failed to open file: %s", path_fs.c_str()); return false; } diff --git a/src/decoder/MikmodDecoderPlugin.hxx b/src/decoder/plugins/MikmodDecoderPlugin.hxx index d25c5f6e7..27ba2a823 100644 --- a/src/decoder/MikmodDecoderPlugin.hxx +++ b/src/decoder/plugins/MikmodDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/ModplugDecoderPlugin.cxx b/src/decoder/plugins/ModplugDecoderPlugin.cxx index e75f5479c..336870817 100644 --- a/src/decoder/ModplugDecoderPlugin.cxx +++ b/src/decoder/plugins/ModplugDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,8 +19,8 @@ #include "config.h" #include "ModplugDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "tag/TagHandler.hxx" #include "system/FatalError.hxx" #include "util/WritableBuffer.hxx" diff --git a/src/decoder/ModplugDecoderPlugin.hxx b/src/decoder/plugins/ModplugDecoderPlugin.hxx index 4cd9f5b25..08f2ecb12 100644 --- a/src/decoder/ModplugDecoderPlugin.hxx +++ b/src/decoder/plugins/ModplugDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/MpcdecDecoderPlugin.cxx b/src/decoder/plugins/MpcdecDecoderPlugin.cxx index dc258623c..18f6e7673 100644 --- a/src/decoder/MpcdecDecoderPlugin.cxx +++ b/src/decoder/plugins/MpcdecDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,8 +19,8 @@ #include "config.h" #include "MpcdecDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" #include "util/Error.hxx" @@ -30,8 +30,6 @@ #include <mpc/mpcdec.h> -#include <assert.h> -#include <unistd.h> #include <math.h> struct mpc_decoder_data { diff --git a/src/decoder/MpcdecDecoderPlugin.hxx b/src/decoder/plugins/MpcdecDecoderPlugin.hxx index 23ecc801e..7f71311fa 100644 --- a/src/decoder/MpcdecDecoderPlugin.hxx +++ b/src/decoder/plugins/MpcdecDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/Mpg123DecoderPlugin.cxx b/src/decoder/plugins/Mpg123DecoderPlugin.cxx index df23f7847..76702a08f 100644 --- a/src/decoder/Mpg123DecoderPlugin.cxx +++ b/src/decoder/plugins/Mpg123DecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,16 +19,16 @@ #include "config.h" /* must be first for large file support */ #include "Mpg123DecoderPlugin.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" +#include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" -#include <glib.h> - #include <mpg123.h> + #include <stdio.h> static constexpr Domain mpg123_domain("mpg123"); @@ -104,7 +104,7 @@ mpd_mpg123_open(mpg123_handle *handle, const char *path_fs, } static void -mpd_mpg123_file_decode(Decoder &decoder, const char *path_fs) +mpd_mpg123_file_decode(Decoder &decoder, Path path_fs) { mpg123_handle *handle; int error; @@ -122,7 +122,7 @@ mpd_mpg123_file_decode(Decoder &decoder, const char *path_fs) } AudioFormat audio_format; - if (!mpd_mpg123_open(handle, path_fs, audio_format)) { + if (!mpd_mpg123_open(handle, path_fs.c_str(), audio_format)) { mpg123_delete(handle); return; } @@ -200,7 +200,7 @@ mpd_mpg123_file_decode(Decoder &decoder, const char *path_fs) } static bool -mpd_mpg123_scan_file(const char *path_fs, +mpd_mpg123_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { mpg123_handle *handle; @@ -216,7 +216,7 @@ mpd_mpg123_scan_file(const char *path_fs, } AudioFormat audio_format; - if (!mpd_mpg123_open(handle, path_fs, audio_format)) { + if (!mpd_mpg123_open(handle, path_fs.c_str(), audio_format)) { mpg123_delete(handle); return false; } diff --git a/src/decoder/Mpg123DecoderPlugin.hxx b/src/decoder/plugins/Mpg123DecoderPlugin.hxx index 10f7c37f5..fd089c6a4 100644 --- a/src/decoder/Mpg123DecoderPlugin.hxx +++ b/src/decoder/plugins/Mpg123DecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OggCodec.cxx b/src/decoder/plugins/OggCodec.cxx index 565dbafcf..c7f39586e 100644 --- a/src/decoder/OggCodec.cxx +++ b/src/decoder/plugins/OggCodec.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,7 @@ #include "config.h" #include "OggCodec.hxx" +#include "../DecoderAPI.hxx" #include <string.h> diff --git a/src/decoder/OggCodec.hxx b/src/decoder/plugins/OggCodec.hxx index 857871607..3b096561c 100644 --- a/src/decoder/OggCodec.hxx +++ b/src/decoder/plugins/OggCodec.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,7 +24,8 @@ #ifndef MPD_OGG_CODEC_HXX #define MPD_OGG_CODEC_HXX -#include "DecoderAPI.hxx" +struct Decoder; +class InputStream; enum ogg_codec { OGG_CODEC_UNKNOWN, diff --git a/src/decoder/OggFind.cxx b/src/decoder/plugins/OggFind.cxx index 65c7fa3ce..df4318da2 100644 --- a/src/decoder/OggFind.cxx +++ b/src/decoder/plugins/OggFind.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OggFind.hxx b/src/decoder/plugins/OggFind.hxx index ad51ccdf3..d93b87505 100644 --- a/src/decoder/OggFind.hxx +++ b/src/decoder/plugins/OggFind.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,11 +21,10 @@ #define MPD_OGG_FIND_HXX #include "check.h" -#include "InputStream.hxx" +#include "input/InputStream.hxx" #include <ogg/ogg.h> -struct InputStream; class OggSyncState; /** diff --git a/src/decoder/OggSyncState.hxx b/src/decoder/plugins/OggSyncState.hxx index 5235c1bd8..024902fff 100644 --- a/src/decoder/OggSyncState.hxx +++ b/src/decoder/plugins/OggSyncState.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OggUtil.cxx b/src/decoder/plugins/OggUtil.cxx index 8f181ce57..6341b0008 100644 --- a/src/decoder/OggUtil.cxx +++ b/src/decoder/plugins/OggUtil.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "config.h" #include "OggUtil.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" bool OggFeed(ogg_sync_state &oy, Decoder *decoder, diff --git a/src/decoder/OggUtil.hxx b/src/decoder/plugins/OggUtil.hxx index 41fc755ba..94c380ef4 100644 --- a/src/decoder/OggUtil.hxx +++ b/src/decoder/plugins/OggUtil.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ #include <stddef.h> -struct InputStream; +class InputStream; struct Decoder; /** diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index f3d7b342e..67bbe7a54 100644 --- a/src/decoder/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,23 +22,19 @@ #include "OpusDomain.hxx" #include "OpusHead.hxx" #include "OpusTags.hxx" -#include "OggUtil.hxx" #include "OggFind.hxx" #include "OggSyncState.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "OggCodec.hxx" -#include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" #include "tag/TagBuilder.hxx" -#include "InputStream.hxx" +#include "input/InputStream.hxx" #include "util/Error.hxx" #include "Log.hxx" #include <opus.h> #include <ogg/ogg.h> -#include <glib.h> - #include <string.h> static constexpr opus_int32 opus_sample_rate = 48000; @@ -107,7 +103,7 @@ public: MPDOpusDecoder::~MPDOpusDecoder() { - g_free(output_buffer); + delete[] output_buffer; if (opus_decoder != nullptr) opus_decoder_destroy(opus_decoder); @@ -272,9 +268,7 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) to hold a quarter second, larger than 120ms required by libopus */ output_size = audio_format.sample_rate / 4; - output_buffer = (opus_int16 *) - g_malloc(sizeof(*output_buffer) * output_size * - audio_format.channels); + output_buffer = new opus_int16[output_size * audio_format.channels]; return decoder_get_command(decoder); } @@ -294,8 +288,7 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet) !tag_builder.IsEmpty()) { decoder_replay_gain(decoder, &rgi); - Tag tag; - tag_builder.Commit(tag); + Tag tag = tag_builder.Commit(); cmd = decoder_tag(decoder, input_stream, std::move(tag)); } else cmd = decoder_get_command(decoder); diff --git a/src/decoder/OpusDecoderPlugin.h b/src/decoder/plugins/OpusDecoderPlugin.h index 263ac6e2d..260dab99a 100644 --- a/src/decoder/OpusDecoderPlugin.h +++ b/src/decoder/plugins/OpusDecoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OpusDomain.cxx b/src/decoder/plugins/OpusDomain.cxx index b00e2a553..1efd64a48 100644 --- a/src/decoder/OpusDomain.cxx +++ b/src/decoder/plugins/OpusDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OpusDomain.hxx b/src/decoder/plugins/OpusDomain.hxx index 2b56c427c..fb19e0301 100644 --- a/src/decoder/OpusDomain.hxx +++ b/src/decoder/plugins/OpusDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OpusHead.cxx b/src/decoder/plugins/OpusHead.cxx index 0417d3905..bfa41d618 100644 --- a/src/decoder/OpusHead.cxx +++ b/src/decoder/plugins/OpusHead.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,6 @@ #include "OpusHead.hxx" #include <stdint.h> -#include <string.h> struct OpusHead { char signature[8]; diff --git a/src/decoder/OpusHead.hxx b/src/decoder/plugins/OpusHead.hxx index fa6a2b666..c478d8d90 100644 --- a/src/decoder/OpusHead.hxx +++ b/src/decoder/plugins/OpusHead.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OpusReader.hxx b/src/decoder/plugins/OpusReader.hxx index 2bb39b748..c5b8e9107 100644 --- a/src/decoder/OpusReader.hxx +++ b/src/decoder/plugins/OpusReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OpusTags.cxx b/src/decoder/plugins/OpusTags.cxx index f7729e5ad..aff5479c0 100644 --- a/src/decoder/OpusTags.cxx +++ b/src/decoder/plugins/OpusTags.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/OpusTags.hxx b/src/decoder/plugins/OpusTags.hxx index e1f1a1ff1..be3ac3a8d 100644 --- a/src/decoder/OpusTags.hxx +++ b/src/decoder/plugins/OpusTags.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/PcmDecoderPlugin.cxx b/src/decoder/plugins/PcmDecoderPlugin.cxx index dbc38fb76..e7e593550 100644 --- a/src/decoder/PcmDecoderPlugin.cxx +++ b/src/decoder/plugins/PcmDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,15 +18,13 @@ */ #include "config.h" -#include "decoder/PcmDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "PcmDecoderPlugin.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "util/Error.hxx" #include "util/ByteReverse.hxx" #include "Log.hxx" -#include <glib.h> -#include <unistd.h> #include <string.h> #include <stdio.h> /* for SEEK_SET */ diff --git a/src/decoder/PcmDecoderPlugin.hxx b/src/decoder/plugins/PcmDecoderPlugin.hxx index 38e4a5020..3582e5856 100644 --- a/src/decoder/PcmDecoderPlugin.hxx +++ b/src/decoder/plugins/PcmDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/SidplayDecoderPlugin.cxx b/src/decoder/plugins/SidplayDecoderPlugin.cxx index 160337594..e3e3b8d96 100644 --- a/src/decoder/SidplayDecoderPlugin.cxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,8 @@ #include "SidplayDecoderPlugin.hxx" #include "../DecoderAPI.hxx" #include "tag/TagHandler.hxx" +#include "fs/Path.hxx" +#include "util/FormatString.hxx" #include "util/Domain.hxx" #include "system/ByteOrder.hxx" #include "Log.hxx" @@ -119,9 +121,9 @@ sidplay_finish() * suffix */ static char * -get_container_name(const char *path_fs) +get_container_name(Path path_fs) { - char *path_container=g_strdup(path_fs); + char *path_container = strdup(path_fs.c_str()); if(!g_pattern_match(path_with_subtune, strlen(path_container), path_container, nullptr)) @@ -158,14 +160,14 @@ get_song_num(const char *path_fs) /* get the song length in seconds */ static int -get_song_length(const char *path_fs) +get_song_length(Path path_fs) { if (songlength_database == nullptr) return -1; - gchar *sid_file=get_container_name(path_fs); + char *sid_file = get_container_name(path_fs); SidTuneMod tune(sid_file); - g_free(sid_file); + free(sid_file); if(!tune) { LogWarning(sidplay_domain, "failed to load file for calculating md5 sum"); @@ -174,7 +176,7 @@ get_song_length(const char *path_fs) char md5sum[SIDTUNE_MD5_LENGTH+1]; tune.createMD5(md5sum); - const unsigned song_num = get_song_num(path_fs); + const unsigned song_num = get_song_num(path_fs.c_str()); gsize num_items; gchar **values=g_key_file_get_string_list(songlength_database, @@ -201,7 +203,7 @@ get_song_length(const char *path_fs) } static void -sidplay_file_decode(Decoder &decoder, const char *path_fs) +sidplay_file_decode(Decoder &decoder, Path path_fs) { int channels; @@ -209,13 +211,13 @@ sidplay_file_decode(Decoder &decoder, const char *path_fs) char *path_container=get_container_name(path_fs); SidTune tune(path_container, nullptr, true); - g_free(path_container); + free(path_container); if (!tune) { LogWarning(sidplay_domain, "failed to load file"); return; } - int song_num=get_song_num(path_fs); + const int song_num = get_song_num(path_fs.c_str()); tune.selectSong(song_num); int song_len=get_song_length(path_fs); @@ -339,14 +341,14 @@ sidplay_file_decode(Decoder &decoder, const char *path_fs) } static bool -sidplay_scan_file(const char *path_fs, +sidplay_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { - int song_num=get_song_num(path_fs); + const int song_num = get_song_num(path_fs.c_str()); char *path_container=get_container_name(path_fs); SidTune tune(path_container, nullptr, true); - g_free(path_container); + free(path_container); if (!tune) return false; @@ -388,9 +390,9 @@ sidplay_scan_file(const char *path_fs, } static char * -sidplay_container_scan(const char *path_fs, const unsigned int tnum) +sidplay_container_scan(Path path_fs, const unsigned int tnum) { - SidTune tune(path_fs, nullptr, true); + SidTune tune(path_fs.c_str(), nullptr, true); if (!tune) return nullptr; @@ -404,9 +406,7 @@ sidplay_container_scan(const char *path_fs, const unsigned int tnum) /* Construct container/tune path names, eg. Delta.sid/tune_001.sid */ if(tnum<=info.songs) { - char *subtune= g_strdup_printf( - SUBTUNE_PREFIX "%03u.sid", tnum); - return subtune; + return FormatNew(SUBTUNE_PREFIX "%03u.sid", tnum); } else return nullptr; } diff --git a/src/decoder/SidplayDecoderPlugin.hxx b/src/decoder/plugins/SidplayDecoderPlugin.hxx index 16544801f..58786e646 100644 --- a/src/decoder/SidplayDecoderPlugin.hxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/SndfileDecoderPlugin.cxx b/src/decoder/plugins/SndfileDecoderPlugin.cxx index 77b132962..6d9e0d31e 100644 --- a/src/decoder/SndfileDecoderPlugin.cxx +++ b/src/decoder/plugins/SndfileDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,10 +19,11 @@ #include "config.h" #include "SndfileDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" +#include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" @@ -187,7 +188,7 @@ sndfile_stream_decode(Decoder &decoder, InputStream &is) } static bool -sndfile_scan_file(const char *path_fs, +sndfile_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { SNDFILE *sf; @@ -196,14 +197,14 @@ sndfile_scan_file(const char *path_fs, info.format = 0; - sf = sf_open(path_fs, SFM_READ, &info); + sf = sf_open(path_fs.c_str(), SFM_READ, &info); if (sf == nullptr) return false; if (!audio_valid_sample_rate(info.samplerate)) { sf_close(sf); FormatWarning(sndfile_domain, - "Invalid sample rate in %s", path_fs); + "Invalid sample rate in %s", path_fs.c_str()); return false; } diff --git a/src/decoder/SndfileDecoderPlugin.hxx b/src/decoder/plugins/SndfileDecoderPlugin.hxx index f8aa65680..d56acdd5a 100644 --- a/src/decoder/SndfileDecoderPlugin.hxx +++ b/src/decoder/plugins/SndfileDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/VorbisComments.cxx b/src/decoder/plugins/VorbisComments.cxx index d4f019b58..2a0820ab5 100644 --- a/src/decoder/VorbisComments.cxx +++ b/src/decoder/plugins/VorbisComments.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,16 +20,13 @@ #include "config.h" #include "VorbisComments.hxx" #include "XiphTags.hxx" -#include "tag/Tag.hxx" #include "tag/TagTable.hxx" #include "tag/TagHandler.hxx" #include "tag/TagBuilder.hxx" #include "ReplayGainInfo.hxx" #include "util/ASCII.hxx" +#include "util/SplitString.hxx" -#include <glib.h> - -#include <assert.h> #include <stddef.h> #include <string.h> #include <stdlib.h> @@ -104,16 +101,11 @@ vorbis_scan_comment(const char *comment, const struct tag_handler *handler, void *handler_ctx) { if (handler->pair != nullptr) { - char *name = g_strdup((const char*)comment); - char *value = strchr(name, '='); - - if (value != nullptr && value > name) { - *value++ = 0; + const SplitString split(comment, '='); + if (split.IsDefined() && !split.IsEmpty()) tag_handler_invoke_pair(handler, handler_ctx, - name, value); - } - - g_free(name); + split.GetFirst(), + split.GetSecond()); } for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i) @@ -145,5 +137,5 @@ vorbis_comments_to_tag(char **comments) vorbis_comments_scan(comments, &add_tag_handler, &tag_builder); return tag_builder.IsEmpty() ? nullptr - : tag_builder.Commit(); + : tag_builder.CommitNew(); } diff --git a/src/decoder/VorbisComments.hxx b/src/decoder/plugins/VorbisComments.hxx index e5a48ef6b..893c89277 100644 --- a/src/decoder/VorbisComments.hxx +++ b/src/decoder/plugins/VorbisComments.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -31,7 +31,7 @@ vorbis_comments_to_replay_gain(ReplayGainInfo &rgi, char **comments); void vorbis_comments_scan(char **comments, - const struct tag_handler *handler, void *handler_ctx); + const tag_handler *handler, void *handler_ctx); Tag * vorbis_comments_to_tag(char **comments); diff --git a/src/decoder/VorbisDecoderPlugin.cxx b/src/decoder/plugins/VorbisDecoderPlugin.cxx index 4d3e48528..fd110f457 100644 --- a/src/decoder/VorbisDecoderPlugin.cxx +++ b/src/decoder/plugins/VorbisDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,13 +21,11 @@ #include "VorbisDecoderPlugin.h" #include "VorbisComments.hxx" #include "VorbisDomain.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "OggCodec.hxx" #include "util/Error.hxx" -#include "util/UriUtil.hxx" #include "util/Macros.hxx" -#include "system/ByteOrder.hxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" #include "Log.hxx" @@ -48,7 +46,6 @@ #define ov_time_seek_page(VF, S) (ov_time_seek_page(VF, (S)*1000)) #endif /* HAVE_TREMOR */ -#include <assert.h> #include <errno.h> struct vorbis_input_stream { diff --git a/src/decoder/VorbisDecoderPlugin.h b/src/decoder/plugins/VorbisDecoderPlugin.h index 54953d83a..b54df2e97 100644 --- a/src/decoder/VorbisDecoderPlugin.h +++ b/src/decoder/plugins/VorbisDecoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2012 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/VorbisDomain.cxx b/src/decoder/plugins/VorbisDomain.cxx index 32ff4d6b7..e3d880efa 100644 --- a/src/decoder/VorbisDomain.cxx +++ b/src/decoder/plugins/VorbisDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/VorbisDomain.hxx b/src/decoder/plugins/VorbisDomain.hxx index a35edd041..48715e328 100644 --- a/src/decoder/VorbisDomain.hxx +++ b/src/decoder/plugins/VorbisDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,8 @@ #include "check.h" -extern const class Domain vorbis_domain; +class Domain; + +extern const Domain vorbis_domain; #endif diff --git a/src/decoder/WavpackDecoderPlugin.cxx b/src/decoder/plugins/WavpackDecoderPlugin.cxx index 98555c5e8..eb15a3380 100644 --- a/src/decoder/WavpackDecoderPlugin.cxx +++ b/src/decoder/plugins/WavpackDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,11 +19,12 @@ #include "config.h" #include "WavpackDecoderPlugin.hxx" -#include "DecoderAPI.hxx" -#include "InputStream.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" #include "tag/ApeTag.hxx" +#include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "util/Macros.hxx" @@ -269,15 +270,16 @@ wavpack_scan_pair(WavpackContext *wpc, const char *name, * Reads metainfo from the specified file. */ static bool -wavpack_scan_file(const char *fname, +wavpack_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { char error[ERRORLEN]; - WavpackContext *wpc = WavpackOpenFileInput(fname, error, OPEN_TAGS, 0); + WavpackContext *wpc = WavpackOpenFileInput(path_fs.c_str(), error, + OPEN_TAGS, 0); if (wpc == nullptr) { FormatError(wavpack_domain, "failed to open WavPack file \"%s\": %s", - fname, error); + path_fs.c_str(), error); return false; } @@ -465,7 +467,7 @@ wavpack_open_wvc(Decoder &decoder, const char *uri, size_t nbytes = decoder_read(decoder, *is_wvc, &first_byte, sizeof(first_byte)); if (nbytes == 0) { - is_wvc->Close(); + delete is_wvc; return nullptr; } @@ -485,7 +487,7 @@ wavpack_streamdecode(Decoder &decoder, InputStream &is) bool can_seek = is.seekable; wavpack_input isp_wvc; - InputStream *is_wvc = wavpack_open_wvc(decoder, is.uri.c_str(), + InputStream *is_wvc = wavpack_open_wvc(decoder, is.GetURI(), is.mutex, is.cond, &isp_wvc); if (is_wvc != nullptr) { @@ -517,7 +519,7 @@ wavpack_streamdecode(Decoder &decoder, InputStream &is) WavpackCloseFile(wpc); if (open_flags & OPEN_WVC) { - is_wvc->Close(); + delete is_wvc; } } @@ -525,16 +527,16 @@ wavpack_streamdecode(Decoder &decoder, InputStream &is) * Decodes a file. */ static void -wavpack_filedecode(Decoder &decoder, const char *fname) +wavpack_filedecode(Decoder &decoder, Path path_fs) { char error[ERRORLEN]; - WavpackContext *wpc = WavpackOpenFileInput(fname, error, + WavpackContext *wpc = WavpackOpenFileInput(path_fs.c_str(), error, OPEN_TAGS | OPEN_WVC | OPEN_NORMALIZE, 23); if (wpc == nullptr) { FormatWarning(wavpack_domain, "failed to open WavPack file \"%s\": %s", - fname, error); + path_fs.c_str(), error); return; } diff --git a/src/decoder/WavpackDecoderPlugin.hxx b/src/decoder/plugins/WavpackDecoderPlugin.hxx index 3a2d94532..2e5f9bd42 100644 --- a/src/decoder/WavpackDecoderPlugin.hxx +++ b/src/decoder/plugins/WavpackDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/WildmidiDecoderPlugin.cxx b/src/decoder/plugins/WildmidiDecoderPlugin.cxx index 3da3f1387..a3a4b2745 100644 --- a/src/decoder/WildmidiDecoderPlugin.cxx +++ b/src/decoder/plugins/WildmidiDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,12 +19,13 @@ #include "config.h" #include "WildmidiDecoderPlugin.hxx" -#include "DecoderAPI.hxx" +#include "../DecoderAPI.hxx" #include "tag/TagHandler.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "fs/AllocatedPath.hxx" #include "fs/FileSystem.hxx" +#include "fs/Path.hxx" #include "system/FatalError.hxx" #include "Log.hxx" @@ -65,7 +66,7 @@ wildmidi_finish(void) } static void -wildmidi_file_decode(Decoder &decoder, const char *path_fs) +wildmidi_file_decode(Decoder &decoder, Path path_fs) { static constexpr AudioFormat audio_format = { WILDMIDI_SAMPLE_RATE, @@ -75,7 +76,7 @@ wildmidi_file_decode(Decoder &decoder, const char *path_fs) midi *wm; const struct _WM_Info *info; - wm = WildMidi_Open(path_fs); + wm = WildMidi_Open(path_fs.c_str()); if (wm == nullptr) return; @@ -118,10 +119,10 @@ wildmidi_file_decode(Decoder &decoder, const char *path_fs) } static bool -wildmidi_scan_file(const char *path_fs, +wildmidi_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { - midi *wm = WildMidi_Open(path_fs); + midi *wm = WildMidi_Open(path_fs.c_str()); if (wm == nullptr) return false; diff --git a/src/decoder/WildmidiDecoderPlugin.hxx b/src/decoder/plugins/WildmidiDecoderPlugin.hxx index a6289612e..fc87aab80 100644 --- a/src/decoder/WildmidiDecoderPlugin.hxx +++ b/src/decoder/plugins/WildmidiDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/XiphTags.cxx b/src/decoder/plugins/XiphTags.cxx index b9958a19a..a5e534086 100644 --- a/src/decoder/XiphTags.cxx +++ b/src/decoder/plugins/XiphTags.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/XiphTags.hxx b/src/decoder/plugins/XiphTags.hxx index 606dfef10..48a27425f 100644 --- a/src/decoder/XiphTags.hxx +++ b/src/decoder/plugins/XiphTags.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify |