How to embed video in GTK+ application window using GStreamer & XOverlay? - c++

I am trying to write a small media player using GTK+ and GStreamer and currently using the XOverlay Interface to embed the video in a GtkDrawing Area INSIDE the mainwindow.
The program was compiled using this command:
g++ /home/phongcao/cacao.cc -o /home/phongcao/cacao `pkg-config --cflags --libs gtk+-2.0 gstreamer-0.10 gstreamer-plugins-base-0.10 gstreamer-interfaces-0.10`
The problem is that the video was displayed in a SEPARATED window (instead of under the toolbar of the main window):
Here is the source code of the program:
#include <gst/interfaces/xoverlay.h>
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gdk/gdkx.h>
GstElement *play;
GtkAdjustment *progress;
GtkWidget *mainwindow, *drawingarea;
class TopWin
{
public:
TopWin();
~TopWin();
int Initialize(int argc, char *argv[]);
int Execute();
static void FileChooser(GtkButton *button, GtkWindow *mainwindow);
static int Play(gchar *addr);
static gboolean print_position(GstElement *element);
private:
};
TopWin::TopWin() {
}
TopWin::~TopWin() {
}
gboolean TopWin::print_position(GstElement *play) {
GstFormat fmt = GST_FORMAT_TIME;
gint64 pos, len;
if (gst_element_query_position(play, &fmt, &pos) && gst_element_query_duration(play, &fmt, &len)) {
g_print("Time: %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT "\r", GST_TIME_ARGS(pos), GST_TIME_ARGS(len));
gtk_adjustment_set_value(GTK_ADJUSTMENT(progress), (pos*100)/len);
}
return TRUE;
}
int TopWin::Play(gchar *addr) {
GMainLoop *loop;
GstBus *bus;
loop = g_main_loop_new(NULL, FALSE);
play = gst_element_factory_make("playbin", "play");
g_object_set(G_OBJECT(play), "uri", addr, NULL);
bus = gst_pipeline_get_bus(GST_PIPELINE(play));
gst_object_unref(bus);
GstElement* x_overlay = gst_element_factory_make("xvimagesink", "videosink");
g_object_set(G_OBJECT(play), "video-sink", x_overlay, NULL);
gst_x_overlay_set_window_handle(GST_X_OVERLAY(x_overlay), GDK_WINDOW_XID(drawingarea->window));
gst_element_set_state(play, GST_STATE_NULL);
g_timeout_add(1000, (GSourceFunc) print_position, play);
gtk_adjustment_set_value(GTK_ADJUSTMENT(progress), 0);
gst_element_set_state(play, GST_STATE_PLAYING);
g_main_loop_run(loop);
gst_element_set_state(play, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(play));
gtk_widget_show_all(mainwindow);
gtk_widget_realize(drawingarea);
return 0;
}
void TopWin::FileChooser(GtkButton *button, GtkWindow *mainwindow) {
GtkWidget *filechooser;
gchar *uri;
filechooser = gtk_file_chooser_dialog_new("Open File...", mainwindow, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser), FALSE);
gint response = gtk_dialog_run(GTK_DIALOG(filechooser));
if (response == GTK_RESPONSE_OK) {
uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(filechooser));
gtk_widget_destroy(filechooser);
Play(uri);
g_free(uri);
}
else if (response == GTK_RESPONSE_CANCEL) {
gtk_widget_destroy(filechooser);
}
}
int TopWin::Initialize(int argc, char *argv[]) {
GtkWidget *playbutton, *openbutton, *volumebutton;
GtkWidget *prefbutton, *notebook;
GtkWidget *vbox, *hbox;
GtkWidget *entry, *hscale;
gtk_init(&argc, &argv);
gst_init(&argc, &argv);
mainwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(mainwindow), 0);
g_signal_connect(G_OBJECT(mainwindow), "destroy", G_CALLBACK(gtk_main_quit), NULL);
playbutton = gtk_button_new();
gtk_button_set_image(GTK_BUTTON(playbutton), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_SMALL_TOOLBAR));
openbutton = gtk_button_new();
gtk_button_set_image(GTK_BUTTON(openbutton), gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_SMALL_TOOLBAR));
g_signal_connect(G_OBJECT(openbutton), "clicked", G_CALLBACK(TopWin::FileChooser), (gpointer) mainwindow);
volumebutton = gtk_button_new();
gtk_button_set_image(GTK_BUTTON(volumebutton), gtk_image_new_from_file("volume.png"));
prefbutton = gtk_button_new();
gtk_button_set_image(GTK_BUTTON(prefbutton), gtk_image_new_from_stock(GTK_STOCK_EXECUTE, GTK_ICON_SIZE_SMALL_TOOLBAR));
entry = gtk_entry_new();
progress = GTK_ADJUSTMENT(gtk_adjustment_new(0.00, 0.00, 100.00, 1.00, 0.00, 0.00));
hscale = gtk_hscale_new(progress);
gtk_scale_set_draw_value(GTK_SCALE(hscale), FALSE);
gtk_widget_set_size_request(hscale, 200, NULL);
hbox = gtk_hbox_new(FALSE, 0);
drawingarea = gtk_drawing_area_new();
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), openbutton, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(hbox), playbutton, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(hbox), hscale, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(hbox), volumebutton, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 2);
gtk_box_pack_start(GTK_BOX(hbox), prefbutton, FALSE, FALSE, 2);
gtk_button_set_relief(GTK_BUTTON(playbutton), GTK_RELIEF_NONE);
gtk_button_set_relief(GTK_BUTTON(openbutton), GTK_RELIEF_NONE);
gtk_button_set_relief(GTK_BUTTON(volumebutton), GTK_RELIEF_NONE);
gtk_button_set_relief(GTK_BUTTON(prefbutton), GTK_RELIEF_NONE);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), drawingarea, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(mainwindow), vbox);
gtk_widget_show_all(mainwindow);
gtk_widget_realize(drawingarea);
return 0;
}
int TopWin::Execute() {
gtk_main();
return 0;
}
int main(int argc, char *argv[])
{
int result = 0;
TopWin* topwin = new TopWin();
if (0 == topwin->Initialize(argc, argv)) {
result = topwin->Execute();
}
delete topwin;
return result;
}
Thank you for helping me with this problem! I have spent almost 3 days scratching over my head for this. The XOverlay reference on GStreamer website is so confusing... :(
Please tell me if you need any additional information... Thank you!!

You need to do something like this:
GstElement* x_overlay=gst_element_factory_make ("xvimagesink", "videosink");
g_object_set(G_OBJECT(play),"video-sink",x_overlay,NULL);
gst_x_overlay_set_window_handle(GST_X_OVERLAY(x_overlay), GDK_WINDOW_XID(drawingarea->window));
Create new XV video sink. Set it as video sink of your playbin. Attach xv video sink to your drawingarea window ID. You also need to add drawingarea to some container before that.
Your program produces warnings and gtk errors, they may be source of some of your problems better fix them.

gst_x_overlay_set_window_handle interface is deprecated now in recent gstreamer library. New interface is gst_video_overlay_set_window_handle. A simple supplementation can be sited from https://web.archive.org/web/20190628124320/http://wikistack.com/how-to-make-your-own-media-player-in-linux-using-gtk-and-gstreamer/

Related

Set Cell in GtkTreeView to edit mode programmatically

I currently write an application that has a GtkTreeView with a GtkCellRendererText which property editable is set to true. When double clicking an item, I can edit it. Now I want to be able to add an empty row and immediately start editing it. I tried using gtk_tree_view_set_cursor_on_cell with start_editing set to true. It selects the row, but it does not start editing. I put together a small example (please not that I do not have any error checking in place as its just a small example).
#include <gtk/gtk.h>
GtkListStore *store;
GtkWidget *window;
GtkWidget *view;
GtkCellRenderer *renderer;
GtkWidget *button;
GtkWidget *vbox;
void sig_inserted(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter)
{
// Set cursor on cell with start_editing = TRUE
gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(view), path, gtk_tree_view_get_column(GTK_TREE_VIEW(view), 0), renderer, TRUE);
}
void button_clicked(GtkButton *button, GdkEvent *event, gpointer user_data)
{
// Add empty row to liststore
GtkTreeIter iter;
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, 0, "", -1);
}
int main (int argc, char **argv)
{
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "delete_event", gtk_main_quit, NULL);
view = gtk_tree_view_new ();
// Create renderer and set editable to TRUE
renderer = gtk_cell_renderer_text_new ();
GValue val = G_VALUE_INIT;
g_value_init(&val, G_TYPE_BOOLEAN);
g_value_set_boolean(&val, TRUE);
g_object_set_property(G_OBJECT(renderer), "editable", &val);
g_value_unset(&val);
// Insert Text column
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Name",
renderer,
"text", 0,
NULL);
store = gtk_list_store_new (1, G_TYPE_STRING);
// Add test item
GtkTreeIter iter;
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
0, "Test",
-1);
gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL(store));
vbox = gtk_vbox_new(0,0);
button = gtk_button_new_with_label("Add edit item");
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show_all (window);
g_signal_connect(G_OBJECT(store), "row-inserted", G_CALLBACK(sig_inserted), NULL);
g_signal_connect(G_OBJECT(button), "button-release-event", G_CALLBACK(button_clicked), NULL);
gtk_main ();
return 0;
}
Compile it with g++ (as I do not comply with all C needs) using
g++ -o tree tree.cc `pkg-config --libs --cflags gtk+-2.0`
It would also work with Gtk3 I think as nothing seems to have changed in the things I use.
Can somebody point me out why it does not start editing?
gtk_list_store_set cancels editing of a cell. Commenting out this line makes everything work.
It can be found this with a little modification to code:
void sig_inserted(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter)
{
GtkTreeViewColumn * col = gtk_tree_view_get_column(GTK_TREE_VIEW(view), 0);
// Set cursor on cell with start_editing = TRUE
g_message ("sig_inserted", __LINE__);
gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(view), path, col, renderer, TRUE);
}
void button_clicked(GtkButton *button, GdkEvent *event, gpointer user_data)
{
// Add empty row to liststore
GtkTreeIter iter;
g_message ("Clicked");
gtk_list_store_append(store, &iter);
g_message ("Appended");
gtk_list_store_set(store, &iter, 0, "x", -1);
g_message ("Set");
}
void
estart (GtkCellRenderer *renderer,
GtkCellEditable *editable,
gchar *path,
gpointer user_data)
{
g_message ("renderer: start edit");
}
void
ecancel (GtkCellRenderer *renderer,
gpointer user_data)
{
g_message ("renderer: cancel edit");
}
And connecting to renderers signals:
g_signal_connect(G_OBJECT(renderer), "editing-started", G_CALLBACK(estart), NULL);
g_signal_connect(G_OBJECT(renderer), "editing-canceled", G_CALLBACK(ecancel), NULL);
Give the following output:
** Message: Clicked
** Message: sig_inserted
** Message: renderer: start edit
** Message: Appended
** Message: renderer: cancel edit
** Message: Set

Gtk Progress bar height

I'm developing a Gtk application in c++. (Gtk 3.0). My problem is that I can't change ProgressBar height!
(https://developer.gnome.org/gtk3/stable/GtkProgressBar.html)
The picture below will show what I need. In general I would like to have a possibility to set up height of progress bar in my code.progress bar height
I'm using following command to compile my example:
gcc progress.cpp `pkg-config --cflags gtk+-3.0` `pkg-config --libs gtk+-3.0
This the sample of my code:
#include <gtk/gtk.h>
static gboolean
fill (gpointer user_data)
{
GtkWidget *progress_bar = user_data;
/*Get the current progress*/
gdouble fraction;
fraction = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (progress_bar));
/*Increase the bar by 10% each time this function is called*/
fraction += 0.1;
/*Fill in the bar with the new fraction*/
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar), fraction);
/*Ensures that the fraction stays below 1.0*/
if (fraction < 1.0)
return TRUE;
return FALSE;
}
static void
activate (GtkApplication *app,
gpointer user_data)
{
GtkWidget *window;
GtkWidget *progress_bar;
gdouble fraction = 0.0;
/*Create a window with a title, and a default size*/
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "ProgressBar Example");
gtk_window_set_default_size (GTK_WINDOW (window), 220, 20);
/*Create a progressbar and add it to the window*/
progress_bar = gtk_progress_bar_new ();
gtk_container_add (GTK_CONTAINER (window), progress_bar);
/*Fill in the given fraction of the bar. Has to be between 0.0-1.0 inclusive*/
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar), fraction);
/*Use the created fill function every 500 milliseconds*/
g_timeout_add (500, fill, GTK_PROGRESS_BAR (progress_bar));
gtk_widget_show_all (window);
}
int
main (int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
For GTK 3.20 and newer, when defining the CSS, it seems that, instead of using the progressbar class, you must use the progress and trough classes. This is what worked for me:
progress, trough {
min-height: 30px;
}

Dereferencing a String Vector in C++ when passing to GTK callback

I have this code which is trying to pass a vector to a callback function:
static void displayvecchoices( GtkWidget *widget, gpointer data ) {
std::vector<std::string> vecp = *(std::vector<std::string> *)(data);
std::cout<<"vec: "<<vecp[0]<<std::endl;
}
int main(int argc, char *argv[] ) {
GtkWidget *window;
GtkWidget *display;
gtk_init(&argc, &argv);
window = gtk_dialog_new ();
g_signal_connect (window, "destroy", G_CALLBACK (destroy), NULL);
gtk_window_fullscreen (GTK_WINDOW(window));
std::vector<std::string> vec;
vec.push_back("1");
display = gtk_button_new_with_label ("Display");
g_signal_connect_swapped (display, "clicked", G_CALLBACK (displayvecchoices), &vec);
gtk_widget_set_can_default (display, TRUE);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), display, TRUE, TRUE, 0);
gtk_widget_grab_default (display);
gtk_widget_show (display);
gtk_main();
return 0;
}
When the button display is clicked, I get an error of 'bad alloc' and the program crashes.
When I try this:
std::vector<std::string>* vecp = (std::vector<std::string> *)(data);
std::cout<<"vec: "<<(*vecp)[0]<<std::endl;
It prints out vec: but no element and the size is said to be 18446744073706420840 of the vector.
It looks, your callback arguments are swapped:
you should either use g_signal_connect_object instead of g_signal_connect_swapped or
change displayvecchoices declaration to
static void displayvecchoices(gpointer data, GtkWidget *widget)

Compilation for GTK+ using OpenGL

Context: I am learning development of GUI using GTK+. I also wanted to draw lines and circles on the GUI. So I started with the tutorials and I am stuck with the part of GtkGLArea. I am following the code given in the GTK+ documentation
The error:
glTrial.cpp:32:13: error: variable or field ‘on_realize’ declared void
on_realize (GtkGLarea *area)
^
glTrial.cpp:32:13: error: ‘GtkGLarea’ was not declared in this scope
glTrial.cpp:32:24: error: ‘area’ was not declared in this scope
on_realize (GtkGLarea *area)
I believe I am not compiling properly and the compiler is not able to find correct headers.
Compilation:
g++ -std=c++14 \`pkg-config --cflags gtk+-3.0\` -o glTrial glTrial.cpp \`pkg-config --libs gtk+-3.0\`
The code:
#include <gtk/gtk.h>
#include <gtkgl-2.0/gtkgl/gdkgl.h>
#include <gtkgl-2.0/gtkgl/gtkglarea.h>
static void
print_hello (GtkWidget *widget,
gpointer user_data)
{
g_print ("Hello World\n");
}
static gboolean
render (GtkGLArea *area, GdkGLContext *context)
{
// inside this function it's safe to use GL; the given
// #GdkGLContext has been made current to the drawable
// surface used by the #GtkGLArea and the viewport has
// already been set to be the size of the allocation
// we can start by clearing the buffer
//glClearColor (0, 0, 0, 0);
// glClear (GL_COLOR_BUFFER_BIT);
// draw your object
// draw_an_object ();
// we completed our drawing; the draw commands will be
// flushed at the end of the signal emission chain, and
// the buffers will be drawn on the window
return TRUE;
}
static void
on_realize (GtkGLarea *area)
{
// We need to make the context current if we want to
// call GL API
gtk_gl_area_make_current (area);
// If there were errors during the initialization or
// when trying to make the context current, this
// function will return a #GError for you to catch
if (gtk_gl_area_get_error (area) != NULL)
return;
// You can also use gtk_gl_area_set_error() in order
// to show eventual initialization errors on the
// GtkGLArea widget itself
GError *internal_error = NULL;
init_buffer_objects (&error);
if (error != NULL)
{
gtk_gl_area_set_error (area, error);
g_error_free (error);
return;
}
init_shaders (&error);
if (error != NULL)
{
gtk_gl_area_set_error (area, error);
g_error_free (error);
return;
}
}
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window;
GtkWidget *grid;
GtkWidget *button;
GtkWidget *gl_area =gtk_gl_area_new();
/* Create a new window, and set its title */
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "Window");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
// gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
/* Here we construct the container that is going to pack the buttons*/
grid = gtk_grid_new();
/* Pack the container in the window */
gtk_container_add (GTK_CONTAINER (window), grid);
button = gtk_button_new_with_label ("Quit");
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 2, 1);
button = gtk_button_new_with_label ("Hello");
g_signal_connect_swapped (button, "clicked", G_CALLBACK (print_hello), NULL);
gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 2, 1);
/* Trial for GL area*/
g_signal_connect (gl_area, "render", G_CALLBACK(render), NULL);
gtk_grid_attach (GTK_GRID (grid), gl_area, 0, 2, 10, 10);
gtk_widget_show_all (window);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
g_application_run (G_APPLICATION (app), argc, argv);
// app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
According to a fast google, you are looking for GtkGLArea, note the uppercase A.
– derhass
you need:
sudo apt install *epoxy*
and
c++ t.c --target=arm-linux-gnu `pkg-config --libs --cflags gtk+-3.0 epoxy ` -o op

rendering multiattributed textview

problem: I have a Textview with different Texttags in different selected text areas. At the end of editing, i would like to render this multi tagged textbuffer/textview into drawingarea.But I am not getting how can I render multi tagged textview using Pango/cairo layout ?. It would be great if I get any Gtk+/Gtkmm code that shows text drawing.
EX: Lets say My text is "AAABBB" and in this AAA has bold tag set and BBB is non bold and italic set..now how can i render this kind of text ?
Assuming you're looking for a c++ example, check if code below would work for you:
#include <gtk/gtk.h>
static gboolean on_expose_event_0(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
GdkScreen *screen = gdk_drawable_get_screen(widget->window);
PangoRenderer *renderer = gdk_pango_renderer_get_default (screen);
GdkGC *gc = gdk_gc_new(widget->window);
gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER (renderer), widget->window);
gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER (renderer), gc);
PangoContext *context = gdk_pango_context_get_for_screen (screen);
PangoLayout *layout = pango_layout_new (context);
pango_layout_set_markup(layout, "<b>AAA</b><i>bbb</i>", -1);
PangoFontDescription *font = pango_font_description_from_string("Times 20");
pango_layout_set_font_description(layout, font);
pango_font_description_free(font);
pango_renderer_draw_layout (renderer, layout, 1, 1);
gdk_pango_renderer_set_drawable(GDK_PANGO_RENDERER (renderer), NULL);
gdk_pango_renderer_set_gc(GDK_PANGO_RENDERER (renderer), NULL);
g_object_unref(layout);
g_object_unref(context);
g_object_unref(gc);
return FALSE;
}
int main( int argc, char *argv[])
{
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);
g_signal_connect(window, "expose-event", G_CALLBACK(on_expose_event_0), NULL);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "pango test");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
There is also an example on using pango with cairo here: Using Pango with Cairo
hope this helps, regards