MPD icon indicating copy to clipboard operation
MPD copied to clipboard

opus not seekable and no duration when using curl storage

Open tguillem opened this issue 5 years ago • 7 comments

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

tguillem avatar Mar 26 '20 21:03 tguillem

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

tguillem avatar Mar 26 '20 21:03 tguillem

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.

tguillem avatar Mar 26 '20 21:03 tguillem

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.

MaxKellermann avatar Apr 02 '20 13:04 MaxKellermann

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.

tguillem avatar Apr 02 '20 15:04 tguillem

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

PVince81 avatar Jan 08 '21 22:01 PVince81

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

Lodger-test avatar Mar 05 '23 17:03 Lodger-test

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).

aagon avatar Jan 13 '24 09:01 aagon