mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-22 01:31:03 +00:00
glade based gstplay version.
Original commit message from CVS: glade based gstplay version. better avi/mpeg support. nice about dialog :-) Drag and drop should work.
This commit is contained in:
parent
f0dca903ae
commit
60b626e820
14 changed files with 372 additions and 702 deletions
|
@ -9,7 +9,6 @@ bin_PROGRAMS = gstplay
|
|||
gstplay_SOURCES = \
|
||||
gstplay.c \
|
||||
mpeg1.c mpeg2.c avi.c\
|
||||
support.c support.h \
|
||||
interface.c interface.h \
|
||||
callbacks.c callbacks.h
|
||||
|
||||
|
@ -17,8 +16,8 @@ noinst_HEADERS = codecs.h
|
|||
|
||||
CFLAGS += -O2 -Wall
|
||||
|
||||
gstplay_CFLAGS = $(shell gnome-config --cflags gnomeui)
|
||||
gstplay_LDFLAGS = $(shell gnome-config --libs gnomeui)
|
||||
gstplay_CFLAGS = $(shell gnome-config --cflags gnomeui) $(shell libglade-config --cflags gnome)
|
||||
gstplay_LDFLAGS = $(shell gnome-config --libs gnomeui) $(shell libglade-config --libs gnome)
|
||||
|
||||
gstplay_LDADD = $(GLIB_LIBS) $(GTK_LIBS) $(top_builddir)/gst/libgst.la \
|
||||
$(top_builddir)/plugins/videosink/gdkxvimage.lo -lXv -lXxf86vm
|
||||
|
|
|
@ -11,6 +11,7 @@ extern GstElement *video_render_queue, *audio_render_queue;
|
|||
void avi_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline)
|
||||
{
|
||||
g_print("***** a new pad %s was created\n", gst_pad_get_name(pad));
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PAUSED);
|
||||
|
||||
// connect to audio pad
|
||||
//if (0) {
|
||||
|
@ -25,6 +26,7 @@ void avi_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline)
|
|||
gst_pad_connect(pad,
|
||||
gst_element_get_pad(video_render_queue,"sink"));
|
||||
}
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
g_print("\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -3,86 +3,60 @@
|
|||
#endif
|
||||
|
||||
#include <gnome.h>
|
||||
#include <glade/glade.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstplay.h"
|
||||
#include "callbacks.h"
|
||||
#include "interface.h"
|
||||
#include "support.h"
|
||||
|
||||
extern GstElement *src;
|
||||
extern gboolean picture_shown;
|
||||
extern GstPlayState state;
|
||||
extern guchar statusline[];
|
||||
extern guchar *statustext;
|
||||
extern GtkFileSelection *open_file_selection;
|
||||
|
||||
void
|
||||
on_file1_activate (GtkMenuItem *menuitem,
|
||||
on_save1_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
printf("file1 activate\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
on_open1_activate (GtkMenuItem *menuitem,
|
||||
on_save_as1_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
printf("file1 activate\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
on_close1_activate (GtkMenuItem *menuitem,
|
||||
on_media2_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
printf("file1 activate\n");
|
||||
|
||||
}
|
||||
void
|
||||
on_preferences1_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
printf("file1 activate\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
on_media1_activate (GtkMenuItem *menuitem,
|
||||
on_open2_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
on_play2_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
update_buttons(0);
|
||||
change_state(GSTPLAY_PLAYING);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
on_pause1_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
update_buttons(1);
|
||||
change_state(GSTPLAY_PAUSE);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
on_stop1_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
update_buttons(2);
|
||||
change_state(GSTPLAY_STOPPED);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
on_about1_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
|
||||
GladeXML *xml;
|
||||
xml = glade_xml_new("gstplay.glade", "fileselection1");
|
||||
/* connect the signals in the interface */
|
||||
glade_xml_signal_autoconnect(xml);
|
||||
open_file_selection = glade_xml_get_widget(xml, "fileselection1");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -138,3 +112,16 @@ on_drawingarea1_configure_event (GtkWidget *widget, GdkEventConfigure *ev
|
|||
gdk_draw_string(widget->window,widget->style->font,widget->style->white_gc, widget->allocation.width-100, 15, statusline);
|
||||
}
|
||||
|
||||
void on_about_activate(GtkWidget *widget, gpointer data)
|
||||
{
|
||||
GladeXML *xml;
|
||||
xml = glade_xml_new("gstplay.glade", "about");
|
||||
/* connect the signals in the interface */
|
||||
glade_xml_signal_autoconnect(xml);
|
||||
}
|
||||
|
||||
void on_gstplay_destroy(GtkWidget *widget, gpointer data)
|
||||
{
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
/* mpeg1.c */
|
||||
void mpeg1_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline);
|
||||
void mpeg1_setup_video_thread(GstPad *pad, GstElement *show, GstElement *pipeline);
|
||||
void mpeg1_setup_audio_thread(GstPad *pad, GstElement *show, GstElement *pipeline);
|
||||
|
||||
/* mpeg2.c */
|
||||
void mpeg2_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline);
|
||||
|
|
|
@ -9,19 +9,20 @@
|
|||
|
||||
//#define DEBUG_ENABLED
|
||||
|
||||
#include <glade/glade.h>
|
||||
#include <gst/gstclock.h>
|
||||
|
||||
#include "gstplay.h"
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "interface.h"
|
||||
#include "support.h"
|
||||
|
||||
#include "codecs.h"
|
||||
|
||||
#define MUTEX_STATUS() (g_mutex_trylock(gdk_threads_mutex)? g_mutex_unlock(gdk_threads_mutex), "was not locked" : "was locked")
|
||||
|
||||
|
||||
#define BUFFER 4
|
||||
#define BUFFER 20
|
||||
|
||||
extern gboolean _gst_plugin_spew;
|
||||
gboolean idle_func(gpointer data);
|
||||
|
@ -30,10 +31,21 @@ GstElement *audio_play, *audio_render_queue;
|
|||
GstElement *src;
|
||||
GstPipeline *pipeline;
|
||||
GstElement *parse = NULL;
|
||||
GstElement *typefind;
|
||||
GstElement *video_render_thread;
|
||||
GstElement *audio_render_thread;
|
||||
GstPlayState state;
|
||||
gboolean picture_shown = FALSE;
|
||||
guchar statusline[200];
|
||||
guchar *statustext = "stopped";
|
||||
GtkWidget *status_area;
|
||||
GtkAdjustment *adjustment;
|
||||
GtkWidget *play_button;
|
||||
GtkWidget *pause_button;
|
||||
GtkWidget *stop_button;
|
||||
GtkFileSelection *open_file_selection;
|
||||
|
||||
gint start_from_file(guchar *filename);
|
||||
|
||||
static void frame_displayed(GstSrc *asrc)
|
||||
{
|
||||
|
@ -42,7 +54,6 @@ static void frame_displayed(GstSrc *asrc)
|
|||
static int prev_time = -1;
|
||||
|
||||
if (!parse) return;
|
||||
return;
|
||||
DEBUG("gstplay: frame displayed %s\n", MUTEX_STATUS());
|
||||
|
||||
mux_rate = gst_util_get_int_arg(GTK_OBJECT(parse),"mux_rate");
|
||||
|
@ -60,9 +71,9 @@ static void frame_displayed(GstSrc *asrc)
|
|||
|
||||
//printf("%d %d %g\n", frame_time, size, frame_time*100.0/size);
|
||||
|
||||
update_status_area();
|
||||
update_status_area(status_area);
|
||||
if (state == GSTPLAY_PLAYING)
|
||||
update_slider(src_pos*100.0/size);
|
||||
update_slider(adjustment, src_pos*100.0/size);
|
||||
}
|
||||
picture_shown = TRUE;
|
||||
|
||||
|
@ -97,33 +108,58 @@ void mute_audio(gboolean mute) {
|
|||
gtk_object_set(GTK_OBJECT(audio_play),"mute",mute,NULL);
|
||||
}
|
||||
|
||||
gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
|
||||
static void gstplay_tear_down()
|
||||
{
|
||||
g_print("setting to NULL state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
target_drag_data_received (GtkWidget *widget,
|
||||
GdkDragContext *context,
|
||||
gint x,
|
||||
gint y,
|
||||
GtkSelectionData *data,
|
||||
guint info,
|
||||
guint time)
|
||||
{
|
||||
if (strstr(data->data, "file:")) {
|
||||
g_print("Got: %s\n",data->data);
|
||||
start_from_file(g_strchomp(&data->data[5]));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
on_exit_menu_activate (GtkMenuItem *menuitem,
|
||||
gpointer user_data)
|
||||
{
|
||||
gdk_threads_leave();
|
||||
g_print("setting to ~PLAYING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),~GST_STATE_PLAYING);
|
||||
g_print("setting to ~RUNNING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),~GST_STATE_RUNNING);
|
||||
gstplay_tear_down();
|
||||
gdk_threads_enter();
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
void on_ok_button1_clicked (GtkButton *button,
|
||||
GtkFileSelection *sel)
|
||||
{
|
||||
gchar *selected_filename;
|
||||
|
||||
selected_filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(open_file_selection));
|
||||
start_from_file(selected_filename);
|
||||
}
|
||||
|
||||
gint on_gstplay_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
|
||||
{
|
||||
gdk_threads_leave();
|
||||
gstplay_tear_down();
|
||||
gdk_threads_enter();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void destroy(GtkWidget *widget, gpointer data)
|
||||
void gstplay_parse_pads_created(GstElement *element, gpointer data)
|
||||
{
|
||||
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
void gstplay_parse_state_changed(GstElement *element, gint state, gpointer data)
|
||||
{
|
||||
printf("gstplay: element \"%s\" state changed %d\n", gst_element_get_name(element), state);
|
||||
|
||||
if (state == GST_STATE_COMPLETE) {
|
||||
g_print("gstplay: setting to PLAYING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
gst_clock_reset(gst_clock_get_system());
|
||||
g_print("gstplay: PLAYING\n");
|
||||
}
|
||||
printf("gstplay: element \"%s\" is ready\n", gst_element_get_name(element));
|
||||
gst_clock_reset(gst_clock_get_system());
|
||||
}
|
||||
|
||||
void change_state(GstPlayState new_state) {
|
||||
|
@ -133,14 +169,14 @@ void change_state(GstPlayState new_state) {
|
|||
case GSTPLAY_PLAYING:
|
||||
mute_audio(FALSE);
|
||||
statustext = "playing";
|
||||
update_status_area();
|
||||
update_status_area(status_area);
|
||||
gtk_idle_add(idle_func,src);
|
||||
state = GSTPLAY_PLAYING;
|
||||
update_buttons(0);
|
||||
break;
|
||||
case GSTPLAY_PAUSE:
|
||||
statustext = "paused";
|
||||
update_status_area();
|
||||
update_status_area(status_area);
|
||||
if (state != GSTPLAY_STOPPED) gtk_idle_remove_by_data(src);
|
||||
mute_audio(TRUE);
|
||||
state = GSTPLAY_PAUSE;
|
||||
|
@ -149,12 +185,12 @@ void change_state(GstPlayState new_state) {
|
|||
case GSTPLAY_STOPPED:
|
||||
if (state != GSTPLAY_PAUSE) gtk_idle_remove_by_data(src);
|
||||
statustext = "stopped";
|
||||
update_status_area();
|
||||
update_status_area(status_area);
|
||||
mute_audio(TRUE);
|
||||
state = GSTPLAY_STOPPED;
|
||||
gtk_object_set(GTK_OBJECT(src),"offset",0,NULL);
|
||||
update_buttons(2);
|
||||
update_slider(0.0);
|
||||
update_slider(adjustment, 0.0);
|
||||
show_next_picture();
|
||||
break;
|
||||
}
|
||||
|
@ -169,6 +205,7 @@ static void have_type(GstSink *sink) {
|
|||
|
||||
g_print("have type %d:%s\n", type, gsttype->mime);
|
||||
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_NULL);
|
||||
gst_bin_remove(GST_BIN(pipeline), GST_ELEMENT(sink));
|
||||
|
||||
gst_pad_disconnect(gst_element_get_pad(src,"src"),
|
||||
|
@ -178,11 +215,15 @@ static void have_type(GstSink *sink) {
|
|||
parse = gst_elementfactory_make("mpeg1parse","mpeg1_system_parse");
|
||||
gtk_signal_connect(GTK_OBJECT(parse),"new_pad",
|
||||
GTK_SIGNAL_FUNC(mpeg1_new_pad_created),pipeline);
|
||||
gtk_signal_connect(GTK_OBJECT(show),"frame_displayed",
|
||||
GTK_SIGNAL_FUNC(frame_displayed),NULL);
|
||||
}
|
||||
else if (strstr(gsttype->mime, "mpeg2-system")) {
|
||||
parse = gst_elementfactory_make("mpeg2parse","mpeg2_system_parse");
|
||||
gtk_signal_connect(GTK_OBJECT(parse),"new_pad",
|
||||
GTK_SIGNAL_FUNC(mpeg2_new_pad_created),pipeline);
|
||||
gtk_signal_connect(GTK_OBJECT(show),"frame_displayed",
|
||||
GTK_SIGNAL_FUNC(frame_displayed),NULL);
|
||||
}
|
||||
else if (strstr(gsttype->mime, "avi")) {
|
||||
parse = gst_elementfactory_make("parseavi","parse");
|
||||
|
@ -193,6 +234,13 @@ static void have_type(GstSink *sink) {
|
|||
mpeg1_setup_video_thread(gst_element_get_pad(src,"src"), video_render_queue, GST_ELEMENT(pipeline));
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
gst_clock_reset(gst_clock_get_system());
|
||||
gtk_signal_connect(GTK_OBJECT(show),"frame_displayed",
|
||||
GTK_SIGNAL_FUNC(frame_displayed),NULL);
|
||||
}
|
||||
else if (strstr(gsttype->mime, "mp3")) {
|
||||
mpeg1_setup_audio_thread(gst_element_get_pad(src,"src"), audio_render_queue, GST_ELEMENT(pipeline));
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
gst_clock_reset(gst_clock_get_system());
|
||||
}
|
||||
else {
|
||||
g_print("unknown media type\n");
|
||||
|
@ -203,20 +251,59 @@ static void have_type(GstSink *sink) {
|
|||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(parse));
|
||||
gst_pad_connect(gst_element_get_pad(src,"src"),
|
||||
gst_element_get_pad(parse,"sink"));
|
||||
gtk_signal_connect(GTK_OBJECT(parse),"state_change",
|
||||
GTK_SIGNAL_FUNC(gstplay_parse_state_changed),pipeline);
|
||||
gtk_signal_connect(GTK_OBJECT(parse),"pads_created",
|
||||
GTK_SIGNAL_FUNC(gstplay_parse_pads_created),pipeline);
|
||||
}
|
||||
g_print("setting to READY state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_READY);
|
||||
g_print("setting to PLAYING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
g_print("set to PLAYING state\n");
|
||||
|
||||
gtk_object_set(GTK_OBJECT(src),"offset",0,NULL);
|
||||
}
|
||||
|
||||
gint start_from_file(guchar *filename)
|
||||
{
|
||||
src = gst_elementfactory_make("disksrc","disk_src");
|
||||
g_return_val_if_fail(src != NULL, -1);
|
||||
g_print("should be using file '%s'\n",filename);
|
||||
gtk_object_set(GTK_OBJECT(src),"location",filename,NULL);
|
||||
|
||||
typefind = gst_elementfactory_make("typefind","typefind");
|
||||
g_return_val_if_fail(typefind != NULL, -1);
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(typefind),"have_type",
|
||||
GTK_SIGNAL_FUNC(have_type),NULL);
|
||||
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(typefind));
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(src),"eos",
|
||||
GTK_SIGNAL_FUNC(eof),NULL);
|
||||
|
||||
gst_pad_connect(gst_element_get_pad(src,"src"),
|
||||
gst_element_get_pad(typefind,"sink"));
|
||||
|
||||
g_print("setting to READY state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_READY);
|
||||
|
||||
state = GSTPLAY_STOPPED;
|
||||
|
||||
change_state(GSTPLAY_PLAYING);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GtkTargetEntry target_table[] = {
|
||||
{ "text/plain", 0, 0 }
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window1;
|
||||
GstElement *typefind;
|
||||
GstElement *video_render_thread;
|
||||
GstElement *audio_render_thread;
|
||||
GladeXML *xml;
|
||||
GtkWidget *slider, *gstplay;
|
||||
|
||||
bindtextdomain (PACKAGE, PACKAGE_LOCALE_DIR);
|
||||
textdomain (PACKAGE);
|
||||
|
@ -224,13 +311,39 @@ main (int argc, char *argv[])
|
|||
g_thread_init(NULL);
|
||||
gtk_init(&argc,&argv);
|
||||
gnome_init ("gstreamer", VERSION, argc, argv);
|
||||
glade_init();
|
||||
glade_gnome_init();
|
||||
gst_init(&argc,&argv);
|
||||
gst_plugin_load("mpeg1parse");
|
||||
gst_plugin_load("mpeg2parse");
|
||||
gst_plugin_load("mp1videoparse");
|
||||
gst_plugin_load("mp3parse");
|
||||
gst_plugin_load("parsewav");
|
||||
gst_plugin_load("parseavi");
|
||||
|
||||
/* load the interface */
|
||||
xml = glade_xml_new("gstplay.glade", "gstplay");
|
||||
/* connect the signals in the interface */
|
||||
|
||||
status_area = glade_xml_get_widget(xml, "status_area");
|
||||
slider = glade_xml_get_widget(xml, "slider");
|
||||
{
|
||||
GtkArg arg;
|
||||
GtkRange *range;
|
||||
arg.name = "adjustment";
|
||||
gtk_object_getv(GTK_OBJECT(slider),1,&arg);
|
||||
range = GTK_RANGE(GTK_VALUE_POINTER(arg));
|
||||
adjustment = gtk_range_get_adjustment(range);
|
||||
gtk_signal_connect(GTK_OBJECT(adjustment),"value_changed",
|
||||
GTK_SIGNAL_FUNC(on_hscale1_value_changed),NULL);
|
||||
}
|
||||
play_button = glade_xml_get_widget(xml, "toggle_play");
|
||||
pause_button = glade_xml_get_widget(xml, "toggle_pause");
|
||||
stop_button = glade_xml_get_widget(xml, "toggle_stop");
|
||||
|
||||
gstplay = glade_xml_get_widget(xml, "gstplay");
|
||||
gtk_drag_dest_set (gstplay,
|
||||
GTK_DEST_DEFAULT_ALL,
|
||||
target_table, 1,
|
||||
GDK_ACTION_COPY);
|
||||
gtk_signal_connect (GTK_OBJECT (gstplay), "drag_data_received",
|
||||
GTK_SIGNAL_FUNC (target_drag_data_received),
|
||||
NULL);
|
||||
|
||||
gst_plugin_load("videosink");
|
||||
|
||||
g_snprintf(statusline, 200, "seeking");
|
||||
|
@ -243,19 +356,22 @@ main (int argc, char *argv[])
|
|||
show = gst_elementfactory_make("videosink","show");
|
||||
g_return_val_if_fail(show != NULL, -1);
|
||||
gtk_object_set(GTK_OBJECT(show),"xv_enabled",FALSE,NULL);
|
||||
window1 = create_window1 (gst_util_get_widget_arg(GTK_OBJECT(show),"widget"));
|
||||
gtk_widget_show (window1);
|
||||
gtk_signal_connect(GTK_OBJECT(window1),"delete_event",
|
||||
GTK_SIGNAL_FUNC(delete_event),NULL);
|
||||
gtk_signal_connect(GTK_OBJECT(window1),"destroy",
|
||||
GTK_SIGNAL_FUNC(destroy),pipeline);
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(show),"frame_displayed",
|
||||
GTK_SIGNAL_FUNC(frame_displayed),NULL);
|
||||
gnome_dock_set_client_area(GNOME_DOCK(glade_xml_get_widget(xml, "dock1")),
|
||||
gst_util_get_widget_arg(GTK_OBJECT(show),"widget"));
|
||||
gst_bin_add(GST_BIN(video_render_thread),GST_ELEMENT(show));
|
||||
gst_element_add_ghost_pad(GST_ELEMENT(video_render_thread),
|
||||
gst_element_get_pad(show,"sink"));
|
||||
|
||||
glade_xml_signal_autoconnect(xml);
|
||||
|
||||
gst_plugin_load("mpeg1parse");
|
||||
gst_plugin_load("mpeg2parse");
|
||||
gst_plugin_load("mp1videoparse");
|
||||
gst_plugin_load("mp3parse");
|
||||
gst_plugin_load("parsewav");
|
||||
gst_plugin_load("parseavi");
|
||||
|
||||
video_render_queue = gst_elementfactory_make("queue","video_render_queue");
|
||||
gtk_object_set(GTK_OBJECT(video_render_queue),"max_level",BUFFER,NULL);
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(video_render_queue));
|
||||
|
@ -280,39 +396,12 @@ main (int argc, char *argv[])
|
|||
gst_element_get_pad(audio_render_thread,"sink"));
|
||||
gtk_object_set(GTK_OBJECT(audio_render_thread),"create_thread",TRUE,NULL);
|
||||
|
||||
|
||||
src = gst_elementfactory_make("disksrc","disk_src");
|
||||
g_return_val_if_fail(src != NULL, -1);
|
||||
g_print("should be using file '%s'\n",argv[1]);
|
||||
gtk_object_set(GTK_OBJECT(src),"location",argv[1],NULL);
|
||||
|
||||
gtk_window_set_title (GTK_WINDOW (window1), g_strdup_printf("GStreamer Media player - %s", argv[1]));
|
||||
|
||||
typefind = gst_elementfactory_make("typefind","typefind");
|
||||
g_return_val_if_fail(typefind != NULL, -1);
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(typefind),"have_type",
|
||||
GTK_SIGNAL_FUNC(have_type),NULL);
|
||||
|
||||
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(src));
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(typefind));
|
||||
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(src),"eos",
|
||||
GTK_SIGNAL_FUNC(eof),NULL);
|
||||
|
||||
gst_pad_connect(gst_element_get_pad(src,"src"),
|
||||
gst_element_get_pad(typefind,"sink"));
|
||||
|
||||
g_print("setting to PREROLL state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PREROLL);
|
||||
g_print("setting to RUNNING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_RUNNING);
|
||||
|
||||
state = GSTPLAY_STOPPED;
|
||||
|
||||
change_state(GSTPLAY_PLAYING);
|
||||
if (argc > 1) {
|
||||
gint ret;
|
||||
|
||||
ret = start_from_file(argv[1]);
|
||||
if (ret < 0) exit(ret);
|
||||
}
|
||||
|
||||
gdk_threads_enter();
|
||||
gtk_main();
|
||||
|
|
|
@ -15,382 +15,63 @@
|
|||
|
||||
#include "callbacks.h"
|
||||
#include "interface.h"
|
||||
#include "support.h"
|
||||
|
||||
GtkWidget *drawingarea1;
|
||||
GtkObject *adjustment;
|
||||
GtkWidget *button6;
|
||||
GtkWidget *button7;
|
||||
GtkWidget *button8;
|
||||
extern GtkWidget *play_button;
|
||||
extern GtkWidget *pause_button;
|
||||
extern GtkWidget *stop_button;
|
||||
extern guchar statusline[];
|
||||
extern guchar *statustext;
|
||||
|
||||
static GnomeUIInfo file1_menu_uiinfo[] =
|
||||
{
|
||||
{
|
||||
GNOME_APP_UI_ITEM, N_("_Open..."),
|
||||
NULL,
|
||||
on_open1_activate, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_OPEN,
|
||||
0, 0, NULL
|
||||
},
|
||||
GNOMEUIINFO_SEPARATOR,
|
||||
{
|
||||
GNOME_APP_UI_ITEM, N_("_Exit"),
|
||||
NULL,
|
||||
on_close1_activate, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_EXIT,
|
||||
0, 0, NULL
|
||||
},
|
||||
GNOMEUIINFO_END
|
||||
};
|
||||
|
||||
static GnomeUIInfo view2_menu_uiinfo[] =
|
||||
{
|
||||
{
|
||||
GNOME_APP_UI_ITEM, N_("_Media..."),
|
||||
NULL,
|
||||
on_media1_activate, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_PROP,
|
||||
0, 0, NULL
|
||||
},
|
||||
GNOMEUIINFO_END
|
||||
};
|
||||
|
||||
static GnomeUIInfo play1_menu_uiinfo[] =
|
||||
{
|
||||
{
|
||||
GNOME_APP_UI_ITEM, N_("_Play"),
|
||||
NULL,
|
||||
on_play2_activate, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_FILENAME, "pixmaps/play.xpm",
|
||||
0, 0, NULL
|
||||
},
|
||||
{
|
||||
GNOME_APP_UI_ITEM, N_("P_ause"),
|
||||
NULL,
|
||||
on_pause1_activate, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_FILENAME, "pixmaps/pause.xpm",
|
||||
0, 0, NULL
|
||||
},
|
||||
{
|
||||
GNOME_APP_UI_ITEM, N_("_Stop"),
|
||||
NULL,
|
||||
on_stop1_activate, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_FILENAME, "pixmaps/stop.xpm",
|
||||
0, 0, NULL
|
||||
},
|
||||
GNOMEUIINFO_END
|
||||
};
|
||||
|
||||
static GnomeUIInfo help1_menu_uiinfo[] =
|
||||
{
|
||||
{
|
||||
GNOME_APP_UI_ITEM, N_("_About"),
|
||||
NULL,
|
||||
on_about1_activate, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_ABOUT,
|
||||
0, 0, NULL
|
||||
},
|
||||
GNOMEUIINFO_END
|
||||
};
|
||||
|
||||
static GnomeUIInfo menubar1_uiinfo[] =
|
||||
{
|
||||
{
|
||||
GNOME_APP_UI_SUBTREE, N_("_File"),
|
||||
NULL,
|
||||
file1_menu_uiinfo, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_NONE, NULL,
|
||||
0, 0, NULL
|
||||
},
|
||||
{
|
||||
GNOME_APP_UI_SUBTREE, N_("_View"),
|
||||
NULL,
|
||||
view2_menu_uiinfo, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_NONE, NULL,
|
||||
0, 0, NULL
|
||||
},
|
||||
{
|
||||
GNOME_APP_UI_SUBTREE, N_("_Play"),
|
||||
NULL,
|
||||
play1_menu_uiinfo, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_NONE, NULL,
|
||||
0, 0, NULL
|
||||
},
|
||||
{
|
||||
GNOME_APP_UI_SUBTREE, N_("_Help"),
|
||||
NULL,
|
||||
help1_menu_uiinfo, NULL, NULL,
|
||||
GNOME_APP_PIXMAP_NONE, NULL,
|
||||
0, 0, NULL
|
||||
},
|
||||
GNOMEUIINFO_END
|
||||
};
|
||||
|
||||
GtkWidget*
|
||||
create_window1 (GtkWidget *video_element)
|
||||
{
|
||||
GtkWidget *window1;
|
||||
GtkWidget *vbox1;
|
||||
GtkWidget *handlebox2;
|
||||
GtkWidget *menubar1;
|
||||
GtkWidget *vbox2;
|
||||
GtkWidget *hscale1;
|
||||
GtkWidget *handlebox1;
|
||||
GtkWidget *toolbar1;
|
||||
GtkWidget *tmp_toolbar_icon;
|
||||
GtkWidget *vseparator1;
|
||||
|
||||
window1 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_object_set_data (GTK_OBJECT (window1), "window1", window1);
|
||||
gtk_window_set_title (GTK_WINDOW (window1), _("GStreamer Media Player"));
|
||||
gtk_window_set_policy(GTK_WINDOW(window1), TRUE, TRUE, FALSE);
|
||||
|
||||
vbox1 = gtk_vbox_new (FALSE, 0);
|
||||
gtk_widget_ref (vbox1);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "vbox1", vbox1,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (vbox1);
|
||||
gtk_container_add (GTK_CONTAINER (window1), vbox1);
|
||||
|
||||
handlebox2 = gtk_handle_box_new ();
|
||||
gtk_widget_ref (handlebox2);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "handlebox2", handlebox2,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (handlebox2);
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), handlebox2, FALSE, FALSE, 0);
|
||||
|
||||
menubar1 = gtk_menu_bar_new ();
|
||||
gtk_widget_ref (menubar1);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "menubar1", menubar1,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (menubar1);
|
||||
gtk_container_add (GTK_CONTAINER (handlebox2), menubar1);
|
||||
gnome_app_fill_menu (GTK_MENU_SHELL (menubar1), menubar1_uiinfo,
|
||||
NULL, FALSE, 0);
|
||||
|
||||
gtk_widget_ref (menubar1_uiinfo[0].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "file1",
|
||||
menubar1_uiinfo[0].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (file1_menu_uiinfo[0].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "open1",
|
||||
file1_menu_uiinfo[0].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (file1_menu_uiinfo[1].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "separator1",
|
||||
file1_menu_uiinfo[1].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (file1_menu_uiinfo[2].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "close1",
|
||||
file1_menu_uiinfo[2].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (menubar1_uiinfo[1].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "view2",
|
||||
menubar1_uiinfo[1].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (view2_menu_uiinfo[0].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "media1",
|
||||
view2_menu_uiinfo[0].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (menubar1_uiinfo[2].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "play1",
|
||||
menubar1_uiinfo[2].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (play1_menu_uiinfo[0].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "play2",
|
||||
play1_menu_uiinfo[0].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (play1_menu_uiinfo[1].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "pause1",
|
||||
play1_menu_uiinfo[1].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (play1_menu_uiinfo[2].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "stop1",
|
||||
play1_menu_uiinfo[2].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (menubar1_uiinfo[3].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "help1",
|
||||
menubar1_uiinfo[3].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_widget_ref (help1_menu_uiinfo[0].widget);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "about1",
|
||||
help1_menu_uiinfo[0].widget,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), video_element, TRUE, TRUE, 0);
|
||||
|
||||
vbox2 = gtk_vbox_new (FALSE, 0);
|
||||
gtk_widget_ref (vbox2);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "vbox2", vbox2,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (vbox2);
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, TRUE, 0);
|
||||
|
||||
adjustment = gtk_adjustment_new (0, 0.0, 110.0, 1, 10.0, 10.0);
|
||||
hscale1 = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
|
||||
gtk_widget_ref (hscale1);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "hscale1", hscale1,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (hscale1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), hscale1, TRUE, TRUE, 3);
|
||||
gtk_scale_set_draw_value (GTK_SCALE (hscale1), FALSE);
|
||||
gtk_scale_set_value_pos (GTK_SCALE (hscale1), GTK_POS_LEFT);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
|
||||
GTK_SIGNAL_FUNC (on_hscale1_value_changed),
|
||||
NULL);
|
||||
|
||||
handlebox1 = gtk_handle_box_new ();
|
||||
gtk_widget_ref (handlebox1);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "handlebox1", handlebox1,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (handlebox1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox2), handlebox1, TRUE, TRUE, 1);
|
||||
gtk_handle_box_set_shadow_type (GTK_HANDLE_BOX (handlebox1), GTK_SHADOW_NONE);
|
||||
gtk_handle_box_set_snap_edge (GTK_HANDLE_BOX (handlebox1), GTK_POS_BOTTOM);
|
||||
|
||||
toolbar1 = gtk_toolbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_ICONS);
|
||||
gtk_widget_ref (toolbar1);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "toolbar1", toolbar1,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (toolbar1);
|
||||
gtk_container_add (GTK_CONTAINER (handlebox1), toolbar1);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (toolbar1), 3);
|
||||
gtk_toolbar_set_space_size (GTK_TOOLBAR (toolbar1), 0);
|
||||
gtk_toolbar_set_button_relief (GTK_TOOLBAR (toolbar1), GTK_RELIEF_NONE);
|
||||
|
||||
tmp_toolbar_icon = create_pixmap (window1, "pixmaps/play.xpm", TRUE);
|
||||
button6 = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar1),
|
||||
GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
|
||||
NULL,
|
||||
_("button6"),
|
||||
NULL, NULL,
|
||||
tmp_toolbar_icon, NULL, NULL);
|
||||
gtk_widget_ref (button6);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "button6", button6,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (button6);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT (button6), "toggled",
|
||||
GTK_SIGNAL_FUNC (on_toggle_play_toggled), NULL);
|
||||
|
||||
tmp_toolbar_icon = create_pixmap (window1, "pixmaps/pause.xpm", TRUE);
|
||||
button7 = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar1),
|
||||
GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
|
||||
NULL,
|
||||
_("button7"),
|
||||
NULL, NULL,
|
||||
tmp_toolbar_icon, NULL, NULL);
|
||||
gtk_widget_ref (button7);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "button7", button7,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (button7);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT (button7), "toggled",
|
||||
GTK_SIGNAL_FUNC (on_toggle_pause_toggled), NULL);
|
||||
|
||||
tmp_toolbar_icon = create_pixmap (window1, "pixmaps/stop.xpm", TRUE);
|
||||
button8 = gtk_toolbar_append_element (GTK_TOOLBAR (toolbar1),
|
||||
GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
|
||||
NULL,
|
||||
_("button8"),
|
||||
NULL, NULL,
|
||||
tmp_toolbar_icon, NULL, NULL);
|
||||
gtk_widget_ref (button8);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "button8", button8,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (button8);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT (button8), "toggled",
|
||||
GTK_SIGNAL_FUNC (on_toggle_stop_toggled), NULL);
|
||||
|
||||
vseparator1 = gtk_vseparator_new ();
|
||||
gtk_widget_ref (vseparator1);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "vseparator1", vseparator1,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (vseparator1);
|
||||
gtk_toolbar_append_widget (GTK_TOOLBAR (toolbar1), vseparator1, NULL, NULL);
|
||||
gtk_widget_set_usize (vseparator1, 8, 21);
|
||||
|
||||
drawingarea1 = gtk_drawing_area_new ();
|
||||
gtk_widget_ref (drawingarea1);
|
||||
gtk_object_set_data_full (GTK_OBJECT (window1), "drawingarea1", drawingarea1,
|
||||
(GtkDestroyNotify) gtk_widget_unref);
|
||||
gtk_widget_show (drawingarea1);
|
||||
gtk_box_pack_start (GTK_BOX (vbox1), drawingarea1, FALSE, TRUE, 1);
|
||||
gtk_widget_set_usize (drawingarea1, -2, 21);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT (drawingarea1), "configure_event",
|
||||
GTK_SIGNAL_FUNC (on_drawingarea1_configure_event),
|
||||
NULL);
|
||||
|
||||
return window1;
|
||||
}
|
||||
|
||||
void update_buttons(int active)
|
||||
{
|
||||
gtk_signal_handler_block_by_func(GTK_OBJECT(button6),
|
||||
gtk_signal_handler_block_by_func(GTK_OBJECT(play_button),
|
||||
GTK_SIGNAL_FUNC (on_toggle_play_toggled),
|
||||
NULL);
|
||||
gtk_signal_handler_block_by_func(GTK_OBJECT(button7),
|
||||
gtk_signal_handler_block_by_func(GTK_OBJECT(pause_button),
|
||||
GTK_SIGNAL_FUNC (on_toggle_pause_toggled),
|
||||
NULL);
|
||||
gtk_signal_handler_block_by_func(GTK_OBJECT(button8),
|
||||
gtk_signal_handler_block_by_func(GTK_OBJECT(stop_button),
|
||||
GTK_SIGNAL_FUNC (on_toggle_stop_toggled),
|
||||
NULL);
|
||||
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button6), FALSE);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button7), FALSE);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button8), FALSE);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_button), FALSE);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pause_button), FALSE);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(stop_button), FALSE);
|
||||
|
||||
if (active == 0) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button6), TRUE);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_button), TRUE);
|
||||
}
|
||||
else if (active == 1) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button7), TRUE);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pause_button), TRUE);
|
||||
}
|
||||
else if (active == 2) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button8), TRUE);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(stop_button), TRUE);
|
||||
}
|
||||
|
||||
gtk_signal_handler_unblock_by_func(GTK_OBJECT(button6),
|
||||
gtk_signal_handler_unblock_by_func(GTK_OBJECT(play_button),
|
||||
GTK_SIGNAL_FUNC (on_toggle_play_toggled),
|
||||
NULL);
|
||||
gtk_signal_handler_unblock_by_func(GTK_OBJECT(button7),
|
||||
gtk_signal_handler_unblock_by_func(GTK_OBJECT(pause_button),
|
||||
GTK_SIGNAL_FUNC (on_toggle_pause_toggled),
|
||||
NULL);
|
||||
gtk_signal_handler_unblock_by_func(GTK_OBJECT(button8),
|
||||
gtk_signal_handler_unblock_by_func(GTK_OBJECT(stop_button),
|
||||
GTK_SIGNAL_FUNC (on_toggle_stop_toggled),
|
||||
NULL);
|
||||
}
|
||||
|
||||
void update_slider(gfloat value)
|
||||
void update_slider(GtkAdjustment *adjustment, gfloat value)
|
||||
{
|
||||
gtk_signal_handler_block_by_func(adjustment,
|
||||
gtk_signal_handler_block_by_func(GTK_OBJECT(adjustment),
|
||||
GTK_SIGNAL_FUNC (on_hscale1_value_changed),
|
||||
NULL);
|
||||
gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustment), value);
|
||||
gtk_signal_handler_unblock_by_func(adjustment,
|
||||
gtk_adjustment_set_value(adjustment, value);
|
||||
gtk_signal_handler_unblock_by_func(GTK_OBJECT(adjustment),
|
||||
GTK_SIGNAL_FUNC (on_hscale1_value_changed),
|
||||
NULL);
|
||||
}
|
||||
|
||||
void update_status_area()
|
||||
void update_status_area(GtkWidget *widget)
|
||||
{
|
||||
GtkWidget *widget = drawingarea1;
|
||||
|
||||
gdk_draw_rectangle(widget->window,
|
||||
widget->style->black_gc,
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* DO NOT EDIT THIS FILE - it is generated by Glade.
|
||||
*/
|
||||
|
||||
GtkWidget* create_window1 (GtkWidget *video_element);
|
||||
void update_status_area();
|
||||
void update_slider(gfloat value);
|
||||
void update_status_area(GtkWidget *area);
|
||||
void update_slider(GtkAdjustment *adjustment, gfloat value);
|
||||
void update_buttons(int active);
|
||||
|
|
|
@ -18,56 +18,64 @@ extern GstElement *audio_render_queue;
|
|||
|
||||
void mpeg1_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline)
|
||||
{
|
||||
GstElement *parse_audio, *decode;
|
||||
GstElement *audio_queue;
|
||||
GstElement *audio_thread;
|
||||
|
||||
g_print("***** a new pad %s was created\n", gst_pad_get_name(pad));
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PAUSED);
|
||||
|
||||
// connect to audio pad
|
||||
//if (0) {
|
||||
if (strncmp(gst_pad_get_name(pad), "audio_", 6) == 0 && audio_render_queue) {
|
||||
gst_plugin_load("mp3parse");
|
||||
gst_plugin_load("mpg123");
|
||||
// construct internal pipeline elements
|
||||
parse_audio = gst_elementfactory_make("mp3parse","parse_audio");
|
||||
g_return_if_fail(parse_audio != NULL);
|
||||
decode = gst_elementfactory_make("mpg123","decode_audio");
|
||||
g_return_if_fail(decode != NULL);
|
||||
|
||||
// create the thread and pack stuff into it
|
||||
audio_thread = gst_thread_new("audio_thread");
|
||||
g_return_if_fail(audio_thread != NULL);
|
||||
gst_bin_add(GST_BIN(audio_thread),GST_ELEMENT(parse_audio));
|
||||
gst_bin_add(GST_BIN(audio_thread),GST_ELEMENT(decode));
|
||||
|
||||
// set up pad connections
|
||||
gst_element_add_ghost_pad(GST_ELEMENT(audio_thread),
|
||||
gst_element_get_pad(parse_audio,"sink"));
|
||||
gst_pad_connect(gst_element_get_pad(parse_audio,"src"),
|
||||
gst_element_get_pad(decode,"sink"));
|
||||
gst_pad_connect(gst_element_get_pad(decode,"src"),
|
||||
gst_element_get_pad(audio_render_queue,"sink"));
|
||||
|
||||
// construct queue and connect everything in the main pipelie
|
||||
audio_queue = gst_elementfactory_make("queue","audio_queue");
|
||||
gtk_object_set(GTK_OBJECT(audio_queue),"max_level",BUFFER,NULL);
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(audio_queue));
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(audio_thread));
|
||||
gst_pad_connect(pad,
|
||||
gst_element_get_pad(audio_queue,"sink"));
|
||||
gst_pad_connect(gst_element_get_pad(audio_queue,"src"),
|
||||
gst_element_get_pad(audio_thread,"sink"));
|
||||
|
||||
// set up thread state and kick things off
|
||||
gtk_object_set(GTK_OBJECT(audio_thread),"create_thread",TRUE,NULL);
|
||||
g_print("setting to RUNNING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(audio_thread),GST_STATE_RUNNING);
|
||||
mpeg1_setup_audio_thread(pad, audio_render_queue, pipeline);
|
||||
|
||||
} else if (strncmp(gst_pad_get_name(pad), "video_", 6) == 0) {
|
||||
//} else if (0) {
|
||||
mpeg1_setup_video_thread(pad, video_render_queue, pipeline);
|
||||
}
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
}
|
||||
|
||||
void mpeg1_setup_audio_thread(GstPad *pad, GstElement *audio_render_queue, GstElement *pipeline)
|
||||
{
|
||||
GstElement *parse_audio, *decode;
|
||||
GstElement *audio_queue;
|
||||
GstElement *audio_thread;
|
||||
|
||||
gst_plugin_load("mp3parse");
|
||||
gst_plugin_load("mpg123");
|
||||
// construct internal pipeline elements
|
||||
parse_audio = gst_elementfactory_make("mp3parse","parse_audio");
|
||||
g_return_if_fail(parse_audio != NULL);
|
||||
decode = gst_elementfactory_make("mpg123","decode_audio");
|
||||
g_return_if_fail(decode != NULL);
|
||||
|
||||
// create the thread and pack stuff into it
|
||||
audio_thread = gst_thread_new("audio_thread");
|
||||
g_return_if_fail(audio_thread != NULL);
|
||||
gst_bin_add(GST_BIN(audio_thread),GST_ELEMENT(parse_audio));
|
||||
gst_bin_add(GST_BIN(audio_thread),GST_ELEMENT(decode));
|
||||
|
||||
// set up pad connections
|
||||
gst_element_add_ghost_pad(GST_ELEMENT(audio_thread),
|
||||
gst_element_get_pad(parse_audio,"sink"));
|
||||
gst_pad_connect(gst_element_get_pad(parse_audio,"src"),
|
||||
gst_element_get_pad(decode,"sink"));
|
||||
gst_pad_connect(gst_element_get_pad(decode,"src"),
|
||||
gst_element_get_pad(audio_render_queue,"sink"));
|
||||
|
||||
// construct queue and connect everything in the main pipelie
|
||||
audio_queue = gst_elementfactory_make("queue","audio_queue");
|
||||
gtk_object_set(GTK_OBJECT(audio_queue),"max_level",BUFFER,NULL);
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(audio_queue));
|
||||
gst_bin_add(GST_BIN(pipeline),GST_ELEMENT(audio_thread));
|
||||
gst_pad_connect(pad,
|
||||
gst_element_get_pad(audio_queue,"sink"));
|
||||
gst_pad_connect(gst_element_get_pad(audio_queue,"src"),
|
||||
gst_element_get_pad(audio_thread,"sink"));
|
||||
|
||||
// set up thread state and kick things off
|
||||
gtk_object_set(GTK_OBJECT(audio_thread),"create_thread",TRUE,NULL);
|
||||
g_print("setting to READY state\n");
|
||||
gst_element_set_state(GST_ELEMENT(audio_thread),GST_STATE_READY);
|
||||
}
|
||||
|
||||
void mpeg1_setup_video_thread(GstPad *pad, GstElement *video_render_queue, GstElement *pipeline)
|
||||
|
@ -111,6 +119,6 @@ void mpeg1_setup_video_thread(GstPad *pad, GstElement *video_render_queue, GstEl
|
|||
// set up thread state and kick things off
|
||||
gtk_object_set(GTK_OBJECT(video_thread),"create_thread",TRUE,NULL);
|
||||
g_print("setting to RUNNING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(video_thread),GST_STATE_RUNNING);
|
||||
gst_element_set_state(GST_ELEMENT(video_thread),GST_STATE_READY);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
extern gboolean _gst_plugin_spew;
|
||||
extern GstElement *video_render_queue, *audio_render_queue;
|
||||
GstElement *merge_subtitles;
|
||||
|
||||
void mpeg2_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline)
|
||||
{
|
||||
|
@ -22,10 +23,12 @@ void mpeg2_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline)
|
|||
GstElement *audio_thread;
|
||||
|
||||
g_print("***** a new pad %s was created\n", gst_pad_get_name(pad));
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PAUSED);
|
||||
|
||||
// connect to audio pad
|
||||
if (strncmp(gst_pad_get_name(pad), "video_", 6) == 0) {
|
||||
mpeg2_setup_video_thread(pad, video_render_queue, pipeline);
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
return;
|
||||
}
|
||||
else if (strncmp(gst_pad_get_name(pad), "private_stream_1.0", 18) == 0) {
|
||||
|
@ -36,7 +39,12 @@ void mpeg2_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline)
|
|||
g_return_if_fail(parse_audio != NULL);
|
||||
decode = gst_elementfactory_make("ac3dec","decode_audio");
|
||||
g_return_if_fail(decode != NULL);
|
||||
}
|
||||
} else if (strncmp(gst_pad_get_name(pad), "subtitle_stream_4", 17) == 0) {
|
||||
gst_pad_connect(pad,
|
||||
gst_element_get_pad(merge_subtitles,"subtitle"));
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
return;
|
||||
}
|
||||
else if (strncmp(gst_pad_get_name(pad), "audio_", 6) == 0) {
|
||||
gst_plugin_load("mp3parse");
|
||||
gst_plugin_load("mpg123");
|
||||
|
@ -46,7 +54,10 @@ void mpeg2_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline)
|
|||
decode = gst_elementfactory_make("mpg123","decode_audio");
|
||||
g_return_if_fail(decode != NULL);
|
||||
}
|
||||
else return;
|
||||
else {
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
return;
|
||||
}
|
||||
|
||||
// create the thread and pack stuff into it
|
||||
audio_thread = gst_thread_new("audio_thread");
|
||||
|
@ -74,10 +85,8 @@ void mpeg2_new_pad_created(GstElement *parse,GstPad *pad,GstElement *pipeline)
|
|||
|
||||
// set up thread state and kick things off
|
||||
gtk_object_set(GTK_OBJECT(audio_thread),"create_thread",TRUE,NULL);
|
||||
g_print("setting to RUNNING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(audio_thread),GST_STATE_RUNNING);
|
||||
g_print("setting to PLAYING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(audio_thread),GST_STATE_PLAYING);
|
||||
g_print("setting to READY state\n");
|
||||
gst_element_set_state(GST_ELEMENT(audio_thread),GST_STATE_READY);
|
||||
|
||||
gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);
|
||||
}
|
||||
|
@ -90,17 +99,22 @@ void mpeg2_setup_video_thread(GstPad *pad, GstElement *show, GstElement *pipelin
|
|||
|
||||
gst_plugin_load("mp1videoparse");
|
||||
gst_plugin_load(VIDEO_DECODER);
|
||||
gst_plugin_load("mpeg2subt");
|
||||
// construct internal pipeline elements
|
||||
parse_video = gst_elementfactory_make("mp1videoparse","parse_video");
|
||||
g_return_if_fail(parse_video != NULL);
|
||||
decode_video = gst_elementfactory_make(VIDEO_DECODER,"decode_video");
|
||||
g_return_if_fail(decode_video != NULL);
|
||||
merge_subtitles = gst_elementfactory_make("mpeg2subt","merge_subtitles");
|
||||
g_return_if_fail(merge_subtitles != NULL);
|
||||
|
||||
// create the thread and pack stuff into it
|
||||
video_thread = gst_thread_new("video_thread");
|
||||
g_return_if_fail(video_thread != NULL);
|
||||
gst_bin_add(GST_BIN(video_thread),GST_ELEMENT(parse_video));
|
||||
gst_bin_add(GST_BIN(video_thread),GST_ELEMENT(decode_video));
|
||||
gst_bin_add(GST_BIN(video_thread),GST_ELEMENT(merge_subtitles));
|
||||
gst_bin_use_cothreads(GST_BIN(video_thread), FALSE);
|
||||
|
||||
// set up pad connections
|
||||
gst_element_add_ghost_pad(GST_ELEMENT(video_thread),
|
||||
|
@ -108,6 +122,8 @@ void mpeg2_setup_video_thread(GstPad *pad, GstElement *show, GstElement *pipelin
|
|||
gst_pad_connect(gst_element_get_pad(parse_video,"src"),
|
||||
gst_element_get_pad(decode_video,"sink"));
|
||||
gst_pad_connect(gst_element_get_pad(decode_video,"src"),
|
||||
gst_element_get_pad(merge_subtitles,"video"));
|
||||
gst_pad_connect(gst_element_get_pad(merge_subtitles,"src"),
|
||||
gst_element_get_pad(video_render_queue,"sink"));
|
||||
|
||||
// construct queue and connect everything in the main pipeline
|
||||
|
@ -123,8 +139,7 @@ void mpeg2_setup_video_thread(GstPad *pad, GstElement *show, GstElement *pipelin
|
|||
// set up thread state and kick things off
|
||||
gtk_object_set(GTK_OBJECT(video_thread),"create_thread",TRUE,NULL);
|
||||
g_print("setting to RUNNING state\n");
|
||||
gst_element_set_state(GST_ELEMENT(video_thread),GST_STATE_RUNNING);
|
||||
gst_element_set_state(GST_ELEMENT(video_thread),GST_STATE_PLAYING);
|
||||
gst_element_set_state(GST_ELEMENT(video_thread),GST_STATE_READY);
|
||||
|
||||
g_print("\n");
|
||||
}
|
||||
|
|
23
gstplay/pause.xpm
Normal file
23
gstplay/pause.xpm
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* XPM */
|
||||
static char * pause_xpm[] = {
|
||||
"13 15 5 1",
|
||||
" c None",
|
||||
". c #949594",
|
||||
"+ c #000000",
|
||||
"@ c #8E8E8E",
|
||||
"# c #FFFFFF",
|
||||
"...... ......",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+++@# .+++@#",
|
||||
".+@@@# .+@@@#",
|
||||
".##### .#####"};
|
23
gstplay/play.xpm
Normal file
23
gstplay/play.xpm
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* XPM */
|
||||
static char * play_back_xpm[] = {
|
||||
"13 15 5 1",
|
||||
" c None",
|
||||
". c #949594",
|
||||
"+ c #000000",
|
||||
"@ c #8E8E8E",
|
||||
"# c #FFFFFF",
|
||||
"...... ",
|
||||
".+++++. ",
|
||||
".++++++. ",
|
||||
".+++++++. ",
|
||||
".++++++++. ",
|
||||
".+++++++++. ",
|
||||
".++++++++++. ",
|
||||
".++++++++++@#",
|
||||
".+++++++++@# ",
|
||||
".++++++++@# ",
|
||||
".+++++++@# ",
|
||||
".++++++@# ",
|
||||
".+++++@# ",
|
||||
".+@@@@# ",
|
||||
".##### "};
|
23
gstplay/stop.xpm
Normal file
23
gstplay/stop.xpm
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* XPM */
|
||||
static char * stop_back_xpm[] = {
|
||||
"13 15 5 1",
|
||||
" c None",
|
||||
". c #949594",
|
||||
"+ c #000000",
|
||||
"@ c #8E8E8E",
|
||||
"# c #FFFFFF",
|
||||
".............",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".++++++++++@#",
|
||||
".+@@@@@@@@@@#",
|
||||
".############"};
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
* DO NOT EDIT THIS FILE - it is generated by Glade.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gnome.h>
|
||||
|
||||
#include "support.h"
|
||||
|
||||
/* This is an internally used function to create pixmaps. */
|
||||
static GtkWidget* create_dummy_pixmap (GtkWidget *widget,
|
||||
gboolean gnome_pixmap);
|
||||
|
||||
GtkWidget*
|
||||
lookup_widget (GtkWidget *widget,
|
||||
const gchar *widget_name)
|
||||
{
|
||||
GtkWidget *parent, *found_widget;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (GTK_IS_MENU (widget))
|
||||
parent = gtk_menu_get_attach_widget (GTK_MENU (widget));
|
||||
else
|
||||
parent = widget->parent;
|
||||
if (parent == NULL)
|
||||
break;
|
||||
widget = parent;
|
||||
}
|
||||
|
||||
found_widget = (GtkWidget*) gtk_object_get_data (GTK_OBJECT (widget),
|
||||
widget_name);
|
||||
if (!found_widget)
|
||||
g_warning ("Widget not found: %s", widget_name);
|
||||
return found_widget;
|
||||
}
|
||||
|
||||
/* This is a dummy pixmap we use when a pixmap can't be found. */
|
||||
static char *dummy_pixmap_xpm[] = {
|
||||
/* columns rows colors chars-per-pixel */
|
||||
"1 1 1 1",
|
||||
" c None",
|
||||
/* pixels */
|
||||
" ",
|
||||
" "
|
||||
};
|
||||
|
||||
/* This is an internally used function to create pixmaps. */
|
||||
static GtkWidget*
|
||||
create_dummy_pixmap (GtkWidget *widget,
|
||||
gboolean gnome_pixmap)
|
||||
{
|
||||
GdkColormap *colormap;
|
||||
GdkPixmap *gdkpixmap;
|
||||
GdkBitmap *mask;
|
||||
GtkWidget *pixmap;
|
||||
|
||||
if (gnome_pixmap)
|
||||
{
|
||||
return gnome_pixmap_new_from_xpm_d (dummy_pixmap_xpm);
|
||||
}
|
||||
|
||||
colormap = gtk_widget_get_colormap (widget);
|
||||
gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask,
|
||||
NULL, dummy_pixmap_xpm);
|
||||
if (gdkpixmap == NULL)
|
||||
g_error ("Couldn't create replacement pixmap.");
|
||||
pixmap = gtk_pixmap_new (gdkpixmap, mask);
|
||||
gdk_pixmap_unref (gdkpixmap);
|
||||
gdk_bitmap_unref (mask);
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
/* This is an internally used function to create pixmaps. */
|
||||
GtkWidget*
|
||||
create_pixmap (GtkWidget *widget,
|
||||
const gchar *filename,
|
||||
gboolean gnome_pixmap)
|
||||
{
|
||||
GtkWidget *pixmap;
|
||||
GdkColormap *colormap;
|
||||
GdkPixmap *gdkpixmap;
|
||||
GdkBitmap *mask;
|
||||
gchar *pathname;
|
||||
|
||||
if (!filename || !filename[0])
|
||||
return create_dummy_pixmap (widget, gnome_pixmap);
|
||||
|
||||
pathname = gnome_pixmap_file (filename);
|
||||
if (!pathname)
|
||||
{
|
||||
g_warning (_("Couldn't find pixmap file: %s"), filename);
|
||||
return create_dummy_pixmap (widget, gnome_pixmap);
|
||||
}
|
||||
|
||||
if (gnome_pixmap)
|
||||
{
|
||||
pixmap = gnome_pixmap_new_from_file (pathname);
|
||||
g_free (pathname);
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
colormap = gtk_widget_get_colormap (widget);
|
||||
gdkpixmap = gdk_pixmap_colormap_create_from_xpm (NULL, colormap, &mask,
|
||||
NULL, pathname);
|
||||
if (gdkpixmap == NULL)
|
||||
{
|
||||
g_warning (_("Couldn't create pixmap from file: %s"), pathname);
|
||||
g_free (pathname);
|
||||
return create_dummy_pixmap (widget, gnome_pixmap);
|
||||
}
|
||||
g_free (pathname);
|
||||
|
||||
pixmap = gtk_pixmap_new (gdkpixmap, mask);
|
||||
gdk_pixmap_unref (gdkpixmap);
|
||||
gdk_bitmap_unref (mask);
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
/* This is an internally used function to create imlib images. */
|
||||
GdkImlibImage*
|
||||
create_image (const gchar *filename)
|
||||
{
|
||||
GdkImlibImage *image;
|
||||
gchar *pathname;
|
||||
|
||||
pathname = gnome_pixmap_file (filename);
|
||||
if (!pathname)
|
||||
{
|
||||
g_warning (_("Couldn't find pixmap file: %s"), filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image = gdk_imlib_load_image (pathname);
|
||||
g_free (pathname);
|
||||
return image;
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* DO NOT EDIT THIS FILE - it is generated by Glade.
|
||||
*/
|
||||
|
||||
#include <gnome.h>
|
||||
|
||||
/*
|
||||
* Public Functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This function returns a widget in a component created by Glade.
|
||||
* Call it with the toplevel widget in the component (i.e. a window/dialog),
|
||||
* or alternatively any widget in the component, and the name of the widget
|
||||
* you want returned.
|
||||
*/
|
||||
GtkWidget* lookup_widget (GtkWidget *widget,
|
||||
const gchar *widget_name);
|
||||
|
||||
/* get_widget() is deprecated. Use lookup_widget instead. */
|
||||
#define get_widget lookup_widget
|
||||
|
||||
|
||||
/*
|
||||
* Private Functions.
|
||||
*/
|
||||
|
||||
/* This is used to create the pixmaps in the interface. */
|
||||
GtkWidget* create_pixmap (GtkWidget *widget,
|
||||
const gchar *filename,
|
||||
gboolean gnome_pixmap);
|
||||
|
||||
GdkImlibImage* create_image (const gchar *filename);
|
||||
|
Loading…
Reference in a new issue