1 // gcc -o main main.c `pkg-config --cflags --libs gtk+-2.0 libvlc`
3 /* Written by Vincent Schüßler */
4 /* License WTFPL http://sam.zoy.org/wtfpl/ */
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
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
;
28 gchar
* get_filepath(GtkWidget
* widget
, GtkFileChooserAction action
) {
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
));
38 gtk_widget_destroy(dialog
);
42 void on_select_source_path(GtkWidget
*widget
, gpointer data
) {
44 char scheme
[] = "file://";
48 path
= (char*) get_filepath(widget
, GTK_FILE_CHOOSER_ACTION_OPEN
);
51 path
= (char*) get_filepath(widget
, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
);
54 uri
= malloc((strlen(scheme
)+strlen(path
)+1) * sizeof(char));
55 if(uri
== NULL
) return;
57 strncat(uri
, scheme
, strlen(scheme
));
58 strncat(uri
, path
, strlen(path
));
61 gtk_entry_set_text(GTK_ENTRY(source_entry
), uri
);
66 void on_select_dest_path(GtkWidget
*widget
, gpointer data
) {
68 path
= get_filepath(widget
, GTK_FILE_CHOOSER_ACTION_SAVE
);
70 gtk_entry_set_text(GTK_ENTRY(dest_entry
), path
);
75 char* get_pos_string(float pos
) {
77 const char format
[] = "%.3f %%";
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
);
87 gboolean
update_progressbar(char *handle
) {
88 float pos
= libvlc_vlm_get_media_instance_position(vlcinst
, handle
, 0);
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
);
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
)) {
116 else if(0 == strcmp(preset
, PRESET_H264_AAC_MP4_LOW
)) {
119 else if(0 == strcmp(preset
, PRESET_THEORA_VORBIS_OGG_HIGH
)) {
122 else if(0 == strcmp(preset
, PRESET_THEORA_VORBIS_OGG_LOW
)) {
125 else if(0 == strcmp(preset
, PRESET_VP8_VORBIS_WEBM_HIGH
)) {
128 else if(0 == strcmp(preset
, PRESET_VP8_VORBIS_WEBM_LOW
)) {
136 void on_run(GtkWidget
*widget
, gpointer data
) {
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
));
143 char file_begin
[] = ":file{dst=";
144 char file_end
[] = "}";
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
));
152 handle
[strlen(source
)+i
] = (char) (((unsigned int) rand()) & 63) + '0';
154 handle
[strlen(source
)+4] = '\0';
155 transcode
= get_transcode_string(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);
165 libvlc_vlm_play_media(vlcinst
, handle
);
166 gtk_widget_show(spinner
);
167 gtk_spinner_start(GTK_SPINNER(spinner
));
169 g_timeout_add(50, (GSourceFunc
) update_progressbar
, handle
);
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
), "");
181 gboolean
on_end(void) {
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
);
190 void on_end_vlc(const libvlc_event_t
*event
, void *data
) {
191 g_idle_add((GSourceFunc
) on_end
, NULL
);
194 gboolean
on_error(void) {
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
);
202 void on_error_vlc(const libvlc_event_t
*event
, void *data
) {
203 g_idle_add((GSourceFunc
) on_error
, NULL
);
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
;
213 libvlc_event_manager_t
*evtman
;
215 gtk_init(&argc
, &argv
);
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");
222 vbox
= gtk_vbox_new(FALSE
, BORDER_WIDTH
);
223 gtk_container_add(GTK_CONTAINER(window
), vbox
);
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
);
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
);
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);
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);
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
);
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
);
304 libvlc_release(vlcinst
);