diff options
Diffstat (limited to 'src/decoder')
-rw-r--r-- | src/decoder/DecoderAPI.cxx | 632 | ||||
-rw-r--r-- | src/decoder/DecoderAPI.hxx | 223 | ||||
-rw-r--r-- | src/decoder/DecoderBuffer.cxx | 178 | ||||
-rw-r--r-- | src/decoder/DecoderBuffer.hxx | 121 | ||||
-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 | 126 | ||||
-rw-r--r-- | src/decoder/DecoderList.cxx | 165 | ||||
-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) | 141 | ||||
-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) | 54 | ||||
-rw-r--r-- | src/decoder/plugins/DsdLib.hxx (renamed from src/decoder/DsdLib.hxx) | 8 | ||||
-rw-r--r-- | src/decoder/plugins/DsdiffDecoderPlugin.cxx (renamed from src/decoder/DsdiffDecoderPlugin.cxx) | 45 | ||||
-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) | 168 | ||||
-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) | 48 | ||||
-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) | 12 | ||||
-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) | 19 | ||||
-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) | 30 | ||||
-rw-r--r-- | src/decoder/plugins/FlacIOHandle.hxx (renamed from src/decoder/FlacIOHandle.hxx) | 6 | ||||
-rw-r--r-- | src/decoder/plugins/FlacInput.cxx (renamed from src/decoder/FlacInput.cxx) | 18 | ||||
-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) | 34 | ||||
-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/Mp4v2DecoderPlugin.cxx | 324 | ||||
-rw-r--r-- | src/decoder/plugins/Mp4v2DecoderPlugin.hxx | 25 | ||||
-rw-r--r-- | src/decoder/plugins/MpcdecDecoderPlugin.cxx (renamed from src/decoder/MpcdecDecoderPlugin.cxx) | 10 | ||||
-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) | 15 | ||||
-rw-r--r-- | src/decoder/plugins/OggFind.hxx (renamed from src/decoder/OggFind.hxx) | 7 | ||||
-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) | 33 | ||||
-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) | 13 | ||||
-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) | 105 | ||||
-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) | 98 | ||||
-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) | 146 | ||||
-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) | 7 | ||||
-rw-r--r-- | src/decoder/plugins/XiphTags.hxx (renamed from src/decoder/XiphTags.hxx) | 2 |
99 files changed, 4145 insertions, 702 deletions
diff --git a/src/decoder/DecoderAPI.cxx b/src/decoder/DecoderAPI.cxx new file mode 100644 index 000000000..ae600260f --- /dev/null +++ b/src/decoder/DecoderAPI.cxx @@ -0,0 +1,632 @@ +/* + * 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 "util/ConstBuffer.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); +} + +InputStream * +decoder_open_uri(Decoder &decoder, const char *uri, Error &error) +{ + assert(decoder.dc.state == DecoderState::START || + decoder.dc.state == DecoderState::DECODE); + + DecoderControl &dc = decoder.dc; + Mutex &mutex = dc.mutex; + Cond &cond = dc.cond; + + InputStream *is = InputStream::Open(uri, mutex, cond, error); + if (is == nullptr) + return nullptr; + + mutex.lock(); + while (true) { + is->Update(); + if (is->IsReady()) { + mutex.unlock(); + return is; + } + + if (dc.command == DecoderCommand::STOP) { + mutex.unlock(); + delete is; + return nullptr; + } + + cond.wait(mutex); + } +} + +/** + * 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) +{ + MusicChunk *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; + auto result = decoder.convert->Convert({data, 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; + } + + data = result.data; + length = result.size; + } else { + assert(dc.in_audio_format == dc.out_audio_format); + } + + while (length > 0) { + MusicChunk *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..c57a02e01 --- /dev/null +++ b/src/decoder/DecoderAPI.hxx @@ -0,0 +1,223 @@ +/* + * 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 + +class Error; + +/** + * 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); + +/** + * Open a new #InputStream and wait until it's ready. Can get + * cancelled by DecoderCommand::STOP (returns nullptr without setting + * #Error). + */ +InputStream * +decoder_open_uri(Decoder &decoder, const char *uri, Error &error); + +/** + * 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..7902e6e89 --- /dev/null +++ b/src/decoder/DecoderBuffer.cxx @@ -0,0 +1,178 @@ +/* + * 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); +} + +const InputStream & +decoder_buffer_get_stream(const DecoderBuffer *buffer) +{ + return *buffer->is; +} + +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; +} + +static const void * +decoder_buffer_head(const DecoderBuffer *buffer) +{ + return buffer->data + buffer->consumed; +} + +size_t +decoder_buffer_available(const DecoderBuffer *buffer) +{ + return buffer->length - buffer->consumed; +} + +ConstBuffer<void> +decoder_buffer_read(const DecoderBuffer *buffer) +{ + return { + decoder_buffer_head(buffer), + decoder_buffer_available(buffer), + }; +} + +ConstBuffer<void> +decoder_buffer_need(DecoderBuffer *buffer, size_t min_size) +{ + while (true) { + const auto available = decoder_buffer_available(buffer); + if (available >= min_size) + return { decoder_buffer_head(buffer), available }; + + if (!decoder_buffer_fill(buffer)) + return nullptr; + } +} + +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) +{ + const size_t available = decoder_buffer_available(buffer); + if (available >= nbytes) { + decoder_buffer_consume(buffer, nbytes); + return true; + } + + decoder_buffer_clear(buffer); + nbytes -= available; + + return decoder_skip(buffer->decoder, *buffer->is, nbytes); +} diff --git a/src/decoder/DecoderBuffer.hxx b/src/decoder/DecoderBuffer.hxx new file mode 100644 index 000000000..4a482be75 --- /dev/null +++ b/src/decoder/DecoderBuffer.hxx @@ -0,0 +1,121 @@ +/* + * 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 +const InputStream & +decoder_buffer_get_stream(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); + +/** + * How many bytes are stored in the buffer? + */ +gcc_pure +size_t +decoder_buffer_available(const 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); + +/** + * Wait until this number of bytes are available. Returns nullptr on + * error. + */ +ConstBuffer<void> +decoder_buffer_need(DecoderBuffer *buffer, size_t min_size); + +/** + * 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..f78ce1a31 --- /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 #MusicChunk 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..416a75b75 --- /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; +} + +MusicChunk * +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..e6c30d071 --- /dev/null +++ b/src/decoder/DecoderInternal.hxx @@ -0,0 +1,126 @@ +/* + * 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 MusicChunk; +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 */ + MusicChunk *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 + */ + MusicChunk *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..5a13e6694 --- /dev/null +++ b/src/decoder/DecoderList.cxx @@ -0,0 +1,165 @@ +/* + * 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/Mp4v2DecoderPlugin.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 HAVE_MP4V2 + &mp4v2_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 9f097f90b..93a6e03a2 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" @@ -33,11 +34,21 @@ #include <assert.h> #include <stdio.h> -/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */ -#define CHUNK_SIZE 1020 - static constexpr Domain audiofile_domain("audiofile"); +static void +audiofile_error_func(long, const char *msg) +{ + LogWarning(audiofile_domain, msg); +} + +static bool +audiofile_init(const config_param &) +{ + afSetErrorHandler(audiofile_error_func); + return true; +} + struct AudioFileInputStream { Decoder *const decoder; InputStream &is; @@ -52,18 +63,14 @@ struct AudioFileInputStream { } }; -static int audiofile_get_duration(const char *file) +gcc_pure +static double +audiofile_get_duration(AFfilehandle fh) { - int total_time; - AFfilehandle af_fp = afOpenFile(file, "r", nullptr); - if (af_fp == AF_NULL_FILEHANDLE) { - return -1; - } - total_time = (int) - ((double)afGetFrameCount(af_fp, AF_DEFAULT_TRACK) - / afGetRate(af_fp, AF_DEFAULT_TRACK)); - afCloseFile(af_fp); - return total_time; + double frame_count = afGetFrameCount(fh, AF_DEFAULT_TRACK); + double rate = afGetRate(fh, AF_DEFAULT_TRACK); + + return frame_count / rate; } static ssize_t @@ -101,18 +108,21 @@ audiofile_file_destroy(AFvirtualfile *vfile) } static AFfileoffset -audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset offset, int is_relative) +audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset _offset, + int is_relative) { AudioFileInputStream &afis = *(AudioFileInputStream *)vfile->closure; InputStream &is = afis.is; - int whence = (is_relative ? SEEK_CUR : SEEK_SET); + InputStream::offset_type offset = _offset; + if (is_relative) + offset += is.GetOffset(); Error error; - if (is.LockSeek(offset, whence, error)) { - LogError(error, "Seek failed"); + if (is.LockSeek(offset, error)) { return is.GetOffset(); } else { + LogError(error, "Seek failed"); return -1; } } @@ -174,86 +184,93 @@ audiofile_setup_sample_format(AFfilehandle af_fp) static void audiofile_stream_decode(Decoder &decoder, InputStream &is) { - AFvirtualfile *vf; - int fs, frame_count; - AFfilehandle af_fp; - AudioFormat audio_format; - float total_time; - uint16_t bit_rate; - int ret; - char chunk[CHUNK_SIZE]; - if (!is.IsSeekable()) { LogWarning(audiofile_domain, "not seekable"); return; } AudioFileInputStream afis{&decoder, is}; - vf = setup_virtual_fops(afis); + AFvirtualfile *const vf = setup_virtual_fops(afis); - af_fp = afOpenVirtualFile(vf, "r", nullptr); - if (af_fp == AF_NULL_FILEHANDLE) { - LogWarning(audiofile_domain, "failed to input stream"); + const AFfilehandle fh = afOpenVirtualFile(vf, "r", nullptr); + if (fh == AF_NULL_FILEHANDLE) return; - } Error error; + AudioFormat audio_format; if (!audio_format_init_checked(audio_format, - afGetRate(af_fp, AF_DEFAULT_TRACK), - audiofile_setup_sample_format(af_fp), - afGetVirtualChannels(af_fp, AF_DEFAULT_TRACK), + afGetRate(fh, AF_DEFAULT_TRACK), + audiofile_setup_sample_format(fh), + afGetVirtualChannels(fh, AF_DEFAULT_TRACK), error)) { LogError(error); - afCloseFile(af_fp); + afCloseFile(fh); return; } - frame_count = afGetFrameCount(af_fp, AF_DEFAULT_TRACK); + const double total_time = audiofile_get_duration(fh); - total_time = ((float)frame_count / (float)audio_format.sample_rate); + const uint16_t kbit_rate = (uint16_t) + (is.GetSize() * 8.0 / total_time / 1000.0 + 0.5); - bit_rate = (uint16_t)(is.GetSize() * 8.0 / total_time / 1000.0 + 0.5); - - fs = (int)afGetVirtualFrameSize(af_fp, AF_DEFAULT_TRACK, 1); + const unsigned frame_size = (unsigned) + afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, true); decoder_initialized(decoder, audio_format, true, total_time); DecoderCommand cmd; do { - ret = afReadFrames(af_fp, AF_DEFAULT_TRACK, chunk, - CHUNK_SIZE / fs); - if (ret <= 0) + /* pick 1020 since its divisible for 8,16,24, and + 32-bit audio */ + char chunk[1020]; + const int nframes = + afReadFrames(fh, AF_DEFAULT_TRACK, chunk, + sizeof(chunk) / frame_size); + if (nframes <= 0) break; cmd = decoder_data(decoder, nullptr, - chunk, ret * fs, - bit_rate); + chunk, nframes * frame_size, + kbit_rate); if (cmd == DecoderCommand::SEEK) { AFframecount frame = decoder_seek_where(decoder) * audio_format.sample_rate; - afSeekFrame(af_fp, AF_DEFAULT_TRACK, frame); + afSeekFrame(fh, AF_DEFAULT_TRACK, frame); decoder_command_finished(decoder); cmd = DecoderCommand::NONE; } } while (cmd == DecoderCommand::NONE); - afCloseFile(af_fp); + afCloseFile(fh); } -static bool -audiofile_scan_file(const char *file, - const struct tag_handler *handler, void *handler_ctx) +gcc_pure +static int +audiofile_get_duration(InputStream &is) { - int total_time = audiofile_get_duration(file); + if (!is.IsSeekable()) + return -1; - if (total_time < 0) { - FormatWarning(audiofile_domain, - "Failed to get total song time from: %s", - file); + AudioFileInputStream afis{nullptr, is}; + AFvirtualfile *vf = setup_virtual_fops(afis); + AFfilehandle fh = afOpenVirtualFile(vf, "r", nullptr); + if (fh == AF_NULL_FILEHANDLE) + return -1; + + int duration = int(audiofile_get_duration(fh)); + afCloseFile(fh); + return duration; +} + +static bool +audiofile_scan_stream(InputStream &is, + const struct tag_handler *handler, void *handler_ctx) +{ + int total_time = audiofile_get_duration(is); + if (total_time < 0) return false; - } tag_handler_invoke_duration(handler, handler_ctx, total_time); return true; @@ -271,12 +288,12 @@ static const char *const audiofile_mime_types[] = { const struct DecoderPlugin audiofile_decoder_plugin = { "audiofile", - nullptr, + audiofile_init, nullptr, audiofile_stream_decode, nullptr, - audiofile_scan_file, nullptr, + audiofile_scan_stream, nullptr, audiofile_suffixes, audiofile_mime_types, 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 eafedda8f..0f10b20e9 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,16 +25,12 @@ #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" -#include <unistd.h> #include <string.h> -#include <stdio.h> /* for SEEK_SET, SEEK_CUR */ #ifdef HAVE_ID3TAG #include <id3tag.h> @@ -54,27 +50,15 @@ DsdId::Equals(const char *s) const */ bool dsdlib_skip_to(Decoder *decoder, InputStream &is, - int64_t offset) + uint64_t offset) { if (is.IsSeekable()) - return is.Seek(offset, SEEK_SET, IgnoreError()); + return is.Seek(offset, IgnoreError()); - if (is.GetOffset() > offset) + if (uint64_t(is.GetOffset()) > offset) return false; - char buffer[8192]; - while (is.GetOffset() < offset) { - size_t length = sizeof(buffer); - if (offset - is.GetOffset() < (int64_t)length) - length = offset - is.GetOffset(); - - size_t nbytes = decoder_read(decoder, is, buffer, length); - if (nbytes == 0) - return false; - } - - assert(is.GetOffset() == offset); - return true; + return dsdlib_skip(decoder, is, offset - is.GetOffset()); } /** @@ -82,30 +66,20 @@ dsdlib_skip_to(Decoder *decoder, InputStream &is, */ bool dsdlib_skip(Decoder *decoder, InputStream &is, - int64_t delta) + uint64_t delta) { - assert(delta >= 0); - if (delta == 0) return true; if (is.IsSeekable()) - return is.Seek(delta, SEEK_CUR, IgnoreError()); + return is.Seek(is.GetOffset() + delta, IgnoreError()); - char buffer[8192]; - while (delta > 0) { - size_t length = sizeof(buffer); - if ((int64_t)length > delta) - length = delta; - - size_t nbytes = decoder_read(decoder, is, buffer, length); - if (nbytes == 0) - return false; - - delta -= nbytes; - } + if (delta > 1024 * 1024) + /* don't skip more than one megabyte; it would be too + expensive */ + return false; - return true; + return decoder_skip(decoder, is, delta); } #ifdef HAVE_ID3TAG diff --git a/src/decoder/DsdLib.hxx b/src/decoder/plugins/DsdLib.hxx index 5c6127149..5250922ac 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]; @@ -60,11 +60,11 @@ public: bool dsdlib_skip_to(Decoder *decoder, InputStream &is, - int64_t offset); + uint64_t offset); bool dsdlib_skip(Decoder *decoder, InputStream &is, - int64_t delta); + uint64_t delta); /** * Add tags from ID3 tag. All tags commonly found in the ID3 tags of diff --git a/src/decoder/DsdiffDecoderPlugin.cxx b/src/decoder/plugins/DsdiffDecoderPlugin.cxx index 60b2e7624..c6880c4f9 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; @@ -70,14 +67,6 @@ struct DsdiffMetaData { unsigned sample_rate, channels; bool bitreverse; uint64_t chunk_size; -#ifdef HAVE_ID3TAG - InputStream::offset_type id3_offset; - uint64_t id3_size; -#endif - /** offset for artist tag */ - InputStream::offset_type diar_offset; - /** offset for title tag */ - InputStream::offset_type diti_offset; }; static bool lsbitfirst; @@ -250,11 +239,13 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, if (!dsdiff_read_chunk_header(decoder, is, chunk_header)) return false; - metadata->diar_offset = 0; - metadata->diti_offset = 0; + /** offset for artist tag */ + InputStream::offset_type artist_offset = 0; + /** offset for title tag */ + InputStream::offset_type title_offset = 0; #ifdef HAVE_ID3TAG - metadata->id3_offset = 0; + InputStream::offset_type id3_offset = 0; #endif /* Now process all the remaining chunk headers in the stream @@ -270,20 +261,19 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, /* DIAR chunk - DSDIFF native tag for Artist */ if (chunk_header->id.Equals("DIAR")) { chunk_size = chunk_header->GetSize(); - metadata->diar_offset = is.GetOffset(); + artist_offset = is.GetOffset(); } /* DITI chunk - DSDIFF native tag for Title */ if (chunk_header->id.Equals("DITI")) { chunk_size = chunk_header->GetSize(); - metadata->diti_offset = is.GetOffset(); + title_offset = is.GetOffset(); } #ifdef HAVE_ID3TAG /* 'ID3 ' chunk, offspec. Used by sacdextract */ if (chunk_header->id.Equals("ID3 ")) { chunk_size = chunk_header->GetSize(); - metadata->id3_offset = is.GetOffset(); - metadata->id3_size = chunk_size; + id3_offset = is.GetOffset(); } #endif @@ -294,22 +284,21 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, /* done processing chunk headers, process tags if any */ #ifdef HAVE_ID3TAG - if (metadata->id3_offset != 0) - { + if (id3_offset != 0) { /* a ID3 tag has preference over the other tags, do not process other tags if we have one */ - dsdlib_tag_id3(is, handler, handler_ctx, metadata->id3_offset); + dsdlib_tag_id3(is, handler, handler_ctx, id3_offset); return true; } #endif - if (metadata->diar_offset != 0) + if (artist_offset != 0) dsdiff_handle_native_tag(is, handler, handler_ctx, - metadata->diar_offset, TAG_ARTIST); + artist_offset, TAG_ARTIST); - if (metadata->diti_offset != 0) + if (title_offset != 0) dsdiff_handle_native_tag(is, handler, handler_ctx, - metadata->diti_offset, TAG_TITLE); + title_offset, TAG_TITLE); return true; } 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 ad5483c32..6a7267f61 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 b446ac5be..6a415bb53 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" @@ -65,34 +66,28 @@ static size_t adts_find_frame(DecoderBuffer *buffer) { while (true) { - size_t length; - const uint8_t *data = (const uint8_t *) - decoder_buffer_read(buffer, &length); - if (data == nullptr || length < 8) { - /* not enough data yet */ - if (!decoder_buffer_fill(buffer)) - /* failed */ - return 0; - - continue; - } + auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_need(buffer, 8)); + if (data.IsNull()) + /* failed */ + return 0; /* 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_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? */ - const size_t frame_length = adts_check_frame(data); + const 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 */ @@ -100,17 +95,10 @@ adts_find_frame(DecoderBuffer *buffer) continue; } - if (length < frame_length) { - /* available buffer size is smaller than the - frame will be - attempt to read more - data */ - if (!decoder_buffer_fill(buffer)) { - /* not enough data; discard this frame - to prevent a possible buffer - overflow */ - decoder_buffer_clear(buffer); - } - + if (decoder_buffer_need(buffer, frame_length).IsNull()) { + /* not enough data; discard this frame to + prevent a possible buffer overflow */ + decoder_buffer_clear(buffer); continue; } @@ -131,21 +119,18 @@ adts_song_duration(DecoderBuffer *buffer) unsigned sample_rate = 0; /* Read all frames to ensure correct time and bitrate */ - unsigned frames; - for (frames = 0;; frames++) { + unsigned frames = 0; + for (;; frames++) { const 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]; if (sample_rate == 0) break; } @@ -184,33 +169,28 @@ faad_song_duration(DecoderBuffer *buffer, InputStream &is) const auto size = is.GetSize(); const size_t fileread = size >= 0 ? size : 0; - decoder_buffer_fill(buffer); - size_t length; - const uint8_t *data = (const uint8_t *) - decoder_buffer_read(buffer, &length); - if (data == nullptr) + auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_need(buffer, 5)); + if (data.IsNull()) return -1; size_t tagsize = 0; - if (length >= 10 && !memcmp(data, "ID3", 3)) { + 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; - const bool success = decoder_buffer_skip(buffer, tagsize) && - decoder_buffer_fill(buffer); - if (!success) + if (!decoder_buffer_skip(buffer, tagsize)) return -1; - data = (const uint8_t *)decoder_buffer_read(buffer, &length); - if (data == nullptr) + data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_need(buffer, 5)); + if (data.IsNull()) return -1; } - if (length >= 8 && adts_check_frame(data) > 0) { + if (data.size >= 8 && adts_check_frame(data.data) > 0) { /* obtain the duration from the ADTS header */ if (!is.IsSeekable()) @@ -218,23 +198,24 @@ faad_song_duration(DecoderBuffer *buffer, InputStream &is) float song_length = adts_song_duration(buffer); - is.LockSeek(tagsize, SEEK_SET, IgnoreError()); + is.LockSeek(tagsize, IgnoreError()); + decoder_buffer_clear(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 */ - 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; - unsigned bit_rate = ((data[4 + skip_size] & 0x0F) << 19) | - (data[5 + skip_size] << 11) | - (data[6 + skip_size] << 3) | - (data[7 + skip_size] & 0xE0); + unsigned 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; @@ -267,11 +248,8 @@ static bool faad_decoder_init(NeAACDecHandle decoder, DecoderBuffer *buffer, AudioFormat &audio_format, Error &error) { - - 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; } @@ -288,8 +266,8 @@ faad_decoder_init(NeAACDecHandle decoder, DecoderBuffer *buffer, #endif long nbytes = NeAACDecInit(decoder, /* deconst hack, libfaad requires this */ - const_cast<unsigned char *>(data), - length, + const_cast<unsigned char *>(data.data), + data.size, sample_rate_p, &channels); if (nbytes < 0) { error.Set(faad_decoder_domain, "Not an AAC stream"); @@ -310,16 +288,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); } /** @@ -330,19 +306,17 @@ faad_decoder_decode(NeAACDecHandle decoder, DecoderBuffer *buffer, static float faad_get_file_time_float(InputStream &is) { - DecoderBuffer *const buffer = + DecoderBuffer *buffer = decoder_buffer_new(nullptr, is, FAAD_MIN_STREAMSIZE * MAX_CHANNELS); - float length = faad_song_duration(buffer, is); if (length < 0) { - AudioFormat audio_format; - NeAACDecHandle decoder = faad_decoder_new(); decoder_buffer_fill(buffer); + AudioFormat audio_format; if (faad_decoder_init(decoder, buffer, audio_format, IgnoreError())) length = 0; @@ -371,23 +345,13 @@ faad_get_file_time(InputStream &is) } static void -faad_stream_decode(Decoder &mpd_decoder, InputStream &is) +faad_stream_decode(Decoder &mpd_decoder, InputStream &is, + DecoderBuffer *buffer, const NeAACDecHandle decoder) { - DecoderBuffer *const buffer = - decoder_buffer_new(&mpd_decoder, is, - FAAD_MIN_STREAMSIZE * MAX_CHANNELS); - const float total_time = faad_song_duration(buffer, is); - /* create the libfaad decoder */ - - const NeAACDecHandle decoder = faad_decoder_new(); - - while (!decoder_buffer_is_full(buffer) && !is.LockIsEOF() && - decoder_get_command(mpd_decoder) == DecoderCommand::NONE) { - adts_find_frame(buffer); - decoder_buffer_fill(buffer); - } + if (adts_find_frame(buffer) == 0) + return; /* initialize it */ @@ -395,8 +359,6 @@ faad_stream_decode(Decoder &mpd_decoder, InputStream &is) AudioFormat audio_format; if (!faad_decoder_init(decoder, buffer, audio_format, error)) { LogError(error); - NeAACDecClose(decoder); - decoder_buffer_free(buffer); return; } @@ -407,7 +369,7 @@ faad_stream_decode(Decoder &mpd_decoder, InputStream &is) /* the decoder loop */ DecoderCommand cmd; - uint16_t bit_rate = 0; + unsigned bit_rate = 0; do { /* find the next frame */ @@ -460,6 +422,20 @@ faad_stream_decode(Decoder &mpd_decoder, InputStream &is) (size_t)frame_info.samples * 2, bit_rate); } while (cmd != DecoderCommand::STOP); +} + +static void +faad_stream_decode(Decoder &mpd_decoder, InputStream &is) +{ + DecoderBuffer *buffer = + decoder_buffer_new(&mpd_decoder, is, + FAAD_MIN_STREAMSIZE * MAX_CHANNELS); + + /* create the libfaad decoder */ + + const NeAACDecHandle decoder = faad_decoder_new(); + + faad_stream_decode(mpd_decoder, is, buffer, decoder); /* cleanup */ @@ -484,7 +460,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 5133f91ac..3c0747514 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" @@ -120,13 +120,32 @@ mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence) { AvioStream *stream = (AvioStream *)opaque; - if (whence == AVSEEK_SIZE) - return stream->input.size; + switch (whence) { + case SEEK_SET: + break; + + case SEEK_CUR: + pos += stream->input.GetOffset(); + break; + + case SEEK_END: + if (!stream->input.KnownSize()) + return -1; + + pos += stream->input.GetSize(); + break; + + case AVSEEK_SIZE: + return stream->input.GetSize(); - if (!stream->input.LockSeek(pos, whence, IgnoreError())) + default: + return -1; + } + + if (!stream->input.LockSeek(pos, IgnoreError())) return -1; - return stream->input.offset; + return stream->input.GetOffset(); } bool @@ -135,7 +154,7 @@ AvioStream::Open() io = avio_alloc_context(buffer, sizeof(buffer), false, this, mpd_ffmpeg_stream_read, nullptr, - input.seekable + input.IsSeekable() ? mpd_ffmpeg_stream_seek : nullptr); return io != nullptr; } @@ -385,7 +404,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 +428,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; @@ -456,8 +475,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; @@ -487,7 +509,7 @@ ffmpeg_decode(Decoder &decoder, InputStream &input) : 0; decoder_initialized(decoder, audio_format, - input.seekable, total_time); + input.IsSeekable(), total_time); #if LIBAVUTIL_VERSION_MAJOR >= 53 AVFrame *frame = av_frame_alloc(); @@ -564,7 +586,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..f06a52cf8 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; @@ -141,7 +137,7 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header) data->frame_size = data->audio_format.GetFrameSize(); decoder_initialized(data->decoder, data->audio_format, - data->input_stream.seekable, + data->input_stream.IsSeekable(), (float)data->total_frames / (float)data->audio_format.sample_rate); 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..39a0fce73 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()); @@ -148,13 +145,13 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd, if (data->initialized) { /* done */ decoder_initialized(data->decoder, data->audio_format, - data->input_stream.seekable, + data->input_stream.IsSeekable(), (float)data->total_frames / (float)data->audio_format.sample_rate); return true; } - if (data->input_stream.seekable) + if (data->input_stream.IsSeekable()) /* allow the workaround below only for nonseekable streams*/ return false; @@ -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..d37cea532 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 @@ -23,6 +23,7 @@ #include "Compiler.h" #include <errno.h> +#include <stdio.h> static size_t FlacIORead(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle) @@ -62,12 +63,31 @@ FlacIORead(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle) } static int -FlacIOSeek(FLAC__IOHandle handle, FLAC__int64 offset, int whence) +FlacIOSeek(FLAC__IOHandle handle, FLAC__int64 _offset, int whence) { InputStream *is = (InputStream *)handle; - Error error; - return is->LockSeek(offset, whence, error) ? 0 : -1; + InputStream::offset_type offset = _offset; + switch (whence) { + case SEEK_SET: + break; + + case SEEK_CUR: + offset += is->GetOffset(); + break; + + case SEEK_END: + if (!is->KnownSize()) + return -1; + + offset += is->GetSize(); + break; + + default: + return -1; + } + + return is->LockSeek(offset, IgnoreError()) ? 0 : -1; } static FLAC__int64 @@ -75,7 +95,7 @@ FlacIOTell(FLAC__IOHandle handle) { InputStream *is = (InputStream *)handle; - return is->offset; + return is->GetOffset(); } static int diff --git a/src/decoder/FlacIOHandle.hxx b/src/decoder/plugins/FlacIOHandle.hxx index b6e563fa3..90acc66af 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> @@ -37,7 +37,7 @@ ToFlacIOHandle(InputStream &is) static inline const FLAC__IOCallbacks & GetFlacIOCallbacks(const InputStream &is) { - return is.seekable + return is.IsSeekable() ? flac_io_callbacks_seekable : flac_io_callbacks; } diff --git a/src/decoder/FlacInput.cxx b/src/decoder/plugins/FlacInput.cxx index ce193101d..5b4c3104d 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" @@ -47,11 +47,11 @@ FlacInput::Read(FLAC__byte buffer[], size_t *bytes) FLAC__StreamDecoderSeekStatus FlacInput::Seek(FLAC__uint64 absolute_byte_offset) { - if (!input_stream.seekable) + if (!input_stream.IsSeekable()) return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; ::Error error; - if (!input_stream.LockSeek(absolute_byte_offset, SEEK_SET, error)) { + if (!input_stream.LockSeek(absolute_byte_offset, error)) { LogError(error); return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; } @@ -62,20 +62,20 @@ FlacInput::Seek(FLAC__uint64 absolute_byte_offset) FLAC__StreamDecoderTellStatus FlacInput::Tell(FLAC__uint64 *absolute_byte_offset) { - if (!input_stream.seekable) + if (!input_stream.IsSeekable()) return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; - *absolute_byte_offset = (FLAC__uint64)input_stream.offset; + *absolute_byte_offset = (FLAC__uint64)input_stream.GetOffset(); return FLAC__STREAM_DECODER_TELL_STATUS_OK; } FLAC__StreamDecoderLengthStatus FlacInput::Length(FLAC__uint64 *stream_length) { - if (input_stream.size < 0) + if (!input_stream.KnownSize()) return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; - *stream_length = (FLAC__uint64)input_stream.size; + *stream_length = (FLAC__uint64)input_stream.GetSize(); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } 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 6f619b34b..886aa1795 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 @@ -208,7 +208,7 @@ inline bool MadDecoder::Seek(long offset) { Error error; - if (!input_stream.LockSeek(offset, SEEK_SET, error)) + if (!input_stream.LockSeek(offset, error)) return false; mad_stream_buffer(&stream, input_buffer, 0); @@ -349,14 +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); if (!decoder_read_full(decoder, input_stream, allocated + count, tagsize - count)) { LogDebug(mad_domain, "error parsing ID3 tag"); - g_free(allocated); + delete[] allocated; return; } @@ -365,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; } @@ -390,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; @@ -667,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", @@ -1096,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/plugins/Mp4v2DecoderPlugin.cxx b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx new file mode 100644 index 000000000..36d02c81d --- /dev/null +++ b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx @@ -0,0 +1,324 @@ +/* + * 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" /* must be first for large file support */ +#include "Mp4v2DecoderPlugin.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 <mp4v2/mp4v2.h> +#include <neaacdec.h> + +#include <cstdio> +#include <cstdlib> + +static constexpr Domain mp4v2_decoder_domain("mp4v2"); + +static MP4TrackId +mp4_get_aac_track(MP4FileHandle handle, NeAACDecHandle decoder, + AudioFormat &audio_format, Error &error) +{ + uint32_t sample_rate; +#ifdef HAVE_FAAD_LONG + /* neaacdec.h declares all arguments as "unsigned long", but + internally expects uint32_t pointers. To avoid gcc + warnings, use this workaround. */ + unsigned long *sample_rate_r = (unsigned long*)&sample_rate; +#else + uint32_t *sample_rate_r = sample_rate; +#endif + + const MP4TrackId tracks = MP4GetNumberOfTracks(handle); + + for (MP4TrackId id = 1; id <= tracks; id++) { + const char* track_type = MP4GetTrackType(handle, id); + + if (track_type == 0) + continue; + + const auto obj_type = MP4GetTrackEsdsObjectTypeId(handle, id); + + if (obj_type == MP4_INVALID_AUDIO_TYPE) + continue; + if (obj_type == MP4_MPEG4_AUDIO_TYPE) { + const auto mpeg_type = MP4GetTrackAudioMpeg4Type(handle, id); + if (!MP4_IS_MPEG4_AAC_AUDIO_TYPE(mpeg_type)) + continue; + } else if (!MP4_IS_AAC_AUDIO_TYPE(obj_type)) + continue; + + if (decoder == nullptr) + /* found audio track, no decoder */ + return id; + + unsigned char *buff = nullptr; + unsigned buff_size = 0; + + if (!MP4GetTrackESConfiguration(handle, id, &buff, &buff_size)) + continue; + + uint8_t channels; + int32_t nbytes = NeAACDecInit(decoder, buff, buff_size, + sample_rate_r, &channels); + + free(buff); + + if (nbytes < 0) + /* invalid stream */ + continue; + + if (!audio_format_init_checked(audio_format, sample_rate, + SampleFormat::S16, + channels, + error)) + continue; + + return id; + } + + error.Set(mp4v2_decoder_domain, "no valid aac track found"); + + return MP4_INVALID_TRACK_ID; +} + +static NeAACDecHandle +mp4_faad_new(MP4FileHandle handle, AudioFormat &audio_format, Error &error) +{ + const NeAACDecHandle decoder = NeAACDecOpen(); + const NeAACDecConfigurationPtr config = + NeAACDecGetCurrentConfiguration(decoder); + config->outputFormat = FAAD_FMT_16BIT; + config->downMatrix = 1; + config->dontUpSampleImplicitSBR = 0; + NeAACDecSetConfiguration(decoder, config); + + const auto track = mp4_get_aac_track(handle, decoder, audio_format, error); + + if (track == MP4_INVALID_TRACK_ID) { + NeAACDecClose(decoder); + return nullptr; + } + + return decoder; +} + +static void +mp4_file_decode(Decoder &mpd_decoder, Path path_fs) +{ + const MP4FileHandle handle = MP4Read(path_fs.c_str()); + + if (handle == MP4_INVALID_FILE_HANDLE) { + FormatError(mp4v2_decoder_domain, + "unable to open file"); + return; + } + + AudioFormat audio_format; + Error error; + const NeAACDecHandle decoder = mp4_faad_new(handle, audio_format, error); + + if (decoder == nullptr) { + LogError(error); + MP4Close(handle); + return; + } + + const MP4TrackId track = mp4_get_aac_track(handle, nullptr, audio_format, error); + + /* initialize the MPD core */ + + const uint32_t scale = MP4GetTrackTimeScale(handle, track); + const float duration = ((float)MP4GetTrackDuration(handle, track)) / scale + 0.5f; + const MP4SampleId num_samples = MP4GetTrackNumberOfSamples(handle, track); + + decoder_initialized(mpd_decoder, audio_format, true, duration); + + /* the decoder loop */ + + DecoderCommand cmd = DecoderCommand::NONE; + + for (MP4SampleId sample = 1; + sample < num_samples && cmd != DecoderCommand::STOP; + sample++) { + unsigned char *data = nullptr; + unsigned int data_length = 0; + + if (cmd == DecoderCommand::SEEK) { + const double offset = decoder_seek_where(mpd_decoder); + sample = MP4GetSampleIdFromTime(handle, track, + (MP4Timestamp)(offset * (double)scale), 0); + decoder_command_finished(mpd_decoder); + } + + /* read */ + if (MP4ReadSample(handle, track, sample, &data, &data_length) == 0) { + FormatError(mp4v2_decoder_domain, "unable to read sample"); + break; + } + + /* decode it */ + NeAACDecFrameInfo frame_info; + const void *const decoded = NeAACDecDecode(decoder, &frame_info, data, data_length); + + if (frame_info.error > 0) { + FormatWarning(mp4v2_decoder_domain, + "error decoding AAC stream: %s", + NeAACDecGetErrorMessage(frame_info.error)); + break; + } + + if (frame_info.channels != audio_format.channels) { + FormatDefault(mp4v2_decoder_domain, + "channel count changed from %u to %u", + audio_format.channels, frame_info.channels); + break; + } + + if (frame_info.samplerate != audio_format.sample_rate) { + FormatDefault(mp4v2_decoder_domain, + "sample rate changed from %u to %lu", + audio_format.sample_rate, + (unsigned long)frame_info.samplerate); + break; + } + + /* update bit rate and position */ + unsigned bit_rate = 0; + + if (frame_info.samples > 0) { + bit_rate = frame_info.bytesconsumed * 8.0 * + frame_info.channels * audio_format.sample_rate / + frame_info.samples / 1000 + 0.5; + } + + /* send PCM samples to MPD */ + + cmd = decoder_data(mpd_decoder, nullptr, decoded, + (size_t)frame_info.samples * 2, + bit_rate); + + free(data); + } + + /* cleanup */ + NeAACDecClose(decoder); + MP4Close(handle); +} + +static inline void +mp4_safe_invoke_tag(const struct tag_handler *handler, void *handler_ctx, + TagType tag, const char *value) +{ + if (value != nullptr) + tag_handler_invoke_tag(handler, handler_ctx, tag, value); +} + +static bool +mp4_scan_file(Path path_fs, + const struct tag_handler *handler, void *handler_ctx) +{ + const MP4FileHandle handle = MP4Read(path_fs.c_str()); + + if (handle == MP4_INVALID_FILE_HANDLE) + return false; + + AudioFormat tmp_audio_format; + Error error; + const MP4TrackId id = mp4_get_aac_track(handle, nullptr, tmp_audio_format, error); + + if (id == MP4_INVALID_TRACK_ID) { + LogError(error); + MP4Close(handle); + return false; + } + + const MP4Duration dur = MP4GetTrackDuration(handle, id) / + MP4GetTrackTimeScale(handle, id); + tag_handler_invoke_duration(handler, handler_ctx, dur); + + const MP4Tags* tags = MP4TagsAlloc(); + MP4TagsFetch(tags, handle); + + mp4_safe_invoke_tag(handler, handler_ctx, TAG_NAME, tags->name); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_ARTIST, tags->artist); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_ALBUM_ARTIST, tags->albumArtist); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_ALBUM, tags->album); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_COMPOSER, tags->composer); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_COMMENT, tags->comments); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_GENRE, tags->genre); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_DATE, tags->releaseDate); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_ARTIST_SORT, tags->sortArtist); + mp4_safe_invoke_tag(handler, handler_ctx, TAG_ALBUM_ARTIST_SORT, tags->sortAlbumArtist); + + char buff[8]; /* tmp buffer for index to string. */ + if (tags->track != nullptr) { + sprintf(buff, "%d", tags->track->index); + tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, buff); + } + + if (tags->disk != nullptr) { + sprintf(buff, "%d", tags->disk->index); + tag_handler_invoke_tag(handler, handler_ctx, TAG_DISC, buff); + } + + MP4TagsFree(tags); + MP4Close(handle); + + return true; +} + +static const char *const mp4_suffixes[] = { + "mp4", + "m4a", + /* "m4p", encrypted */ + /* "m4b", audio book */ + /* "m4r", ring tones */ + /* "m4v", video */ + nullptr +}; + +static const char *const mp4_mime_types[] = { + "application/mp4", + "application/m4a", + "audio/mp4", + "audio/m4a", + /* "audio/m4p", */ + /* "audio/m4b", */ + /* "audio/m4r", */ + /* "audio/m4v", */ + nullptr +}; + +const struct DecoderPlugin mp4v2_decoder_plugin = { + "mp4v2", + nullptr, + nullptr, + nullptr, + mp4_file_decode, + mp4_scan_file, + nullptr, + nullptr, + mp4_suffixes, + mp4_mime_types +}; diff --git a/src/decoder/plugins/Mp4v2DecoderPlugin.hxx b/src/decoder/plugins/Mp4v2DecoderPlugin.hxx new file mode 100644 index 000000000..57585dec4 --- /dev/null +++ b/src/decoder/plugins/Mp4v2DecoderPlugin.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_MP4V2_HXX +#define MPD_DECODER_MP4V2_HXX + +extern const struct DecoderPlugin mp4v2_decoder_plugin; + +#endif diff --git a/src/decoder/MpcdecDecoderPlugin.cxx b/src/decoder/plugins/MpcdecDecoderPlugin.cxx index dc258623c..f86cb3c81 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 { @@ -59,7 +57,7 @@ mpc_seek_cb(mpc_reader *reader, mpc_int32_t offset) struct mpc_decoder_data *data = (struct mpc_decoder_data *)reader->data; - return data->is.LockSeek(offset, SEEK_SET, IgnoreError()); + return data->is.LockSeek(offset, IgnoreError()); } static mpc_int32_t 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..15e9c5c92 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 @@ -22,8 +22,6 @@ #include "OggSyncState.hxx" #include "util/Error.hxx" -#include <stdio.h> - bool OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet) { @@ -41,7 +39,7 @@ OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet) bool OggSeekPageAtOffset(OggSyncState &oy, ogg_stream_state &os, InputStream &is, - InputStream::offset_type offset, int whence) + InputStream::offset_type offset) { oy.Reset(); @@ -49,7 +47,7 @@ OggSeekPageAtOffset(OggSyncState &oy, ogg_stream_state &os, InputStream &is, data */ ogg_stream_reset(&os); - return is.LockSeek(offset, whence, IgnoreError()) && + return is.LockSeek(offset, IgnoreError()) && oy.ExpectPageSeekIn(os); } @@ -57,12 +55,15 @@ bool OggSeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet, InputStream &is) { - if (is.size > 0 && is.size - is.offset < 65536) + if (!is.KnownSize()) + return false; + + if (is.GetRest() < 65536) return OggFindEOS(oy, os, packet); if (!is.CheapSeeking()) return false; - return OggSeekPageAtOffset(oy, os, is, -65536, SEEK_END) && + return OggSeekPageAtOffset(oy, os, is, is.GetSize() - 65536) && OggFindEOS(oy, os, packet); } diff --git a/src/decoder/OggFind.hxx b/src/decoder/plugins/OggFind.hxx index ad51ccdf3..8ced7fd86 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; /** @@ -42,7 +41,7 @@ OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet); */ bool OggSeekPageAtOffset(OggSyncState &oy, ogg_stream_state &os, InputStream &is, - InputStream::offset_type offset, int whence); + InputStream::offset_type offset); /** * Try to find the end-of-stream (EOS) packet. Seek to the end of the 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 01ea3687a..8d1f75e72 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> #include <stdio.h> @@ -108,7 +104,7 @@ public: MPDOpusDecoder::~MPDOpusDecoder() { - g_free(output_buffer); + delete[] output_buffer; if (opus_decoder != nullptr) opus_decoder_destroy(opus_decoder); @@ -190,7 +186,7 @@ LoadEOSPacket(InputStream &is, Decoder *decoder, int serialno, troubl */ return -1; - const auto old_offset = is.offset; + const auto old_offset = is.GetOffset(); if (old_offset < 0) return -1; @@ -204,7 +200,7 @@ LoadEOSPacket(InputStream &is, Decoder *decoder, int serialno, ogg_stream_clear(&os); /* restore the previous file position */ - is.Seek(old_offset, SEEK_SET, IgnoreError()); + is.Seek(old_offset, IgnoreError()); return result; } @@ -273,9 +269,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); } @@ -295,8 +289,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); @@ -340,19 +333,19 @@ bool MPDOpusDecoder::Seek(OggSyncState &oy, double where_s) { assert(eos_granulepos > 0); - assert(input_stream.seekable); - assert(input_stream.size > 0); - assert(input_stream.offset >= 0); + assert(input_stream.IsSeekable()); + assert(input_stream.KnownSize()); + assert(input_stream.GetOffset() >= 0); const ogg_int64_t where_granulepos(where_s * opus_sample_rate); /* interpolate the file offset where we expect to find the given granule position */ /* TODO: implement binary search */ - InputStream::offset_type offset(where_granulepos * input_stream.size + InputStream::offset_type offset(where_granulepos * input_stream.GetSize() / eos_granulepos); - if (!OggSeekPageAtOffset(oy, os, input_stream, offset, SEEK_SET)) + if (!OggSeekPageAtOffset(oy, os, input_stream, offset)) return false; decoder_timestamp(decoder, where_s); 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..3b9c60691 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,17 +18,14 @@ */ #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 */ static void pcm_stream_decode(Decoder &decoder, InputStream &is) @@ -78,7 +75,7 @@ pcm_stream_decode(Decoder &decoder, InputStream &is) decoder_seek_where(decoder)); Error error; - if (is.LockSeek(offset, SEEK_SET, error)) { + if (is.LockSeek(offset, error)) { decoder_command_finished(decoder); } else { LogError(error); 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 bcdf6d7ca..c656302f9 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" @@ -31,6 +32,13 @@ static constexpr Domain sndfile_domain("sndfile"); +static bool +sndfile_init(gcc_unused const config_param ¶m) +{ + LogDebug(sndfile_domain, sf_version_string()); + return true; +} + struct SndfileInputStream { Decoder *const decoder; InputStream &is; @@ -54,13 +62,33 @@ sndfile_vio_get_filelen(void *user_data) } static sf_count_t -sndfile_vio_seek(sf_count_t offset, int whence, void *user_data) +sndfile_vio_seek(sf_count_t _offset, int whence, void *user_data) { SndfileInputStream &sis = *(SndfileInputStream *)user_data; InputStream &is = sis.is; + InputStream::offset_type offset = _offset; + switch (whence) { + case SEEK_SET: + break; + + case SEEK_CUR: + offset += is.GetOffset(); + break; + + case SEEK_END: + if (!is.KnownSize()) + return -1; + + offset += is.GetSize(); + break; + + default: + return -1; + } + Error error; - if (!is.LockSeek(offset, whence, error)) { + if (!is.LockSeek(offset, error)) { LogError(error, "Seek failed"); return -1; } @@ -127,16 +155,12 @@ time_to_frame(float t, const AudioFormat *audio_format) static void sndfile_stream_decode(Decoder &decoder, InputStream &is) { - SNDFILE *sf; SF_INFO info; - size_t frame_size; - sf_count_t read_frames, num_frames; - int buffer[4096]; info.format = 0; SndfileInputStream sis{&decoder, is}; - sf = sf_open_virtual(&vio, SFM_READ, &info, &sis); + SNDFILE *const sf = sf_open_virtual(&vio, SFM_READ, &info, &sis); if (sf == nullptr) { LogWarning(sndfile_domain, "sf_open_virtual() failed"); return; @@ -157,12 +181,14 @@ sndfile_stream_decode(Decoder &decoder, InputStream &is) decoder_initialized(decoder, audio_format, info.seekable, frame_to_time(info.frames, &audio_format)); - frame_size = audio_format.GetFrameSize(); - read_frames = sizeof(buffer) / frame_size; + int buffer[4096]; + + const size_t frame_size = audio_format.GetFrameSize(); + const sf_count_t read_frames = sizeof(buffer) / frame_size; DecoderCommand cmd; do { - num_frames = sf_readf_int(sf, buffer, read_frames); + sf_count_t num_frames = sf_readf_int(sf, buffer, read_frames); if (num_frames <= 0) break; @@ -185,44 +211,53 @@ sndfile_stream_decode(Decoder &decoder, InputStream &is) sf_close(sf); } +static void +sndfile_handle_tag(SNDFILE *sf, int str, TagType tag, + const struct tag_handler *handler, void *handler_ctx) +{ + const char *value = sf_get_string(sf, str); + if (value != nullptr) + tag_handler_invoke_tag(handler, handler_ctx, tag, value); +} + +static constexpr struct { + int8_t str; + TagType tag; +} sndfile_tags[] = { + { SF_STR_TITLE, TAG_TITLE }, + { SF_STR_ARTIST, TAG_ARTIST }, + { SF_STR_COMMENT, TAG_COMMENT }, + { SF_STR_DATE, TAG_DATE }, + { SF_STR_ALBUM, TAG_ALBUM }, + { SF_STR_TRACKNUMBER, TAG_TRACK }, + { SF_STR_GENRE, TAG_GENRE }, +}; + static bool -sndfile_scan_file(const char *path_fs, - const struct tag_handler *handler, void *handler_ctx) +sndfile_scan_stream(InputStream &is, + const struct tag_handler *handler, void *handler_ctx) { - SNDFILE *sf; SF_INFO info; - const char *p; info.format = 0; - sf = sf_open(path_fs, SFM_READ, &info); + SndfileInputStream sis{nullptr, is}; + SNDFILE *const sf = sf_open_virtual(&vio, SFM_READ, &info, &sis); 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", is.GetURI()); return false; } tag_handler_invoke_duration(handler, handler_ctx, info.frames / info.samplerate); - p = sf_get_string(sf, SF_STR_TITLE); - if (p != nullptr) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_TITLE, p); - - p = sf_get_string(sf, SF_STR_ARTIST); - if (p != nullptr) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_ARTIST, p); - - p = sf_get_string(sf, SF_STR_DATE); - if (p != nullptr) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_DATE, p); + for (auto i : sndfile_tags) + sndfile_handle_tag(sf, i.str, i.tag, handler, handler_ctx); sf_close(sf); @@ -261,12 +296,12 @@ static const char *const sndfile_mime_types[] = { const struct DecoderPlugin sndfile_decoder_plugin = { "sndfile", - nullptr, + sndfile_init, nullptr, sndfile_stream_decode, nullptr, - sndfile_scan_file, nullptr, + sndfile_scan_stream, nullptr, sndfile_suffixes, sndfile_mime_types, 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..72542e3a2 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,20 +46,23 @@ #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 { - Decoder *decoder; +struct VorbisInputStream { + Decoder *const decoder; - InputStream *input_stream; + InputStream &input_stream; bool seekable; + + VorbisInputStream(Decoder *_decoder, InputStream &_is) + :decoder(_decoder), input_stream(_is), + seekable(input_stream.CheapSeeking()) {} }; static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data) { - struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data; - size_t ret = decoder_read(vis->decoder, *vis->input_stream, + VorbisInputStream *vis = (VorbisInputStream *)data; + size_t ret = decoder_read(vis->decoder, vis->input_stream, ptr, size * nmemb); errno = 0; @@ -69,15 +70,37 @@ static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data) return ret / size; } -static int ogg_seek_cb(void *data, ogg_int64_t offset, int whence) +static int ogg_seek_cb(void *data, ogg_int64_t _offset, int whence) { - struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data; + VorbisInputStream *vis = (VorbisInputStream *)data; + InputStream &is = vis->input_stream; - Error error; - return vis->seekable && - (vis->decoder == nullptr || - decoder_get_command(*vis->decoder) != DecoderCommand::STOP) && - vis->input_stream->LockSeek(offset, whence, error) + if (!vis->seekable || + (vis->decoder != nullptr && + decoder_get_command(*vis->decoder) == DecoderCommand::STOP)) + return -1; + + InputStream::offset_type offset = _offset; + switch (whence) { + case SEEK_SET: + break; + + case SEEK_CUR: + offset += is.GetOffset(); + break; + + case SEEK_END: + if (!is.KnownSize()) + return -1; + + offset += is.GetSize(); + break; + + default: + return -1; + } + + return is.LockSeek(offset, IgnoreError()) ? 0 : -1; } @@ -89,9 +112,9 @@ static int ogg_close_cb(gcc_unused void *data) static long ogg_tell_cb(void *data) { - struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data; + VorbisInputStream *vis = (VorbisInputStream *)data; - return (long)vis->input_stream->offset; + return (long)vis->input_stream.GetOffset(); } static const ov_callbacks vorbis_is_callbacks = { @@ -126,17 +149,12 @@ vorbis_strerror(int code) } static bool -vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf, - Decoder *decoder, InputStream &input_stream) +vorbis_is_open(VorbisInputStream *vis, OggVorbis_File *vf) { - vis->decoder = decoder; - vis->input_stream = &input_stream; - vis->seekable = input_stream.CheapSeeking(); - int ret = ov_open_callbacks(vis, vf, nullptr, 0, vorbis_is_callbacks); if (ret < 0) { - if (decoder == nullptr || - decoder_get_command(*decoder) == DecoderCommand::NONE) + if (vis->decoder == nullptr || + decoder_get_command(*vis->decoder) == DecoderCommand::NONE) FormatWarning(vorbis_domain, "Failed to open Ogg Vorbis stream: %s", vorbis_strerror(ret)); @@ -165,8 +183,8 @@ vorbis_interleave(float *dest, const float *const*src, { for (const float *const*src_end = src + channels; src != src_end; ++src, ++dest) { - float *d = dest; - for (const float *s = *src, *s_end = s + nframes; + float *gcc_restrict d = dest; + for (const float *gcc_restrict s = *src, *s_end = s + nframes; s != s_end; ++s, d += channels) *d = *s; } @@ -174,6 +192,16 @@ vorbis_interleave(float *dest, const float *const*src, #endif /* public */ + +static bool +vorbis_init(gcc_unused const config_param ¶m) +{ +#ifndef HAVE_TREMOR + LogDebug(vorbis_domain, vorbis_version_string()); +#endif + return true; +} + static void vorbis_stream_decode(Decoder &decoder, InputStream &input_stream) @@ -185,9 +213,9 @@ vorbis_stream_decode(Decoder &decoder, moved it */ input_stream.LockRewind(IgnoreError()); - struct vorbis_input_stream vis; + VorbisInputStream vis(&decoder, input_stream); OggVorbis_File vf; - if (!vorbis_is_open(&vis, &vf, &decoder, input_stream)) + if (!vorbis_is_open(&vis, &vf)) return; const vorbis_info *vi = ov_info(&vf, -1); @@ -306,10 +334,10 @@ static bool vorbis_scan_stream(InputStream &is, const struct tag_handler *handler, void *handler_ctx) { - struct vorbis_input_stream vis; + VorbisInputStream vis(nullptr, is); OggVorbis_File vf; - if (!vorbis_is_open(&vis, &vf, nullptr, is)) + if (!vorbis_is_open(&vis, &vf)) return false; tag_handler_invoke_duration(handler, handler_ctx, @@ -340,7 +368,7 @@ static const char *const vorbis_mime_types[] = { const struct DecoderPlugin vorbis_decoder_plugin = { "vorbis", - nullptr, + vorbis_init, nullptr, vorbis_stream_decode, nullptr, 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..2f60090c1 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; } @@ -323,32 +325,43 @@ wavpack_scan_file(const char *fname, */ /* This struct is needed for per-stream last_byte storage. */ -struct wavpack_input { - Decoder *decoder; - InputStream *is; +struct WavpackInput { + Decoder &decoder; + InputStream &is; /* Needed for push_back_byte() */ int last_byte; + + constexpr WavpackInput(Decoder &_decoder, InputStream &_is) + :decoder(_decoder), is(_is), last_byte(EOF) {} + + int32_t ReadBytes(void *data, size_t bcount); }; /** - * Little wrapper for struct wavpack_input to cast from void *. + * Little wrapper for struct WavpackInput to cast from void *. */ -static struct wavpack_input * +static WavpackInput * wpin(void *id) { assert(id); - return (struct wavpack_input *)id; + return (WavpackInput *)id; } static int32_t wavpack_input_read_bytes(void *id, void *data, int32_t bcount) { + return wpin(id)->ReadBytes(data, bcount); +} + +int32_t +WavpackInput::ReadBytes(void *data, size_t bcount) +{ uint8_t *buf = (uint8_t *)data; int32_t i = 0; - if (wpin(id)->last_byte != EOF) { - *buf++ = wpin(id)->last_byte; - wpin(id)->last_byte = EOF; + if (last_byte != EOF) { + *buf++ = last_byte; + last_byte = EOF; --bcount; ++i; } @@ -356,9 +369,7 @@ wavpack_input_read_bytes(void *id, void *data, int32_t bcount) /* wavpack fails if we return a partial read, so we just wait until the buffer is full */ while (bcount > 0) { - size_t nbytes = decoder_read( - wpin(id)->decoder, *wpin(id)->is, buf, bcount - ); + size_t nbytes = decoder_read(&decoder, is, buf, bcount); if (nbytes == 0) { /* EOF, error or a decoder command */ break; @@ -375,19 +386,41 @@ wavpack_input_read_bytes(void *id, void *data, int32_t bcount) static uint32_t wavpack_input_get_pos(void *id) { - return wpin(id)->is->offset; + return wpin(id)->is.GetOffset(); } static int wavpack_input_set_pos_abs(void *id, uint32_t pos) { - return wpin(id)->is->LockSeek(pos, SEEK_SET, IgnoreError()) ? 0 : -1; + return wpin(id)->is.LockSeek(pos, IgnoreError()) ? 0 : -1; } static int wavpack_input_set_pos_rel(void *id, int32_t delta, int mode) { - return wpin(id)->is->LockSeek(delta, mode, IgnoreError()) ? 0 : -1; + InputStream &is = wpin(id)->is; + + InputStream::offset_type offset = delta; + switch (mode) { + case SEEK_SET: + break; + + case SEEK_CUR: + offset += is.GetOffset(); + break; + + case SEEK_END: + if (!is.KnownSize()) + return -1; + + offset += is.GetSize(); + break; + + default: + return -1; + } + + return is.LockSeek(offset, IgnoreError()) ? 0 : -1; } static int @@ -404,16 +437,16 @@ wavpack_input_push_back_byte(void *id, int c) static uint32_t wavpack_input_get_length(void *id) { - if (wpin(id)->is->size < 0) + if (!wpin(id)->is.KnownSize()) return 0; - return wpin(id)->is->size; + return wpin(id)->is.GetSize(); } static int wavpack_input_can_seek(void *id) { - return wpin(id)->is->seekable; + return wpin(id)->is.IsSeekable(); } static WavpackStreamReader mpd_is_reader = { @@ -427,19 +460,8 @@ static WavpackStreamReader mpd_is_reader = { nullptr /* no need to write edited tags */ }; -static void -wavpack_input_init(struct wavpack_input *isp, Decoder &decoder, - InputStream &is) -{ - isp->decoder = &decoder; - isp->is = &is; - isp->last_byte = EOF; -} - -static InputStream * -wavpack_open_wvc(Decoder &decoder, const char *uri, - Mutex &mutex, Cond &cond, - struct wavpack_input *wpi) +static WavpackInput * +wavpack_open_wvc(Decoder &decoder, const char *uri) { /* * As we use dc->utf8url, this function will be bad for @@ -450,29 +472,13 @@ wavpack_open_wvc(Decoder &decoder, const char *uri, char *wvc_url = g_strconcat(uri, "c", nullptr); - InputStream *is_wvc = InputStream::Open(wvc_url, mutex, cond, - IgnoreError()); + InputStream *is_wvc = decoder_open_uri(decoder, uri, IgnoreError()); g_free(wvc_url); if (is_wvc == nullptr) return nullptr; - /* - * And we try to buffer in order to get know - * about a possible 404 error. - */ - char first_byte; - size_t nbytes = decoder_read(decoder, *is_wvc, - &first_byte, sizeof(first_byte)); - if (nbytes == 0) { - is_wvc->Close(); - return nullptr; - } - - /* push it back */ - wavpack_input_init(wpi, decoder, *is_wvc); - wpi->last_byte = first_byte; - return is_wvc; + return new WavpackInput(decoder, *is_wvc); } /* @@ -482,29 +488,23 @@ static void wavpack_streamdecode(Decoder &decoder, InputStream &is) { int open_flags = OPEN_NORMALIZE; - bool can_seek = is.seekable; + bool can_seek = is.IsSeekable(); - wavpack_input isp_wvc; - InputStream *is_wvc = wavpack_open_wvc(decoder, is.uri.c_str(), - is.mutex, is.cond, - &isp_wvc); - if (is_wvc != nullptr) { + WavpackInput *wvc = wavpack_open_wvc(decoder, is.GetURI()); + if (wvc != nullptr) { open_flags |= OPEN_WVC; - can_seek &= is_wvc->seekable; + can_seek &= wvc->is.IsSeekable(); } if (!can_seek) { open_flags |= OPEN_STREAMING; } - wavpack_input isp; - wavpack_input_init(&isp, decoder, is); + WavpackInput isp(decoder, is); char error[ERRORLEN]; WavpackContext *wpc = - WavpackOpenFileInputEx(&mpd_is_reader, &isp, - open_flags & OPEN_WVC - ? &isp_wvc : nullptr, + WavpackOpenFileInputEx(&mpd_is_reader, &isp, wvc, error, open_flags, 23); if (wpc == nullptr) { @@ -516,8 +516,10 @@ wavpack_streamdecode(Decoder &decoder, InputStream &is) wavpack_decode(decoder, wpc, can_seek); WavpackCloseFile(wpc); - if (open_flags & OPEN_WVC) { - is_wvc->Close(); + + if (wvc != nullptr) { + delete &wvc->is; + delete 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..11a0bcd42 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 @@ -17,6 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +/* This File contains additional Tags for Xiph-Based Formats like Ogg-Vorbis, + * Flac and Opus which will be used in addition to the Tags in tag/TagNames.c + * see https://www.xiph.org/vorbis/doc/v-comment.html for further Info + */ #include "config.h" #include "XiphTags.hxx" @@ -24,5 +28,6 @@ const struct tag_table xiph_tags[] = { { "tracknumber", TAG_TRACK }, { "discnumber", TAG_DISC }, { "album artist", TAG_ALBUM_ARTIST }, + { "description", TAG_COMMENT }, { nullptr, TAG_NUM_OF_ITEM_TYPES } }; 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 |