Conversation
Signed-off-by: Stefan Sichler <stsichler@web.de>
| $attr->{VBR_SCALE} = exists $meta->{vbr_scale} | ||
| ? $meta->{vbr_scale} | ||
| : ( exists $cbr{ $meta->{bitrate} } ) ? undef : 1; |
There was a problem hiding this comment.
vbrScale currently is checked for the exact value of 1 in
slimserver/Slim/Schema/Track.pm
Lines 359 to 371 in c96f8f2
@SamInPgh - is there any reason why you are expecting the exact value of 1 there? Couldn't we accept any truthy value?
Can that value ever be 0 at all? Otherwise this statement could be simplified to something like:
$attr->{VBR_SCALE} = $meta->{vbr_scale} || !$cbr{ $meta->{bitrate} } || undef;There was a problem hiding this comment.
Your change would break that.
I don't think that my change breaks anything here, because all locations in the code that currently call setRemoteMetadata() build up the EDIT: $meta parameter set in-place and none of them currently provides the vbr_scale metadata key at all.
It is only the code added here in UPnP/DLNA Media Renderer by now that actually sets it and it does so by only either setting it to EDIT: undef or don't provide it at all.
is there any reason why you are expecting the exact value of 1 there?
This question is a pretty good one:
what's the actual meaning of the vbr_scale track attribute / column in the track database? Is it a boolean or is it a scale value?
Maybe I should instead resolve that by renaming the freshly added vbr_scale attribute here to something different like vbr to clearly state that it is boolean-only here.
There was a problem hiding this comment.
See the edit of my previous post. I wrote $attr, but was actually talking of $meta.
There was a problem hiding this comment.
I crawled through the code now and these are my findings:
the VBR_SCALE track attribute is traditionally a string in the database (see vbr_scale definition in the schema), not a boolean, but all places currently setting it (including the local track scanners) either set it to 1, 0 or leave it undefined.
So it is actually used as a bool, not a scale value.
There was a problem hiding this comment.
@SamInPgh - is there any reason why you are expecting the exact value of
1there? Couldn't we accept any truthy value?
Theoretically, yes.
Can that value ever be 0 at all?
Yes. As currently used, it can be 0, 1 or undefined
See @stsichler's comments, which accurately describe how the vbr_scale attribute is defined and used.
There was a problem hiding this comment.
@SamInPgh related question (but somewhat off-topic - sorry): Slim::Formats::Flac defines vbr_scale = 1 for FLAC. Isn't this wrong?
slimserver/Slim/Formats/FLAC.pm
Line 277 in c0881b2
There was a problem hiding this comment.
Slim::Formats::Flacdefinesvbr_scale = 1for FLAC. Isn't this wrong?
AFAIK, Flac is always VBR, so I think that this is correct.
There was a problem hiding this comment.
I'm sorry for being thick. @stsichler am I finally getting it, understanding you don't want this change to transport the actual VBR value, but still only a flag? Just read it from
$meta(if available) instead of looking it up in thevbrtable?
Yes, vbr_scale is a flag. And the only purpose of this change is to bypass that MP3-targeted heuristic via "table of known CBR-rates" when the caller of the function knows it better.
There was a problem hiding this comment.
Slim::Formats::Flacdefinesvbr_scale = 1for FLAC. Isn't this wrong?AFAIK, Flac is always VBR, so I think that this is correct.
What he said. ⬆️
There was a problem hiding this comment.
Thanks for the clarification!
14232fc to
c88293c
Compare
|
Note: I just added adaptive size of buffered data in the on-the-fly bitrate scan to support MP3s with large cover images (same logic as in |
|
@michaelherger It turned out that it requires a small fix in |
c88293c to
4fd89f2
Compare
| # enough data buffered (or stream ended) - scan once and stop | ||
| ${*$self}{'_bitrateScan'} = 0; | ||
|
|
||
| my ( $bitrate, $vbr ) = eval { $scan->{formatClass}->scanBitrate( $scan->{fh}, $scan->{url} ) }; |
There was a problem hiding this comment.
Hm... After further digging into the code, I'm unsure whether it would be better to instead directly call Audio::Scan->scan_fh() here, bypassing setting of further track attributes like TITLE and/or caching a cover image, which is done in scanBitrate(). That's something I wasn't really expecting anyway here...
There was a problem hiding this comment.
Would you only ever scan local files?
There was a problem hiding this comment.
No. This scans the first 128kB (or more) of remote tracks that are copied to a local temporary file meanwhile the file is received from the DLNA server for playback. So it's an on-the-fly scan.
There was a problem hiding this comment.
instead directly call
Audio::Scan->scan_fh()
Okay, I now have a working version directly using scan_fh() instead of scanBitrate() and this seems to be the better solution, because:
scan_fh()really only determines bitrate, vbr and duration, instead of also extracting title and cover artwork- I'm able to add support for all formats that are currently advertised as sink formats, namely mp3, wav, aif, ogg, ops and flc.
I'll put this PR on hold because I'd love to hear your option about this first, before I rebase it onto the scan_fh()-based version.
There was a problem hiding this comment.
my imagination is poor - I could give better feedback if I had code to look at. Would you have the commit somewhere? Even a temporary PR in draft mode?
There was a problem hiding this comment.
Would you have the commit somewhere?
Yes, see this commit in my fork
There was a problem hiding this comment.
So you'd replace scanBitrate() with using your own code around the same scan_fh() as in scanBitrate() for better control? Or because scanBitrate() was not available for all formats? If the latter was the case, I'd rather see those missing implementations added. Otherwise we'll just end up with partially redundant, but not completely identical implementations - which I'd try to avoid.
There was a problem hiding this comment.
So you'd replace scanBitrate() with using your own code around the same scan_fh() as in scanBitrate() for better control? Or because scanBitrate() was not available for all formats? If the latter was the case, I'd rather see those missing implementations added.
These are exactly my thoughts, too.
The primary reason for this is better control: The current scanBitrate() implementations of MP3, Ogg and Opus do more than their name suggests and not only scan the bitrate, but do also (re-)set title information to "<title> BY <artist> FROM <album>" and do also add a cover to the cache, both of this cannot be switched off..
But the DLNA servers I tested do set title, artist, album (and cover) already correctly (by the DIDL-Lite info) and it is only the bitrate that's missing.
The scanBitrate() implementation for WAV/AIFF, on the other hand, is simply missing, but could be added, of course.
So, maybe the best alternative solution would be to add a parameter to scanBitrate() (like $bitrateOnly) to be able to really do a bitrate-only scan, but this would require a more invasive change, because it's in use in Slim::Utils::Scanner::Remote::parseRemoteHeader() / scanUrl().
There was a problem hiding this comment.
Okay, after looking more closely at the alternatives, I think that directly using Audio::Scan->scan_fh() here is the best compromise, because the code duplication is minimal enough and the use case is a bit different from scanBitrate().
So, I've re-based this PR to the scan_fh()-based version.
What would that change be? |
I just pushed it here as separate commit for easy review. |
Could you please test without the fix, but with latest 9.2 and the experimental WebSocket plugin enabled? You might have to wipe your browser's cache, or make sure you force-reload the page. The websocket should push changes to Default skin as soon as they happen. Would the current version (without WebSocket support) not update after a few seconds? The idea at the time was to have one really light weight request to see whether something important has changed. Adding parameters to that request kind of defeats the purpose of that light call. |
401d419 to
4fd89f2
Compare
You're right! It seems that it's working without having to patch (I previously tested a patched 9.1 installation without WebSocket) |
A proper bitrate value is crucial for seeking, so this improves bitrate heuristic for LPCM & WAV and when no bitrate is sent by the server at all, do an on-the-fly bitrate detection using Audio::Scan->scan_fh() on the first 128kB of data (or more when a lengthy MP3v2 tag, like a large cover image, is detected). This also adds support for Icy Metadata, although it is not part of the UPnP/DLNA spec, because some Media Servers (like AirMusic) are sending it. In addition, canSeek() now honors the DLNA.ORG_OP seekable flag instead of always assuming Range support. **Parts of this code are generated using AI.** Signed-off-by: Stefan Sichler <stsichler@web.de>
4fd89f2 to
a4df36e
Compare
|
I think I missed an important detail: this Protocol Handler is used to stream from one of those UPnP servers to a Squeezebox? |
Yes. Exactly. |
|
|
||
| main::DEBUGLOG && $log->is_debug && $log->debug( 'Metadata returned for ' . $meta->{title} ); | ||
| # support Icy Metadata updates | ||
| my $title = Slim::Music::Info::getCurrentTitle( $client, $url ); |
There was a problem hiding this comment.
Is this the right thing to do? I believe getMetadataFor() should return as bare naked data as possible. But getCurrentTitle() would format according to title formatting settings - which could include an album name or other metadata, depending on the user's settings. I might be wrong, but please double check.
A proper bitrate value is crucial for seeking, so this improves bitrate heuristic for LPCM & WAV and when no bitrate is sent by the server at all, we do an on-the-fly bitrate detection using
Audio::Scan->scan_fh()on the first 128kB of data (or more when a lengthy MP3v2 tag, like a large cover image, is detected).Unfortunately, we cannot directly use
parseAudioStream()here, because it would create its own connection (which would be rejected by e.g. pa-dlna) and currently does not support HTTP/1.1 chunked transfer encoding, so it required an implementation that is embedded into_sysread()instead.We also improve metadata handling here, so that this also adds support for Icy Metadata, although it is not part of the UPnP/DLNA spec, because some Media Servers (like AirMusic) are sending it.
In addition,
canSeek()now honors theDLNA.ORG_OPseekable flag (in DIDL-Lite) instead of always assuming Range support.Note that this also required a minor change to
Slim::Music::Info::setRemoteMetadata()(for cosmetic reasons), becausesetRemoteMetadata()supposed that all bitrates that are not typical MP3-bitrates are VBR, which is in fact very buggy.We solve this by adding support for an optional
vbr_scalemetadata attribute which is now preferred over guessing.Tested with:
Audio formats tested:
audio/wavaudio/L16audio/mpeg