Advertisement
Guest User

main.c

a guest
Jun 23rd, 2019
448
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.35 KB | None | 0 0
  1. /* We are trying to replicate
  2.     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
  3.  
  4. Use "gcc -o test main.c `pkg-config --libs --cflags gtk+-3.0 gstreamer-1.0`" to compile.
  5.  
  6. Sorry but I was too lazy to add error checking for every single possible failure, but I did add error checking for the most
  7. frequent failures. I hope you don't mind. */
  8.  
  9. #include <gtk/gtk.h>
  10. #include <gst/gst.h>
  11.  
  12. // Some basic options.
  13. #define CAMERA_PATH "/dev/video0"   // Path to the camera input file.
  14. #define FILE_NAME "/tmp/test.mkv"   // Path of the output.
  15. #define PREVIEW_SYNC FALSE      // Synchronize the screen output?
  16.  
  17. // State of the pipeline.
  18. static gboolean recording = TRUE;
  19.  
  20. // Return codes, first one designates success, all others various failure errors.
  21. enum ReturnCodes {
  22.             RETURN_SUCCESS,             // Everything is OK (no error).
  23.             RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS, // At least one pipeline element could not be created.
  24.             RETURN_CANNOT_LINK_PIPELINE_ELEMENTS,   // At least one pipeline element could not be linked.
  25.         };
  26.  
  27. // Widgets constituting our window.
  28. struct Window
  29. {
  30.     GtkWidget *window;      // Window itself.
  31.     GtkWidget *box;         // Box attached to the window, holds other elements.
  32.     GtkWidget *button_pause;    // Button that pauses recording of the video.
  33.     GtkWidget *button_stop;     // Button that stops recording of the video, closing the application.
  34. };
  35.  
  36. // Our pipeline for recording and displaying video.
  37. struct Pipeline
  38. {
  39.     GstElement *pipeline;       // Pipeline itself, contains other elements.
  40.     GstElement *source;     // Video source (camera).
  41.     GstElement *converter;      // To convert source data to raw video stream.
  42.     GstElement *queue_source;   // Queue collecting converted video source data.
  43.     GstElement *tee;        // Tee connecting video source to the preview window and file.
  44.     GstPad *tee_src_preview;    // Tee source pad connected to the preview.
  45.     GstPad *tee_src_file;       // Tee source pad connected to the file.
  46.     GstElement *queue_preview;  // Collects data from preview pad of the tee and forwards it toward the preview window.
  47.     GstElement *preview;        // Preview window.
  48.     GstElement *queue_format;   // Collects data from file pad of the tee and forwards it toward formatting sink.
  49.     GstElement *format;     // Encodes the data into a video format (here MKV).
  50.     GstPad *format_sink_video;  // Video sink pad of the formatter.
  51.     GstElement *queue_file;     // Forwards the encoded data toward a file / sink if output is unpaused / paused, respectively.
  52.     GstElement *sink_file;      // File to which we write video source.
  53.     GstElement *sink_fake;      // Fake sink that discards anything that's written to it, we will use it for implementing pause functionality.
  54. };
  55.  
  56. static void print_error(const int error_code)
  57. {
  58.     g_printerr("Error: ");
  59.  
  60.     // Print error according to the supplied error code.
  61.     switch (error_code)
  62.     {
  63.         case RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS:
  64.             g_printerr("Not all pipeline elements could be created.");
  65.             break;
  66.         case RETURN_CANNOT_LINK_PIPELINE_ELEMENTS:
  67.             g_printerr("Not all pipeline elements could be linked.");
  68.             break;
  69.         default:
  70.             g_printerr("Unknown error code.");
  71.     }
  72.  
  73.     g_printerr("\n");
  74. }
  75.  
  76. static void action_pause(GtkWidget *button, struct Pipeline * const pipeline)
  77. {
  78.     // Get the current recording state.
  79.     recording = !recording;
  80.  
  81.     // Change button's text accordingly and replace file sink with fake sink
  82.     // to pause the recording or fake sink with file sink to resume it.
  83.     if (recording)  // We're recording, unpause.
  84.     {
  85.         // Replace fake sink with file sink.
  86.         if (gst_element_set_state(pipeline->pipeline, GST_STATE_PAUSED))
  87.         gst_element_set_state(pipeline->sink_fake, GST_STATE_NULL);
  88.         gst_element_unlink(pipeline->queue_file, pipeline->sink_fake);
  89.         gst_bin_remove(GST_BIN(pipeline->pipeline), pipeline->sink_fake);
  90.         gst_bin_add(GST_BIN(pipeline->pipeline), pipeline->sink_file);
  91.         gst_element_link(pipeline->queue_file, pipeline->sink_file);
  92.         gst_element_set_state(pipeline->pipeline, GST_STATE_PLAYING);
  93.  
  94.         // Change button's text so it says "Pause".
  95.         gtk_button_set_label(GTK_BUTTON(button), "Pause");
  96.     }
  97.     else    // We're not recording, pause.
  98.     {
  99.         // Replace file sink with fake sink.
  100.         gst_element_set_state(pipeline->pipeline, GST_STATE_PAUSED);
  101.         gst_element_set_state(pipeline->sink_file, GST_STATE_NULL);
  102.         gst_element_unlink(pipeline->queue_file, pipeline->sink_file);
  103.         gst_bin_remove(GST_BIN(pipeline->pipeline), pipeline->sink_file);
  104.         gst_bin_add(GST_BIN(pipeline->pipeline), pipeline->sink_fake);
  105.         gst_element_link(pipeline->queue_file, pipeline->sink_fake);
  106.         gst_element_set_state(pipeline->pipeline, GST_STATE_PLAYING);
  107.  
  108.         // Change button's text so it says "Unpause".
  109.         gtk_button_set_label(GTK_BUTTON(button), "Unpause");
  110.     }
  111. }
  112.  
  113. static void action_stop(GtkWidget *button, struct Pipeline * const pipeline)
  114. {
  115.     gst_element_send_event(pipeline->pipeline, gst_event_new_eos());
  116.  
  117.     GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline->pipeline));
  118.     GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS);
  119.     gst_object_unref(bus);
  120.  
  121.     gst_message_unref(msg);
  122.  
  123.     // Destroy all internal components of the pipeline.
  124.     gst_element_set_state(pipeline->pipeline, GST_STATE_NULL);
  125.  
  126.     /* The following is not really necessary.
  127.     // Unlink all elements.
  128.     if (recording)
  129.     {
  130.         gst_element_unlink(pipeline->queue_file, pipeline->sink_file);
  131.         gst_bin_remove(GST_BIN(pipeline->pipeline), pipeline->sink_file);
  132.     }
  133.     else
  134.     {
  135.         gst_element_unlink(pipeline->queue_file, pipeline->sink_fake);
  136.         gst_bin_remove(GST_BIN(pipeline->pipeline), pipeline->sink_fake);
  137.     }
  138.     gst_element_unlink(pipeline->format, pipeline->queue_file);
  139.     gst_pad_unlink(gst_element_get_static_pad(pipeline->queue_format, "src"), pipeline->format_sink_video);
  140.     gst_pad_unlink(pipeline->tee_src_file, gst_element_get_static_pad(pipeline->queue_format, "sink"));
  141.     gst_element_unlink(pipeline->queue_preview, pipeline->preview);
  142.     gst_pad_unlink(pipeline->tee_src_preview, gst_element_get_static_pad(pipeline->queue_preview, "sink"));
  143.     gst_element_unlink_many(pipeline->source, pipeline->queue_source, pipeline->tee, NULL);
  144.  
  145.     // Remove all remaining elements.
  146.     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);
  147.     */
  148.  
  149.     // Destroy all requested pads.
  150.     gst_object_unref(pipeline->tee_src_preview);
  151.     gst_object_unref(pipeline->tee_src_file);
  152.     gst_object_unref(pipeline->format_sink_video);
  153.  
  154.     // Destroy the pipeline.
  155.     gst_object_unref(pipeline->pipeline);
  156.  
  157.     // Terminate the application.
  158.     gtk_main_quit();
  159. }
  160.  
  161. static void create_window(struct Window * const window)
  162. {
  163.     // Create window and set its properties.
  164.     window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  165.     gtk_window_set_default_size(GTK_WINDOW(window->window), 100, 100);
  166.     gtk_window_set_title(GTK_WINDOW(window->window), "Test GStreamer");
  167.     gtk_window_set_resizable(GTK_WINDOW(window->window), FALSE);
  168.  
  169.     // Create main box and populate it with buttons.
  170.     window->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  171.     window->button_pause = gtk_button_new_with_label("Pause");
  172.     window->button_stop = gtk_button_new_with_label("Stop");
  173.     gtk_box_pack_start(GTK_BOX(window->box), window->button_pause, TRUE, TRUE, 0);
  174.     gtk_box_pack_start(GTK_BOX(window->box), window->button_stop, TRUE, TRUE, 0);
  175.  
  176.     // Attach everything to the window.
  177.     gtk_container_add(GTK_CONTAINER(window->window), window->box);
  178. }
  179.  
  180. static void connect_signals(struct Window * const window, struct Pipeline * const pipeline)
  181. {
  182.     // Connect widgets signals to event handlers.
  183.     g_signal_connect(window->window, "delete-event", G_CALLBACK(action_stop), pipeline);
  184.     g_signal_connect(window->button_pause, "clicked", G_CALLBACK(action_pause), pipeline);
  185.     g_signal_connect(window->button_stop, "clicked", G_CALLBACK(action_stop), pipeline);
  186. }
  187.  
  188. static int create_pipeline(struct Pipeline * const pipeline)
  189. {
  190.     // Create pipeline to hold all GStreamer elements.
  191.     pipeline->pipeline = gst_pipeline_new(NULL);
  192.  
  193.     // Create all needed gstreamer elements.
  194.     pipeline->source = gst_element_factory_make("v4l2src", NULL);
  195.     pipeline->converter = gst_element_factory_make("videoconvert", NULL);
  196.     pipeline->queue_source = gst_element_factory_make("queue", NULL);
  197.     pipeline->tee = gst_element_factory_make("tee", NULL);
  198.     pipeline->tee_src_preview = gst_element_get_request_pad(pipeline->tee, "src_%u");
  199.     pipeline->tee_src_file = gst_element_get_request_pad(pipeline->tee, "src_%u");
  200.     pipeline->queue_preview = gst_element_factory_make("queue", NULL);
  201.     pipeline->preview = gst_element_factory_make("autovideosink", NULL);
  202.     pipeline->queue_format = gst_element_factory_make("queue", NULL);
  203.     pipeline->format = gst_element_factory_make("matroskamux", NULL);
  204.     pipeline->format_sink_video = gst_element_get_request_pad(pipeline->format, "video_%u");
  205.     pipeline->queue_file = gst_element_factory_make("queue", NULL);
  206.     pipeline->sink_file = gst_element_factory_make("filesink", NULL);
  207.     pipeline->sink_fake = gst_element_factory_make("fakesink", NULL);
  208.  
  209.     // Check if all elements could be created.
  210.     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)
  211.     {
  212.         print_error(RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS);
  213.         return RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS;
  214.     }
  215.  
  216.     // Set some element properties.
  217.     g_object_set(pipeline->source, "device", CAMERA_PATH, NULL);
  218.     g_object_set(pipeline->sink_file, "location", FILE_NAME, "async", 0, NULL);
  219.     g_object_set(pipeline->preview, "sync", PREVIEW_SYNC, NULL);
  220.  
  221.     // Add all elements to the pipeline (except for fake sink, since the default state is recording).
  222.     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);
  223.  
  224.     // Link all elements together appropriately.
  225.     if (!gst_element_link_many(pipeline->source, pipeline->converter, pipeline->queue_source, pipeline->tee, NULL))
  226.     {
  227.         print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
  228.         return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
  229.     }
  230.     if (gst_pad_link(pipeline->tee_src_preview, gst_element_get_static_pad(pipeline->queue_preview, "sink")) != GST_PAD_LINK_OK)
  231.     {
  232.         print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
  233.         return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
  234.     }
  235.     if (!gst_element_link(pipeline->queue_preview, pipeline->preview))
  236.     {
  237.         print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
  238.         return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
  239.     }
  240.     if (gst_pad_link(pipeline->tee_src_file, gst_element_get_static_pad(pipeline->queue_format, "sink")) != GST_PAD_LINK_OK)
  241.     {
  242.         print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
  243.         return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
  244.     }
  245.     if (gst_pad_link(gst_element_get_static_pad(pipeline->queue_format, "src"), pipeline->format_sink_video) != GST_PAD_LINK_OK)
  246.     {
  247.         print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
  248.         return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
  249.     }
  250.     if (!gst_element_link_many(pipeline->format, pipeline->queue_file, pipeline->sink_file, NULL))
  251.     {
  252.         print_error(RETURN_CANNOT_LINK_PIPELINE_ELEMENTS);
  253.         return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
  254.     }
  255.  
  256.     return RETURN_SUCCESS;
  257. }
  258.  
  259. int main()
  260. {
  261.     // Initialize our librarires.
  262.     gst_init(NULL, NULL);
  263.     gtk_init(NULL, NULL);
  264.  
  265.     // Create window and pipeline data structures.
  266.     struct Window window;
  267.     struct Pipeline pipeline;
  268.  
  269.     // Create window and populate it.
  270.     create_window(&window);
  271.  
  272.     // Create pipeline.
  273.     switch (create_pipeline(&pipeline))
  274.     {
  275.         case RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS:
  276.             return RETURN_CANNOT_CREATE_PIPELINE_ELEMENTS;
  277.         case RETURN_CANNOT_LINK_PIPELINE_ELEMENTS:
  278.             return RETURN_CANNOT_LINK_PIPELINE_ELEMENTS;
  279.     }
  280.  
  281.     // To connect button events to function handlers.
  282.     connect_signals(&window, &pipeline);
  283.  
  284.     // Start the program!
  285.     gst_element_set_state(pipeline.pipeline, GST_STATE_PLAYING);
  286.     gtk_widget_show_all(window.window);
  287.     gtk_main();
  288.  
  289.     return RETURN_SUCCESS;
  290. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement