MusicPlayerDaemon / MPD

Music Player Daemon
https://www.musicpd.org/
GNU General Public License v2.0
2.18k stars 352 forks source link

MPD not reading metadata when playing DSF streams #993

Open patrickkfkan opened 4 years ago

patrickkfkan commented 4 years ago

Bug report

Describe the bug

When playing a DSF stream, MPD does not read metadata from the stream. Clients therefore do not show title, album, artist, etc. Only the URI of the stream is shown.

Expected Behavior

MPD reads metadata from DSF stream and makes it available to clients.

Actual Behavior

Only the URI of the stream is returned to clients.

Version

Music Player Daemon 0.22.2 (0.22.2)
Copyright 2003-2007 Warren Dukes <warren.dukes@gmail.com>
Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>
This is free software; see the source for copying conditions.  There is NO
warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Database plugins:
 simple proxy upnp

Storage plugins:
 local udisks nfs curl

Neighbor plugins:
 upnp udisks

Decoders plugins:
 [mad] mp3 mp2
 [mpg123] mp3
 [vorbis] ogg oga
 [oggflac] ogg oga
 [flac] flac
 [opus] opus ogg oga
 [sndfile] wav aiff aif au snd paf iff svx sf voc w64 pvf xi htk caf sd2
 [audiofile] wav au aiff aif
 [dsdiff] dff
 [dsf] dsf
 [hybrid_dsd] m4a
 [faad] aac
 [mpcdec] mpc
 [wavpack] wv
 [modplug] 669 amf ams dbm dfm dsm far it med mdl mod mtm mt2 okt s3m stm ult umx xm
 [mikmod] amf dsm far gdm imf it med mod mtm s3m stm stx ult uni xm
 [sidplay] sid mus str prg P00
 [wildmidi] mid
 [fluidsynth] mid
 [adplug] amd d00 hsc laa rad raw sa2
 [ffmpeg] 16sv 3g2 3gp 4xm 8svx aa3 aac ac3 adx afc aif aifc aiff al alaw amr anim apc ape asf atrac au aud avi avm2 avs bap bfi c93 cak cin cmv cpk daud dct divx dts dv dvd dxa eac3 film flac flc fli fll flx flv g726 gsm gxf iss m1v m2v m2t m2ts m4a m4b m4v mad mj2 mjpeg mjpg mka mkv mlp mm mmf mov mp+ mp1 mp2 mp3 mp4 mpc mpeg mpg mpga mpp mpu mve mvi mxf nc nsv nut nuv oga ogm ogv ogx oma ogg omg opus psp pva qcp qt r3d ra ram rl2 rm rmvb roq rpl rvc shn smk snd sol son spx str swf tak tgi tgq tgv thp ts tsp tta xa xvid uv uv2 vb vid vob voc vp6 vmd wav webm wma wmv wsaud wsvga wv wve rtp:// rtsp:// rtsps://
 [gme] ay gbs gym hes kss nsf nsfe sap spc vgm vgz
 [pcm]

Filters:
 libsamplerate soxr

Tag plugins:
 id3tag

Output plugins:
 shout null fifo sndio pipe alsa ao openal pulse jack httpd recorder

Encoder plugins:
 null vorbis opus lame twolame wave flac shine

Archive plugins:
 [bz2] bz2
 [zzip] zip
 [iso] iso

Input plugins:
 file archive alsa tidal qobuz curl ffmpeg nfs mms cdio_paranoia

Playlist plugins:
 extm3u m3u pls xspf asx rss soundcloud flac cue embcue

Protocols:
 file:// alsa:// cdda:// ftp:// ftps:// gopher:// hls+http:// hls+https:// http:// https:// mms:// mmsh:// mmst:// mmsu:// nfs:// qobuz:// rtmp:// rtmpe:// rtmps:// rtmpt:// rtmpte:// rtmpts:// rtp:// rtsp:// rtsps:// smb:// srtp:// tidal://

Other features:
 avahi dbus udisks epoll icu inotify ipv6 systemd tcp un

How to reproduce

Following example uses mpc 0.33 as the MPD client. DSF sample was downloaded from http://www.2l.no/hires/ (Hoff: Innocence - Stereo DSD 64 2.8224Mbit/s) and streamed from a Jellyfin server.

$ mpc add "http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true"

$ mpc play
http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true
[playing] #1/1   0:00/0:00 (0%)
volume:100%   repeat: off   random: off   single: off   consume: off

$ mpc
http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true
[playing] #1/1   0:01/5:07 (0%)
volume:100%   repeat: off   random: off   single: off   consume: off

Log

Nov 01 08:51 : avahi: Initializing interface
Nov 01 08:51 : avahi: Client changed to state 2
Nov 01 08:51 : avahi: Client is RUNNING
Nov 01 08:51 : avahi: Registering service _mpd._tcp/Music Player @ atsd3554
Nov 01 08:51 : avahi: Service group changed to state 0
Nov 01 08:51 : avahi: Service group is UNCOMMITED
Nov 01 08:51 : update: spawned thread for update job id 1
Nov 01 08:51 : state_file: Loading state file /var/lib/mpd/state
Nov 01 08:51 : avahi: Service group changed to state 1
Nov 01 08:51 : avahi: Service group is REGISTERING
Nov 01 08:51 : exception: RTIOThread could not get realtime scheduling, continuing anyway: sched_setscheduler failed: Operation not permitted
Nov 01 08:51 : update: starting
Nov 01 08:51 : simple_db: removing empty directories from DB
Nov 01 08:51 : simple_db: sorting DB
Nov 01 08:51 : simple_db: writing DB
Nov 01 08:51 : update: finished
Nov 01 08:51 : avahi: Service group changed to state 2
Nov 01 08:51 : avahi: Service 'Music Player @ atsd3554' successfully established.
Nov 01 08:52 : client: [0] opened from local
Nov 01 08:52 : client: [0] process command list
Nov 01 08:52 : client: process command "add "http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true""
Nov 01 08:52 : client: command returned 0
Nov 01 08:52 : client: [0] process command list returned 0
Nov 01 08:52 : client: [0] closed
Nov 01 08:52 : client: [1] opened from local
Nov 01 08:52 : client: [1] process command "play"
Nov 01 08:52 : playlist: play 0:"http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true"
Nov 01 08:52 : client: [1] command returned 0
Nov 01 08:52 : client: [1] process command list
Nov 01 08:52 : client: process command "status"
ALSA lib conf.c:3725:(snd_config_hooks_call) Cannot open shared library libasound_module_conf_pulse.so (/opt/mpd-0.22.2/lib/alsa-lib/libasound_module_conf_pulse.so: cannot open shared object file: No such file or directory)
ALSA lib control.c:1471:(snd_ctl_open_noupdate) Invalid CTL default
Nov 01 08:52 : exception: Failed to read mixer for 'My ALSA Device': failed to attach to default: No such file or directory
Nov 01 08:52 : client: command returned 0
Nov 01 08:52 : client: process command "currentsong"
Nov 01 08:52 : client: command returned 0
Nov 01 08:52 : client: [1] process command list returned 0
Nov 01 08:52 : client: [1] closed
Nov 01 08:52 : decoder_thread: probing plugin dsf
Nov 01 08:52 : decoder: audio_format=dsd64:2, seekable=true
Nov 01 08:52 : exception: OutputThread could not get realtime scheduling, continuing anyway: sched_setscheduler failed: Operation not permitted
Nov 01 08:52 : alsa_output: opened default type=PLUG
Nov 01 08:52 : alsa_output: buffer: size=15052..60211 time=42664..170667
Nov 01 08:52 : alsa_output: period: size=7526..7527 time=21333..21334
Nov 01 08:52 : alsa_output: default period_time = buffer_time/4 = 170666/4 = 42666
Nov 01 08:52 : alsa_output: format=S24_LE (Signed 24 bit Little Endian)
Nov 01 08:52 : alsa_output: buffer_size=60211 period_size=7526
Nov 01 08:52 : output: opened "My ALSA Device" (alsa) audio_format=352800:24:2
Nov 01 08:52 : output: converting in=dsd64:2 -> f=dsd64:2 -> out=352800:24:2
Nov 01 08:52 : client: [2] opened from local
Nov 01 08:52 : client: [2] process command list
Nov 01 08:52 : client: process command "status"
Nov 01 08:52 : client: command returned 0
Nov 01 08:52 : client: process command "currentsong"
Nov 01 08:52 : client: command returned 0
Nov 01 08:52 : client: [2] process command list returned 0
Nov 01 08:52 : client: [2] closed

