Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7b94f0e36b | ||
![]() |
504e8d564a | ||
![]() |
ac395429c3 | ||
![]() |
388768b3a6 | ||
![]() |
5c4169e64e | ||
![]() |
d40e9de2d2 | ||
![]() |
1e54297be8 | ||
![]() |
44b200240f | ||
![]() |
a2340c313f | ||
![]() |
37b07a5e7c | ||
![]() |
73013a3c04 | ||
![]() |
e8099f01b5 | ||
![]() |
672bdd3a56 | ||
![]() |
c2c2c29658 | ||
![]() |
c745e14f47 | ||
![]() |
e8f08cda53 | ||
![]() |
8266ab5588 | ||
![]() |
ea552208fc | ||
![]() |
e86015a72a | ||
![]() |
cf7ec2c9d3 | ||
![]() |
dadd3ca671 | ||
![]() |
79535212c8 | ||
![]() |
ef5f96a193 | ||
![]() |
418f71ec0f |
AUTHORSNEWS
android
configure.acdoc
python/build
src
5
AUTHORS
5
AUTHORS
@@ -31,3 +31,8 @@ The following people have contributed code to MPD:
|
||||
Jean-Francois Dockes <jf@dockes.org>
|
||||
Yue Wang <yuleopen@gmail.com>
|
||||
Matthew Leon Grinshpun <ml@matthewleon.com>
|
||||
Dimitris Papastamos <sin@2f30.org>
|
||||
Florian Schlichting <fsfs@debian.org>
|
||||
François Revol <revol@free.fr>
|
||||
Jacob Vosmaer <contact@jacobvosmaer.nl>
|
||||
Thomas Guillem <thomas@gllm.fr>
|
||||
|
14
NEWS
14
NEWS
@@ -1,3 +1,17 @@
|
||||
ver 0.20.19 (2018/04/26)
|
||||
* protocol
|
||||
- validate absolute seek time, reject negative values
|
||||
* database
|
||||
- proxy: fix "search already in progress" errors
|
||||
- proxy: implement "list ... group"
|
||||
* input
|
||||
- mms: fix lockup bug and a crash bug
|
||||
* decoder
|
||||
- ffmpeg: fix av_register_all() deprecation warning (FFmpeg 4.0)
|
||||
* player
|
||||
- fix spurious "Not seekable" error when switching radio streams
|
||||
* macOS: fix crash bug
|
||||
|
||||
ver 0.20.18 (2018/02/24)
|
||||
* input
|
||||
- curl: allow authentication methods other than "Basic"
|
||||
|
@@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="17"
|
||||
android:versionName="0.20.18">
|
||||
android:versionCode="18"
|
||||
android:versionName="0.20.19">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"/>
|
||||
|
||||
|
@@ -3,13 +3,14 @@
|
||||
import os, os.path
|
||||
import sys, subprocess
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: build.py SDK_PATH NDK_PATH [configure_args...]", file=sys.stderr)
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: build.py SDK_PATH NDK_PATH ABI [configure_args...]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
sdk_path = sys.argv[1]
|
||||
ndk_path = sys.argv[2]
|
||||
configure_args = sys.argv[3:]
|
||||
android_abi = sys.argv[3]
|
||||
configure_args = sys.argv[4:]
|
||||
|
||||
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
|
||||
print("SDK not found in", ndk_path, file=sys.stderr)
|
||||
@@ -19,8 +20,27 @@ if not os.path.isdir(ndk_path):
|
||||
print("NDK not found in", ndk_path, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
android_abis = {
|
||||
'armeabi-v7a': {
|
||||
'arch': 'arm-linux-androideabi',
|
||||
'ndk_arch': 'arm',
|
||||
'toolchain_arch': 'arm-linux-androideabi',
|
||||
'llvm_triple': 'armv7-none-linux-androideabi',
|
||||
'cflags': '-march=armv7-a -mfpu=vfp -mfloat-abi=softfp',
|
||||
},
|
||||
|
||||
'x86': {
|
||||
'arch': 'i686-linux-android',
|
||||
'ndk_arch': 'x86',
|
||||
'toolchain_arch': 'x86',
|
||||
'llvm_triple': 'i686-none-linux-android',
|
||||
'cflags': '-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
|
||||
},
|
||||
}
|
||||
|
||||
# select the NDK target
|
||||
arch = 'arm-linux-androideabi'
|
||||
abi_info = android_abis[android_abi]
|
||||
arch = abi_info['arch']
|
||||
|
||||
# the path to the MPD sources
|
||||
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
|
||||
@@ -44,8 +64,7 @@ class AndroidNdkToolchain:
|
||||
self.src_path = src_path
|
||||
self.build_path = build_path
|
||||
|
||||
self.ndk_arch = 'arm'
|
||||
android_abi = 'armeabi-v7a'
|
||||
ndk_arch = abi_info['ndk_arch']
|
||||
ndk_platform = 'android-14'
|
||||
|
||||
# select the NDK compiler
|
||||
@@ -53,7 +72,7 @@ class AndroidNdkToolchain:
|
||||
|
||||
ndk_platform_path = os.path.join(ndk_path, 'platforms', ndk_platform)
|
||||
sysroot = os.path.join(ndk_path, 'sysroot')
|
||||
target_root = os.path.join(ndk_platform_path, 'arch-' + self.ndk_arch)
|
||||
target_root = os.path.join(ndk_platform_path, 'arch-' + ndk_arch)
|
||||
|
||||
install_prefix = os.path.join(arch_path, 'root')
|
||||
|
||||
@@ -61,13 +80,13 @@ class AndroidNdkToolchain:
|
||||
self.install_prefix = install_prefix
|
||||
self.sysroot = sysroot
|
||||
|
||||
toolchain_path = os.path.join(ndk_path, 'toolchains', arch + '-' + gcc_version, 'prebuilt', build_arch)
|
||||
toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch)
|
||||
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
|
||||
llvm_triple = 'armv7-none-linux-androideabi'
|
||||
llvm_triple = abi_info['llvm_triple']
|
||||
|
||||
common_flags = '-Os -g'
|
||||
common_flags += ' -fPIC'
|
||||
common_flags += ' -march=armv7-a -mfpu=vfp -mfloat-abi=softfp'
|
||||
common_flags += ' ' + abi_info['cflags']
|
||||
|
||||
toolchain_bin = os.path.join(toolchain_path, 'bin')
|
||||
llvm_bin = os.path.join(llvm_path, 'bin')
|
||||
@@ -95,7 +114,7 @@ class AndroidNdkToolchain:
|
||||
' ' + common_flags
|
||||
self.libs = ''
|
||||
|
||||
self.is_arm = self.ndk_arch == 'arm'
|
||||
self.is_arm = ndk_arch == 'arm'
|
||||
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
|
||||
self.is_windows = False
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
AC_PREREQ(2.60)
|
||||
|
||||
AC_INIT(mpd, 0.20.18, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_INIT(mpd, 0.20.19, musicpd-dev-team@lists.sourceforge.net)
|
||||
|
||||
VERSION_MAJOR=0
|
||||
VERSION_MINOR=20
|
||||
VERSION_REVISION=18
|
||||
VERSION_REVISION=19
|
||||
VERSION_EXTRA=0
|
||||
|
||||
AC_CONFIG_SRCDIR([src/Main.cxx])
|
||||
@@ -454,7 +454,7 @@ dnl ---------------------------------------------------------------------------
|
||||
dnl Mandatory Libraries
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
AX_BOOST_BASE([1.46],, [AC_MSG_ERROR([Boost not found])])
|
||||
AX_BOOST_BASE([1.54],, [AC_MSG_ERROR([Boost not found])])
|
||||
|
||||
AC_ARG_ENABLE(icu,
|
||||
AS_HELP_STRING([--enable-icu],
|
||||
|
60
doc/user.xml
60
doc/user.xml
@@ -79,7 +79,10 @@
|
||||
|
||||
<para>
|
||||
If you need to tweak the configuration, you can create a file
|
||||
called <filename>mpd.conf</filename> on the data partition.
|
||||
called <filename>mpd.conf</filename> on the data partition
|
||||
(the directory which is returned by Android's <ulink
|
||||
url="https://developer.android.com/reference/android/os/Environment.html#getExternalStorageDirectory()">getExternalStorageDirectory()</ulink>
|
||||
API function).
|
||||
</para>
|
||||
</section>
|
||||
|
||||
@@ -111,7 +114,7 @@ cd mpd-version</programlisting>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<ulink url="http://www.boost.org/">Boost 1.46</ulink>
|
||||
<ulink url="http://www.boost.org/">Boost 1.54</ulink>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
@@ -265,6 +268,59 @@ apt-get install g++ \
|
||||
script.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="android_build">
|
||||
<title>Compiling for Android</title>
|
||||
|
||||
<para>
|
||||
MPD can be compiled as an Android app. It can be installed
|
||||
easily with <link linkend="install_android">Google
|
||||
Play</link>, but if you want to build it from source, follow
|
||||
this section.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You need:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Android SDK
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<ulink
|
||||
url="https://developer.android.com/ndk/downloads/index.html">Android
|
||||
NDK</ulink>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Just like with the native build, unpack the
|
||||
<application>MPD</application> source tarball and change
|
||||
into the directory. Then, instead of
|
||||
<command>./configure</command>, type:
|
||||
</para>
|
||||
|
||||
<programlisting>./android/build.py SDK_PATH NDK_PATH ABI
|
||||
make android/build/mpd-debug.apk</programlisting>
|
||||
|
||||
<para>
|
||||
<varname>SDK_PATH</varname> is the absolute path where you
|
||||
installed the Android SDK; <varname>NDK_PATH</varname> is
|
||||
the Android NDK installation path; <varname>ABI</varname> is
|
||||
the Android ABI to be built, e.g. "armeabi-v7a".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This downloads various library sources, and then configures
|
||||
and builds <application>MPD</application>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="systemd_socket">
|
||||
|
@@ -17,12 +17,17 @@ libogg = AutotoolsProject(
|
||||
)
|
||||
|
||||
libvorbis = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz',
|
||||
'28cb28097c07a735d6af56e598e1c90f',
|
||||
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.tar.xz',
|
||||
'af00bb5a784e7c9e69f56823de4637c350643deedaf333d0fa86ecdba6fcb415',
|
||||
'lib/libvorbis.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
],
|
||||
|
||||
edits={
|
||||
# this option is not understood by clang
|
||||
'configure': lambda data: data.replace('-mno-ieee-fp', ' '),
|
||||
}
|
||||
)
|
||||
|
||||
opus = AutotoolsProject(
|
||||
@@ -100,8 +105,8 @@ liblame = AutotoolsProject(
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-3.4.2.tar.xz',
|
||||
'2b92e9578ef8b3e49eeab229e69305f5f4cbc1fdaa22e927fc7fca18acccd740',
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.0.tar.xz',
|
||||
'ed945daf40b124e77a685893cc025d086f638bc703183460aff49508edb3a43f',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -329,8 +334,8 @@ ffmpeg = FfmpegProject(
|
||||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.58.0.tar.xz',
|
||||
'6a813875243609eb75f37fa72044e4ad618b55ec15a4eafdac2df6a7e800e3e3',
|
||||
'http://curl.haxx.se/download/curl-7.59.0.tar.xz',
|
||||
'e44eaabdf916407585bf5c7939ff1161e6242b6b015d3f2f5b758b2a330461fc',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
|
19
src/Main.cxx
19
src/Main.cxx
@@ -107,10 +107,6 @@
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
static constexpr size_t KILOBYTE = 1024;
|
||||
@@ -483,21 +479,8 @@ try {
|
||||
daemonize_begin(options.daemon);
|
||||
#endif
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
/* Runs the OS X native event loop in the main thread, and runs
|
||||
the rest of mpd_main on a new thread. This lets CoreAudio receive
|
||||
route change notifications (e.g. plugging or unplugging headphones).
|
||||
All hardware output on OS X ultimately uses CoreAudio internally.
|
||||
This must be run after forking; if dispatch is called before forking,
|
||||
the child process will have a broken internal dispatch state. */
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
exit(mpd_main_after_fork(config));
|
||||
});
|
||||
dispatch_main();
|
||||
return EXIT_FAILURE; // unreachable, because dispatch_main never returns
|
||||
#else
|
||||
return mpd_main_after_fork(config);
|
||||
#endif
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
LogError(e);
|
||||
return EXIT_FAILURE;
|
||||
|
@@ -325,6 +325,34 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SendGroupMask(mpd_connection *connection, tag_mask_t mask)
|
||||
{
|
||||
#if LIBMPDCLIENT_CHECK_VERSION(2,12,0)
|
||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
|
||||
if ((mask & (tag_mask_t(1) << i)) == 0)
|
||||
continue;
|
||||
|
||||
const auto tag = Convert(TagType(i));
|
||||
if (tag == MPD_TAG_COUNT)
|
||||
throw std::runtime_error("Unsupported tag");
|
||||
|
||||
if (!mpd_search_add_group_tag(connection, tag))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
(void)connection;
|
||||
(void)mask;
|
||||
|
||||
if (mask != 0)
|
||||
throw std::runtime_error("Grouping requires libmpdclient 2.12");
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
|
||||
const ConfigBlock &block)
|
||||
:Database(proxy_db_plugin),
|
||||
@@ -682,7 +710,7 @@ static void
|
||||
SearchSongs(struct mpd_connection *connection,
|
||||
const DatabaseSelection &selection,
|
||||
VisitSong visit_song)
|
||||
{
|
||||
try {
|
||||
assert(selection.recursive);
|
||||
assert(visit_song);
|
||||
|
||||
@@ -709,6 +737,11 @@ SearchSongs(struct mpd_connection *connection,
|
||||
|
||||
if (!mpd_response_finish(connection))
|
||||
ThrowError(connection);
|
||||
} catch (...) {
|
||||
if (connection != nullptr)
|
||||
mpd_search_cancel(connection);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -756,9 +789,9 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
|
||||
void
|
||||
ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
||||
TagType tag_type,
|
||||
gcc_unused tag_mask_t group_mask,
|
||||
tag_mask_t group_mask,
|
||||
VisitTag visit_tag) const
|
||||
{
|
||||
try {
|
||||
// TODO: eliminate the const_cast
|
||||
const_cast<ProxyDatabase *>(this)->EnsureConnected();
|
||||
|
||||
@@ -767,32 +800,47 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
||||
throw std::runtime_error("Unsupported tag");
|
||||
|
||||
if (!mpd_search_db_tags(connection, tag_type2) ||
|
||||
!SendConstraints(connection, selection))
|
||||
!SendConstraints(connection, selection) ||
|
||||
!SendGroupMask(connection, group_mask))
|
||||
ThrowError(connection);
|
||||
|
||||
// TODO: use group_mask
|
||||
|
||||
if (!mpd_search_commit(connection))
|
||||
ThrowError(connection);
|
||||
|
||||
while (auto *pair = mpd_recv_pair_tag(connection, tag_type2)) {
|
||||
TagBuilder builder;
|
||||
|
||||
while (auto *pair = mpd_recv_pair(connection)) {
|
||||
AtScopeExit(this, pair) {
|
||||
mpd_return_pair(connection, pair);
|
||||
};
|
||||
|
||||
TagBuilder tag;
|
||||
tag.AddItem(tag_type, pair->value);
|
||||
const auto current_type = tag_name_parse_i(pair->name);
|
||||
if (current_type == TAG_NUM_OF_ITEM_TYPES)
|
||||
continue;
|
||||
|
||||
if (tag.IsEmpty())
|
||||
if (current_type == tag_type && !builder.IsEmpty()) {
|
||||
try {
|
||||
visit_tag(builder.Commit());
|
||||
} catch (...) {
|
||||
mpd_response_finish(connection);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
builder.AddItem(current_type, pair->value);
|
||||
|
||||
if (!builder.HasType(current_type))
|
||||
/* if no tag item has been added, then the
|
||||
given value was not acceptable
|
||||
(e.g. empty); forcefully insert an empty
|
||||
tag in this case, as the caller expects the
|
||||
given tag type to be present */
|
||||
tag.AddEmptyItem(tag_type);
|
||||
builder.AddEmptyItem(current_type);
|
||||
}
|
||||
|
||||
if (!builder.IsEmpty()) {
|
||||
try {
|
||||
visit_tag(tag.Commit());
|
||||
visit_tag(builder.Commit());
|
||||
} catch (...) {
|
||||
mpd_response_finish(connection);
|
||||
throw;
|
||||
@@ -801,6 +849,11 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
||||
|
||||
if (!mpd_response_finish(connection))
|
||||
ThrowError(connection);
|
||||
} catch (...) {
|
||||
if (connection != nullptr)
|
||||
mpd_search_cancel(connection);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
DatabaseStats
|
||||
|
@@ -308,9 +308,14 @@ struct DecoderControl {
|
||||
bool IsCurrentSong(const DetachedSong &_song) const noexcept;
|
||||
|
||||
gcc_pure
|
||||
bool LockIsCurrentSong(const DetachedSong &_song) const noexcept {
|
||||
bool IsSeekableCurrentSong(const DetachedSong &_song) const noexcept {
|
||||
return seekable && IsCurrentSong(_song);
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool LockIsSeeakbleCurrentSong(const DetachedSong &_song) const noexcept {
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
return IsCurrentSong(_song);
|
||||
return IsSeekableCurrentSong(_song);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@@ -26,8 +26,12 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
ThreadInputStream::~ThreadInputStream()
|
||||
void
|
||||
ThreadInputStream::Stop() noexcept
|
||||
{
|
||||
if (!thread.IsDefined())
|
||||
return;
|
||||
|
||||
{
|
||||
const std::lock_guard<Mutex> lock(mutex);
|
||||
close = true;
|
||||
@@ -42,6 +46,7 @@ ThreadInputStream::~ThreadInputStream()
|
||||
buffer->Clear();
|
||||
HugeFree(buffer->Write().data, buffer_size);
|
||||
delete buffer;
|
||||
buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +73,7 @@ ThreadInputStream::ThreadFunc()
|
||||
Open();
|
||||
} catch (...) {
|
||||
postponed_exception = std::current_exception();
|
||||
cond.broadcast();
|
||||
SetReady();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
template<typename T> class CircularBuffer;
|
||||
@@ -39,6 +40,11 @@ template<typename T> class CircularBuffer;
|
||||
* manages the thread and the buffer.
|
||||
*
|
||||
* This works only for "streams": unknown length, no seeking, no tags.
|
||||
*
|
||||
* The implementation must call Stop() before its destruction
|
||||
* completes. This cannot be done in ~ThreadInputStream() because at
|
||||
* this point, the class has been morphed back to #ThreadInputStream
|
||||
* and the still-running thread will crash due to pure method call.
|
||||
*/
|
||||
class ThreadInputStream : public InputStream {
|
||||
const char *const plugin;
|
||||
@@ -76,7 +82,13 @@ public:
|
||||
thread(BIND_THIS_METHOD(ThreadFunc)),
|
||||
buffer_size(_buffer_size) {}
|
||||
|
||||
virtual ~ThreadInputStream();
|
||||
#ifndef NDEBUG
|
||||
~ThreadInputStream() override {
|
||||
/* Stop() must have been called already */
|
||||
assert(!thread.IsDefined());
|
||||
assert(buffer == nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize the object and start the thread.
|
||||
@@ -90,6 +102,12 @@ public:
|
||||
size_t Read(void *ptr, size_t size) override final;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Stop the thread and free the buffer. This must be called
|
||||
* before destruction of this object completes.
|
||||
*/
|
||||
void Stop() noexcept;
|
||||
|
||||
void SetMimeType(const char *_mime) {
|
||||
assert(thread.IsInside());
|
||||
|
||||
|
@@ -39,6 +39,10 @@ public:
|
||||
MMS_BUFFER_SIZE) {
|
||||
}
|
||||
|
||||
~MmsInputStream() noexcept override {
|
||||
Stop();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Open() override;
|
||||
virtual size_t ThreadRead(void *ptr, size_t size) override;
|
||||
|
@@ -33,6 +33,9 @@ FfmpegInit()
|
||||
{
|
||||
av_log_set_callback(FfmpegLogCallback);
|
||||
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
|
||||
/* deprecated as of FFmpeg 4.0 */
|
||||
av_register_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -40,6 +40,9 @@ public:
|
||||
~ScopeNetInit() noexcept {
|
||||
WSACleanup();
|
||||
}
|
||||
#else
|
||||
public:
|
||||
ScopeNetInit() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@@ -46,19 +46,20 @@ pcm_dsd_to_dop(PcmBuffer &buffer, unsigned channels,
|
||||
assert(audio_valid_channel_count(channels));
|
||||
assert(_src.size % channels == 0);
|
||||
|
||||
const unsigned num_src_samples = _src.size;
|
||||
const unsigned num_src_frames = num_src_samples / channels;
|
||||
const size_t num_src_samples = _src.size;
|
||||
const size_t num_src_frames = num_src_samples / channels;
|
||||
|
||||
/* this rounds down and discards the last odd frame; not
|
||||
/* this rounds down and discards up to 3 odd frames; not
|
||||
elegant, but good enough for now */
|
||||
const unsigned num_frames = num_src_frames / 2;
|
||||
const unsigned num_samples = num_frames * channels;
|
||||
const size_t num_dop_quads = num_src_frames / 4;
|
||||
const size_t num_frames = num_dop_quads * 2;
|
||||
const size_t num_samples = num_frames * channels;
|
||||
|
||||
uint32_t *const dest0 = (uint32_t *)buffer.GetT<uint32_t>(num_samples),
|
||||
*dest = dest0;
|
||||
|
||||
auto src = _src.data;
|
||||
for (unsigned i = num_frames / 2; i > 0; --i) {
|
||||
for (size_t i = num_dop_quads; i > 0; --i) {
|
||||
for (unsigned c = channels; c > 0; --c) {
|
||||
/* each 24 bit sample has 16 DSD sample bits
|
||||
plus the magic 0x05 marker */
|
||||
|
@@ -584,7 +584,7 @@ Player::SeekDecoder()
|
||||
|
||||
const SongTime start_time = pc.next_song->GetStartTime();
|
||||
|
||||
if (!dc.LockIsCurrentSong(*pc.next_song)) {
|
||||
if (!dc.LockIsSeeakbleCurrentSong(*pc.next_song)) {
|
||||
/* the decoder is already decoding the "next" song -
|
||||
stop it and start the previous song again */
|
||||
|
||||
|
@@ -164,6 +164,10 @@ SongTime
|
||||
ParseCommandArgSongTime(const char *s)
|
||||
{
|
||||
auto value = ParseCommandArgFloat(s);
|
||||
if (value < 0)
|
||||
throw FormatProtocolError(ACK_ERROR_ARG,
|
||||
"Negative value not allowed: %s", s);
|
||||
|
||||
return SongTime::FromS(value);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user