Testing the code for playlist management
This commit is contained in:
parent
e16475c2ae
commit
17c7fd4eaf
|
@ -1,6 +1,6 @@
|
|||
playlist:
|
||||
# path: ./CMakeLists.txt
|
||||
path: ./playlist.txt
|
||||
path: playlist.txt
|
||||
path_reload_when_touched: .
|
||||
icecast_server:
|
||||
address: stream.streamhostt.com
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue