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:
|
packages:
|
||||||
- ccache
|
- ccache
|
||||||
- meson
|
- meson
|
||||||
|
- icu4c
|
||||||
|
- ffmpeg
|
||||||
|
- libnfs
|
||||||
|
- yajl
|
||||||
|
- libupnp
|
||||||
|
- libid3tag
|
||||||
|
- chromaprint
|
||||||
|
- libsamplerate
|
||||||
|
- libsoxr
|
||||||
|
- libzzip
|
||||||
|
- flac
|
||||||
|
- opus
|
||||||
|
- libvorbis
|
||||||
|
- faad2
|
||||||
|
- wavpack
|
||||||
|
- libmpdclient
|
||||||
update: true
|
update: true
|
||||||
env:
|
env:
|
||||||
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
|
- 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
|
* switch to C++17
|
||||||
- GCC 7 or clang 4 (or newer) recommended
|
- 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)
|
ver 0.21.21 (2020/03/19)
|
||||||
* configuration
|
* configuration
|
||||||
- fix bug in "metadata_to_use" setting
|
- fix bug in "metadata_to_use" setting
|
||||||
|
|
|
@ -2,18 +2,25 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.musicpd"
|
package="org.musicpd"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="44"
|
android:versionCode="45"
|
||||||
android:versionName="0.21.21">
|
android:versionName="0.21.22">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
|
<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.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application android:allowBackup="true"
|
<application android:allowBackup="true"
|
||||||
android:icon="@drawable/icon"
|
android:icon="@drawable/icon"
|
||||||
|
android:banner="@drawable/icon"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<activity android:name=".Settings"
|
<activity android:name=".Settings"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
|
@ -22,6 +29,14 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</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">
|
<receiver android:name=".Receiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.musicpd;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
@ -35,6 +36,9 @@ import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class Main extends Service implements Runnable {
|
public class Main extends Service implements Runnable {
|
||||||
private static final String TAG = "Main";
|
private static final String TAG = "Main";
|
||||||
private static final String REMOTE_ERROR = "MPD process was killed";
|
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);
|
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() {
|
private void start() {
|
||||||
if (mThread != null)
|
if (mThread != null)
|
||||||
return;
|
return;
|
||||||
mThread = new Thread(this);
|
|
||||||
mThread.start();
|
|
||||||
|
|
||||||
final Intent mainIntent = new Intent(this, Settings.class);
|
final Intent mainIntent = new Intent(this, Settings.class);
|
||||||
mainIntent.setAction("android.intent.action.MAIN");
|
mainIntent.setAction("android.intent.action.MAIN");
|
||||||
|
@ -168,13 +197,25 @@ public class Main extends Service implements Runnable {
|
||||||
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||||
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
mainIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
|
||||||
Notification notification = new Notification.Builder(this)
|
Notification.Builder nBuilder;
|
||||||
.setContentTitle(getText(R.string.notification_title_mpd_running))
|
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))
|
.setContentText(getText(R.string.notification_text_mpd_running))
|
||||||
.setSmallIcon(R.drawable.notification_icon)
|
.setSmallIcon(R.drawable.notification_icon)
|
||||||
.setContentIntent(contentIntent)
|
.setContentIntent(contentIntent)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
mThread = new Thread(this);
|
||||||
|
mThread.start();
|
||||||
|
|
||||||
startForeground(R.string.notification_title_mpd_running, notification);
|
startForeground(R.string.notification_title_mpd_running, notification);
|
||||||
startService(new Intent(this, Main.class));
|
startService(new Intent(this, Main.class));
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,12 +105,13 @@ public class Settings extends Activity {
|
||||||
else
|
else
|
||||||
mRunButton.setChecked(false);
|
mRunButton.setChecked(false);
|
||||||
mFirstRun = true;
|
mFirstRun = true;
|
||||||
|
mTextStatus.setText("");
|
||||||
break;
|
break;
|
||||||
case MSG_STARTED:
|
case MSG_STARTED:
|
||||||
Log.d(TAG, "onStarted");
|
Log.d(TAG, "onStarted");
|
||||||
mRunButton.setChecked(true);
|
mRunButton.setChecked(true);
|
||||||
mFirstRun = true;
|
mFirstRun = true;
|
||||||
mTextStatus.setText("CAUTION: this version is EXPERIMENTAL!"); // XXX
|
mTextStatus.setText("MPD service started");
|
||||||
break;
|
break;
|
||||||
case MSG_LOG:
|
case MSG_LOG:
|
||||||
if (mLogListArray.size() > MAX_LOGS)
|
if (mLogListArray.size() > MAX_LOGS)
|
||||||
|
|
|
@ -311,6 +311,7 @@ if not is_android
|
||||||
else
|
else
|
||||||
sources += [
|
sources += [
|
||||||
'src/android/Context.cxx',
|
'src/android/Context.cxx',
|
||||||
|
'src/android/AudioManager.cxx',
|
||||||
'src/android/Environment.cxx',
|
'src/android/Environment.cxx',
|
||||||
'src/android/LogListener.cxx',
|
'src/android/LogListener.cxx',
|
||||||
]
|
]
|
||||||
|
@ -332,6 +333,7 @@ subdir('src/util')
|
||||||
subdir('src/time')
|
subdir('src/time')
|
||||||
subdir('src/system')
|
subdir('src/system')
|
||||||
subdir('src/thread')
|
subdir('src/thread')
|
||||||
|
subdir('src/net')
|
||||||
subdir('src/event')
|
subdir('src/event')
|
||||||
|
|
||||||
subdir('src/lib/dbus')
|
subdir('src/lib/dbus')
|
||||||
|
@ -359,7 +361,6 @@ subdir('src/lib/crypto')
|
||||||
|
|
||||||
subdir('src/fs')
|
subdir('src/fs')
|
||||||
subdir('src/config')
|
subdir('src/config')
|
||||||
subdir('src/net')
|
|
||||||
subdir('src/tag')
|
subdir('src/tag')
|
||||||
subdir('src/pcm')
|
subdir('src/pcm')
|
||||||
subdir('src/neighbor')
|
subdir('src/neighbor')
|
||||||
|
|
|
@ -9,8 +9,8 @@ from build.ffmpeg import FfmpegProject
|
||||||
from build.boost import BoostProject
|
from build.boost import BoostProject
|
||||||
|
|
||||||
libmpdclient = MesonProject(
|
libmpdclient = MesonProject(
|
||||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.17.tar.xz',
|
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.18.tar.xz',
|
||||||
'ee9b8f1c7e95b65c8f18a354daf7b16bfcd455fc52a0f3b5abe402316bce3559',
|
'4cb01e1f567e0169aca94875fb6e1200e7f5ce35b63a4df768ec1591fb1081fa',
|
||||||
'lib/libmpdclient.a',
|
'lib/libmpdclient.a',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
|
||||||
)
|
)
|
||||||
|
|
||||||
curl = AutotoolsProject(
|
curl = AutotoolsProject(
|
||||||
'http://curl.haxx.se/download/curl-7.68.0.tar.xz',
|
'http://curl.haxx.se/download/curl-7.69.1.tar.xz',
|
||||||
'b724240722276a27f6e770b952121a3afd097129d8c9fe18e6272dc34192035a',
|
'03c7d5e6697f7b7e40ada1b2256e565a555657398e6c1fcfa4cb251ccd819d4f',
|
||||||
'lib/libcurl.a',
|
'lib/libcurl.a',
|
||||||
[
|
[
|
||||||
'--disable-shared', '--enable-static',
|
'--disable-shared', '--enable-static',
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "playlist/PlaylistRegistry.hxx"
|
#include "playlist/PlaylistRegistry.hxx"
|
||||||
#include "playlist/PlaylistPlugin.hxx"
|
#include "playlist/PlaylistPlugin.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
#include "fs/StandardDirectory.hxx"
|
#include "fs/StandardDirectory.hxx"
|
||||||
|
@ -378,17 +379,7 @@ ParseCommandLine(int argc, char **argv, struct options &options,
|
||||||
|
|
||||||
if (config_file != nullptr) {
|
if (config_file != nullptr) {
|
||||||
/* use specified configuration file */
|
/* use specified configuration file */
|
||||||
#ifdef _UNICODE
|
ReadConfigFile(config, FromNarrowPath(config_file));
|
||||||
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
|
|
||||||
return;
|
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/Class.hxx"
|
||||||
#include "java/Exception.hxx"
|
#include "java/Exception.hxx"
|
||||||
#include "java/File.hxx"
|
#include "java/File.hxx"
|
||||||
|
#include "java/String.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
|
|
||||||
|
#include "AudioManager.hxx"
|
||||||
|
|
||||||
AllocatedPath
|
AllocatedPath
|
||||||
Context::GetCacheDir(JNIEnv *env) const noexcept
|
Context::GetCacheDir(JNIEnv *env) const noexcept
|
||||||
{
|
{
|
||||||
|
@ -39,3 +42,21 @@ Context::GetCacheDir(JNIEnv *env) const noexcept
|
||||||
|
|
||||||
return Java::File::ToAbsolutePath(env, file);
|
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"
|
#include "java/Object.hxx"
|
||||||
|
|
||||||
class AllocatedPath;
|
class AllocatedPath;
|
||||||
|
class AudioManager;
|
||||||
|
|
||||||
class Context : public Java::GlobalObject {
|
class Context : public Java::GlobalObject {
|
||||||
public:
|
public:
|
||||||
|
@ -31,6 +32,9 @@ public:
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
|
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
AudioManager *GetAudioManager(JNIEnv *env) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "util/Alloc.hxx"
|
#include "util/Alloc.hxx"
|
||||||
#include "util/DeleteDisposer.hxx"
|
#include "util/DeleteDisposer.hxx"
|
||||||
|
#include "util/StringCompare.hxx"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -70,7 +71,15 @@ Directory::GetName() const noexcept
|
||||||
{
|
{
|
||||||
assert(!IsRoot());
|
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 *
|
Directory *
|
||||||
|
|
|
@ -287,7 +287,7 @@ FfmpegReceiveFrames(DecoderClient &client, InputStream &is,
|
||||||
*/
|
*/
|
||||||
static DecoderCommand
|
static DecoderCommand
|
||||||
ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
||||||
AVPacket &&packet,
|
const AVPacket &packet,
|
||||||
AVCodecContext &codec_context,
|
AVCodecContext &codec_context,
|
||||||
const AVStream &stream,
|
const AVStream &stream,
|
||||||
AVFrame &frame,
|
AVFrame &frame,
|
||||||
|
@ -340,24 +340,6 @@ ffmpeg_send_packet(DecoderClient &client, InputStream &is,
|
||||||
return cmd;
|
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
|
gcc_const
|
||||||
static SampleFormat
|
static SampleFormat
|
||||||
ffmpeg_sample_format(enum AVSampleFormat sample_fmt) noexcept
|
ffmpeg_sample_format(enum AVSampleFormat sample_fmt) noexcept
|
||||||
|
|
|
@ -26,6 +26,7 @@ event_dep = declare_dependency(
|
||||||
link_with: event,
|
link_with: event,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
thread_dep,
|
thread_dep,
|
||||||
|
net_dep,
|
||||||
system_dep,
|
system_dep,
|
||||||
boost_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"
|
#include "Path.hxx"
|
||||||
|
|
||||||
#ifdef _UNICODE
|
#ifdef _UNICODE
|
||||||
#include "lib/icu/Win32.hxx"
|
#include "AllocatedPath.hxx"
|
||||||
#include "util/AllocatedString.hxx"
|
#include "util/AllocatedString.hxx"
|
||||||
#include <windows.h>
|
|
||||||
#else
|
#else
|
||||||
#include "util/StringPointer.hxx"
|
#include "util/StringPointer.hxx"
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,12 +46,7 @@ class NarrowPath {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#ifdef _UNICODE
|
#ifdef _UNICODE
|
||||||
explicit NarrowPath(Path _path)
|
explicit NarrowPath(Path _path) noexcept;
|
||||||
:value(WideCharToMultiByte(CP_ACP, _path.c_str())) {
|
|
||||||
if (value.IsNull())
|
|
||||||
/* fall back to empty string */
|
|
||||||
value = Value::Empty();
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
explicit NarrowPath(Path _path):value(_path.c_str()) {}
|
explicit NarrowPath(Path _path):value(_path.c_str()) {}
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
|
@ -50,7 +50,7 @@ class BufferedReader {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BufferedReader(Reader &_reader) noexcept
|
explicit BufferedReader(Reader &_reader) noexcept
|
||||||
:reader(_reader), buffer(4096) {}
|
:reader(_reader), buffer(16384) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the internal state. Should be called after rewinding
|
* Reset the internal state. Should be called after rewinding
|
||||||
|
|
|
@ -45,7 +45,7 @@ class GunzipReader final : public Reader {
|
||||||
|
|
||||||
z_stream z;
|
z_stream z;
|
||||||
|
|
||||||
StaticFifoBuffer<Bytef, 4096> buffer;
|
StaticFifoBuffer<Bytef, 65536> buffer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -62,7 +62,7 @@ GzipOutputStream::Flush()
|
||||||
z.avail_in = 0;
|
z.avail_in = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
Bytef output[4096];
|
Bytef output[16384];
|
||||||
z.next_out = output;
|
z.next_out = output;
|
||||||
z.avail_out = sizeof(output);
|
z.avail_out = sizeof(output);
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ GzipOutputStream::Write(const void *_data, size_t size)
|
||||||
z.avail_in = size;
|
z.avail_in = size;
|
||||||
|
|
||||||
while (z.avail_in > 0) {
|
while (z.avail_in > 0) {
|
||||||
Bytef output[4096];
|
Bytef output[16384];
|
||||||
z.next_out = output;
|
z.next_out = output;
|
||||||
z.avail_out = sizeof(output);
|
z.avail_out = sizeof(output);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ fs_sources = [
|
||||||
'Path.cxx',
|
'Path.cxx',
|
||||||
'Path2.cxx',
|
'Path2.cxx',
|
||||||
'AllocatedPath.cxx',
|
'AllocatedPath.cxx',
|
||||||
|
'NarrowPath.cxx',
|
||||||
'FileSystem.cxx',
|
'FileSystem.cxx',
|
||||||
'List.cxx',
|
'List.cxx',
|
||||||
'StandardDirectory.cxx',
|
'StandardDirectory.cxx',
|
||||||
|
|
|
@ -56,7 +56,9 @@ CurlRequest::CurlRequest(CurlGlobal &_global,
|
||||||
easy.SetUserAgent("Music Player Daemon " VERSION);
|
easy.SetUserAgent("Music Player Daemon " VERSION);
|
||||||
easy.SetHeaderFunction(_HeaderFunction, this);
|
easy.SetHeaderFunction(_HeaderFunction, this);
|
||||||
easy.SetWriteFunction(WriteFunction, this);
|
easy.SetWriteFunction(WriteFunction, this);
|
||||||
|
#ifndef ANDROID
|
||||||
easy.SetOption(CURLOPT_NETRC, 1L);
|
easy.SetOption(CURLOPT_NETRC, 1L);
|
||||||
|
#endif
|
||||||
easy.SetErrorBuffer(error_buffer);
|
easy.SetErrorBuffer(error_buffer);
|
||||||
easy.SetNoProgress();
|
easy.SetNoProgress();
|
||||||
easy.SetNoSignal();
|
easy.SetNoSignal();
|
||||||
|
|
|
@ -29,6 +29,7 @@ struct MixerPlugin;
|
||||||
|
|
||||||
extern const MixerPlugin null_mixer_plugin;
|
extern const MixerPlugin null_mixer_plugin;
|
||||||
extern const MixerPlugin software_mixer_plugin;
|
extern const MixerPlugin software_mixer_plugin;
|
||||||
|
extern const MixerPlugin android_mixer_plugin;
|
||||||
extern const MixerPlugin alsa_mixer_plugin;
|
extern const MixerPlugin alsa_mixer_plugin;
|
||||||
extern const MixerPlugin haiku_mixer_plugin;
|
extern const MixerPlugin haiku_mixer_plugin;
|
||||||
extern const MixerPlugin oss_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'
|
mixer_plugins_sources += 'WinmmMixerPlugin.cxx'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if is_android
|
||||||
|
mixer_plugins_sources += 'AndroidMixerPlugin.cxx'
|
||||||
|
endif
|
||||||
|
|
||||||
mixer_plugins = static_library(
|
mixer_plugins = static_library(
|
||||||
'mixer_plugins',
|
'mixer_plugins',
|
||||||
mixer_plugins_sources,
|
mixer_plugins_sources,
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "thread/Cond.hxx"
|
#include "thread/Cond.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "util/ByteOrder.hxx"
|
#include "util/ByteOrder.hxx"
|
||||||
|
#include "mixer/MixerList.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <SLES/OpenSLES.h>
|
#include <SLES/OpenSLES.h>
|
||||||
|
@ -412,5 +413,5 @@ const struct AudioOutputPlugin sles_output_plugin = {
|
||||||
"sles",
|
"sles",
|
||||||
sles_test_default_device,
|
sles_test_default_device,
|
||||||
SlesOutput::Create,
|
SlesOutput::Create,
|
||||||
nullptr,
|
&android_mixer_plugin,
|
||||||
};
|
};
|
||||||
|
|
|
@ -160,6 +160,7 @@ static const char *const rss_suffixes[] = {
|
||||||
|
|
||||||
static const char *const rss_mime_types[] = {
|
static const char *const rss_mime_types[] = {
|
||||||
"application/rss+xml",
|
"application/rss+xml",
|
||||||
|
"application/xml",
|
||||||
"text/xml",
|
"text/xml",
|
||||||
nullptr
|
nullptr
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
Mutex tag_pool_lock;
|
Mutex tag_pool_lock;
|
||||||
|
|
||||||
static constexpr size_t NUM_SLOTS = 4093;
|
static constexpr size_t NUM_SLOTS = 16127;
|
||||||
|
|
||||||
struct TagPoolSlot {
|
struct TagPoolSlot {
|
||||||
TagPoolSlot *next;
|
TagPoolSlot *next;
|
||||||
|
|
|
@ -77,15 +77,15 @@ static time_t
|
||||||
GetTimeZoneOffset() noexcept
|
GetTimeZoneOffset() noexcept
|
||||||
{
|
{
|
||||||
time_t t = 1234567890;
|
time_t t = 1234567890;
|
||||||
struct tm tm;
|
|
||||||
tm.tm_isdst = 0;
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
struct tm *p = gmtime(&t);
|
struct tm *p = gmtime(&t);
|
||||||
#else
|
#else
|
||||||
|
struct tm tm;
|
||||||
|
tm.tm_isdst = 0;
|
||||||
struct tm *p = &tm;
|
struct tm *p = &tm;
|
||||||
gmtime_r(&t, p);
|
gmtime_r(&t, p);
|
||||||
#endif
|
#endif
|
||||||
return t - mktime(&tm);
|
return t - mktime(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* !__GLIBC__ */
|
#endif /* !__GLIBC__ */
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "decoder/DecoderList.hxx"
|
#include "decoder/DecoderList.hxx"
|
||||||
#include "decoder/DecoderPlugin.hxx"
|
#include "decoder/DecoderPlugin.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "fs/io/StdioOutputStream.hxx"
|
#include "fs/io/StdioOutputStream.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "fs/io/BufferedOutputStream.hxx"
|
||||||
#include "util/PrintException.hxx"
|
#include "util/PrintException.hxx"
|
||||||
|
@ -63,7 +64,7 @@ try {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path path = Path::FromFS(argv[1]);
|
const FromNarrowPath path = argv[1];
|
||||||
|
|
||||||
const ScopeDecoderPluginsInit decoder_plugins_init({});
|
const ScopeDecoderPluginsInit decoder_plugins_init({});
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "ConfigGlue.hxx"
|
#include "ConfigGlue.hxx"
|
||||||
#include "tag/Config.hxx"
|
#include "tag/Config.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "event/Thread.hxx"
|
#include "event/Thread.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
#include "util/PrintException.hxx"
|
#include "util/PrintException.hxx"
|
||||||
|
@ -106,7 +107,7 @@ try {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path config_path = Path::FromFS(argv[1]);
|
const FromNarrowPath config_path = argv[1];
|
||||||
const char *const plugin_name = argv[2];
|
const char *const plugin_name = argv[2];
|
||||||
|
|
||||||
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
|
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "tag/ApeLoader.hxx"
|
#include "tag/ApeLoader.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "input/LocalOpen.hxx"
|
#include "input/LocalOpen.hxx"
|
||||||
#include "util/StringView.hxx"
|
#include "util/StringView.hxx"
|
||||||
|
@ -58,7 +59,7 @@ try {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path path = Path::FromFS(argv[1]);
|
const FromNarrowPath path = argv[1];
|
||||||
|
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,8 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
ShutdownHandler::ShutdownHandler(EventLoop &loop) {}
|
inline ShutdownHandler::ShutdownHandler(EventLoop &) {}
|
||||||
ShutdownHandler::~ShutdownHandler() {}
|
inline ShutdownHandler::~ShutdownHandler() {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fs/io/FileOutputStream.hxx"
|
#include "fs/io/FileOutputStream.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "util/PrintException.hxx"
|
#include "util/PrintException.hxx"
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
@ -55,7 +56,7 @@ try {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path path = Path::FromFS(argv[1]);
|
const FromNarrowPath path = argv[1];
|
||||||
|
|
||||||
FileOutputStream fos(path);
|
FileOutputStream fos(path);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "playlist/PlaylistRegistry.hxx"
|
#include "playlist/PlaylistRegistry.hxx"
|
||||||
#include "playlist/PlaylistPlugin.hxx"
|
#include "playlist/PlaylistPlugin.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "fs/io/BufferedOutputStream.hxx"
|
||||||
#include "fs/io/StdioOutputStream.hxx"
|
#include "fs/io/StdioOutputStream.hxx"
|
||||||
#include "thread/Cond.hxx"
|
#include "thread/Cond.hxx"
|
||||||
|
@ -54,7 +55,7 @@ try {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path config_path = Path::FromFS(argv[1]);
|
const FromNarrowPath config_path = argv[1];
|
||||||
uri = argv[2];
|
uri = argv[2];
|
||||||
|
|
||||||
/* initialize MPD */
|
/* initialize MPD */
|
||||||
|
|
|
@ -24,6 +24,7 @@ gtest_dep = declare_dependency(
|
||||||
)
|
)
|
||||||
|
|
||||||
subdir('net')
|
subdir('net')
|
||||||
|
subdir('time')
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'read_conf',
|
'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(
|
test('TestRewindInputStream', executable(
|
||||||
'TestRewindInputStream',
|
'TestRewindInputStream',
|
||||||
'TestRewindInputStream.cxx',
|
'TestRewindInputStream.cxx',
|
||||||
|
@ -326,6 +314,11 @@ if curl_dep.found()
|
||||||
include_directories: inc,
|
include_directories: inc,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
curl_dep,
|
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
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
|
@ -34,13 +34,22 @@
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static std::string
|
static std::string
|
||||||
ToString(const struct in_addr &a)
|
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];
|
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)
|
if (result == nullptr)
|
||||||
throw std::runtime_error("inet_ntop() failed");
|
throw std::runtime_error("inet_ntop() failed");
|
||||||
return result;
|
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
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
|
@ -34,13 +34,22 @@
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static std::string
|
static std::string
|
||||||
ToString(const struct in6_addr &a)
|
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];
|
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)
|
if (result == nullptr)
|
||||||
throw std::runtime_error("inet_ntop() failed");
|
throw std::runtime_error("inet_ntop() failed");
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "config/Param.hxx"
|
#include "config/Param.hxx"
|
||||||
#include "config/File.hxx"
|
#include "config/File.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "util/PrintException.hxx"
|
#include "util/PrintException.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ try {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path config_path = Path::FromFS(argv[1]);
|
const FromNarrowPath config_path = argv[1];
|
||||||
const char *name = argv[2];
|
const char *name = argv[2];
|
||||||
|
|
||||||
const auto option = ParseConfigOptionName(name);
|
const auto option = ParseConfigOptionName(name);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "tag/Handler.hxx"
|
#include "tag/Handler.hxx"
|
||||||
#include "tag/Generic.hxx"
|
#include "tag/Generic.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "pcm/AudioFormat.hxx"
|
#include "pcm/AudioFormat.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
#include "util/StringBuffer.hxx"
|
#include "util/StringBuffer.hxx"
|
||||||
|
@ -97,7 +98,7 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder_name = argv[1];
|
decoder_name = argv[1];
|
||||||
const Path path = Path::FromFS(argv[2]);
|
const char *path = argv[2];
|
||||||
|
|
||||||
EventThread io_thread;
|
EventThread io_thread;
|
||||||
io_thread.Start();
|
io_thread.Start();
|
||||||
|
@ -116,7 +117,7 @@ try {
|
||||||
DumpTagHandler h;
|
DumpTagHandler h;
|
||||||
bool success;
|
bool success;
|
||||||
try {
|
try {
|
||||||
success = plugin->ScanFile(path, h);
|
success = plugin->ScanFile(FromNarrowPath(path), h);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
PrintException(std::current_exception());
|
PrintException(std::current_exception());
|
||||||
success = false;
|
success = false;
|
||||||
|
@ -126,7 +127,7 @@ try {
|
||||||
InputStreamPtr is;
|
InputStreamPtr is;
|
||||||
|
|
||||||
if (!success && plugin->scan_stream != nullptr) {
|
if (!success && plugin->scan_stream != nullptr) {
|
||||||
is = InputStream::OpenReady(path.c_str(), mutex);
|
is = InputStream::OpenReady(path, mutex);
|
||||||
success = plugin->ScanStream(*is, h);
|
success = plugin->ScanStream(*is, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +140,7 @@ try {
|
||||||
if (is)
|
if (is)
|
||||||
ScanGenericTags(*is, h);
|
ScanGenericTags(*is, h);
|
||||||
else
|
else
|
||||||
ScanGenericTags(path, h);
|
ScanGenericTags(FromNarrowPath(path), h);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "input/Init.hxx"
|
#include "input/Init.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "pcm/AudioFormat.hxx"
|
#include "pcm/AudioFormat.hxx"
|
||||||
#include "util/OptionDef.hxx"
|
#include "util/OptionDef.hxx"
|
||||||
#include "util/OptionParser.hxx"
|
#include "util/OptionParser.hxx"
|
||||||
|
@ -44,7 +45,7 @@ struct CommandLine {
|
||||||
const char *decoder = nullptr;
|
const char *decoder = nullptr;
|
||||||
const char *uri = nullptr;
|
const char *uri = nullptr;
|
||||||
|
|
||||||
Path config_path = nullptr;
|
FromNarrowPath config_path;
|
||||||
|
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ ParseCommandLine(int argc, char **argv)
|
||||||
while (auto o = option_parser.Next()) {
|
while (auto o = option_parser.Next()) {
|
||||||
switch (Option(o.index)) {
|
switch (Option(o.index)) {
|
||||||
case OPTION_CONFIG:
|
case OPTION_CONFIG:
|
||||||
c.config_path = Path::FromFS(o.value);
|
c.config_path = o.value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPTION_VERBOSE:
|
case OPTION_VERBOSE:
|
||||||
|
@ -205,7 +206,7 @@ try {
|
||||||
MyDecoderClient client(c.seek_where);
|
MyDecoderClient client(c.seek_where);
|
||||||
if (plugin->file_decode != nullptr) {
|
if (plugin->file_decode != nullptr) {
|
||||||
try {
|
try {
|
||||||
plugin->FileDecode(client, Path::FromFS(c.uri));
|
plugin->FileDecode(client, FromNarrowPath(c.uri));
|
||||||
} catch (StopDecoder) {
|
} catch (StopDecoder) {
|
||||||
}
|
}
|
||||||
} else if (plugin->stream_decode != nullptr) {
|
} else if (plugin->stream_decode != nullptr) {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "ConfigGlue.hxx"
|
#include "ConfigGlue.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "filter/LoadOne.hxx"
|
#include "filter/LoadOne.hxx"
|
||||||
#include "filter/Filter.hxx"
|
#include "filter/Filter.hxx"
|
||||||
#include "filter/Prepared.hxx"
|
#include "filter/Prepared.hxx"
|
||||||
|
@ -123,7 +124,7 @@ try {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path config_path = Path::FromFS(argv[1]);
|
const FromNarrowPath config_path = argv[1];
|
||||||
|
|
||||||
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "fs/io/GunzipReader.hxx"
|
#include "fs/io/GunzipReader.hxx"
|
||||||
#include "fs/io/FileReader.hxx"
|
#include "fs/io/FileReader.hxx"
|
||||||
#include "fs/io/StdioOutputStream.hxx"
|
#include "fs/io/StdioOutputStream.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "util/PrintException.hxx"
|
#include "util/PrintException.hxx"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -62,7 +63,7 @@ try {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path path = Path::FromFS(argv[1]);
|
FromNarrowPath path = argv[1];
|
||||||
|
|
||||||
CopyGunzip(stdout, path);
|
CopyGunzip(stdout, path);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
#include "LogBackend.hxx"
|
#include "LogBackend.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "fs/io/BufferedOutputStream.hxx"
|
||||||
#include "fs/io/StdioOutputStream.hxx"
|
#include "fs/io/StdioOutputStream.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
@ -51,7 +52,7 @@
|
||||||
struct CommandLine {
|
struct CommandLine {
|
||||||
const char *uri = nullptr;
|
const char *uri = nullptr;
|
||||||
|
|
||||||
Path config_path = nullptr;
|
FromNarrowPath config_path;
|
||||||
|
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
||||||
|
@ -79,7 +80,7 @@ ParseCommandLine(int argc, char **argv)
|
||||||
while (auto o = option_parser.Next()) {
|
while (auto o = option_parser.Next()) {
|
||||||
switch (Option(o.index)) {
|
switch (Option(o.index)) {
|
||||||
case OPTION_CONFIG:
|
case OPTION_CONFIG:
|
||||||
c.config_path = Path::FromFS(o.value);
|
c.config_path = o.value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPTION_VERBOSE:
|
case OPTION_VERBOSE:
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "ConfigGlue.hxx"
|
#include "ConfigGlue.hxx"
|
||||||
#include "event/Thread.hxx"
|
#include "event/Thread.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
#include "fs/NarrowPath.hxx"
|
||||||
#include "pcm/AudioParser.hxx"
|
#include "pcm/AudioParser.hxx"
|
||||||
#include "pcm/AudioFormat.hxx"
|
#include "pcm/AudioFormat.hxx"
|
||||||
#include "util/StringBuffer.hxx"
|
#include "util/StringBuffer.hxx"
|
||||||
|
@ -111,7 +112,7 @@ try {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Path config_path = Path::FromFS(argv[1]);
|
const FromNarrowPath config_path = argv[1];
|
||||||
|
|
||||||
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
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