release v0.21.22
-----BEGIN PGP SIGNATURE----- iQJEBAABCgAuFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAl6GCWgQHG1heEBtdXNp Y3BkLm9yZwAKCRAjbopYxttFEvkQD/9qg6cnCgCKSOmTjteJu1ayeXBMDiL3cCCg AwarHQoxsB0102NpV+MLka+4HIwHn+WNL55TzYgfbSh0nfmnki2fZ0YpsZoQR79w MP11iMPnFH1oKqj58minBkFNmAis2aLYHJGKaQNUh7wcf0WhbVTqtWBUrKb07RQ0 Zj5lXtg65O/+yaCVdQGS6fMk2t7CqBM+S3RmbXCib/JRMC6aozoC7nWPvj8b2R8d PgxwKMRzyslyFoxDQZrusDjJ1piyigzUMMr32yzYDED4Xr8jsEELaJfULbr6qWT2 ZNYF91e+D5V7riASAtlFTaVMaISx4QbHjKWR5Xcx0q/SJPAXTxF8RrAqGvqpWYmc kqmC8iNxQsW5o3sNhI9qg6sOkq2dIu43VasRCvuo19GABR36wwTK5ORoazIi8fbU /Ki/oZHtZczHRop9Cd6698Qr9jyTPdIs55FbgejzrVADvAmslqtcA6XxCBuG1nSF Qo48dp9Px7J74qNNuDt5/xLnQGJKaW60/BXrMK9G9QG4x9r4zuCayDO6Qc7FMWWR DG7k9nYoXJU5YG7xp9Rk+Yj3Ade8kqhTnKfqw0f2JfkLt0ChPG+rZcxICWSdzZm0 AfHxuEQlwKaaFHFEsZhVehlsXkeU9OVywo+QDzmY2uqQ2ddlBZ47Qm2MWgTsv8i/ euVsrzLtag== =EedX -----END PGP SIGNATURE----- Merge tag 'v0.21.22' release v0.21.22
This commit is contained in:
commit
12b97bbe38
16
.travis.yml
16
.travis.yml
|
@ -100,6 +100,22 @@ jobs:
|
|||
packages:
|
||||
- ccache
|
||||
- meson
|
||||
- icu4c
|
||||
- ffmpeg
|
||||
- libnfs
|
||||
- yajl
|
||||
- libupnp
|
||||
- libid3tag
|
||||
- chromaprint
|
||||
- libsamplerate
|
||||
- libsoxr
|
||||
- libzzip
|
||||
- flac
|
||||
- opus
|
||||
- libvorbis
|
||||
- faad2
|
||||
- wavpack
|
||||
- libmpdclient
|
||||
update: true
|
||||
env:
|
||||
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
|
||||
|
|
15
NEWS
15
NEWS
|
@ -35,6 +35,21 @@ ver 0.22 (not yet released)
|
|||
* switch to C++17
|
||||
- GCC 7 or clang 4 (or newer) recommended
|
||||
|
||||
ver 0.21.22 (2020/04/02)
|
||||
* database
|
||||
- simple: optimize startup
|
||||
* input
|
||||
- curl: fix streaming errors on Android
|
||||
* playlist
|
||||
- rss: support MIME type application/xml
|
||||
* mixer
|
||||
- android: new mixer plugin for "sles" output
|
||||
* Android
|
||||
- TV support
|
||||
* Windows
|
||||
- fix time zone offset check
|
||||
* fix build failures with uClibc-ng
|
||||
|
||||
ver 0.21.21 (2020/03/19)
|
||||
* configuration
|
||||
- fix bug in "metadata_to_use" setting
|
||||
|
|
|
@ -2,18 +2,25 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="44"
|
||||
android:versionName="0.21.21">
|
||||
android:versionCode="45"
|
||||
android:versionName="0.21.22">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
|
||||
|
||||
<uses-feature android:name="android.software.leanback"
|
||||
android:required="false" />
|
||||
<uses-feature android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application android:allowBackup="true"
|
||||
android:icon="@drawable/icon"
|
||||
android:banner="@drawable/icon"
|
||||
android:label="@string/app_name">
|
||||
<activity android:name=".Settings"
|
||||
android:label="@string/app_name">
|
||||
|
@ -22,6 +29,14 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".Settings"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<receiver android:name=".Receiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.musicpd;
|
|||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
|
@ -35,6 +36,9 @@ import android.os.RemoteException;
|
|||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Main extends Service implements Runnable {
|
||||
private static final String TAG = "Main";
|
||||
private static final String REMOTE_ERROR = "MPD process was killed";
|
||||
|
@ -156,11 +160,36 @@ public class Main extends Service implements Runnable {
|
|||
sendMessage(MSG_SEND_STATUS, mStatus, 0, mError);
|
||||
}
|
||||
|
||||
private Notification.Builder createNotificationBuilderWithChannel() {
|
||||
final NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager == null)
|
||||
return null;
|
||||
|
||||
final String id = "org.musicpd";
|
||||
final String name = "MPD service";
|
||||
final int importance = 3; /* NotificationManager.IMPORTANCE_DEFAULT */
|
||||
|
||||
try {
|
||||
Class<?> ncClass = Class.forName("android.app.NotificationChannel");
|
||||
Constructor<?> ncCtor = ncClass.getConstructor(String.class, CharSequence.class, int.class);
|
||||
Object nc = ncCtor.newInstance(id, name, importance);
|
||||
|
||||
Method nmCreateNotificationChannelMethod =
|
||||
NotificationManager.class.getMethod("createNotificationChannel", ncClass);
|
||||
nmCreateNotificationChannelMethod.invoke(notificationManager, nc);
|
||||
|
||||
Constructor nbCtor = Notification.Builder.class.getConstructor(Context.class, String.class);
|
||||
return (Notification.Builder) nbCtor.newInstance(this, id);
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.e(TAG, "error creating the NotificationChannel", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void start() {
|
||||
if (mThread != null)
|
||||
return;
|
||||
mThread = new Thread(this);
|
||||
mThread.start();
|
||||
|
||||
final Intent mainIntent = new Intent(this, Settings.class);
|
||||
mainIntent.setAction("android.intent.action.MAIN");
|
||||
|
@ -168,13 +197,25 @@ public class Main extends Service implements Runnable {
|
|||
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
Notification notification = new Notification.Builder(this)
|
||||
.setContentTitle(getText(R.string.notification_title_mpd_running))
|
||||
Notification.Builder nBuilder;
|
||||
if (Build.VERSION.SDK_INT >= 26 /* Build.VERSION_CODES.O */)
|
||||
{
|
||||
nBuilder = createNotificationBuilderWithChannel();
|
||||
if (nBuilder == null)
|
||||
return;
|
||||
}
|
||||
else
|
||||
nBuilder = new Notification.Builder(this);
|
||||
|
||||
Notification notification = nBuilder.setContentTitle(getText(R.string.notification_title_mpd_running))
|
||||
.setContentText(getText(R.string.notification_text_mpd_running))
|
||||
.setSmallIcon(R.drawable.notification_icon)
|
||||
.setContentIntent(contentIntent)
|
||||
.build();
|
||||
|
||||
mThread = new Thread(this);
|
||||
mThread.start();
|
||||
|
||||
startForeground(R.string.notification_title_mpd_running, notification);
|
||||
startService(new Intent(this, Main.class));
|
||||
}
|
||||
|
|
|
@ -105,12 +105,13 @@ public class Settings extends Activity {
|
|||
else
|
||||
mRunButton.setChecked(false);
|
||||
mFirstRun = true;
|
||||
mTextStatus.setText("");
|
||||
break;
|
||||
case MSG_STARTED:
|
||||
Log.d(TAG, "onStarted");
|
||||
mRunButton.setChecked(true);
|
||||
mFirstRun = true;
|
||||
mTextStatus.setText("CAUTION: this version is EXPERIMENTAL!"); // XXX
|
||||
mTextStatus.setText("MPD service started");
|
||||
break;
|
||||
case MSG_LOG:
|
||||
if (mLogListArray.size() > MAX_LOGS)
|
||||
|
|
|
@ -311,6 +311,7 @@ if not is_android
|
|||
else
|
||||
sources += [
|
||||
'src/android/Context.cxx',
|
||||
'src/android/AudioManager.cxx',
|
||||
'src/android/Environment.cxx',
|
||||
'src/android/LogListener.cxx',
|
||||
]
|
||||
|
@ -332,6 +333,7 @@ subdir('src/util')
|
|||
subdir('src/time')
|
||||
subdir('src/system')
|
||||
subdir('src/thread')
|
||||
subdir('src/net')
|
||||
subdir('src/event')
|
||||
|
||||
subdir('src/lib/dbus')
|
||||
|
@ -359,7 +361,6 @@ subdir('src/lib/crypto')
|
|||
|
||||
subdir('src/fs')
|
||||
subdir('src/config')
|
||||
subdir('src/net')
|
||||
subdir('src/tag')
|
||||
subdir('src/pcm')
|
||||
subdir('src/neighbor')
|
||||
|
|
|
@ -9,8 +9,8 @@ from build.ffmpeg import FfmpegProject
|
|||
from build.boost import BoostProject
|
||||
|
||||
libmpdclient = MesonProject(
|
||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.17.tar.xz',
|
||||
'ee9b8f1c7e95b65c8f18a354daf7b16bfcd455fc52a0f3b5abe402316bce3559',
|
||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.18.tar.xz',
|
||||
'4cb01e1f567e0169aca94875fb6e1200e7f5ce35b63a4df768ec1591fb1081fa',
|
||||
'lib/libmpdclient.a',
|
||||
)
|
||||
|
||||
|
@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
|
|||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.68.0.tar.xz',
|
||||
'b724240722276a27f6e770b952121a3afd097129d8c9fe18e6272dc34192035a',
|
||||
'http://curl.haxx.se/download/curl-7.69.1.tar.xz',
|
||||
'03c7d5e6697f7b7e40ada1b2256e565a555657398e6c1fcfa4cb251ccd819d4f',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "playlist/PlaylistRegistry.hxx"
|
||||
#include "playlist/PlaylistPlugin.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/StandardDirectory.hxx"
|
||||
|
@ -378,17 +379,7 @@ ParseCommandLine(int argc, char **argv, struct options &options,
|
|||
|
||||
if (config_file != nullptr) {
|
||||
/* use specified configuration file */
|
||||
#ifdef _UNICODE
|
||||
wchar_t buffer[MAX_PATH];
|
||||
auto result = MultiByteToWideChar(CP_ACP, 0, config_file, -1,
|
||||
buffer, std::size(buffer));
|
||||
if (result <= 0)
|
||||
throw MakeLastError("MultiByteToWideChar() failed");
|
||||
|
||||
ReadConfigFile(config, Path::FromFS(buffer));
|
||||
#else
|
||||
ReadConfigFile(config, Path::FromFS(config_file));
|
||||
#endif
|
||||
ReadConfigFile(config, FromNarrowPath(config_file));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2003-2020 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 "AudioManager.hxx"
|
||||
#include "java/Class.hxx"
|
||||
#include "java/Exception.hxx"
|
||||
#include "java/File.hxx"
|
||||
|
||||
#define STREAM_MUSIC 3
|
||||
|
||||
AudioManager::AudioManager(JNIEnv *env, jobject obj) noexcept
|
||||
: Java::GlobalObject(env, obj)
|
||||
{
|
||||
Java::Class cls(env, env->GetObjectClass(Get()));
|
||||
jmethodID method = env->GetMethodID(cls, "getStreamMaxVolume", "(I)I");
|
||||
assert(method);
|
||||
maxVolume = env->CallIntMethod(Get(), method, STREAM_MUSIC);
|
||||
|
||||
getStreamVolumeMethod = env->GetMethodID(cls, "getStreamVolume", "(I)I");
|
||||
assert(getStreamVolumeMethod);
|
||||
|
||||
setStreamVolumeMethod = env->GetMethodID(cls, "setStreamVolume", "(III)V");
|
||||
assert(setStreamVolumeMethod);
|
||||
}
|
||||
|
||||
int
|
||||
AudioManager::GetVolume(JNIEnv *env)
|
||||
{
|
||||
if (maxVolume == 0)
|
||||
return 0;
|
||||
return env->CallIntMethod(Get(), getStreamVolumeMethod, STREAM_MUSIC);
|
||||
}
|
||||
|
||||
void
|
||||
AudioManager::SetVolume(JNIEnv *env, int volume)
|
||||
{
|
||||
if (maxVolume == 0)
|
||||
return;
|
||||
env->CallVoidMethod(Get(), setStreamVolumeMethod, STREAM_MUSIC, volume, 0);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2003-2020 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_ANDROID_AUDIO_MANAGER_HXX
|
||||
#define MPD_ANDROID_AUDIO_MANAGER_HXX
|
||||
|
||||
#include "java/Object.hxx"
|
||||
|
||||
class AudioManager : public Java::GlobalObject {
|
||||
int maxVolume;
|
||||
jmethodID getStreamVolumeMethod;
|
||||
jmethodID setStreamVolumeMethod;
|
||||
|
||||
public:
|
||||
AudioManager(JNIEnv *env, jobject obj) noexcept;
|
||||
|
||||
AudioManager(std::nullptr_t) noexcept { maxVolume = 0; }
|
||||
|
||||
~AudioManager() noexcept {}
|
||||
|
||||
int GetMaxVolume() { return maxVolume; }
|
||||
int GetVolume(JNIEnv *env);
|
||||
void SetVolume(JNIEnv *env, int);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -21,8 +21,11 @@
|
|||
#include "java/Class.hxx"
|
||||
#include "java/Exception.hxx"
|
||||
#include "java/File.hxx"
|
||||
#include "java/String.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
|
||||
#include "AudioManager.hxx"
|
||||
|
||||
AllocatedPath
|
||||
Context::GetCacheDir(JNIEnv *env) const noexcept
|
||||
{
|
||||
|
@ -39,3 +42,21 @@ Context::GetCacheDir(JNIEnv *env) const noexcept
|
|||
|
||||
return Java::File::ToAbsolutePath(env, file);
|
||||
}
|
||||
|
||||
AudioManager *
|
||||
Context::GetAudioManager(JNIEnv *env) noexcept
|
||||
{
|
||||
assert(env != nullptr);
|
||||
|
||||
Java::Class cls(env, env->GetObjectClass(Get()));
|
||||
jmethodID method = env->GetMethodID(cls, "getSystemService",
|
||||
"(Ljava/lang/String;)Ljava/lang/Object;");
|
||||
assert(method);
|
||||
|
||||
Java::String name(env, "audio");
|
||||
jobject am = env->CallObjectMethod(Get(), method, name.Get());
|
||||
if (Java::DiscardException(env) || am == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return new AudioManager(env, am);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "java/Object.hxx"
|
||||
|
||||
class AllocatedPath;
|
||||
class AudioManager;
|
||||
|
||||
class Context : public Java::GlobalObject {
|
||||
public:
|
||||
|
@ -31,6 +32,9 @@ public:
|
|||
|
||||
gcc_pure
|
||||
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
|
||||
|
||||
gcc_pure
|
||||
AudioManager *GetAudioManager(JNIEnv *env) noexcept;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "fs/Traits.hxx"
|
||||
#include "util/Alloc.hxx"
|
||||
#include "util/DeleteDisposer.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
@ -70,7 +71,15 @@ Directory::GetName() const noexcept
|
|||
{
|
||||
assert(!IsRoot());
|
||||
|
||||
return PathTraitsUTF8::GetBase(path.c_str());
|
||||
if (parent->IsRoot())
|
||||
return path.c_str();
|
||||
|
||||
assert(StringAfterPrefix(path.c_str(), parent->path.c_str()) != nullptr);
|
||||
assert(*StringAfterPrefix(path.c_str(), parent->path.c_str()) == PathTraitsUTF8::SEPARATOR);
|
||||
|
||||
/* strip the parent directory path and the slash separator
|
||||
from this directory's path, and the base name remains */
|
||||
return path.c_str() + parent->path.length() + 1;
|
||||
}
|
||||
|
||||
Directory *
|
||||
|
|
|
@ -287,7 +287,7 @@ FfmpegReceiveFrames(DecoderClient &client, InputStream &is,
|
|||
*/
|
||||
static DecoderCommand
|
||||
ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
||||
AVPacket &&packet,
|
||||
const AVPacket &packet,
|
||||
AVCodecContext &codec_context,
|
||||
const AVStream &stream,
|
||||
AVFrame &frame,
|
||||
|
@ -340,24 +340,6 @@ ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
|||
return cmd;
|
||||
}
|
||||
|
||||
static DecoderCommand
|
||||
ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
||||
const AVPacket &packet,
|
||||
AVCodecContext &codec_context,
|
||||
const AVStream &stream,
|
||||
AVFrame &frame,
|
||||
uint64_t min_frame, size_t pcm_frame_size,
|
||||
FfmpegBuffer &buffer)
|
||||
{
|
||||
return ffmpeg_send_packet(client, is,
|
||||
/* copy the AVPacket, because FFmpeg
|
||||
< 3.0 requires this */
|
||||
AVPacket(packet),
|
||||
codec_context, stream,
|
||||
frame, min_frame, pcm_frame_size,
|
||||
buffer);
|
||||
}
|
||||
|
||||
gcc_const
|
||||
static SampleFormat
|
||||
ffmpeg_sample_format(enum AVSampleFormat sample_fmt) noexcept
|
||||
|
|
|
@ -26,6 +26,7 @@ event_dep = declare_dependency(
|
|||
link_with: event,
|
||||
dependencies: [
|
||||
thread_dep,
|
||||
net_dep,
|
||||
system_dep,
|
||||
boost_dep,
|
||||
],
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2003-2018 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 "NarrowPath.hxx"
|
||||
|
||||
#ifdef _UNICODE
|
||||
|
||||
#include "lib/icu/Win32.hxx"
|
||||
#include "system/Error.hxx"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
NarrowPath::NarrowPath(Path _path) noexcept
|
||||
:value(WideCharToMultiByte(CP_ACP, _path.c_str()))
|
||||
{
|
||||
if (value.IsNull())
|
||||
/* fall back to empty string */
|
||||
value = Value::Empty();
|
||||
}
|
||||
|
||||
static AllocatedPath
|
||||
AcpToAllocatedPath(const char *s)
|
||||
{
|
||||
wchar_t buffer[MAX_PATH];
|
||||
auto result = MultiByteToWideChar(CP_ACP, 0, s, -1,
|
||||
buffer, std::size(buffer));
|
||||
if (result <= 0)
|
||||
throw MakeLastError("MultiByteToWideChar() failed");
|
||||
|
||||
return AllocatedPath::FromFS(buffer);
|
||||
}
|
||||
|
||||
FromNarrowPath::FromNarrowPath(const char *s)
|
||||
:value(AcpToAllocatedPath(s))
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* _UNICODE */
|
|
@ -23,9 +23,8 @@
|
|||
#include "Path.hxx"
|
||||
|
||||
#ifdef _UNICODE
|
||||
#include "lib/icu/Win32.hxx"
|
||||
#include "AllocatedPath.hxx"
|
||||
#include "util/AllocatedString.hxx"
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include "util/StringPointer.hxx"
|
||||
#endif
|
||||
|
@ -47,12 +46,7 @@ class NarrowPath {
|
|||
|
||||
public:
|
||||
#ifdef _UNICODE
|
||||
explicit NarrowPath(Path _path)
|
||||
:value(WideCharToMultiByte(CP_ACP, _path.c_str())) {
|
||||
if (value.IsNull())
|
||||
/* fall back to empty string */
|
||||
value = Value::Empty();
|
||||
}
|
||||
explicit NarrowPath(Path _path) noexcept;
|
||||
#else
|
||||
explicit NarrowPath(Path _path):value(_path.c_str()) {}
|
||||
#endif
|
||||
|
@ -66,4 +60,38 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A path name converted from a "narrow" string. This is used to
|
||||
* import an existing narrow string to a #Path.
|
||||
*/
|
||||
class FromNarrowPath {
|
||||
#ifdef _UNICODE
|
||||
using Value = AllocatedPath;
|
||||
#else
|
||||
using Value = Path;
|
||||
#endif
|
||||
|
||||
Value value{nullptr};
|
||||
|
||||
public:
|
||||
FromNarrowPath() = default;
|
||||
|
||||
#ifdef _UNICODE
|
||||
/**
|
||||
* Throws on error.
|
||||
*/
|
||||
FromNarrowPath(const char *s);
|
||||
#else
|
||||
constexpr FromNarrowPath(const char *s) noexcept
|
||||
:value(Value::FromFS(s)) {}
|
||||
#endif
|
||||
|
||||
#ifndef _UNICODE
|
||||
constexpr
|
||||
#endif
|
||||
operator Path() const noexcept {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -50,7 +50,7 @@ class BufferedReader {
|
|||
|
||||
public:
|
||||
explicit BufferedReader(Reader &_reader) noexcept
|
||||
:reader(_reader), buffer(4096) {}
|
||||
:reader(_reader), buffer(16384) {}
|
||||
|
||||
/**
|
||||
* Reset the internal state. Should be called after rewinding
|
||||
|
|
|
@ -45,7 +45,7 @@ class GunzipReader final : public Reader {
|
|||
|
||||
z_stream z;
|
||||
|
||||
StaticFifoBuffer<Bytef, 4096> buffer;
|
||||
StaticFifoBuffer<Bytef, 65536> buffer;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
|
@ -62,7 +62,7 @@ GzipOutputStream::Flush()
|
|||
z.avail_in = 0;
|
||||
|
||||
while (true) {
|
||||
Bytef output[4096];
|
||||
Bytef output[16384];
|
||||
z.next_out = output;
|
||||
z.avail_out = sizeof(output);
|
||||
|
||||
|
@ -87,7 +87,7 @@ GzipOutputStream::Write(const void *_data, size_t size)
|
|||
z.avail_in = size;
|
||||
|
||||
while (z.avail_in > 0) {
|
||||
Bytef output[4096];
|
||||
Bytef output[16384];
|
||||
z.next_out = output;
|
||||
z.avail_out = sizeof(output);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ fs_sources = [
|
|||
'Path.cxx',
|
||||
'Path2.cxx',
|
||||
'AllocatedPath.cxx',
|
||||
'NarrowPath.cxx',
|
||||
'FileSystem.cxx',
|
||||
'List.cxx',
|
||||
'StandardDirectory.cxx',
|
||||
|
|
|
@ -56,7 +56,9 @@ CurlRequest::CurlRequest(CurlGlobal &_global,
|
|||
easy.SetUserAgent("Music Player Daemon " VERSION);
|
||||
easy.SetHeaderFunction(_HeaderFunction, this);
|
||||
easy.SetWriteFunction(WriteFunction, this);
|
||||
#ifndef ANDROID
|
||||
easy.SetOption(CURLOPT_NETRC, 1L);
|
||||
#endif
|
||||
easy.SetErrorBuffer(error_buffer);
|
||||
easy.SetNoProgress();
|
||||
easy.SetNoSignal();
|
||||
|
|
|
@ -29,6 +29,7 @@ struct MixerPlugin;
|
|||
|
||||
extern const MixerPlugin null_mixer_plugin;
|
||||
extern const MixerPlugin software_mixer_plugin;
|
||||
extern const MixerPlugin android_mixer_plugin;
|
||||
extern const MixerPlugin alsa_mixer_plugin;
|
||||
extern const MixerPlugin haiku_mixer_plugin;
|
||||
extern const MixerPlugin oss_mixer_plugin;
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright 2003-2020 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 "mixer/MixerInternal.hxx"
|
||||
#include "filter/plugins/VolumeFilterPlugin.hxx"
|
||||
#include "pcm/Volume.hxx"
|
||||
#include "android/Context.hxx"
|
||||
#include "android/AudioManager.hxx"
|
||||
|
||||
#include "Main.hxx"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
class AndroidMixer final : public Mixer {
|
||||
AudioManager *audioManager;
|
||||
int currentVolume;
|
||||
int maxAndroidVolume;
|
||||
int lastAndroidVolume;
|
||||
public:
|
||||
explicit AndroidMixer(MixerListener &_listener);
|
||||
|
||||
~AndroidMixer() override;
|
||||
|
||||
/* virtual methods from class Mixer */
|
||||
void Open() override {
|
||||
}
|
||||
|
||||
void Close() noexcept override {
|
||||
}
|
||||
|
||||
int GetVolume() override;
|
||||
|
||||
void SetVolume(unsigned volume) override;
|
||||
};
|
||||
|
||||
static Mixer *
|
||||
android_mixer_init([[maybe_unused]] EventLoop &event_loop,
|
||||
[[maybe_unused]] AudioOutput &ao,
|
||||
MixerListener &listener,
|
||||
[[maybe_unused]] const ConfigBlock &block)
|
||||
{
|
||||
return new AndroidMixer(listener);
|
||||
}
|
||||
|
||||
AndroidMixer::AndroidMixer(MixerListener &_listener)
|
||||
:Mixer(android_mixer_plugin, _listener)
|
||||
{
|
||||
JNIEnv *env = Java::GetEnv();
|
||||
audioManager = context->GetAudioManager(env);
|
||||
|
||||
maxAndroidVolume = audioManager->GetMaxVolume();
|
||||
if (maxAndroidVolume != 0)
|
||||
{
|
||||
lastAndroidVolume = audioManager->GetVolume(env);
|
||||
currentVolume = 100 * lastAndroidVolume / maxAndroidVolume;
|
||||
}
|
||||
}
|
||||
|
||||
AndroidMixer::~AndroidMixer()
|
||||
{
|
||||
delete audioManager;
|
||||
}
|
||||
|
||||
int
|
||||
AndroidMixer::GetVolume()
|
||||
{
|
||||
JNIEnv *env = Java::GetEnv();
|
||||
if (maxAndroidVolume == 0)
|
||||
return -1;
|
||||
|
||||
// The android volume index (or scale) is very likely inferior to the
|
||||
// MPD one (100). The last volume set by MPD is saved into
|
||||
// currentVolume, this volume is returned instead of the Android one
|
||||
// when the Android mixer was not touched by an other application. This
|
||||
// allows to fake a 0..100 scale from MPD.
|
||||
|
||||
int volume = audioManager->GetVolume(env);
|
||||
if (volume == lastAndroidVolume)
|
||||
return currentVolume;
|
||||
|
||||
return 100 * volume / maxAndroidVolume;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidMixer::SetVolume(unsigned newVolume)
|
||||
{
|
||||
JNIEnv *env = Java::GetEnv();
|
||||
if (maxAndroidVolume == 0)
|
||||
return;
|
||||
currentVolume = newVolume;
|
||||
lastAndroidVolume = currentVolume * maxAndroidVolume / 100;
|
||||
audioManager->SetVolume(env, lastAndroidVolume);
|
||||
|
||||
}
|
||||
|
||||
const MixerPlugin android_mixer_plugin = {
|
||||
android_mixer_init,
|
||||
true,
|
||||
};
|
|
@ -34,6 +34,10 @@ if is_windows
|
|||
mixer_plugins_sources += 'WinmmMixerPlugin.cxx'
|
||||
endif
|
||||
|
||||
if is_android
|
||||
mixer_plugins_sources += 'AndroidMixerPlugin.cxx'
|
||||
endif
|
||||
|
||||
mixer_plugins = static_library(
|
||||
'mixer_plugins',
|
||||
mixer_plugins_sources,
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "thread/Cond.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/ByteOrder.hxx"
|
||||
#include "mixer/MixerList.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
|
@ -412,5 +413,5 @@ const struct AudioOutputPlugin sles_output_plugin = {
|
|||
"sles",
|
||||
sles_test_default_device,
|
||||
SlesOutput::Create,
|
||||
nullptr,
|
||||
&android_mixer_plugin,
|
||||
};
|
||||
|
|
|
@ -160,6 +160,7 @@ static const char *const rss_suffixes[] = {
|
|||
|
||||
static const char *const rss_mime_types[] = {
|
||||
"application/rss+xml",
|
||||
"application/xml",
|
||||
"text/xml",
|
||||
nullptr
|
||||
};
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
Mutex tag_pool_lock;
|
||||
|
||||
static constexpr size_t NUM_SLOTS = 4093;
|
||||
static constexpr size_t NUM_SLOTS = 16127;
|
||||
|
||||
struct TagPoolSlot {
|
||||
TagPoolSlot *next;
|
||||
|
|
|
@ -77,15 +77,15 @@ static time_t
|
|||
GetTimeZoneOffset() noexcept
|
||||
{
|
||||
time_t t = 1234567890;
|
||||
struct tm tm;
|
||||
tm.tm_isdst = 0;
|
||||
#ifdef _WIN32
|
||||
struct tm *p = gmtime(&t);
|
||||
#else
|
||||
struct tm tm;
|
||||
tm.tm_isdst = 0;
|
||||
struct tm *p = &tm;
|
||||
gmtime_r(&t, p);
|
||||
#endif
|
||||
return t - mktime(&tm);
|
||||
return t - mktime(p);
|
||||
}
|
||||
|
||||
#endif /* !__GLIBC__ */
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "decoder/DecoderList.hxx"
|
||||
#include "decoder/DecoderPlugin.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "fs/io/StdioOutputStream.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "util/PrintException.hxx"
|
||||
|
@ -63,7 +64,7 @@ try {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Path path = Path::FromFS(argv[1]);
|
||||
const FromNarrowPath path = argv[1];
|
||||
|
||||
const ScopeDecoderPluginsInit decoder_plugins_init({});
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "ConfigGlue.hxx"
|
||||
#include "tag/Config.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "event/Thread.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/PrintException.hxx"
|
||||
|
@ -106,7 +107,7 @@ try {
|
|||
return 1;
|
||||
}
|
||||
|
||||
const Path config_path = Path::FromFS(argv[1]);
|
||||
const FromNarrowPath config_path = argv[1];
|
||||
const char *const plugin_name = argv[2];
|
||||
|
||||
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "tag/ApeLoader.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "input/LocalOpen.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
|
@ -58,7 +59,7 @@ try {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Path path = Path::FromFS(argv[1]);
|
||||
const FromNarrowPath path = argv[1];
|
||||
|
||||
Mutex mutex;
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ public:
|
|||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
ShutdownHandler::ShutdownHandler(EventLoop &loop) {}
|
||||
ShutdownHandler::~ShutdownHandler() {}
|
||||
inline ShutdownHandler::ShutdownHandler(EventLoop &) {}
|
||||
inline ShutdownHandler::~ShutdownHandler() {}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "fs/io/FileOutputStream.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "util/PrintException.hxx"
|
||||
|
||||
#include <cerrno>
|
||||
|
@ -55,7 +56,7 @@ try {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Path path = Path::FromFS(argv[1]);
|
||||
const FromNarrowPath path = argv[1];
|
||||
|
||||
FileOutputStream fos(path);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "playlist/PlaylistRegistry.hxx"
|
||||
#include "playlist/PlaylistPlugin.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "fs/io/StdioOutputStream.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
|
@ -54,7 +55,7 @@ try {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Path config_path = Path::FromFS(argv[1]);
|
||||
const FromNarrowPath config_path = argv[1];
|
||||
uri = argv[2];
|
||||
|
||||
/* initialize MPD */
|
||||
|
|
|
@ -24,6 +24,7 @@ gtest_dep = declare_dependency(
|
|||
)
|
||||
|
||||
subdir('net')
|
||||
subdir('time')
|
||||
|
||||
executable(
|
||||
'read_conf',
|
||||
|
@ -52,19 +53,6 @@ test('TestUtil', executable(
|
|||
],
|
||||
))
|
||||
|
||||
test(
|
||||
'TestTime',
|
||||
executable(
|
||||
'TestTime',
|
||||
'TestISO8601.cxx',
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
time_dep,
|
||||
gtest_dep,
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
test('TestRewindInputStream', executable(
|
||||
'TestRewindInputStream',
|
||||
'TestRewindInputStream.cxx',
|
||||
|
@ -326,6 +314,11 @@ if curl_dep.found()
|
|||
include_directories: inc,
|
||||
dependencies: [
|
||||
curl_dep,
|
||||
|
||||
# Explicitly linking with zlib here works around a linker
|
||||
# failure on Windows, because our Windows CURL build is
|
||||
# statically linked and thus declares no dependency on zlib
|
||||
zlib_dep,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2020 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -34,13 +34,22 @@
|
|||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
static std::string
|
||||
ToString(const struct in_addr &a)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/* on mingw32, the parameter is non-const (PVOID) */
|
||||
const auto p = const_cast<struct in_addr *>(&a);
|
||||
#else
|
||||
const auto p = &a;
|
||||
#endif
|
||||
|
||||
char buffer[256];
|
||||
const char *result = inet_ntop(AF_INET, &a, buffer, sizeof(buffer));
|
||||
const char *result = inet_ntop(AF_INET, p, buffer, sizeof(buffer));
|
||||
if (result == nullptr)
|
||||
throw std::runtime_error("inet_ntop() failed");
|
||||
return result;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
|
||||
* Copyright 2012-2020 Max Kellermann <max.kellermann@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -34,13 +34,22 @@
|
|||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
static std::string
|
||||
ToString(const struct in6_addr &a)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/* on mingw32, the parameter is non-const (PVOID) */
|
||||
const auto p = const_cast<struct in6_addr *>(&a);
|
||||
#else
|
||||
const auto p = &a;
|
||||
#endif
|
||||
|
||||
char buffer[256];
|
||||
const char *result = inet_ntop(AF_INET6, &a, buffer, sizeof(buffer));
|
||||
const char *result = inet_ntop(AF_INET6, p, buffer, sizeof(buffer));
|
||||
if (result == nullptr)
|
||||
throw std::runtime_error("inet_ntop() failed");
|
||||
return result;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "config/Param.hxx"
|
||||
#include "config/File.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "util/PrintException.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
|
@ -34,7 +35,7 @@ try {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Path config_path = Path::FromFS(argv[1]);
|
||||
const FromNarrowPath config_path = argv[1];
|
||||
const char *name = argv[2];
|
||||
|
||||
const auto option = ParseConfigOptionName(name);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "tag/Handler.hxx"
|
||||
#include "tag/Generic.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/StringBuffer.hxx"
|
||||
|
@ -97,7 +98,7 @@ try {
|
|||
}
|
||||
|
||||
decoder_name = argv[1];
|
||||
const Path path = Path::FromFS(argv[2]);
|
||||
const char *path = argv[2];
|
||||
|
||||
EventThread io_thread;
|
||||
io_thread.Start();
|
||||
|
@ -116,7 +117,7 @@ try {
|
|||
DumpTagHandler h;
|
||||
bool success;
|
||||
try {
|
||||
success = plugin->ScanFile(path, h);
|
||||
success = plugin->ScanFile(FromNarrowPath(path), h);
|
||||
} catch (...) {
|
||||
PrintException(std::current_exception());
|
||||
success = false;
|
||||
|
@ -126,7 +127,7 @@ try {
|
|||
InputStreamPtr is;
|
||||
|
||||
if (!success && plugin->scan_stream != nullptr) {
|
||||
is = InputStream::OpenReady(path.c_str(), mutex);
|
||||
is = InputStream::OpenReady(path, mutex);
|
||||
success = plugin->ScanStream(*is, h);
|
||||
}
|
||||
|
||||
|
@ -139,7 +140,7 @@ try {
|
|||
if (is)
|
||||
ScanGenericTags(*is, h);
|
||||
else
|
||||
ScanGenericTags(path, h);
|
||||
ScanGenericTags(FromNarrowPath(path), h);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "input/Init.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
#include "util/OptionDef.hxx"
|
||||
#include "util/OptionParser.hxx"
|
||||
|
@ -44,7 +45,7 @@ struct CommandLine {
|
|||
const char *decoder = nullptr;
|
||||
const char *uri = nullptr;
|
||||
|
||||
Path config_path = nullptr;
|
||||
FromNarrowPath config_path;
|
||||
|
||||
bool verbose = false;
|
||||
|
||||
|
@ -72,7 +73,7 @@ ParseCommandLine(int argc, char **argv)
|
|||
while (auto o = option_parser.Next()) {
|
||||
switch (Option(o.index)) {
|
||||
case OPTION_CONFIG:
|
||||
c.config_path = Path::FromFS(o.value);
|
||||
c.config_path = o.value;
|
||||
break;
|
||||
|
||||
case OPTION_VERBOSE:
|
||||
|
@ -205,7 +206,7 @@ try {
|
|||
MyDecoderClient client(c.seek_where);
|
||||
if (plugin->file_decode != nullptr) {
|
||||
try {
|
||||
plugin->FileDecode(client, Path::FromFS(c.uri));
|
||||
plugin->FileDecode(client, FromNarrowPath(c.uri));
|
||||
} catch (StopDecoder) {
|
||||
}
|
||||
} else if (plugin->stream_decode != nullptr) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "ConfigGlue.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "filter/LoadOne.hxx"
|
||||
#include "filter/Filter.hxx"
|
||||
#include "filter/Prepared.hxx"
|
||||
|
@ -123,7 +124,7 @@ try {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Path config_path = Path::FromFS(argv[1]);
|
||||
const FromNarrowPath config_path = argv[1];
|
||||
|
||||
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "fs/io/GunzipReader.hxx"
|
||||
#include "fs/io/FileReader.hxx"
|
||||
#include "fs/io/StdioOutputStream.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "util/PrintException.hxx"
|
||||
|
||||
#include <stdio.h>
|
||||
|
@ -62,7 +63,7 @@ try {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Path path = Path::FromFS(argv[1]);
|
||||
FromNarrowPath path = argv[1];
|
||||
|
||||
CopyGunzip(stdout, path);
|
||||
return EXIT_SUCCESS;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "Log.hxx"
|
||||
#include "LogBackend.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "fs/io/StdioOutputStream.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
@ -51,7 +52,7 @@
|
|||
struct CommandLine {
|
||||
const char *uri = nullptr;
|
||||
|
||||
Path config_path = nullptr;
|
||||
FromNarrowPath config_path;
|
||||
|
||||
bool verbose = false;
|
||||
|
||||
|
@ -79,7 +80,7 @@ ParseCommandLine(int argc, char **argv)
|
|||
while (auto o = option_parser.Next()) {
|
||||
switch (Option(o.index)) {
|
||||
case OPTION_CONFIG:
|
||||
c.config_path = Path::FromFS(o.value);
|
||||
c.config_path = o.value;
|
||||
break;
|
||||
|
||||
case OPTION_VERBOSE:
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "ConfigGlue.hxx"
|
||||
#include "event/Thread.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "pcm/AudioParser.hxx"
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
#include "util/StringBuffer.hxx"
|
||||
|
@ -111,7 +112,7 @@ try {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const Path config_path = Path::FromFS(argv[1]);
|
||||
const FromNarrowPath config_path = argv[1];
|
||||
|
||||
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "time/Convert.hxx"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
static constexpr time_t times[] = {
|
||||
1234567890,
|
||||
1580566807,
|
||||
1585750807,
|
||||
1590934807,
|
||||
};
|
||||
|
||||
TEST(Time, LocalTime)
|
||||
{
|
||||
/* convert back and forth using local time zone */
|
||||
|
||||
for (const auto t : times) {
|
||||
auto tp = std::chrono::system_clock::from_time_t(t);
|
||||
auto tm = LocalTime(tp);
|
||||
EXPECT_EQ(MakeTime(tm), tp);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Time, GmTime)
|
||||
{
|
||||
/* convert back and forth using UTC */
|
||||
|
||||
for (const auto t : times) {
|
||||
auto tp = std::chrono::system_clock::from_time_t(t);
|
||||
auto tm = GmTime(tp);
|
||||
EXPECT_EQ(std::chrono::system_clock::to_time_t(TimeGm(tm)),
|
||||
t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
test_time_sources = [
|
||||
'TestConvert.cxx',
|
||||
'TestISO8601.cxx',
|
||||
]
|
||||
|
||||
test(
|
||||
'TestTime',
|
||||
executable(
|
||||
'TestTime',
|
||||
test_time_sources,
|
||||
include_directories: inc,
|
||||
dependencies: [
|
||||
time_dep,
|
||||
gtest_dep,
|
||||
],
|
||||
),
|
||||
)
|
Loading…
Reference in New Issue