mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-17 22:06:41 +00:00
3bb8700577
On macOS, a Cocoa event loop is needed in the main thread to ensure things like opening a GL window work correctly. In the past, this was patched into glib via Cerbero, but that prevented us from updating it. This workaround simply runs an NSApplication and then calls the main function on a secondary thread, allowing GStreamer to correctly display windows and/or system permission prompts, for example. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3532>
238 lines
7 KiB
C
238 lines
7 KiB
C
#include <gst/gst.h>
|
|
|
|
#ifdef __APPLE__
|
|
#include <TargetConditionals.h>
|
|
#endif
|
|
|
|
/* Functions below print the Capabilities in a human-friendly format */
|
|
static gboolean
|
|
print_field (GQuark field, const GValue * value, gpointer pfx)
|
|
{
|
|
gchar *str = gst_value_serialize (value);
|
|
|
|
g_print ("%s %15s: %s\n", (gchar *) pfx, g_quark_to_string (field), str);
|
|
g_free (str);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
print_caps (const GstCaps * caps, const gchar * pfx)
|
|
{
|
|
guint i;
|
|
|
|
g_return_if_fail (caps != NULL);
|
|
|
|
if (gst_caps_is_any (caps)) {
|
|
g_print ("%sANY\n", pfx);
|
|
return;
|
|
}
|
|
if (gst_caps_is_empty (caps)) {
|
|
g_print ("%sEMPTY\n", pfx);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < gst_caps_get_size (caps); i++) {
|
|
GstStructure *structure = gst_caps_get_structure (caps, i);
|
|
|
|
g_print ("%s%s\n", pfx, gst_structure_get_name (structure));
|
|
gst_structure_foreach (structure, print_field, (gpointer) pfx);
|
|
}
|
|
}
|
|
|
|
/* Prints information about a Pad Template, including its Capabilities */
|
|
static void
|
|
print_pad_templates_information (GstElementFactory * factory)
|
|
{
|
|
const GList *pads;
|
|
GstStaticPadTemplate *padtemplate;
|
|
|
|
g_print ("Pad Templates for %s:\n",
|
|
gst_element_factory_get_longname (factory));
|
|
if (!gst_element_factory_get_num_pad_templates (factory)) {
|
|
g_print (" none\n");
|
|
return;
|
|
}
|
|
|
|
pads = gst_element_factory_get_static_pad_templates (factory);
|
|
while (pads) {
|
|
padtemplate = pads->data;
|
|
pads = g_list_next (pads);
|
|
|
|
if (padtemplate->direction == GST_PAD_SRC)
|
|
g_print (" SRC template: '%s'\n", padtemplate->name_template);
|
|
else if (padtemplate->direction == GST_PAD_SINK)
|
|
g_print (" SINK template: '%s'\n", padtemplate->name_template);
|
|
else
|
|
g_print (" UNKNOWN!!! template: '%s'\n", padtemplate->name_template);
|
|
|
|
if (padtemplate->presence == GST_PAD_ALWAYS)
|
|
g_print (" Availability: Always\n");
|
|
else if (padtemplate->presence == GST_PAD_SOMETIMES)
|
|
g_print (" Availability: Sometimes\n");
|
|
else if (padtemplate->presence == GST_PAD_REQUEST)
|
|
g_print (" Availability: On request\n");
|
|
else
|
|
g_print (" Availability: UNKNOWN!!!\n");
|
|
|
|
if (padtemplate->static_caps.string) {
|
|
GstCaps *caps;
|
|
|
|
g_print (" Capabilities:\n");
|
|
caps = gst_static_caps_get (&padtemplate->static_caps);
|
|
print_caps (caps, " ");
|
|
gst_caps_unref (caps);
|
|
}
|
|
|
|
g_print ("\n");
|
|
}
|
|
}
|
|
|
|
/* Shows the CURRENT capabilities of the requested pad in the given element */
|
|
static void
|
|
print_pad_capabilities (GstElement * element, gchar * pad_name)
|
|
{
|
|
GstPad *pad = NULL;
|
|
GstCaps *caps = NULL;
|
|
|
|
/* Retrieve pad */
|
|
pad = gst_element_get_static_pad (element, pad_name);
|
|
if (!pad) {
|
|
g_printerr ("Could not retrieve pad '%s'\n", pad_name);
|
|
return;
|
|
}
|
|
|
|
/* Retrieve negotiated caps (or acceptable caps if negotiation is not finished yet) */
|
|
caps = gst_pad_get_current_caps (pad);
|
|
if (!caps)
|
|
caps = gst_pad_query_caps (pad, NULL);
|
|
|
|
/* Print and free */
|
|
g_print ("Caps for the %s pad:\n", pad_name);
|
|
print_caps (caps, " ");
|
|
gst_caps_unref (caps);
|
|
gst_object_unref (pad);
|
|
}
|
|
|
|
int
|
|
tutorial_main (int argc, char *argv[])
|
|
{
|
|
GstElement *pipeline, *source, *sink;
|
|
GstElementFactory *source_factory, *sink_factory;
|
|
GstBus *bus;
|
|
GstMessage *msg;
|
|
GstStateChangeReturn ret;
|
|
gboolean terminate = FALSE;
|
|
|
|
/* Initialize GStreamer */
|
|
gst_init (&argc, &argv);
|
|
|
|
/* Create the element factories */
|
|
source_factory = gst_element_factory_find ("audiotestsrc");
|
|
sink_factory = gst_element_factory_find ("autoaudiosink");
|
|
if (!source_factory || !sink_factory) {
|
|
g_printerr ("Not all element factories could be created.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Print information about the pad templates of these factories */
|
|
print_pad_templates_information (source_factory);
|
|
print_pad_templates_information (sink_factory);
|
|
|
|
/* Ask the factories to instantiate actual elements */
|
|
source = gst_element_factory_create (source_factory, "source");
|
|
sink = gst_element_factory_create (sink_factory, "sink");
|
|
|
|
/* Create the empty pipeline */
|
|
pipeline = gst_pipeline_new ("test-pipeline");
|
|
|
|
if (!pipeline || !source || !sink) {
|
|
g_printerr ("Not all elements could be created.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Build the pipeline */
|
|
gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
|
|
if (gst_element_link (source, sink) != TRUE) {
|
|
g_printerr ("Elements could not be linked.\n");
|
|
gst_object_unref (pipeline);
|
|
return -1;
|
|
}
|
|
|
|
/* Print initial negotiated caps (in NULL state) */
|
|
g_print ("In NULL state:\n");
|
|
print_pad_capabilities (sink, "sink");
|
|
|
|
/* Start playing */
|
|
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
if (ret == GST_STATE_CHANGE_FAILURE) {
|
|
g_printerr
|
|
("Unable to set the pipeline to the playing state (check the bus for error messages).\n");
|
|
}
|
|
|
|
/* Wait until error, EOS or State Change */
|
|
bus = gst_element_get_bus (pipeline);
|
|
do {
|
|
msg =
|
|
gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
|
|
GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STATE_CHANGED);
|
|
|
|
/* Parse message */
|
|
if (msg != NULL) {
|
|
GError *err;
|
|
gchar *debug_info;
|
|
|
|
switch (GST_MESSAGE_TYPE (msg)) {
|
|
case GST_MESSAGE_ERROR:
|
|
gst_message_parse_error (msg, &err, &debug_info);
|
|
g_printerr ("Error received from element %s: %s\n",
|
|
GST_OBJECT_NAME (msg->src), err->message);
|
|
g_printerr ("Debugging information: %s\n",
|
|
debug_info ? debug_info : "none");
|
|
g_clear_error (&err);
|
|
g_free (debug_info);
|
|
terminate = TRUE;
|
|
break;
|
|
case GST_MESSAGE_EOS:
|
|
g_print ("End-Of-Stream reached.\n");
|
|
terminate = TRUE;
|
|
break;
|
|
case GST_MESSAGE_STATE_CHANGED:
|
|
/* We are only interested in state-changed messages from the pipeline */
|
|
if (GST_MESSAGE_SRC (msg) == GST_OBJECT (pipeline)) {
|
|
GstState old_state, new_state, pending_state;
|
|
gst_message_parse_state_changed (msg, &old_state, &new_state,
|
|
&pending_state);
|
|
g_print ("\nPipeline state changed from %s to %s:\n",
|
|
gst_element_state_get_name (old_state),
|
|
gst_element_state_get_name (new_state));
|
|
/* Print the current capabilities of the sink element */
|
|
print_pad_capabilities (sink, "sink");
|
|
}
|
|
break;
|
|
default:
|
|
/* We should not reach here because we only asked for ERRORs, EOS and STATE_CHANGED */
|
|
g_printerr ("Unexpected message received.\n");
|
|
break;
|
|
}
|
|
gst_message_unref (msg);
|
|
}
|
|
} while (!terminate);
|
|
|
|
/* Free resources */
|
|
gst_object_unref (bus);
|
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
gst_object_unref (pipeline);
|
|
gst_object_unref (source_factory);
|
|
gst_object_unref (sink_factory);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
|
|
return gst_macos_main (tutorial_main, argc, argv, NULL);
|
|
#else
|
|
return tutorial_main (argc, argv);
|
|
#endif
|
|
}
|