diff --git a/NEWS b/NEWS
index 5976541d8..ac9ca0788 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,8 @@ ver 0.21 (not yet released)
ver 0.20.11 (not yet released)
* storage
- curl: support Content-Type application/xml
+* decoder
+ - ffmpeg: more reliable song duration
ver 0.20.10 (2017/08/24)
* decoder
diff --git a/doc/user.xml b/doc/user.xml
index c5c7d341b..e010d5e3e 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -2991,6 +2991,60 @@ run
+
+ opus
+
+
+ Encodes into Ogg Opus.
+
+
+
+
+
+
+ Setting
+ Description
+
+
+
+
+
+ bitrate
+
+
+ Sets the data rate in bit per second. The special
+ value "auto" lets libopus
+ choose a rate (which is the default), and "max" uses
+ the maximum possible data rate.
+
+
+
+
+
+ complexity
+
+
+ Sets the Opus
+ complexity.
+
+
+
+
+
+ signal
+
+
+ Sets the Opus signal type. Valid values are "auto"
+ (the default), "voice" and "music".
+
+
+
+
+
+
+
vorbis
diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
index a035e058c..73d18e4ce 100644
--- a/src/decoder/plugins/FfmpegDecoderPlugin.cxx
+++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
@@ -712,7 +712,9 @@ FfmpegDecode(DecoderClient &client, InputStream &input,
#endif
const SignedSongTime total_time =
- FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base);
+ av_stream.duration != (int64_t)AV_NOPTS_VALUE
+ ? FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base)
+ : FromFfmpegTimeChecked(format_context.duration, AV_TIME_BASE_Q);
client.Ready(audio_format, input.IsSeekable(), total_time);
@@ -842,6 +844,10 @@ FfmpegScanStream(AVFormatContext &format_context,
tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(stream.duration,
stream.time_base));
+ else if (format_context.duration != (int64_t)AV_NOPTS_VALUE)
+ tag_handler_invoke_duration(handler, handler_ctx,
+ FromFfmpegTime(format_context.duration,
+ AV_TIME_BASE_Q));
FfmpegScanMetadata(format_context, audio_stream, handler, handler_ctx);
diff --git a/src/output/plugins/SndioOutputPlugin.cxx b/src/output/plugins/SndioOutputPlugin.cxx
index 1ae92f1bf..285882665 100644
--- a/src/output/plugins/SndioOutputPlugin.cxx
+++ b/src/output/plugins/SndioOutputPlugin.cxx
@@ -66,16 +66,14 @@ SndioOutput::Create(EventLoop &, const ConfigBlock &block) {
static bool
sndio_test_default_device()
{
- struct sio_hdl *sio_hdl;
-
- sio_hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
- if (!sio_hdl) {
+ auto *hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
+ if (!hdl) {
FormatError(sndio_output_domain,
- "Error opening default sndio device");
+ "Error opening default sndio device");
return false;
}
- sio_close(sio_hdl);
+ sio_close(hdl);
return true;
}
@@ -85,8 +83,8 @@ SndioOutput::Open(AudioFormat &audio_format)
struct sio_par par;
unsigned bits, rate, chans;
- sio_hdl = sio_open(device, SIO_PLAY, 0);
- if (!sio_hdl)
+ hdl = sio_open(device, SIO_PLAY, 0);
+ if (!hdl)
throw std::runtime_error("Failed to open default sndio device");
switch (audio_format.format) {
@@ -116,9 +114,9 @@ SndioOutput::Open(AudioFormat &audio_format)
par.le = SIO_LE_NATIVE;
par.appbufsz = rate * buffer_time / 1000;
- if (!sio_setpar(sio_hdl, &par) ||
- !sio_getpar(sio_hdl, &par)) {
- sio_close(sio_hdl);
+ if (!sio_setpar(hdl, &par) ||
+ !sio_getpar(hdl, &par)) {
+ sio_close(hdl);
throw std::runtime_error("Failed to set/get audio params");
}
@@ -128,21 +126,21 @@ SndioOutput::Open(AudioFormat &audio_format)
par.pchan != chans ||
par.sig != 1 ||
par.le != SIO_LE_NATIVE) {
- sio_close(sio_hdl);
+ sio_close(hdl);
throw std::runtime_error("Requested audio params cannot be satisfied");
}
// Set volume after opening fresh audio stream which does
// know nothing about previous audio streams.
- sio_setvol(sio_hdl, raw_volume);
+ sio_setvol(hdl, raw_volume);
// sio_onvol returns 0 if no volume knob is available.
// This is the case on raw audio devices rather than
// the sndiod audio server.
- if (sio_onvol(sio_hdl, VolumeCallback, this) == 0)
+ if (sio_onvol(hdl, VolumeCallback, this) == 0)
raw_volume = -1;
- if (!sio_start(sio_hdl)) {
- sio_close(sio_hdl);
+ if (!sio_start(hdl)) {
+ sio_close(hdl);
throw std::runtime_error("Failed to start audio device");
}
}
@@ -150,7 +148,7 @@ SndioOutput::Open(AudioFormat &audio_format)
void
SndioOutput::Close() noexcept
{
- sio_close(sio_hdl);
+ sio_close(hdl);
}
size_t
@@ -158,15 +156,16 @@ SndioOutput::Play(const void *chunk, size_t size)
{
size_t n;
- n = sio_write(sio_hdl, chunk, size);
- if (n == 0 && sio_eof(sio_hdl) != 0)
+ n = sio_write(hdl, chunk, size);
+ if (n == 0 && sio_eof(hdl) != 0)
throw std::runtime_error("sndio write failed");
return n;
}
void
-SndioOutput::SetVolume(unsigned int volume) {
- sio_setvol(sio_hdl, volume * SIO_MAXVOL / 100);
+SndioOutput::SetVolume(unsigned int volume)
+{
+ sio_setvol(hdl, volume * SIO_MAXVOL / 100);
}
static inline unsigned int
diff --git a/src/output/plugins/SndioOutputPlugin.hxx b/src/output/plugins/SndioOutputPlugin.hxx
index f98c07489..848918898 100644
--- a/src/output/plugins/SndioOutputPlugin.hxx
+++ b/src/output/plugins/SndioOutputPlugin.hxx
@@ -30,7 +30,7 @@ class SndioOutput final : AudioOutput {
MixerListener *listener = nullptr;
const char *const device;
const unsigned buffer_time; /* in ms */
- struct sio_hdl *sio_hdl;
+ struct sio_hdl *hdl;
int raw_volume;
public: