Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* We are trying to replicate
- gst-launch-1.0 -e v4l2src device=/dev/video0 ! videoconvert ! queue ! tee name=t t. ! matroskamux ! queue ! filesink location=/tmp/test.mkv t. ! queue ! autovideosink sync=false
- Use "gcc -o test main.c `pkg-config --libs --cflags gtk+-3.0 gstreamer-1.0`" to compile.
- Sorry but I was too lazy to add error checking for every single possible failure, but I did add error checking for the most
- frequent failures. I hope you don't mind. */
- #include <gtk/gtk.h>
- #include <gst/gst.h>
- // Some basic options.
- #define CAMERA_PATH "/dev/video0" // Path to the camera input file.
- #define FILE_NAME "/tmp/test.mkv" // Path of the output.
- #define PREVIEW_SYNC FALSE // Synchronize the screen output?
- // State of the pipeline.
- static gboolean recording = TRUE;
- // Return codes, first one designates success, all others various failure errors.
- enum ReturnCodes {
- RETURN_SUCCESS, // Everything is OK (no error).
- RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS, // At least one pipeline element could not be created.
- RETURN_CANNOT_LINK_PIPELINE_ELEMENTS, // At least one pipeline element could not be linked.
- };
- // Widgets constituting our window.
- struct Window
- {
- GtkWidget *window; // Window itself.
- GtkWidget *box; // Box attached to the window, holds other elements.
- GtkWidget *button_pause; // Button that pauses recording of the video.
- GtkWidget *button_stop; // Button that stops recording of the video, closing the application.
- };
- // Our pipeline for recording and displaying video.
- struct Pipeline
- {
- GstElement *pipeline; // Pipeline itself, contains other elements.
- GstElement *source; // Video source (camera).
- GstElement *converter; // To convert source data to raw video stream.
- GstElement *queue_source; // Queue collecting converted video source data.
- GstElement *tee; // Tee connecting video source to the preview window and file.
- GstPad *tee_src_preview; // Tee source pad connected to the preview.
- GstPad *tee_src_file; // Tee source pad connected to the file.
- GstElement *queue_preview; // Collects data from preview pad of the tee and forwards it toward the preview window.
- GstElement *preview; // Preview window.
- GstElement *queue_format; // Collects data from file pad of the tee and forwards it toward formatting sink.
- GstElement *format; // Encodes the data into a video format (here MKV).
- GstPad *format_sink_video; // Video sink pad of the formatter.
- GstElement *queue_file; // Forwards the encoded data toward a file / sink if output is unpaused / paused, respectively.
- GstElement *sink_file; // File to which we write video source.
- GstElement *sink_fake; // Fake sink that discards anything that's written to it, we will use it for implementing pause functionality.
- };
- static void print_error(const int error_code)
- {
- g_printerr("Error: ");
- // Print error according to the supplied error code.
- switch (error_code)
- {
- case RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS:
- g_printerr("Not all pipeline elements could be created.");
- break;
- case RETURN_CANNOT_LINK_PIPELINE_ELEMENTS:
- g_printerr("Not all pipeline elements could be linked.");
- break;
- default:
- g_printerr("Unknown error code.");
- }
- g_printerr("\n");
- }
- static void action_pause(GtkWidget *button, struct Pipeline * const pipeline)
- {
- // Get the current recording state.
- recording = !recording;
- // Change button's text accordingly and replace file sink with fake sink
- // to pause the recording or fake sink with file sink to resume it.
- if (recording) // We're recording, unpause.
- {
- // Replace fake sink with file sink.
- if (gst_element_set_state(pipeline->pipeline, GST_STATE_PAUSED))
- gst_element_set_state(pipeline->sink_fake, GST_STATE_NULL);
- gst_element_unlink(pipeline->queue_file, pipeline->sink_fake);
- gst_bin_remove(GST_BIN(pipeline->pipeline), pipeline->sink_fake);
- gst_bin_add(GST_BIN(pipeline->pipeline), pipeline->sink_file);
- gst_element_link(pipeline->queue_file, pipeline->sink_file);
- gst_element_set_state(pipeline->pipeline, GST_STATE_PLAYING);
- // Change button's text so it says "Pause".
- gtk_button_set_label(GTK_BUTTON(button), "Pause");
- }
- else // We're not recording, pause.
- {
- // Replace file sink with fake sink.
- gst_element_set_state(pipeline->pipeline, GST_STATE_PAUSED);
- gst_element_set_state(pipeline->sink_file, GST_STATE_NULL);
- gst_element_unlink(pipeline->queue_file, pipeline->sink_file);
- gst_bin_remove(GST_BIN(pipeline->pipeline), pipeline->sink_file);
- gst_bin_add(GST_BIN(pipeline->pipeline), pipeline->sink_fake);
- gst_element_link(pipeline->queue_file, pipeline->sink_fake);
- gst_element_set_state(pipeline->pipeline, GST_STATE_PLAYING);
- // Change button's text so it says "Unpause".
- gtk_button_set_label(GTK_BUTTON(button), "Unpause");
- }
- }
- static void action_stop(GtkWidget *button, struct Pipeline * const pipeline)
- {
- gst_element_send_event(pipeline->pipeline, gst_event_new_eos());
- GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline->pipeline));
- GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS);
- gst_object_unref(bus);
- gst_message_unref(msg);
- // Destroy all internal components of the pipeline.
- gst_element_set_state(pipeline->pipeline, GST_STATE_NULL);
- /* The following is not really necessary.
- // Unlink all elements.
- if (recording)
- {
- gst_element_unlink(pipeline->queue_file, pipeline->sink_file);
- gst_bin_remove(GST_BIN(pipeline->pipeline), pipeline->sink_file);
- }
- else
- {
- gst_element_unlink(pipeline->queue_file, pipeline->sink_fake);
- gst_bin_remove(GST_BIN(pipeline->pipeline), pipeline->sink_fake);
- }
- gst_element_unlink(pipeline->format, pipeline->queue_file);
- gst_pad_unlink(gst_element_get_static_pad(pipeline->queue_format, "src"), pipeline->format_sink_video);
- gst_pad_unlink(pipeline->tee_src_file, gst_element_get_static_pad(pipeline->queue_format, "sink"));
- gst_element_unlink(pipeline->queue_preview, pipeline->preview);
- gst_pad_unlink(pipeline->tee_src_preview, gst_element_get_static_pad(pipeline->queue_preview, "sink"));
- gst_element_unlink_many(pipeline->source, pipeline->queue_source, pipeline->tee, NULL);
- // Remove all remaining elements.
- gst_bin_remove_many(GST_BIN(pipeline->pipeline), pipeline->source, pipeline->queue_source, pipeline->tee, pipeline->queue_preview, pipeline->preview, pipeline->queue_format, pipeline->format, pipeline->queue_file, NULL);
- */
- // Destroy all requested pads.
- gst_object_unref(pipeline->tee_src_preview);
- gst_object_unref(pipeline->tee_src_file);
- gst_object_unref(pipeline->format_sink_video);
- // Destroy the pipeline.
- gst_object_unref(pipeline->pipeline);
- // Terminate the application.
- gtk_main_quit();
- }
- static void create_window(struct Window * const window)
- {
- // Create window and set its properties.
- window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- gtk_window_set_default_size(GTK_WINDOW(window->window), 100, 100);
- gtk_window_set_title(GTK_WINDOW(window->window), "Test GStreamer");
- gtk_window_set_resizable(GTK_WINDOW(window->window), FALSE);
- // Create main box and populate it with buttons.
- window->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- window->button_pause = gtk_button_new_with_label("Pause");
- window->button_stop = gtk_button_new_with_label("Stop");
- gtk_box_pack_start(GTK_BOX(window->box), window->button_pause, TRUE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(window->box), window->button_stop, TRUE, TRUE, 0);
- // Attach everything to the window.
- gtk_container_add(GTK_CONTAINER(window->window), window->box);
- }
- static void connect_signals(struct Window * const window, struct Pipeline * const pipeline)
- {
- // Connect widgets signals to event handlers.
- g_signal_connect(window->window, "delete-event", G_CALLBACK(action_stop), pipeline);
- g_signal_connect(window->button_pause, "clicked", G_CALLBACK(action_pause), pipeline);
- g_signal_connect(window->button_stop, "clicked", G_CALLBACK(action_stop), pipeline);
- }
- static int create_pipeline(struct Pipeline * const pipeline)
- {
- // Create pipeline to hold all GStreamer elements.
- pipeline->pipeline = gst_pipeline_new(NULL);
- // Create all needed gstreamer elements.
- pipeline->source = gst_element_factory_make("v4l2src", NULL);
- pipeline->converter = gst_element_factory_make("videoconvert", NULL);
- pipeline->queue_source = gst_element_factory_make("queue", NULL);
- pipeline->tee = gst_element_factory_make("tee", NULL);
- pipeline->tee_src_preview = gst_element_get_request_pad(pipeline->tee, "src_%u");
- pipeline->tee_src_file = gst_element_get_request_pad(pipeline->tee, "src_%u");
- pipeline->queue_preview = gst_element_factory_make("queue", NULL);
- pipeline->preview = gst_element_factory_make("autovideosink", NULL);
- pipeline->queue_format = gst_element_factory_make("queue", NULL);
- pipeline->format = gst_element_factory_make("matroskamux", NULL);
- pipeline->format_sink_video = gst_element_get_request_pad(pipeline->format, "video_%u");
- pipeline->queue_file = gst_element_factory_make("queue", NULL);
- pipeline->sink_file = gst_element_factory_make("filesink", NULL);
- pipeline->sink_fake = gst_element_factory_make("fakesink", NULL);
- // Check if all elements could be created.
- if (!pipeline->source || !pipeline->converter || !pipeline->queue_source || !pipeline->tee || !pipeline->tee_src_preview || !pipeline->tee_src_file || !pipeline->queue_preview || !pipeline->preview || !pipeline->queue_format || !pipeline->format || !pipeline->format_sink_video || !pipeline->queue_file || !pipeline->sink_file || !pipeline->sink_fake)
- {
- print_error(RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS);
- return RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS;
- }
- // Set some element properties.
- g_object_set(pipeline->source, "device", CAMERA_PATH, NULL);
- g_object_set(pipeline->sink_file, "location", FILE_NAME, "async", 0, NULL);
- g_object_set(pipeline->preview, "sync", PREVIEW_SYNC, NULL);
- // Add all elements to the pipeline (except for fake sink, since the default state is recording).
- gst_bin_add_many(GST_BIN(pipeline->pipeline), pipeline->source, pipeline->converter, pipeline->queue_source, pipeline->tee, pipeline->queue_preview, pipeline->preview, pipeline->queue_format, pipeline->format, pipeline->queue_file, pipeline->sink_file, NULL);
- // Link all elements together appropriately.
- if (!gst_element_link_many(pipeline->source, pipeline->converter, pipeline->queue_source, pipeline->tee, NULL))
- {
- print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
- return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
- }
- if (gst_pad_link(pipeline->tee_src_preview, gst_element_get_static_pad(pipeline->queue_preview, "sink")) != GST_PAD_LINK_OK)
- {
- print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
- return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
- }
- if (!gst_element_link(pipeline->queue_preview, pipeline->preview))
- {
- print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
- return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
- }
- if (gst_pad_link(pipeline->tee_src_file, gst_element_get_static_pad(pipeline->queue_format, "sink")) != GST_PAD_LINK_OK)
- {
- print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
- return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
- }
- if (gst_pad_link(gst_element_get_static_pad(pipeline->queue_format, "src"), pipeline->format_sink_video) != GST_PAD_LINK_OK)
- {
- print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
- return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
- }
- if (!gst_element_link_many(pipeline->format, pipeline->queue_file, pipeline->sink_file, NULL))
- {
- print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
- return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
- }
- return RETURN_SUCCESS;
- }
- int main()
- {
- // Initialize our librarires.
- gst_init(NULL, NULL);
- gtk_init(NULL, NULL);
- // Create window and pipeline data structures.
- struct Window window;
- struct Pipeline pipeline;
- // Create window and populate it.
- create_window(&window);
- // Create pipeline.
- switch (create_pipeline(&pipeline))
- {
- case RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS:
- return RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS;
- case RETURN_CANNOT_LINK_PIPELINE_ELEMENTS:
- return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
- }
- // To connect button events to function handlers.
- connect_signals(&window, &pipeline);
- // Start the program!
- gst_element_set_state(pipeline.pipeline, GST_STATE_PLAYING);
- gtk_widget_show_all(window.window);
- gtk_main();
- return RETURN_SUCCESS;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement