Closed diracsbracket closed 3 years ago
It's working for me with gstreamer 1.18.2. Do you have an aac decoder element installed? (You can look at gst-inspect-1.0)
@jbroadus
Do you have an aac decoder element installed
As mentioned, it works when the stream is added manually?
In any case, this is the output of gst-inspect-1.0
:
buster@debian:~$ gst-inspect-1.0 | grep aac
voaacenc: voaacenc: AAC audio encoder
libav: avmux_adts: libav ADTS AAC (Advanced Audio Coding) muxer (not recommended, use aacparse instead)
libav: avdec_aac_latm: libav AAC LATM (Advanced Audio Coding LATM syntax) decoder
libav: avdec_aac_fixed: libav AAC (Advanced Audio Coding) decoder
libav: avdec_aac: libav AAC (Advanced Audio Coding) decoder
libav: avenc_aac: libav AAC (Advanced Audio Coding) encoder
audioparsers: aacparse: AAC audio stream parser
typefindfunctions: audio/aac: aac, adts, adif, loas
Also tried with Clementine build version:
Version 1.4.0rc1-566-gf04657e7e
I just build GStreamer from source,
[gst-master] buster@debian:~/gst-build/build$ gst-launch-1.0 --version
gst-launch-1.0 version 1.19.0
GStreamer 1.19.0 (GIT)
Unknown package origin
[gst-master] buster@debian:~/gst-build/build$
And when I launch:
gst-master] buster@debian:~/gst-build/build$ GST_DEBUG=*:3 gst-launch-1.0 -v playbin uri= http://serpent0.duckdns.org:8088/kbsfm.pls
I get:
[gst-master] buster@debian:~/gst-build/build$ GST_DEBUG=*:3 gst-launch-1.0 -v playbin uri= http://serpent0.duckdns.org:8088/kbsfm.pls
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: ring-buffer-max-size = 0
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: buffer-size = -1
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: buffer-duration = -1
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: force-sw-decoders = false
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: use-buffering = false
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: download = false
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: uri = http://serpent0.duckdns.org:8088/kbsfm.pls
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: connection-speed = 0
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: source = "\(GstSoupHTTPSrc\)\ source"
0:00:00.038604197 25480 0x55922d154070 WARN structure gststructure.c:2093:priv_gst_structure_append_to_gstring: No value transform to serialize field 'session' of type 'SoupSession'
Got context from element 'source': gst.soup.session=context, session=(SoupSession)NULL, force=(boolean)false;
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstTypeFindElement:typefindelement0.GstPad:src: caps = text/plain
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstDecodeBin:decodebin0/GstTypeFindElement:typefind: force-caps = text/plain
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstDecodeBin:decodebin0: sink-caps = text/plain
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0: bitrate = 0
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0: bitrate = 0
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0.GstPad:sink: caps = text/plain
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0: bitrate = 0
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0: bitrate = 0
Setting pipeline to PLAYING ...
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0.GstPad:src: caps = text/plain
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0: bitrate = 0
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstDecodeBin:decodebin0.GstGhostPad:sink.GstProxyPad:proxypad0: caps = text/plain
/GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstDecodeBin:decodebin0/GstTypeFindElement:typefind.GstPad:src: caps = text/plain
0:00:00.230288908 25480 0x7f610c413770 WARN decodebin gstdecodebin2.c:2890:type_found:<decodebin0> error: This appears to be a text file
0:00:00.230350827 25480 0x7f610c413770 WARN decodebin gstdecodebin2.c:2890:type_found:<decodebin0> error: decodebin cannot decode plain text files
ERROR: from element /GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstDecodeBin:decodebin0: This appears to be a text file
Additional debug info:
../subprojects/gst-plugins-base/gst/playback/gstdecodebin2.c(2890): type_found (): /GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstDecodeBin:decodebin0:
decodebin cannot decode plain text files
0:00:00.230483403 25480 0x7f610c413770 WARN queue2 gstqueue2.c:3242:gst_queue2_loop:<queue2-0> error: Internal data stream error.
0:00:00.230531074 25480 0x7f610c413770 WARN queue2 gstqueue2.c:3242:gst_queue2_loop:<queue2-0> error: streaming stopped, reason not-linked (-1)
Execution ended after 0:00:00.000350034
Setting pipeline to NULL ...
ERROR: from element /GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0: Internal data stream error.
Additional debug info:
../subprojects/gstreamer/plugins/elements/gstqueue2.c(3242): gst_queue2_loop (): /GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstQueue2:queue2-0:
streaming stopped, reason not-linked (-1)
Freeing pipeline ..
@jbroadus,
It appears GStreamer just does not support .pls files, and that they must be parsed first by external means. When the stream is added manually, SongLoader::LoadRemotePlaylist()
is invoked, which downloads the .pls
file and parses it with a ParserBase
object.
That does NOT happen when the corresponding RadioBrowser channel is clicked, as the .pls
file is sent directly to GStreamer.
So, it's weird that it works on your side...
Yeah, I'm sorry, I completely missed this line in your original description:
When the .pls URL is added manually via the Add Stream menu, it works however.
I am seeing the same behavior. It seems like we should be able to use the same stream discovery logic in the stream services.
Yeah, I'm sorry, I completely missed this line in your original description:
No worries. And thank you so much for the time and effort you put in all this.
Hi.
I found a possible solution to this, by duplicating (!) the code from SongLoader::LoadRemotePlaylist(()
and adding it to the RadioBrowserService
class like so:
//.h
#if 1
class PlaylistParser;
#endif
class RadioBrowserService : public InternetService {
Q_OBJECT
...
#if 1
void checkIfPlaylist(const QUrl& url, Song& song);
#endif
...
#if 1
PlaylistParser* playlist_parser_;
#endif
}
//.cpp
#if 1
#include "library/librarybackend.h"
#include "playlistparsers/playlistparser.h"
#include "playlistparsers/parserbase.h"
#include "core/waitforsignal.h"
#endif
...
RadioBrowserService::RadioBrowserService(Application* app,
InternetModel* parent)
: InternetService(kServiceName, app, parent, parent)
...
#if 1
, playlist_parser_(new PlaylistParser(app->library_backend(), this))
#endif
{
...
}
void RadioBrowserService::checkIfPlaylist(const QUrl& url, Song& song) {
//Test if playlist
QNetworkRequest req(url);
// Getting headers:
QNetworkReply* const headers_reply = network_->head(req);
WaitForSignal(headers_reply, SIGNAL(finished()));
if (headers_reply->error() != QNetworkReply::NoError) {
qLog(Error) << url.toString() << headers_reply->errorString();
return;
}
// Now we check if there is a parser that can handle that MIME type.
QString mime_type = headers_reply->header(QNetworkRequest::ContentTypeHeader).toString();
ParserBase* const parser = playlist_parser_->ParserForMimeType(mime_type);
if (parser == nullptr) {
qLog(Debug) << url.toString() << "seems to not be a playlist";
return;
}
// We know it is a playlist!
// Getting its contents:
QNetworkReply* const data_reply = network_->get(req);
WaitForSignal(data_reply, SIGNAL(finished()));
if (data_reply->error() != QNetworkReply::NoError) {
qLog(Error) << url.toString() << data_reply->errorString();
return;
}
QList<Song> songs = parser->Load(data_reply, QString(), QString());
if (songs.length()>0) {
const QUrl new_url = songs.at(0).url();
song.set_url(new_url);
qLog(Debug) << "found playlist" << new_url.toString();
}
}
and modifying RadioBrowserService::ResolveStationUrl()
as follows:
void RadioBrowserService::ResolveStationUrl(const QUrl& original_url) {
...
#if 0 // UNUSED!
StreamList list;
#endif
...
Song ret;
ret.set_valid(true);
ret.set_title(item["name"].toString());
QUrl url(item["url"].toString());
ret.set_url(url);
ret.set_art_automatic(item["favicon"].toString());
#if 1
//Replace the url by the one in the playlist
checkIfPlaylist(url, ret);
#endif
emit StreamMetadataFound(original_url, ret);
});
}
Hopefully you can find a better way that avoids the above code duplication.
It looks like we might just be able to use "url_resolved" instead of "url" from the station structure. https://fr1.api.radio-browser.info/#Struct_station
cc @ctrlaltca
It looks like we might just be able to use "url_resolved" instead of "url" from the station structure.
Things get simpler and simpler.... Nice catch!
Sorry, I missed the notification for this issue. PR created: https://github.com/clementine-player/Clementine/pull/7035
When selecting the channel "KBS Classic FM" from Korea in Radio-Browser.info stream, I get a "This appears to be a text file" message from GStreamer.
When the
.pls
URL is added manually via theAdd Stream
menu, it works however.Steps to reproduce:
To add it manually from menu :
Playlist | Add Stream
, with URLPlatform: Linux Buster
Clementine built from source:
GStreamer packages installed: