opus not seekable and no duration when using curl storage
Bug report
Describe the bug
When using the curl storage (browsing a local http apache server with DAV), it is impossible to seek opus files. There is also no duration.
Expected Behavior
opus files should be seekable and with a valid duration, like when using local or nfs storage.
Actual Behavior
opus files are not seekable.
Version
Own armv8 Android build from master tree (1ec283d)
Log
With nfs input:
03-26 22:03:39.836 26423 1742 D MPD : decoder_thread: probing plugin opus
03-26 22:03:39.885 26423 1742 D MPD : decoder: audio_format=48000:16:2, seekable=true
With curl input (WebDav):
03-26 22:06:11.023 26423 1764 D MPD : decoder_thread: probing plugin vorbis
03-26 22:06:11.024 26423 1764 D MPD : decoder_thread: probing plugin oggflac
03-26 22:06:11.024 26423 1764 D MPD : decoder_thread: probing plugin opus
03-26 22:06:11.024 26423 1764 D MPD : decoder: audio_format=48000:16:2, seekable=false
Same problem when forcing ffmpeg to handle http
03-26 22:22:28.422 2704 2744 D MPD : ffmpeg/http: Setting default whitelist 'http,https,tls,rtp,tcp,udp,crypto,httpproxy'
03-26 22:22:28.422 2704 2744 D MPD : ffmpeg/tcp: Original list of addresses:
03-26 22:22:28.423 2704 2744 D MPD : ffmpeg/tcp: Address 192.168.42.142 port 80
03-26 22:22:28.423 2704 2744 D MPD : ffmpeg/tcp: Interleaved list of addresses:
03-26 22:22:28.424 2704 2744 D MPD : ffmpeg/tcp: Address 192.168.42.142 port 80
03-26 22:22:28.425 2704 2744 D MPD : ffmpeg/tcp: Starting connection attempt to 192.168.42.142 port 80
03-26 22:22:28.446 2704 2744 D MPD : ffmpeg/tcp: Successfully connected to 192.168.42.142 port 80
03-26 22:22:28.446 2704 2744 D MPD : ffmpeg/http: request: GET /media/music/04-bydate/2020/02-February/Bad%20Breeding%20-%202019%20-%20Exiled/01%20-%20Exiled.opus HTTP/1.1
03-26 22:22:28.446 2704 2744 D MPD : User-Agent: Lavf/58.29.100
03-26 22:22:28.446 2704 2744 D MPD : Accept: */*
03-26 22:22:28.446 2704 2744 D MPD : Range: bytes=0-
03-26 22:22:28.446 2704 2744 D MPD : Connection: close
03-26 22:22:28.446 2704 2744 D MPD : Host: 192.168.42.142
03-26 22:22:28.446 2704 2744 D MPD : Icy-MetaData: 1
03-26 22:22:28.457 2704 2744 D MPD : ffmpeg/http: header='HTTP/1.1 206 Partial Content'
03-26 22:22:28.457 2704 2744 D MPD : ffmpeg/http: http_code=206
03-26 22:22:28.458 2704 2744 D MPD : ffmpeg/http: header='Date: Thu, 26 Mar 2020 21:22:30 GMT'
03-26 22:22:28.460 2704 2744 D MPD : ffmpeg/http: header='Server: Apache/2.4.38 (Raspbian)'
03-26 22:22:28.460 2704 2744 D MPD : ffmpeg/http: header='Last-Modified: Mon, 10 Feb 2020 11:28:17 GMT'
03-26 22:22:28.461 2704 2744 D MPD : ffmpeg/http: header='ETag: "3e4acc-59e3709270b9e"'
03-26 22:22:28.462 2704 2744 D MPD : ffmpeg/http: header='Accept-Ranges: bytes'
03-26 22:22:28.462 2704 2744 D MPD : ffmpeg/http: header='Content-Length: 4082380'
03-26 22:22:28.462 2704 2744 D MPD : ffmpeg/http: header='Content-Range: bytes 0-4082379/4082380'
03-26 22:22:28.463 2704 2744 D MPD : ffmpeg/http: header='Connection: close'
03-26 22:22:28.463 2704 2744 D MPD : ffmpeg/http: header='Content-Type: audio/ogg'
03-26 22:22:28.463 2704 2744 D MPD : ffmpeg/http: header=''
03-26 22:22:28.463 2704 2744 D MPD : decoder_thread: probing plugin opus
03-26 22:22:28.464 2704 2744 D MPD : decoder: audio_format=48000:16:2, seekable=false
No problems with mp3
03-26 22:25:26.068 2704 2732 D MPD : playlist: play 0:"04-bydate/2019/11-November/Gnod - 2017 - Just Say No to the Psycho Right-Wing Capitalist Fascist Industrial Death Machine/01 - Bodies For Money.mp3"
03-26 22:25:26.068 2704 2744 D MPD : ffmpeg/http: Setting default whitelist 'http,https,tls,rtp,tcp,udp,crypto,httpproxy'
03-26 22:25:26.068 2704 2744 D MPD : ffmpeg/tcp: Original list of addresses:
03-26 22:25:26.069 2704 2744 D MPD : ffmpeg/tcp: Address 192.168.42.142 port 80
03-26 22:25:26.069 2704 2744 D MPD : ffmpeg/tcp: Interleaved list of addresses:
03-26 22:25:26.069 2704 2744 D MPD : ffmpeg/tcp: Address 192.168.42.142 port 80
03-26 22:25:26.071 2704 2744 D MPD : ffmpeg/tcp: Starting connection attempt to 192.168.42.142 port 80
03-26 22:25:26.076 2704 2744 D MPD : ffmpeg/tcp: Successfully connected to 192.168.42.142 port 80
03-26 22:25:26.077 2704 2744 D MPD : ffmpeg/http: request: GET /media/music/04-bydate/2019/11-November/Gnod%20-%202017%20-%20Just%20Say%20No%20to%20the%20Psycho%20Right-Wing%20Capitalist%20Fascist%20Industrial%20Death%20Machine/01%20-%20Bodies%20For%20Money.mp3 HTTP/1.1
03-26 22:25:26.077 2704 2744 D MPD : User-Agent: Lavf/58.29.100
03-26 22:25:26.077 2704 2744 D MPD : Accept: */*
03-26 22:25:26.077 2704 2744 D MPD : Range: bytes=0-
03-26 22:25:26.077 2704 2744 D MPD : Connection: close
03-26 22:25:26.077 2704 2744 D MPD : Host: 192.168.42.142
03-26 22:25:26.077 2704 2744 D MPD : Icy-MetaData: 1
03-26 22:25:26.141 2704 2744 D MPD : ffmpeg/http: header='HTTP/1.1 206 Partial Content'
03-26 22:25:26.141 2704 2744 D MPD : ffmpeg/http: http_code=206
03-26 22:25:26.141 2704 2744 D MPD : ffmpeg/http: header='Date: Thu, 26 Mar 2020 21:25:28 GMT'
03-26 22:25:26.141 2704 2744 D MPD : ffmpeg/http: header='Server: Apache/2.4.38 (Raspbian)'
03-26 22:25:26.142 2704 2744 D MPD : ffmpeg/http: header='Last-Modified: Mon, 04 Nov 2019 15:47:58 GMT'
03-26 22:25:26.142 2704 2744 D MPD : ffmpeg/http: header='ETag: "b82ec9-596873d6a452f"'
03-26 22:25:26.142 2704 2744 D MPD : ffmpeg/http: header='Accept-Ranges: bytes'
03-26 22:25:26.142 2704 2744 D MPD : ffmpeg/http: header='Content-Length: 12070601'
03-26 22:25:26.143 2704 2744 D MPD : ffmpeg/http: header='Content-Range: bytes 0-12070600/12070601'
03-26 22:25:26.143 2704 2744 D MPD : ffmpeg/http: header='Connection: close'
03-26 22:25:26.143 2704 2744 D MPD : ffmpeg/http: header='Content-Type: audio/mpeg'
03-26 22:25:26.144 2704 2744 D MPD : ffmpeg/http: header=''
03-26 22:25:26.144 2704 2744 D MPD : decoder_thread: probing plugin ffmpeg
03-26 22:25:26.195 2704 2744 D MPD : ffmpeg/mp3: Format mp3 probed with size=131072 and score=51
03-26 22:25:26.196 2704 2744 D MPD : ffmpeg/mp3: pad 576 978
03-26 22:25:26.197 2704 2744 D MPD : ffmpeg/mp3: Skipping 0 bytes of junk at 63118.
03-26 22:25:26.197 2704 2744 D MPD : ffmpeg: detected input format 'mp3' ((null))
03-26 22:25:26.198 2704 2744 D MPD : ffmpeg/mp3: Before avformat_find_stream_info() pos: 63118 bytes read:131454 seeks:0 nb_streams:2
03-26 22:25:26.199 2704 2744 W MPD : ffmpeg/mp3float: Warning: not compiled with thread support, using thread emulation
03-26 22:25:26.201 2704 2744 D MPD : ffmpeg/mp3: demuxer injecting skip 1105 / discard 0
03-26 22:25:26.201 2704 2744 D MPD : ffmpeg/mp3float: skip 1105 / discard 0 samples due to side data
03-26 22:25:26.202 2704 2744 D MPD : ffmpeg/mp3float: skip 1105/1152 samples
03-26 22:25:26.255 2704 2744 D MPD : ffmpeg/mp3: max_analyze_duration 5000000 reached at 5015510 microseconds st:0
03-26 22:25:26.255 2704 2744 D MPD : ffmpeg/mp3: stream 0: start_time: 0.025 duration: 346.279
03-26 22:25:26.256 2704 2744 D MPD : ffmpeg/mp3: stream 1: start_time: 0.025 duration: 346.279
03-26 22:25:26.256 2704 2744 D MPD : ffmpeg/mp3: format: start_time: 0.025 duration: 346.279 bitrate=278 kb/s
03-26 22:25:26.256 2704 2744 W MPD : ffmpeg/mp3: Could not find codec parameters for stream 1 (Video: mjpeg, none): unspecified size
03-26 22:25:26.256 2704 2744 W MPD : Consider increasing the value for the 'analyzeduration' and 'probesize' options
03-26 22:25:26.256 2704 2744 D MPD : ffmpeg/mp3: After avformat_find_stream_info() pos: 263822 bytes read:264670 seeks:0 frames:195
03-26 22:25:26.257 2704 2744 D MPD : ffmpeg: codec 'mp3'
03-26 22:25:26.257 2704 2744 W MPD : ffmpeg/mp3float: Warning: not compiled with thread support, using thread emulation
03-26 22:25:26.257 2704 2744 D MPD : decoder: audio_format=44100:f:2, seekable=true
And no problem when using libopus from ffmpeg, with the following patch:
index 6c0753618..849604a34 100644
--- a/python/build/ffmpeg.py
+++ b/python/build/ffmpeg.py
@@ -51,6 +51,8 @@ class FfmpegProject(Project):
if toolchain.is_armv7:
configure.append('--cpu=cortex-a8')
- subprocess.check_call(configure, cwd=build, env=toolchain.env)
+ env = dict(toolchain.env)
+ env['PKG_CONFIG_LIBDIR'] = toolchain.install_prefix+'/lib/pkgconfig'
+ subprocess.check_call(configure, cwd=build, env=env)
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], cwd=build, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', 'install'], cwd=build, env=toolchain.env)
diff --git a/python/build/libs.py b/python/build/libs.py
index 7c317d46a..39ae78780 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -154,7 +154,6 @@ ffmpeg = FfmpegProject(
'--disable-parser=mlp',
'--disable-parser=mpeg4video',
'--disable-parser=mpegvideo',
- '--disable-parser=opus',
'--disable-parser=vc1',
'--disable-parser=vp3',
'--disable-parser=vp8',
@@ -203,8 +202,8 @@ ffmpeg = FfmpegProject(
# we don't need these decoders, because we have the dedicated
# libraries
+ '--enable-libopus',
'--disable-decoder=flac',
- '--disable-decoder=opus',
'--disable-decoder=vorbis',
# audio codecs nobody uses
diff --git a/src/decoder/DecoderList.cxx b/src/decoder/DecoderList.cxx
index b860d4c2d..15d8a24ec 100644
--- a/src/decoder/DecoderList.cxx
+++ b/src/decoder/DecoderList.cxx
@@ -59,6 +59,9 @@ const struct DecoderPlugin *const decoder_plugins[] = {
#ifdef ENABLE_MPG123
&mpg123_decoder_plugin,
#endif
+#ifdef ENABLE_FFMPEG
+ &ffmpeg_decoder_plugin,
+#endif
#ifdef ENABLE_VORBIS_DECODER
&vorbis_decoder_plugin,
#endif
@@ -107,9 +110,6 @@ const struct DecoderPlugin *const decoder_plugins[] = {
#ifdef ENABLE_ADPLUG
&adplug_decoder_plugin,
#endif
-#ifdef ENABLE_FFMPEG
- &ffmpeg_decoder_plugin,
-#endif
#ifdef ENABLE_GME
&gme_decoder_plugin,
#endif
But that is not a proper solution.
Here's the cause: https://github.com/MusicPlayerDaemon/MPD/blob/7d7bd51bc0a4748593e774decc91a1dbc939fd90/src/decoder/plugins/OggDecoder.cxx#L31-L35 Of course, you can let FFmpeg do it, but then you'll have a huge amount of overhead. Seeking (on the file level, not on the song level) is very expensive - MPD has to close the existing connection, discarding all pending data, open a new TCP connection, send a new HTTP request and so on, each time the decoder wants to seek.
Thanks for giving me the cause, I'll rework my WIP branch to just disable this check for my usecase (and disable ffmpeg for opus).
We have the same kind of check in VLC, we call that fastseek.
I see 2 solutions for this problems:
- Improve CheapSeeking() to detect local network (and returns true in that case). Ideally it could detect fast connections instead.
- Add a new config option that enables expensive seeks.
or third solution:
- download the complete file into the cache directory and continue streaming while doing so
In my context, I'm streaming from a Nextcloud instance via Webdav which is available on the local network. Previously I was using a davfs mount which itself automatically downloads the files before reaching them to whatever program opened it. But it could take up to 30 seconds to open a file because it doesn't start streaming the contents until the full file is downloaded (I believe).
Not sure what the "input_cache" option is about though ? https://www.musicpd.org/doc/html/user.html#configuring-the-input-cache
Thanks for giving me the cause, I'll rework my WIP branch to just disable this check for my usecase (and disable ffmpeg for opus).
We have the same kind of check in VLC, we call that fastseek.
I see 2 solutions for this problems:
* Improve CheapSeeking() to detect local network (and returns true in that case). Ideally it could detect fast connections instead. * Add a new config option that enables expensive seeks.
Flac files from webdav are also useekable in the current mpd version 0.23.12
Flac files from webdav are also useekable in the current mpd version 0.23.12
I cannot reproduce this, on the same exact version. For me, flac and mp3 files from webdav are perfectly seekable. Only ogg and opus files are unseekable.
Which brings me to my second (naive, and very possibly stupid) question to the
cause mentioned by @MaxKellermann : why is this behaviour different according to
the file type ? Or, why was the check only implemented in OggDecoder.cxx ?
If seeking around remote files is expensive, then the file type should not matter.
The fact that I can seek without issues around flac and mp3 files may be explained by the fact that by the time I seek, the file has been completely downloaded already (I have input_cache set up). But this would also be the case for the corresponding opus or ogg files (which are 5 times smaller than the corresponding flac).