OSDN Git Service

some code cleanup
[eb123/eb123.git] / src / video.c
1
2 #include "defs.h"
3
4 #include "eb123.h"
5 #include "ebgstsrc.h"
6 #include "history.h"
7 #include "mainwnd.h"
8 #include "render.h"
9 #include "video.h"
10
11 G_DEFINE_TYPE(Video, video, GTK_TYPE_FRAME);
12
13 #define video_DECODER    "decoder"
14 #define video_COLOR         "colorspace"
15 #define video_SCALE         "videoscale"
16 #define video_SINK          "videosink"
17
18 gboolean video_save_mpeg(RESULT *res, gchar *file)
19 {
20     char binary_data[EB_SIZE_PAGE];
21     guint argv[4];
22     EB_Error_Code error_code;
23     ssize_t read_length;
24     FILE *fp;
25
26     BOOK_INFO *binfo = res->binfo;
27     if(!binfo)
28         return FALSE;
29
30     error_code = eb_set_subbook(binfo->book, binfo->subbook_no);
31     if(error_code != EB_SUCCESS)
32     {
33         LOG(LOG_CRITICAL, "Failed to set subbook %s, %d : %s", binfo->path, binfo->subbook_no, eb_error_message(error_code));
34         return FALSE;
35     }
36
37     if((error_code = eb_decompose_movie_file_name(argv, res->filename)) != EB_SUCCESS)
38         return FALSE;
39
40     error_code = eb_set_binary_mpeg(binfo->book, argv);
41     if(error_code != EB_SUCCESS)
42     {
43         LOG(LOG_CRITICAL, "Failed to set binary mpeg: %s", eb_error_message(error_code));
44         return FALSE;
45     }
46
47     fp = fopen(file, "wb");
48     if(fp == NULL)
49     {
50         LOG(LOG_CRITICAL, "Failed to open file: %s", file);
51         return FALSE;
52     }
53
54     for(;;)
55     {
56         error_code = eb_read_binary(binfo->book, EB_SIZE_PAGE, binary_data, &read_length);
57         if(error_code != EB_SUCCESS || read_length == 0)
58         {
59             fclose(fp);
60             return FALSE;
61         }
62         fwrite(binary_data, read_length, 1, fp);
63     }
64
65     return TRUE;
66 }
67
68 void video_save_as(GtkWidget *w, gpointer data)
69 {       
70     static gchar *path = NULL;
71     path = app_browse_disk(_("Save Video As"), mainwnd_get_wnd(), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_SAVE_AS, path);
72     if(path)
73     {
74         RESULT *res = (RESULT*)data;
75         if(video_save_mpeg(res, path) != EB_SUCCESS)
76             LOG(LOG_WARNING, _("Failed to save video"));
77     }
78
79 }
80
81 #ifdef ENABLE_GSTREAMER
82
83 static gpointer video_parent_class_ptr = NULL;
84
85 static void video_finalize(GObject *obj)
86 {
87     g_return_if_fail(obj != NULL);
88     g_return_if_fail(EB_IS_VIDEO(obj));
89
90     Video *v = video(obj);
91     g_idle_remove_by_data(v->pipeline);
92     gst_element_set_state(v->pipeline, GST_STATE_NULL);
93     gst_object_unref(GST_OBJECT(v->pipeline));
94     G_OBJECT_CLASS(video_parent_class_ptr)->finalize(obj);
95 }
96
97 static void video_play_pause_set_icon(Video *self, gboolean play)
98 {
99     GtkWidget *image = gtk_bin_get_child(GTK_BIN(self->btn));
100     gtk_widget_destroy(image);
101     image = gtk_image_new_from_stock(play ? GTK_STOCK_MEDIA_PAUSE : GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_SMALL_TOOLBAR);
102     gtk_button_set_image(GTK_BUTTON(self->btn), image);
103 }
104
105 static void video_play_pause(Video *self, gboolean play)
106 {
107     if(self->output->window && self->pipeline)
108     {
109         gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(self->sink), GDK_WINDOW_XID(self->output->window));
110         gst_element_set_state(self->pipeline, play ? GST_STATE_PLAYING : GST_STATE_PAUSED);
111         video_play_pause_set_icon(self, play);
112     }
113 }
114
115 static gboolean video_pause(gpointer data)
116 {
117     Video *v = video(data);
118     video_play_pause(v, FALSE);
119     return FALSE;
120 }
121
122 static gboolean video_reset(gpointer data)
123 {
124     Video *v = video(data);
125     video_play_pause(v, TRUE);
126     g_idle_add(video_pause, data);
127     return FALSE;
128 }
129
130 static void video_resize(Video *self, gint w, gint h)
131 {
132     GdkScreen *screen = gdk_screen_get_default();
133     gint sw = gdk_screen_get_width(screen), sh = gdk_screen_get_height(screen);
134     if((w > 0) && (h > 0) && (w <= sw) && (h <= sh))
135     {
136         gtk_widget_set_size_request(self->output, w, h);
137 #if 0
138         GtkWidget *wnd = gtk_widget_get_toplevel(self->output);
139         gtk_widget_set_size_request(wnd, -1, h + 15*GTK_ICON_SIZE_SMALL_TOOLBAR);
140 #endif
141         self->video_resized = TRUE;
142     }
143 }
144
145 gboolean video_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
146 {
147     Video *v = video(data);
148     switch (GST_MESSAGE_TYPE(msg))
149     {
150         case GST_MESSAGE_STATE_CHANGED:
151         {
152             if(v->video_resized)
153                 break;
154             GstState oldstate, newstate;
155             gst_message_parse_state_changed(msg, &oldstate, &newstate, NULL);
156             if(oldstate == GST_STATE_READY && newstate == GST_STATE_PAUSED)
157             {
158                 gchar *name = gst_element_get_name(msg->src);
159                 if(!g_strcmp0(name, video_DECODER))
160                 {
161                     GstPad *pad = gst_element_get_static_pad(GST_ELEMENT(msg->src), "src");
162                     if(pad)
163                     {
164                         gint w, h;
165                         gst_video_get_size(pad, &w, &h);
166                         video_resize(v, w, h);
167                     }
168                 }
169                 g_free(name);
170             }
171             break;
172         }
173         case GST_MESSAGE_EOS:
174         {
175             gst_element_set_state(v->pipeline, GST_STATE_NULL);
176             g_idle_add(video_reset, (gpointer)v);
177             break;
178         }
179
180         case GST_MESSAGE_ERROR:
181         {
182             gchar  *debug;
183             GError *error;
184
185             gst_message_parse_error(msg, &error, &debug);
186             g_free(debug);
187
188             LOG(LOG_INFO, "%s\n", error->message);
189             g_error_free(error);
190
191             break;
192         }
193          default:
194             break;
195     }
196
197     return TRUE;
198 }
199
200 static gboolean video_timeout(gpointer data)
201 {
202     if(!EB_IS_VIDEO(data)) return FALSE;
203
204     Video *v = video(data);
205
206     GstFormat format = GST_FORMAT_TIME;
207     gchar time[32];
208     gint64 pos;
209     gint hh, mm, ss;
210
211     if(!GST_IS_ELEMENT(v->pipeline))
212         return FALSE;
213
214     gst_element_query_position(v->pipeline, &format, &pos);
215     if(format != GST_FORMAT_TIME)
216         return FALSE;
217
218     pos /= 1000000000;
219     ss = pos % 60;
220     pos /= 60;
221     mm = pos % 60;
222     hh = pos / 60;
223
224     if(!hh)
225         sprintf(time, "%.2d:%.2d", mm, ss);
226     else
227         sprintf(time, "%.2d:%.2d:%.2d", hh, mm, ss);
228
229     gtk_label_set_text(GTK_LABEL(v->time), time);
230
231     gst_x_overlay_expose(GST_X_OVERLAY(v->sink));
232
233     return TRUE;
234 }
235
236 static void video_output_prepare(Video *self)
237 {
238     GstElement *src, *dec, *color, *scale;
239     GstBus *bus;
240     RESULT *res = render.link;
241
242     self->pipeline = gst_pipeline_new("pipeline");
243     src = g_object_new(EB_GST_TYPE_SRC, NULL);
244     EB_GST_SRC(src)->res = res;
245     dec = gst_element_factory_make("mpeg2dec", video_DECODER);
246     color = gst_element_factory_make("ffmpegcolorspace", video_COLOR);
247     scale = gst_element_factory_make("videoscale", video_SCALE);
248     self->sink = gst_element_factory_make("ximagesink", video_SINK); 
249
250     gst_bin_add_many(GST_BIN(self->pipeline), src, dec, color, scale, self->sink, NULL);
251     gst_element_link_many(src, dec, color, scale, self->sink, NULL);
252
253     bus = gst_pipeline_get_bus(GST_PIPELINE(self->pipeline));
254     gst_bus_add_watch(bus, video_bus_cb, (gpointer)self);
255     gst_object_unref(bus);
256
257     g_timeout_add(500, video_timeout, (gpointer)self);
258
259     video_reset((gpointer)self);
260 }
261
262 void video_play_pause_cb(GtkWidget *w, gpointer data)
263 {
264     Video *v = video(data);
265     GstState state, pending;
266     gst_element_get_state(GST_ELEMENT(v->pipeline), &state, &pending, GST_CLOCK_TIME_NONE);
267     gboolean play = (state != GST_STATE_PLAYING);
268     video_play_pause(v, play);
269 }
270
271 #endif
272
273 static void video_class_init(VideoClass *klass)
274 {
275 #ifdef ENABLE_GSTREAMER
276     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
277     gobject_class->finalize = video_finalize;
278     video_parent_class_ptr = g_type_class_peek_parent(klass);
279 #endif
280 }
281
282 static void video_init(Video *self)
283 {
284     GtkWidget *hbox = NULL;
285 #ifdef ENABLE_GSTREAMER
286     GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
287     gtk_container_add(GTK_CONTAINER(self), vbox);
288
289     self->output = gtk_drawing_area_new();
290     gtk_box_pack_start(GTK_BOX(vbox), self->output, TRUE, TRUE, 0);
291     gtk_widget_set_size_request(self->output, 320, 240);
292     self->video_resized = FALSE;
293
294     hbox = gtk_hbox_new(FALSE, 0);
295     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
296
297     self->btn = gtk_button_new();
298     GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_SMALL_TOOLBAR);
299     gtk_button_set_image(GTK_BUTTON(self->btn), image);
300     g_signal_connect(G_OBJECT(self->btn), "clicked", G_CALLBACK(video_play_pause_cb), (gpointer)self);
301     gtk_box_pack_start(GTK_BOX(hbox), self->btn, FALSE, TRUE, 0);
302
303     self->time = gtk_label_new("");
304     gtk_misc_set_alignment(GTK_MISC(self->time), 1, 0.5);
305     gtk_box_pack_start(GTK_BOX(hbox), self->time, TRUE, TRUE, 0);
306 #else
307     hbox = gtk_hbox_new(FALSE, 0);
308     gtk_container_add(GTK_CONTAINER(self), hbox);
309
310     GtkWidget *label = gtk_label_new(_("Video"));
311     gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
312 #endif
313     GtkWidget *btn = gtk_button_new();
314     GtkWidget *img = gtk_image_new_from_stock(GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_SMALL_TOOLBAR);
315     gtk_container_add(GTK_CONTAINER(btn), img);
316     //g_signal_connect(G_OBJECT(btn), "clicked", G_CALLBACK(video_save_as), render.link);
317     gtk_box_pack_start(GTK_BOX(hbox), btn, FALSE, TRUE, 0);
318 }
319
320 void video_render(Video *self)
321 {
322 #if 0
323     GtkTextIter iter1, iter2;
324     if(render_get_last_mark("mpeg", &iter1, &iter2))
325     {
326         gtk_text_buffer_delete(render.buf, &iter1, &iter2);
327         gtk_text_buffer_get_end_iter(render.buf, &iter1);
328         gtk_text_buffer_insert(render.buf, &iter1, "\n", -1);
329         gtk_text_buffer_get_end_iter(render.buf, &iter1);
330         GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(render.buf, &iter1);
331         gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(render.view), GTK_WIDGET(self), anchor);
332         gtk_text_buffer_insert(render.buf, &iter1, "\n", -1);
333         g_object_set_data_full(G_OBJECT(self), "video", render.link, result_free);
334         gtk_widget_show_all(GTK_WIDGET(self));
335         gtk_widget_realize(GTK_WIDGET(self));
336         while(gtk_events_pending())
337             gtk_main_iteration();
338 #ifdef ENABLE_GSTREAMER
339         video_output_prepare(self);
340 #endif
341     }
342 #endif
343 }
344