To verify that the stream does contain metadata:

$ mediainfo "http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true"
General
Complete name                            : http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true
Format                                   : DSF
Format/Info                              : Direct Stream Digital Stream File
Format version                           : Version 1
File size                                : 207 MiB
Duration                                 : 5 min 7 s
Overall bit rate                         : 5 647 kb/s
Album                                    : POLARITY — an acoustic jazz project
Album/Performer                          : Hoff Ensemble
Part/Position                            : 1
Track name                               : Innocence
Track name/Position                      : 1
Track name/Total                         : 12
Performer                                : Hoff Ensemble: Jan Gunnar Hoff, Audun Kleive & Anders Jormin
Composer                                 : Jan Gunnar Hoff
Encoded by                               : Merging Technologies Album Publishing
Publisher                                : 2L
Genre                                    : Jazz
Recorded date                            : 2018
ISRC                                     : NOMPP1801010
Cover                                    : Yes
Cover type                               : Cover (front)
Cover MIME                               : image/jpeg
Comment                                  : Generated by Merging Technologies Album Publishing / Label Code: 2L / 7041888523628
FileExtension_Invalid                    : dsf

Audio
Format                                   : DSD
Format/Info                              : Direct Stream Digital
Commercial name                          : DSD64
Format settings                          : Little
Duration                                 : 5 min 7 s
Bit rate                                 : 5 645 kb/s
Channel(s)                               : 2 channels
Channel layout                           : L R
Sampling rate                            : 2 822 kHz
Compression mode                         : Lossless
Stream size                              : 207 MiB (100%)

Metadata is available when streaming other types of media such as FLAC. For example (same track as above but in FLAC format):

$ ./mpc add "http://jelly.szesewsew.com:21915/audio/6e232678-a131-5b06-0bba-183681ab85a0/stream.flac?DeviceId=test&MediaSourceId=6e232678a1315b060bba183681ab85a0&Static=true&Tag=5bc9e6e0ac28828fabe9824753c9772a&dlnaheaders=true"

$ ./mpc play
http://jelly.szesewsew.com:21915/audio/6e232678-a131-5b06-0bba-183681ab85a0/stream.flac?DeviceId=test&MediaSourceId=6e232678a1315b060bba183681ab85a0&Static=true&Tag=5bc9e6e0ac28828fabe9824753c9772a&dlnaheaders=true
[playing] #1/1   0:00/0:00 (0%)
volume:100%   repeat: off   random: off   single: off   consume: off

$ ./mpc
Hoff Ensemble: Jan Gunnar Hoff, Audun Kleive & Anders Jormin - Innocence
[playing] #1/1   0:03/5:09 (0%)
volume:100%   repeat: off   random: off   single: off   consume: off

MPD Conf

mpd.conf:
---------------------------------------------------------
music_directory     "/var/lib/mpd/music"
playlist_directory  "/var/lib/mpd/playlists"
db_file         "/var/lib/mpd/tag_cache"
log_file        "/var/log/mpd/mpd.log"
pid_file        "/run/mpd/pid"
state_file      "/var/lib/mpd/state"
sticker_file            "/var/lib/mpd/sticker.sql"

user            "mpd"
bind_to_address     "/run/mpd/socket"

input {
        plugin "curl"
}

audio_output {
    type        "alsa"
    name        "My ALSA Device"
}

log_level       "verbose"
MaxKellermann commented 3 years ago

That's because the FLAC file has its metadata at the beginning; it comes before any audio data, and therefore MPD can read it before playing anything. The DSF file has an ID3 tag at the end of the file. So MPD plays and plays, and only when the song is over, it sees the ID3 tag. Scanning the whole file for tags would require several additional HTTP requests and would cause long delays. Do you want MPD to do that?

patrickkfkan commented 3 years ago

Thanks for looking into this.

Would it be possible to implement this in such a way that MPD starts playing the stream first, followed by making these additional HTTP requests in the background and updating itself when it obtains the metadata?

In my tests, mediainfo and ffmpeg both took less than a second to fetch the metadata from the DSF sample stream, which I think is not too bad (the stream itself is ~207MB):

> time mediainfo "http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true"

Complete name                            : http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true
Format                                   : DSF
Format/Info                              : Direct Stream Digital Stream File
Format version                           : Version 1
File size                                : 207 MiB
Duration                                 : 5 min 7 s
Overall bit rate                         : 5 647 kb/s
Album                                    : POLARITY — an acoustic jazz project
Album/Performer                          : Hoff Ensemble
Part/Position                            : 1
Track name                               : Innocence
...

real    0m0.128s
user    0m0.022s
sys 0m0.007s

> time ffmpeg -i 'http://jelly.szesewsew.com:21915/audio/3c6ccea4-424c-c1c8-4977-03792c2e12de/stream.dsf?DeviceId=test&MediaSourceId=3c6ccea4424cc1c8497703792c2e12de&Static=true&Tag=3a45a9c240c1077bfa1d2e53ae0854d4&dlnaheaders=true' -f ffmetadata /tmp/dsf-metadata.txt

...
 Metadata:
    encoded_by      : Merging Technologies Album Publishing
    comment         : Generated by Merging Technologies Album Publishing
                    : Label Code: 2L
    title           : Innocence
    artist          : Hoff Ensemble: Jan Gunnar Hoff, Audun Kleive & Anders Jormin
    album_artist    : Hoff Ensemble
    composer        : Jan Gunnar Hoff
    album           : POLARITY — an acoustic jazz project
...
real    0m0.276s
user    0m0.059s
sys 0m0.017s

> cat /tmp/dsf-metadata.txt

;FFMETADATA1
encoded_by=Merging Technologies Album Publishing
comment=Generated by Merging Technologies Album Publishing\
Label Code: 2L
title=Innocence
artist=Hoff Ensemble: Jan Gunnar Hoff, Audun Kleive & Anders Jormin
album_artist=Hoff Ensemble
composer=Jan Gunnar Hoff
album=POLARITY — an acoustic jazz project
track=1/12
disc=1
TSRC=NOMPP1801010
=7041888523628
publisher=2L
date=2018
genre=Jazz
encoder=Lavf57.83.100

Perhaps MPD can utilize ffmpeg for retrieving the metadata from the stream?

MaxKellermann commented 3 years ago

MPD can read those tags just fine without FFmpeg. MPD just doesn't bother doing that, and nobody spent time on implementing your idea, because this problem doesn't exist on sane codecs (i.e. FLAC, Vorbis, Opus). Your ffmpeg command takes 4 seconds here, and involves 3 connections to the server and 3 HTTP requests, and receives 77 kB of data from the internet. Bottom line: yes, it can be done, and maybe somebody volunteers and sends me the code. But I feel that my time is better spent on other things.

patrickkfkan commented 3 years ago

For me at least, I would prefer to have the stream play ASAP instead of having to wait a couple of seconds for MPD to retrieve metadata. And I respect that you don't want to spend time implementing this through threading just for the sake of one codec (and the fact that this doesn't affect the majority of users, apparently). Unfortunately, I know nothing about C / C++ to be able to do this myself, so I guess we should just leave things as they are :)

patrickkfkan commented 3 years ago

On the other hand, would you consider adding a config option so that users can choose whether MPD should fetch metadata before playing a DSF stream? In my use case, I can enable this option when streaming on local network and the delay should be unnoticeable.

Thanks.

adelias commented 3 years ago

Wouldn't compressing the dsf files with wavpack accomplish what you are after?

patrickkfkan commented 3 years ago

Wouldn't compressing the dsf files with wavpack accomplish what you are after?

Hello,

Thanks for the suggestion. If the DSF streams are served from a remote server, then I cannot compress them into Wavpack if I don't have access to that server.

Fortunately in my case, since the streams are remote and I do have the metadata at hand (provided by the server hosting the DSF), I am able to work around the issue by using MPD's addtagid command to specify the song title, artist, etc.

I agree with Max's comment that it may not be worthwhile to implement metadata fetching for DSFs, given the overhead involved, but I leave it to his discretion whether this issue should be closed as "not going to be resolved".

Regards, Patrick