]> git.sesse.net Git - vlc/blob - doc/libvlc/libvlc_DVD_ripper.c
decoder: add input_DecoderFlush()
[vlc] / doc / libvlc / libvlc_DVD_ripper.c
1 // gcc -o main main.c `pkg-config --cflags --libs gtk+-2.0 libvlc`
2
3 /* Written by Vincent Schüßler */
4 /* License WTFPL http://sam.zoy.org/wtfpl/ */
5
6 #include <stdlib.h>
7 #include <time.h>
8 #include <string.h>
9 #include <gtk/gtk.h>
10 #include <vlc/vlc.h>
11
12 #define PRESET_H264_AAC_MP4_HIGH "MP4 high (h.264 + AAC)"
13 #define PRESET_H264_AAC_MP4_LOW "MP4 low (h.264 + AAC)"
14 #define PRESET_THEORA_VORBIS_OGG_HIGH "OGG high (Theora + Vorbis)"
15 #define PRESET_THEORA_VORBIS_OGG_LOW "OGG low (Theora + Vorbis)"
16 #define PRESET_VP8_VORBIS_WEBM_HIGH "WebM high (VP8 + Vorbis)"
17 #define PRESET_VP8_VORBIS_WEBM_LOW "WebM low (VP8 + Vorbis)"
18 #define BORDER_WIDTH 6
19
20 GtkWidget *window;
21 GtkWidget *source_entry, *dest_entry;
22 GtkWidget *progressbar;
23 libvlc_instance_t *vlcinst;
24 libvlc_media_list_t *medialist;
25 GtkWidget *run_button, *format_chooser, *spinner;
26 char stopped;
27
28 gchar* get_filepath(GtkWidget* widget, GtkFileChooserAction action) {
29     GtkWidget *dialog;
30     gchar *path;
31     dialog = gtk_file_chooser_dialog_new("Choose location", GTK_WINDOW(gtk_widget_get_toplevel(widget)), action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
32     if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
33         path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
34     }
35     else {
36         path = NULL;
37     }
38     gtk_widget_destroy(dialog);
39     return path;
40 }
41
42 void on_select_source_path(GtkWidget *widget, gpointer data) {
43     char *path;
44     char scheme[] = "file://";
45     char *uri;
46
47     if(data==NULL) {
48         path = (char*) get_filepath(widget, GTK_FILE_CHOOSER_ACTION_OPEN);
49     }
50     else {
51         path = (char*) get_filepath(widget, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
52     }
53     if(path != NULL) {
54         uri = malloc((strlen(scheme)+strlen(path)+1) * sizeof(char));
55         if(uri == NULL) return;
56         uri[0] = '\0';
57         strncat(uri, scheme, strlen(scheme));
58         strncat(uri, path, strlen(path));
59         g_free(path);
60         
61         gtk_entry_set_text(GTK_ENTRY(source_entry), uri);
62         free(uri);
63     }
64 }
65
66 void on_select_dest_path(GtkWidget *widget, gpointer data) {
67     gchar *path;
68     path = get_filepath(widget, GTK_FILE_CHOOSER_ACTION_SAVE);
69     if(path != NULL) {
70         gtk_entry_set_text(GTK_ENTRY(dest_entry), path);
71         g_free(path);
72     }
73 }
74
75 char* get_pos_string(float pos) {
76     int len;
77     const char format[] = "%.3f %%";
78     char *pos_string;
79     pos *= 100;
80     len = snprintf(NULL, 0, format, pos);
81     pos_string = malloc(len);
82     if(pos_string==NULL) return NULL;
83     sprintf(pos_string, format, pos);
84     return pos_string;
85 }
86
87 gboolean update_progressbar(char *handle) {
88     float pos = libvlc_vlm_get_media_instance_position(vlcinst, handle, 0);
89     char *pos_string;
90     if(pos < 1.0 && 0.0 <= pos) {
91         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), pos);
92         pos_string = get_pos_string(pos);
93         if(pos_string != NULL) {
94             gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressbar), pos_string);
95             free(pos_string);
96         }
97         return TRUE;
98     }
99     if(stopped = 1) {
100         free(handle);
101         return FALSE;
102     }
103 }
104
105 const char* get_transcode_string(char *preset) {
106     static const char mp4_high[] = "#transcode{vcodec=h264,venc=x264{cfr=16},scale=1,acodec=mp4a,ab=160,channels=2,samplerate=44100}";
107     static const char mp4_low[] = "#transcode{vcodec=h264,venc=x264{cfr=40},scale=1,acodec=mp4a,ab=96,channels=2,samplerate=44100}";
108     static const char ogg_high[] = "#transcode{vcodec=theo,venc=theora{quality=9},scale=1,acodec=vorb,ab=160,channels=2,samplerate=44100}";
109     static const char ogg_low[] = "#transcode{vcodec=theo,venc=theora{quality=4},scale=1,acodec=vorb,ab=96,channels=2,samplerate=44100}";
110     static const char webm_high[] = "#transcode{vcodec=VP80,vb=2000,scale=1,acodec=vorb,ab=160,channels=2,samplerate=44100}";
111     static const char webm_low[] = "#transcode{vcodec=VP80,vb=1000,scale=1,acodec=vorb,ab=96,channels=2,samplerate=44100}";
112     static const char nothing[] = "";
113     if(0 == strcmp(preset, PRESET_H264_AAC_MP4_HIGH)) {
114         return mp4_high;
115     }
116     else if(0 == strcmp(preset, PRESET_H264_AAC_MP4_LOW)) {
117         return mp4_low;
118     }
119     else if(0 == strcmp(preset, PRESET_THEORA_VORBIS_OGG_HIGH)) {
120         return ogg_high;
121     }
122     else if(0 == strcmp(preset, PRESET_THEORA_VORBIS_OGG_LOW)) {
123         return ogg_low;
124     }
125     else if(0 == strcmp(preset, PRESET_VP8_VORBIS_WEBM_HIGH)) {
126         return webm_high;
127     }
128     else if(0 == strcmp(preset, PRESET_VP8_VORBIS_WEBM_LOW)) {
129         return webm_low;
130     }
131     else {
132         return nothing;
133     }
134 }
135
136 void on_run(GtkWidget *widget, gpointer data) {
137     char *handle;
138     const char *transcode;
139     char *source = (char*) gtk_entry_get_text(GTK_ENTRY(source_entry));
140     char *dest = (char*) gtk_entry_get_text(GTK_ENTRY(dest_entry));
141     char *preset= (char*) gtk_combo_box_get_active_text(GTK_COMBO_BOX(format_chooser));
142     int i;
143     char file_begin[] = ":file{dst=";
144     char file_end[] = "}";
145     char *sout;
146     if(preset == NULL) return;
147     gtk_widget_set_sensitive(widget, FALSE);
148     handle = malloc((strlen(source)+4+1) * sizeof(char));
149     if(handle == NULL) return;
150     strncpy(handle, source, strlen(source));
151     for(i=0;i<=3;++i) {
152         handle[strlen(source)+i] = (char) (((unsigned int) rand()) & 63) + '0';
153     }
154     handle[strlen(source)+4] = '\0';
155     transcode = get_transcode_string(preset);
156     free(preset);
157     sout = malloc((strlen(transcode)+strlen(file_begin)+strlen(dest)+strlen(file_end)+1) * sizeof(char));
158     if(sout == NULL) return;
159     strncpy(sout, transcode, strlen(transcode)+1);
160     strncat(sout, file_begin, strlen(file_begin));
161     strncat(sout, dest, strlen(dest));
162     strncat(sout, file_end, strlen(file_end));
163     libvlc_vlm_add_broadcast(vlcinst, handle, source, sout, 0, NULL, 1, 0);
164     free(sout);
165     libvlc_vlm_play_media(vlcinst, handle);
166     gtk_widget_show(spinner);
167     gtk_spinner_start(GTK_SPINNER(spinner));
168     stopped = 0;
169     g_timeout_add(50, (GSourceFunc) update_progressbar, handle);
170 }
171
172 void stop(void) {
173     stopped = 1;
174     gtk_spinner_stop(GTK_SPINNER(spinner));
175     gtk_widget_hide(spinner);
176     gtk_widget_set_sensitive(run_button, TRUE);
177     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), 0.0);
178     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progressbar), "");
179 }
180
181 gboolean on_end(void) {
182     GtkWidget *dialog;
183     stop();
184     dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "Rip done");
185     gtk_dialog_run(GTK_DIALOG(dialog));
186     gtk_widget_destroy(dialog);
187     return FALSE;
188 }
189
190 void on_end_vlc(const libvlc_event_t *event, void *data) {
191     g_idle_add((GSourceFunc) on_end, NULL);
192 }
193
194 gboolean on_error(void) {
195     GtkWidget *dialog;
196     stop();
197     dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Error");gtk_dialog_run(GTK_DIALOG(dialog));
198     gtk_widget_destroy(dialog);
199     return FALSE;
200 }
201
202 void on_error_vlc(const libvlc_event_t *event, void *data) {
203     g_idle_add((GSourceFunc) on_error, NULL);
204 }
205
206 int main (int argc, char *argv[]) {
207     GtkWidget *media_list, *scrolled;
208     GtkWidget *choose_frame, *output_frame, *run_frame;
209     GtkWidget *vbox, *choose_table, *output_table, *run_table;
210     GtkWidget *source_label, *source_button, *source_folder_button;
211     GtkWidget *dest_label, *dest_button;
212
213     libvlc_event_manager_t *evtman;
214
215     gtk_init(&argc, &argv);
216
217     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
218     gtk_container_set_border_width(GTK_CONTAINER(window), BORDER_WIDTH);
219     gtk_window_set_title(GTK_WINDOW(window), "libVLC DVD ripper");
220
221     //setup vbox
222     vbox = gtk_vbox_new(FALSE, BORDER_WIDTH);
223     gtk_container_add(GTK_CONTAINER(window), vbox);
224
225     // setup media list with scrolled window
226     media_list = gtk_tree_view_new();
227     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(media_list), FALSE);
228     scrolled = gtk_scrolled_window_new(NULL, NULL);
229     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
230     gtk_container_add(GTK_CONTAINER(scrolled), media_list);
231
232     // setup "choose"-frame
233     choose_frame = gtk_frame_new("Choose DVD");
234     gtk_box_pack_start(GTK_BOX(vbox), choose_frame, TRUE, TRUE, 0);
235     choose_table = gtk_table_new(1, 4, FALSE);
236     gtk_table_set_row_spacings(GTK_TABLE(choose_table), BORDER_WIDTH/2);
237     gtk_table_set_col_spacings(GTK_TABLE(choose_table), BORDER_WIDTH/2);
238     gtk_container_set_border_width(GTK_CONTAINER(choose_table), BORDER_WIDTH);
239     source_label = gtk_label_new("Input file or folder:");
240     source_entry = gtk_entry_new();
241     gtk_entry_set_text(GTK_ENTRY(source_entry), "dvd://");
242     source_button = gtk_button_new_with_label("Open file");
243     source_folder_button = gtk_button_new_with_label("Open folder");
244     g_signal_connect(G_OBJECT(source_button), "clicked", G_CALLBACK(on_select_source_path), NULL);
245     g_signal_connect(G_OBJECT(source_folder_button), "clicked", G_CALLBACK(on_select_source_path), (gpointer) 1);
246     gtk_table_attach(GTK_TABLE(choose_table), source_label, 0, 1, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
247     gtk_table_attach(GTK_TABLE(choose_table), source_entry, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
248     gtk_table_attach(GTK_TABLE(choose_table), source_button, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
249     gtk_table_attach(GTK_TABLE(choose_table), source_folder_button, 3, 4, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
250     gtk_container_add(GTK_CONTAINER(choose_frame), choose_table);
251
252     // setup "output"-frame
253     output_frame = gtk_frame_new("Output settings");
254     gtk_box_pack_start(GTK_BOX(vbox), output_frame, TRUE, TRUE, 0);
255     output_table = gtk_table_new(2, 3, FALSE);
256     gtk_table_set_row_spacings(GTK_TABLE(output_table), BORDER_WIDTH/2);
257     gtk_table_set_col_spacings(GTK_TABLE(output_table), BORDER_WIDTH/2);
258     gtk_container_set_border_width(GTK_CONTAINER(output_table), BORDER_WIDTH);
259     gtk_container_add(GTK_CONTAINER(output_frame), output_table);
260     dest_label = gtk_label_new("Output file:");
261     dest_entry = gtk_entry_new();
262     format_chooser = gtk_combo_box_new_text();
263     gtk_combo_box_append_text(GTK_COMBO_BOX(format_chooser), PRESET_H264_AAC_MP4_HIGH);
264     gtk_combo_box_append_text(GTK_COMBO_BOX(format_chooser), PRESET_H264_AAC_MP4_LOW);
265     gtk_combo_box_append_text(GTK_COMBO_BOX(format_chooser), PRESET_THEORA_VORBIS_OGG_HIGH);
266     gtk_combo_box_append_text(GTK_COMBO_BOX(format_chooser), PRESET_THEORA_VORBIS_OGG_LOW);
267     gtk_combo_box_append_text(GTK_COMBO_BOX(format_chooser), PRESET_VP8_VORBIS_WEBM_HIGH);
268     gtk_combo_box_append_text(GTK_COMBO_BOX(format_chooser), PRESET_VP8_VORBIS_WEBM_LOW);
269     gtk_combo_box_set_active(GTK_COMBO_BOX(format_chooser), 0);
270     dest_button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
271     g_signal_connect(G_OBJECT(dest_button), "clicked", G_CALLBACK(on_select_dest_path), NULL);
272     gtk_table_attach(GTK_TABLE(output_table), dest_label, 0, 1, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
273     gtk_table_attach(GTK_TABLE(output_table), dest_entry, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
274     gtk_table_attach(GTK_TABLE(output_table), dest_button, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
275     gtk_table_attach(GTK_TABLE(output_table), format_chooser, 0, 4, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
276
277     // setup "run"-frame
278     run_frame = gtk_frame_new("Run");
279     gtk_box_pack_start(GTK_BOX(vbox), run_frame, TRUE, TRUE, 0);
280     run_table = gtk_table_new(3, 2, FALSE);
281     gtk_table_set_row_spacings(GTK_TABLE(run_table), BORDER_WIDTH/2);
282     gtk_table_set_col_spacings(GTK_TABLE(run_table), BORDER_WIDTH/2);
283     gtk_container_set_border_width(GTK_CONTAINER(run_table), BORDER_WIDTH);
284     gtk_container_add(GTK_CONTAINER(run_frame), run_table);
285     progressbar = gtk_progress_bar_new();
286     spinner = gtk_spinner_new();
287     run_button = gtk_button_new_from_stock(GTK_STOCK_OK);
288     g_signal_connect(G_OBJECT(run_button), "clicked", G_CALLBACK(on_run), NULL);
289     gtk_table_attach(GTK_TABLE(run_table), progressbar, 0, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
290     gtk_table_attach(GTK_TABLE(run_table), gtk_label_new(""), 0, 1, 1, 2, GTK_EXPAND, GTK_SHRINK, 0, 0);
291     gtk_table_attach(GTK_TABLE(run_table), spinner, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
292     gtk_table_attach(GTK_TABLE(run_table), run_button, 2, 3, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
293
294     // setup vlc
295     vlcinst = libvlc_new(0, NULL);
296     evtman = libvlc_vlm_get_event_manager(vlcinst);
297         libvlc_event_attach(evtman, libvlc_VlmMediaInstanceStatusEnd, on_end_vlc, NULL);
298         libvlc_event_attach(evtman, libvlc_VlmMediaInstanceStatusError, on_error_vlc, NULL);
299
300     g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
301     gtk_widget_show_all(window);
302     gtk_widget_hide(spinner);
303     gtk_main ();
304     libvlc_release(vlcinst);
305     return 0;
306 }