I’m trying to build a media player using GstPlay
and am having trouble implementing the error
signal callback. Other callbacks are working fine when I pass a class’s this
pointer to the callback as extra data, but the error
callback receives NULL
instead. This is all taken from the gst-play.c
example. I wondered if this was some kind of scoping issue with the way I’m wrapping things in C++ but the state-changed
callback fires afterwards and receives a valid pointer to the instance. Any ideas what might be going wrong or what I’ve missed?
#include <csignal>
#include <memory>
#include <string>
#include <gst/gst.h>
#include <gst/play/play.h>
struct Result {
bool is_error;
std::string description;
Result() = delete;
operator bool() { return !is_error; }
static Result ok() { return {false, ""}; }
static Result error(std::string description) { return {true, description}; }
};
class App {
public:
App() :
loop_(nullptr),
player_(nullptr),
signal_adapter_(nullptr)
{}
App(App&) = delete;
Result init() {
player_ = gst_play_new(NULL);
if (!player_) {
return Result::error("failed to initialise GstPlay object");
}
loop_ = g_main_loop_new(NULL, FALSE);
if (!loop_) {
return Result::error("failed to initialise GMainLoop object");
}
signal_adapter_ = gst_play_signal_adapter_new(player_);
if (!signal_adapter_) {
return Result::error("failed to initialise GstPlaySignalAdapter object");
}
g_signal_connect(signal_adapter_, "end-of-stream", G_CALLBACK(end_of_stream_cb), this);
g_signal_connect(signal_adapter_, "error", G_CALLBACK(error_cb), this);
g_signal_connect(signal_adapter_, "state-changed", G_CALLBACK(state_changed_cb), this);
g_signal_connect(signal_adapter_, "buffering", G_CALLBACK(buffering_cb), this);
return Result::ok();
}
void set_uri(std::string uri) {
g_object_set(G_OBJECT(player_), "uri", uri.c_str(), (void*)0);
}
void run() {
gst_play_play(player_);
g_main_loop_run(loop_);
}
void stop() {
g_main_loop_quit(loop_);
}
~App() {
if (signal_adapter_) {
g_clear_object(&signal_adapter_);
}
if (player_) {
gst_object_unref(player_);
}
if (loop_) {
g_main_loop_unref(loop_);
}
}
private:
static void end_of_stream_cb(GstPlaySignalAdapter* adapter, App* app) {
gst_print("EOS\n");
app->stop();
}
static void error_cb(GstPlaySignalAdapter* adapter, GError* err, App* app) {
gst_printerr("ERROR: %s, %p\n", err->message, app);
if(app) {
app->stop();
} else {
gst_printerr("ERROR: cannot access app to stop it\n");
}
}
static void state_changed_cb(GstPlaySignalAdapter* adapter, GstPlayState state, App* app) {
gst_print("State changed: %s, %p\n", gst_play_state_get_name(state), app);
}
static void buffering_cb(GstPlaySignalAdapter* adapter, gint percent, App* app) {
gst_print("Buffering: %d\n", percent);
}
GMainLoop* loop_;
GstPlay* player_;
GstPlaySignalAdapter* signal_adapter_;
};
static App* global_app_ptr;
void sigint_handler(int) {
if(global_app_ptr) {
global_app_ptr->stop();
}
}
int main (int argc, char** argv) {
gst_init(&argc, &argv);
if (argc != 1) {
g_printerr("WARNING: this command takes no arguments\n");
}
App app;
global_app_ptr = &app;
std::signal(SIGINT, sigint_handler);
Result result = app.init();
if (!result) {
g_printerr("ERROR: %s\n", result.description.c_str());
return 1;
}
app.set_uri("file:///missing.wav");
app.run();
g_print("Done\n");
return 0;
}
This outputs the following:
State changed: buffering, 0000002F518FF8B0
(scl6play.exe:19340): GStreamer-CRITICAL **: 13:41:51.011: gst_structure_copy: assertion 'structure != NULL' failed
(scl6play.exe:19340): GStreamer-CRITICAL **: 13:41:51.011: structure_serialize: assertion 'structure != NULL' failed
(scl6play.exe:19340): GStreamer-CRITICAL **: 13:41:51.012: structure_serialize: assertion 'structure != NULL' failed
(scl6play.exe:19340): GStreamer-CRITICAL **: 13:41:51.012: gst_structure_free: assertion 'structure != NULL' failed
(scl6play.exe:19340): GStreamer-CRITICAL **: 13:41:51.013: structure_serialize: assertion 'structure != NULL' failed
(scl6play.exe:19340): GStreamer-CRITICAL **: 13:41:51.015: structure_serialize: assertion 'structure != NULL' failed
ERROR: Failed to play, 0000000000000000
ERROR: cannot access app to stop it
State changed: stopped, 0000002F518FF8B0
Done