Testing the code for playlist management

This commit is contained in:
László Károlyi 2022-01-24 19:32:35 +01:00
parent e16475c2ae
commit 17c7fd4eaf
Signed by: karolyi
GPG Key ID: 2DCAF25E55735BFE
4 changed files with 140 additions and 44 deletions

View File

@ -1,6 +1,6 @@
playlist:
# path: ./CMakeLists.txt
path: ./playlist.txt
path: playlist.txt
path_reload_when_touched: .
icecast_server:
address: stream.streamhostt.com

View File

@ -113,5 +113,12 @@ PlayerBusException::PlayerBusException(const std::string &error)
const char *PlayerBusException::what() { return error_msg.c_str(); }
// ===== PlayerBusException END =====
// ===== NoReadablePlaylistItemException START =====
NoReadablePlaylistItemException::NoReadablePlaylistItemException(const std::string &path)
: error_msg("No readable path found in playlist file: " + path) {}
const char *NoReadablePlaylistItemException::what() { return error_msg.c_str(); }
// ===== NoReadablePlaylistItemException END =====
} // namespace Exceptions
} // namespace IceGStreamer

View File

@ -89,5 +89,13 @@ public:
virtual const char *what();
};
class NoReadablePlaylistItemException : public PlayerExceptionBase {
const std::string error_msg;
public:
NoReadablePlaylistItemException(const std::string &path);
virtual const char *what();
};
} // namespace Exceptions
} // namespace IceGStreamer

View File

@ -10,6 +10,8 @@
namespace IceGStreamer {
namespace Streamer {
class StreamerInternals;
class PlaylistHandler {
private:
Config::_config_playlist &config;
@ -44,25 +46,30 @@ public:
void load_next_track() {
std::string line{};
bool is_eof_reached{false};
while (true) {
std::getline(playlist_file, line);
std::cout << "LINE IN PLAYLIST: " << line << std::endl;
// std::cout << "LINE IN PLAYLIST: " << line << std::endl;
if (playlist_file.eof()) {
std::cout << "PLAYLIST EOF, SEEKING TO START" << std::endl;
if (is_eof_reached)
throw Exceptions::NoReadablePlaylistItemException(config.path);
// std::cout << "PLAYLIST EOF, SEEKING TO START" << std::endl;
playlist_file.clear();
playlist_file.seekg(0);
is_eof_reached = true;
continue;
}
if (line[0] == '#' || line.empty())
continue;
if (!std::ifstream(line).good()) {
std::cout << "FILE UNREADABLE: "
<< "'" << line << "'" << std::endl;
// std::cout << "FILE UNREADABLE: "
// << "'" << line << "'" << std::endl;
continue;
}
break;
}
current_track = std::move(line);
std::cout << "NEXT TRACK: " << current_track << std::endl;
}
~PlaylistHandler() { playlist_file.close(); }
@ -70,72 +77,131 @@ public:
class StreamerInternals {
private:
Config::_config &config;
PlaylistHandler playlist_handler;
GstElement *pipeline{}, *filesrc{}, *decodebin{}, *convert{}, *sink{};
GstBus *bus;
GstMessage *msg;
GMainLoop *main_loop;
const char *decodebin_name_{"icegstreamer-decodebin"};
typedef enum {
INT_STATUS_UNKNOWN_FILETYPE = std::uint16_t(1 << 0),
} InternalStatus;
std::uint16_t internal_status_{0};
public:
Config::_config &config;
StreamerInternals(Config::_config &config)
: config(config), playlist_handler(config.playlist) {}
static void cb_decodebin_pad_added(
GstElement &object, GstPad *pad, StreamerInternals &instance) {
if (!gst_pad_is_linked(pad))
gst_pad_link(pad, gst_element_get_static_pad(instance.convert, "sink"));
std::cout << "cb_decodebin_pad_added, " << pad->object.name << ", "
<< object.object.name << std::endl;
const GstElement &object, GstPad *pad,
const StreamerInternals &instance) {
std::cout << "cb_decodebin_pad_added: " << GST_OBJECT_NAME(pad)
<< std::endl;
if (!gst_pad_is_linked(pad)) {
const GstPadLinkReturn pad_return = gst_pad_link(
pad, gst_element_get_static_pad(instance.convert, "sink"));
std::cout << "cb_decodebin_pad_added->link: " << pad_return << std::endl;
}
GstCaps *caps = gst_pad_query_caps(pad, nullptr);
// GstCaps &caps = *gst_pad_get_current_caps(pad);
for (int idx_caps = 0; idx_caps < gst_caps_get_size(caps); idx_caps++) {
GstStructure *gst_struct = gst_caps_get_structure(caps, idx_caps);
std::cout << "cb_decodebin_pad_added->structure(" << idx_caps
<< "): " << gst_structure_get_name(gst_struct) << std::endl;
GstCapsFeatures *features = gst_caps_get_features(caps, idx_caps);
std::cout << "| size: " << gst_caps_features_get_size(features)
<< std::endl;
std::cout << "| features: " << gst_caps_features_to_string(features)
<< std::endl;
}
gst_caps_unref(caps);
}
static void cb_decodebin_pad_removed(
GstElement &object, GstPad *pad, StreamerInternals &instance) {
// gst_pad_link(pad, gst_element_get_static_pad(instance.convert, "sink"));
gst_pad_unlink(pad, gst_element_get_static_pad(instance.convert, "sink"));
std::cout << "cb_decodebin_pad_removed, " << pad->object.name << ", "
<< object.object.name << std::endl;
GstStructure *gst_struct = gst_structure_new_empty("test-gst-struct");
GstMessage *msg = gst_message_new_application(&object.object, gst_struct);
gst_bus_post(instance.bus, msg);
printf("BUS MESSAGE POSTED\n");
const GstElement &object, const GstPad *pad,
const StreamerInternals &instance) {
std::cout << "cb_decodebin_pad_removed: " << GST_OBJECT_NAME(pad)
<< std::endl;
// GstStructure *gst_struct = gst_structure_new_empty("test-gst-struct");
// GstMessage *msg = gst_message_new_application(&object.object,
// gst_struct); gst_bus_post(instance.bus, msg); printf("BUS MESSAGE
// POSTED\n");
}
static void
cb_decodebin_drained(GstElement &object, StreamerInternals &instance) {
std::cout << "cb_decodebin_drained, " << object.object.name << std::endl;
static void cb_decodebin_drained(
const GstElement &object, const StreamerInternals &instance) {
std::cout << "cb_decodebin_drained: " << GST_OBJECT_NAME(&object)
<< std::endl;
}
static void cb_decodebin_no_more_pads(
const GstElement &object, StreamerInternals &instance) {
std::cout << "cb_decodebin_no_more_pads: " << GST_OBJECT_NAME(&object)
<< std::endl;
}
static void cb_decodebin_unknown_type(
const GstElement &object, const GstPad &pad, const GstCaps &caps,
StreamerInternals &instance) {
std::cout << "cb_decodebin_unknown_type: " << GST_OBJECT_NAME(&pad)
<< std::endl;
const GstStructure &gststruct = *gst_caps_get_structure(&caps, 0);
std::cout << "NAME: " << gst_structure_get_name(&gststruct) << std::endl;
const GstMessage *msg =
gst_bus_pop_filtered(instance.bus, GST_MESSAGE_ERROR);
std::cout << "MSG: " << msg << std::endl;
instance.internal_status_ |= INT_STATUS_UNKNOWN_FILETYPE;
}
void process_bus_message_application(const GstStructure &msg_struct) {
std::cout << "APP MESSAGE CAUGHT: " << gst_structure_get_name(&msg_struct)
<< std::endl;
}
void process_bus_unknown_type_error() {
internal_status_ &= ~INT_STATUS_UNKNOWN_FILETYPE;
GstStateChangeReturn ret;
ret = gst_element_set_state(pipeline, GST_STATE_NULL);
playlist_handler.load_next_track();
g_object_set(
filesrc, "location", playlist_handler.current_track.c_str(), nullptr);
ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
// std::cout << "PLAY RESULT:" << ret << std::endl;
}
void throw_on_unhandled_buserror(GstMessage &msg) {
GError *err;
gchar *debug;
gst_message_parse_error(&msg, &err, &debug);
gst_element_set_state(pipeline, GST_STATE_READY);
g_main_loop_quit(main_loop);
const std::string str_message{static_cast<std::string>(err->message)};
const std::string str_debug{debug};
g_error_free(err);
g_free(debug);
throw Exceptions::PlayerBusException(str_message + ": " + str_debug);
}
static void
cb_bus_message(GstBus &bus, GstMessage &msg, StreamerInternals &instance) {
switch (msg.type) {
case GST_MESSAGE_APPLICATION: {
const GstStructure gst_struct = *gst_message_get_structure(&msg);
printf("APP MESSAGE CAUGHT: %s\n", gst_structure_get_name(&gst_struct));
break;
}
case GST_MESSAGE_STATE_CHANGED: {
// printf(
// "BUS MESSAGE: CHANGED: %d, %d, %d\n",
// instance.pipeline->current_state, instance.pipeline->next_state,
// instance.pipeline->pending_state);
instance.process_bus_message_application(
*gst_message_get_structure(&msg));
break;
}
case GST_MESSAGE_ERROR: {
GError *err;
gchar *debug;
gst_message_parse_error(&msg, &err, &debug);
gst_element_set_state(instance.pipeline, GST_STATE_READY);
g_main_loop_quit(instance.main_loop);
const std::string str_message{std::string(err->message)};
const std::string str_debug{debug};
g_error_free(err);
g_free(debug);
throw Exceptions::PlayerBusException(str_message + ": " + str_debug);
std::cout << "GST_MESSAGE_ERROR CAUGHT FROM: " << GST_OBJECT_NAME(msg.src)
<< std::endl;
if (*GST_OBJECT_NAME(msg.src) == *instance.decodebin_name_ &&
instance.internal_status_ & INT_STATUS_UNKNOWN_FILETYPE)
return instance.process_bus_unknown_type_error();
instance.throw_on_unhandled_buserror(msg);
break;
}
case GST_MESSAGE_EOS: {
printf("GST_MESSAGE_EOS CAUGHT\n");
std::cout << "GST_MESSAGE_EOS CAUGHT" << std::endl;
gst_element_set_state(instance.pipeline, GST_STATE_READY);
instance.playlist_handler.load_next_track();
g_object_set(
@ -150,10 +216,20 @@ public:
}
}
// void my_test() {
// GstCaps *caps = gst_caps_new_empty();
// GstStructure *gst_struct = gst_structure_new_empty("audio/*");
// gst_structure_set_
// // g_object_get(G_OBJECT(decodebin), "sink-caps", &result, nullptr);
// std::cout << "MY_TEST: " << gst_struct << std::endl;
// // g_free(result);
// // gst_object_unref(GST_OBJECT(&result));
// }
void start(int &argc, char *argv[]) {
gst_init(&argc, &argv);
filesrc = gst_element_factory_make("filesrc", "icegstreamer-filesrc");
decodebin = gst_element_factory_make("decodebin", "icegstreamer-decodebin");
decodebin = gst_element_factory_make("decodebin", decodebin_name_);
convert = gst_element_factory_make("audioconvert", "icegstreamer-convert");
sink = gst_element_factory_make("autoaudiosink", "icegstreamer-sink");
pipeline = gst_pipeline_new("icegstreamer-pipeline");
@ -171,8 +247,13 @@ public:
decodebin, "pad-removed", G_CALLBACK(cb_decodebin_pad_removed), this);
g_signal_connect(
decodebin, "drained", G_CALLBACK(cb_decodebin_drained), this);
g_signal_connect(
decodebin, "unknown-type", G_CALLBACK(cb_decodebin_unknown_type), this);
g_signal_connect(
decodebin, "no-more-pads", G_CALLBACK(cb_decodebin_no_more_pads), this);
g_object_set(
filesrc, "location", playlist_handler.current_track.c_str(), nullptr);
// my_test();
const GstStateChangeReturn ret =
gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {