/* # # Main interface tool for GOPchop. Implements multiple utility functions. # # $Id: Main.cpp 346 2009-03-02 23:26:02Z keescook $ # # Copyright (C) 2001-2009 Kees Cook # kees@outflux.net, http://outflux.net/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # http://www.gnu.org/copyleft/gpl.html # */ /* * Initial main.c file generated by Glade. Edit as required. * Glade will not overwrite this file. */ #include "GOPchop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_NLS # include #endif #include "Vector.h" #include "List.h" #include "ElementStream.h" #include "Pack.h" #include "GroupOfPictures.h" #include "Picture.h" #include "MPEG2Parser.h" #include "mpeg2consts.h" #include "widgets.h" #include "FirstPack.h" #include "GOPutils.h" #ifdef __cplusplus extern "C" { #endif /* libmpeg2 */ #include "video_out.h" #include #ifdef __cplusplus } #endif #include #include #include #include #include "main.h" #include "display.h" #include /* */ /* globals for everyone to beat on */ /* */ // strings char *GOPchop_cvsid = "$Id: Main.cpp 346 2009-03-02 23:26:02Z keescook $"; // objects MPEG2Parser *mpeg2parser; // command line options char * opt_videoout = NULL; // default to the first libvo driver char * opt_pipe = NULL; // default to no command int opt_show_states = 0; // default to not reporting libmpeg2 states // decode variables int desired_output = GOPCHOP_OUTPUT_LIBVO; /* static mpeg2dec_t *mpeg2dec = NULL; static vo_open_t *output_open = NULL; static vo_instance_t *output = NULL; */ struct display_engine_t * engine = NULL; // mark settings gint main_clist_count = 0; uint32_t mark_start = 0; uint32_t mark_end = 0; // pipe variables int pipes[2]; int *client_pipe = NULL; /* global preferences */ /* * add new options below. * If you want it saved to the rc file, add it to the "parsable_items" list. * Update the "handle_rc" function below for rc options. * Update the menu activation handler to auto-save the rc file. */ global_options options={ run_loop: 0, /* don't wrap arround by default */ run_speed: 1, /* one frame at a time by default */ default_run_speed: 1, /* one frame at a time by default */ auto_refresh: 1, /* show gop position changes by default */ ignore_errors: 0, /* don't ignore errors by default */ drop_orphaned_frames: 0, /* orphaned frame dropping can be nasty */ adjust_timestamps: 1, /* adjust by default */ video_driver_ptr: NULL, /* prefered video driver */ video_driver: "\0", force_system_header: 0, /* force prepended system header pack */ drop_trailing_pack_with_system_header: 0, /* drop final pack if it has a system header */ ignore_endcode: 0, /* don't stop parsing when an End Code is seen */ }; rc_parse_item parsable_items[] = { { "run_loop", &options.run_loop, RC_TYPE_BOOLEAN }, { "default_run_speed", &options.default_run_speed, RC_TYPE_INTEGER }, { "auto_refresh", &options.auto_refresh, RC_TYPE_BOOLEAN }, { "ignore_errors", &options.ignore_errors, RC_TYPE_BOOLEAN }, { "drop_orphaned_frames", &options.drop_orphaned_frames, RC_TYPE_BOOLEAN }, { "adjust_timestamps", &options.adjust_timestamps, RC_TYPE_BOOLEAN }, { "video_driver_ptr", &options.video_driver_ptr, RC_TYPE_STRING }, { "force_system_header", &options.force_system_header, RC_TYPE_BOOLEAN }, { "drop_trailing_pack_with_system_header", &options.drop_trailing_pack_with_system_header, RC_TYPE_BOOLEAN }, { "ignore_endcode", &options.ignore_endcode, RC_TYPE_BOOLEAN }, { NULL, NULL, 0 } }; ///// My various supporting functions void flush() { while (g_main_iteration(FALSE)); /* while(gtk_events_pending()) gtk_main_iteration(); */ } void status_on(const gchar * owner, char *text) { guint context; context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar), owner); gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), context, text); flush(); } void status_off(const gchar * owner) { guint context; context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar), owner); gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), context); flush(); //gtk_main_iteration_do(0); } const gchar *fileops = "fileops"; void progress(char *task, float done) { static gchar *lasttask = NULL; static float lastdone = 0.0; float diff; int tasksdiffer; gint context; context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar), fileops); if (lasttask) { tasksdiffer = strcmp(lasttask, task); } else { tasksdiffer = 1; } if (tasksdiffer) { if (lasttask) gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), context); lasttask = (char *) g_realloc(lasttask, strlen(task) + 1); strcpy(lasttask, task); gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), context, lasttask); flush(); } // always reset the progress bar when the progress reaches 100% if (done == 1.0) { done = 0.0; } else { diff = fabs(done - lastdone); if (!tasksdiffer && diff < 0.01) return; } lastdone = done; gtk_progress_set_percentage(GTK_PROGRESS(main_progressbar), lastdone); /* sending to stderr instead... if (mpeg2parser->getError()) show_error(mpeg2parser->getError()); */ flush(); } void show_error(const gchar *text) { GtkTextIter iter; GtkTextBuffer *buffer; buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(error_text_why)); gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(buffer),&iter); gtk_text_iter_forward_to_end(&iter); gtk_text_buffer_insert(buffer,&iter,text,-1); } void get_clips_from_list(GtkTreeModel *model, GtkTreeIter *iter, struct clip_list_t * clips) { GValue list_GOP_first; GValue list_GOP_last; if (!model || !iter || !clips) return; memset(&list_GOP_first,0,sizeof(list_GOP_first)); memset(&list_GOP_last, 0,sizeof(list_GOP_last)); gtk_tree_model_get_value(GTK_TREE_MODEL(model), iter, ITEM_GOP_FIRST, &list_GOP_first); gtk_tree_model_get_value(GTK_TREE_MODEL(model), iter, ITEM_GOP_LAST, &list_GOP_last); clips->GOP_first = g_value_get_uint(&list_GOP_first); clips->GOP_last = g_value_get_uint(&list_GOP_last); g_value_unset(&list_GOP_first); g_value_unset(&list_GOP_last); } /* "foreach" functions used to walk the main_clist */ gboolean foreach_count_GOPs(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { uint32_t * count = (uint32_t *)data; struct clip_list_t clips; if (!count) return TRUE; get_clips_from_list(model,iter,&clips); *count += clips.GOP_last - clips.GOP_first + 1; return FALSE; /* keep traversing */ } gboolean foreach_save_clips_to_file(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { struct clip_list_t clips; uint32_t i; struct save_clip_info_t * clip_info = (struct save_clip_info_t*)data; if (!clip_info) return TRUE; /* abort */ get_clips_from_list(model,iter,&clips); for (i = clips.GOP_first; i <= clips.GOP_last; i++) { progress(clip_info->progress_task, (float)( (float) clip_info->count++ / (float) clip_info->total ) ); clip_info->written_bytes += write_GOP(clip_info->file, mpeg2parser, i, ((clip_info->GOP_previous + 1) == i), options.drop_orphaned_frames, options.adjust_timestamps, &clip_info->written_pictures, //TI! (clip_info->total - clip_info->count)==0, options.drop_trailing_pack_with_system_header); clip_info->GOP_previous = i; } return FALSE; /* keep on steppin */ } void save_file(char *filename) { char buf[1024]; FILE *mpeg2; struct save_clip_info_t clip_info; clip_info.progress_task = _("Saving MPEG2"); if (!(mpeg2 = fopen(filename, "w"))) { snprintf(buf, 1024, "%s: %s\n", filename, strerror(errno)); show_error(buf); } else { time_t begin, finish; char report[REPORT_LENGTH]; time(&begin); clip_info.total=0; /* count up how many GOPs there are to write */ gtk_tree_model_foreach(GTK_TREE_MODEL(main_list_store), foreach_count_GOPs, &clip_info.total); clip_info.file=mpeg2; clip_info.count=0; clip_info.written_pictures=0; clip_info.written_bytes=0; // make sure the first GOP is closed clip_info.GOP_previous = (uint32_t) -2; //TI! save first dummy pack with valid system header if (options.force_system_header) { clip_info.written_bytes+=(mpeg2parser->getFirstPack())->saveFirstPack(mpeg2); } /* save each clip in the list */ gtk_tree_model_foreach(GTK_TREE_MODEL(main_list_store), foreach_save_clips_to_file, &clip_info); progress(clip_info.progress_task, 1.0); status_off(fileops); // write an end-of-PS marker fwrite(eos_buf, sizeof(eos_buf), 1, mpeg2); fclose(mpeg2); time(&finish); finish -= begin; clip_info.written_bytes /= 1048576; if (finish > 0) clip_info.written_bytes /= finish; if (clip_info.written_bytes == 0) clip_info.written_bytes = 1; // FIXME: bad pluralization snprintf(report,REPORT_LENGTH,finish == 1 ? _("Wrote %s (GOPs: %u, pictures: %u) in %d second (%%%sMB/s)\n") : _("Wrote %s (GOPs: %u, pictures: %u) in %d seconds (%%%sMB/s)\n"), filename, clip_info.total, clip_info.written_pictures, finish, OFF_T_FORMAT); fprintf(stderr, report, clip_info.written_bytes); } } void open_file(char *filename) { time_t begin, finish; char buf[128]; off_t speed; char report[REPORT_LENGTH]; char * gop_cache_filename; time(&begin); if (!mpeg2parser->init(filename, progress)) { show_error(mpeg2parser->getError()); } else { mpeg2parser->parse(options.ignore_errors); if (mpeg2parser->getError()) show_error(mpeg2parser->getError()); // make sure we actually HAVE something... List *GOPs = mpeg2parser->getGOPs(); if (GOPs && GOPs->getNum()) file_is_loaded(); else mpeg2parser->reset(); } // load-time calculation time(&finish); finish -= begin; speed = mpeg2parser->getSize() / 1048576; // max speed is max size if (finish > 0) speed /= finish; if (speed == 0) speed = 1; // claim at least 1MB/s snprintf(report,REPORT_LENGTH,finish == 1 ? _("Loaded %s in %d second (%%%sMB/s)\n") : _("Loaded %s in %d seconds (%%%sMB/s)\n"), filename, finish, OFF_T_FORMAT); fprintf(stderr, report, speed); progress(_("Caching"), 1.0); /* write out the offset map */ if (!(gop_cache_filename=(char*)malloc(strlen(filename)+5))) { perror("malloc"); } else { sprintf(gop_cache_filename,"%s.gop",filename); if (write_gop_cache(gop_cache_filename,mpeg2parser)) { printf("%s",_("Skipping offset cache writing.\n")); } } progress(_("Done"), 1.0); status_off(fileops); } /* gtk stupid: no way to get row count from a clist */ void update_GOP_slice_count(int count) { gchar buf[128]; /* update the count of clips */ sprintf(buf, _("Clips: %d"), count); gtk_label_set_text(GTK_LABEL(main_label_clips), buf); } void add_GOP_slice(uint32_t start, uint32_t end) { GtkTreeIter iter; gchar *pathname; pathname = mpeg2parser->getFilename(); if (!pathname) pathname = _("unknown pathname"); gtk_list_store_append (main_list_store, &iter); gtk_list_store_set (main_list_store, &iter, ITEM_FILENAME, pathname, ITEM_GOP_FIRST, start, ITEM_GOP_LAST, end, -1); main_clist_count++; update_GOP_slice_count(main_clist_count); } void clear_GOP_slices() { gchar buf[128]; gtk_list_store_clear(main_list_store); main_clist_count=0; update_GOP_slice_count(main_clist_count); } void remove_GOP_slice(GtkTreeIter *iter) { if (!iter) return; gtk_list_store_remove(main_list_store,iter); main_clist_count--; update_GOP_slice_count(main_clist_count); } GdkFilterReturn filter_event(GdkXEvent *xevent, GdkEvent *event, gpointer data) { //report_event(event); printf("got event\n"); return GDK_FILTER_CONTINUE; } const mpeg2_info_t *parser_info = NULL; // decode_start // decode_stop void clear_GOP_info() { gtk_list_store_clear(GOP_list_store); gtk_label_set_text(GTK_LABEL(GOP_label_filename), ""); gtk_label_set_text(GTK_LABEL(GOP_label_GOP), _("No GOP selected")); gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info), _("No Sequence Info Found") ); } void update_GOP_info(uint32_t num) { int i; GroupOfPictures *GOP; Bounds *picture_bounds; Bounds *packet_bounds; Picture *picture; List *GOPs; List *picture_list; List *packet_list; List *audio_list; List *video_list; Vector *head_vector; uint32_t picture_index, picture_max; uint32_t packet_min, packet_index, packet_max; uint32_t audio_index, audio_max; off_t gop_len, gop_start; off_t audio_len; //int aindex, amax; uint8_t *head; int drop, hour, min, sec, pictures, closed, broken; GtkTreeIter iter; gchar datatype[128]; gchar dataoffset[128]; gchar datasize[128]; gchar seqbuf[128]; struct sequence_info info; /* wipe out the old list */ gtk_list_store_clear(GOP_list_store); // wipe out labels sprintf(seqbuf,_("GOP %u information:"),num); gtk_label_set_text(GTK_LABEL(GOP_label_GOP), seqbuf); gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info), _("No Sequence Info Found")); if (!(GOPs = mpeg2parser->getGOPs())) { printf(_("no GOPs ?!\n")); goto failure; } if (!(packet_list = mpeg2parser->getPackets())) { printf(_("NULL picture list ?!\n")); goto failure; } if (!(picture_list = mpeg2parser->getPictures())) { printf(_("NULL picture list ?!\n")); goto failure; } if (!(audio_list = mpeg2parser->getAudio())) { printf(_("NULL audio list ?!\n")); goto failure; } if (!(video_list = mpeg2parser->getVideo())) { printf(_("NULL video list ?!\n")); goto failure; } if (!(GOP = (GroupOfPictures *) GOPs->vector(num))) { printf(_("no such GOP %d ?!\n"), num); goto failure; } if (!(head_vector = GOP->getHeader())) { printf(_("GOP header missing?!\n")); goto failure; } if (!(head = mpeg2parser->bytesAvail(head_vector->getStart(), 8))) { printf(_("GOP header not available?!\n")); goto failure; } // calculate GOP information /* 4 5 6 7 | | | | 7 65432 107654 3 210765 432107 6 543210 1 11111 111111 1 111111 111111 1 1 d hour min m sec pic c b r a l roken o r osed p k */ drop = ((head[4] & 0x80) > 0); hour = ((head[4] & 0x7C) >> 2); min = ((head[4] & 0x3) << 4) | ((head[5] & 0xF0) >> 4); // sec/picture decoding corrected, care of Scott Smith sec = ((head[5] & 0x7) << 3) | ((head[6] & 0xE0) >> 5); pictures = ((head[6] & 0x1F) << 1) | ((head[7] & 0x80) >> 7); closed = ((head[7] & 0x40) > 0); broken = ((head[7] & 0x20) > 0); if (!(picture_bounds = GOP->getPictureBounds())) { printf(_("NULL GOP picture bounds?!\n")); goto failure; } // FIXME: technically, this loop does a lot of needless // reassigning of values. However, this loop doesn't // happen often, and it's the easiest way I could think // of to sort by offset. // get picture bounds picture_index = picture_bounds->getFirst(); picture_max = picture_bounds->getMax(); for (; picture_index < picture_max; picture_index++) { Bounds *pic_bounds; uint32_t ves, ves_min, ves_max; Vector *pic_vector; off_t pic_start; off_t pic_len; if (!(picture = (Picture *) picture_list->vector(picture_index))) { printf(_("NULL picture?!\n")); goto failure; } if (!(pic_bounds = picture->getVideoBounds())) { printf(_("NULL picture bounds?!\n")); goto failure; } ves_min = pic_bounds->getFirst(); ves_max = pic_bounds->getMax(); pic_len = 0; for (ves = ves_min; ves < ves_max; ves++) { if (!(pic_vector = (Vector *) video_list->vector(ves))) { printf(_("NULL picture VES vector?!\n")); goto failure; } if (ves == ves_min) pic_start = pic_vector->getStart(); pic_len += pic_vector->getLen(); } snprintf(datatype, 128, _(" Picture (%s: %02d)"), frame_type_str(picture->getType()), picture->getTime()); snprintf(dataoffset, 128, "%" OFF_T_FORMAT, pic_start); snprintf(datasize, 128, "%" OFF_T_FORMAT, pic_len); gtk_list_store_append(GOP_list_store, &iter); gtk_list_store_set(GOP_list_store, &iter, ITEM_DATATYPE, datatype, ITEM_OFFSET, dataoffset, ITEM_SIZE, datasize, -1); } // process GOP packets if (!(packet_bounds = GOP->getPacketBounds())) { printf(_("NULL GOP packet bounds?!\n")); goto failure; } packet_min = packet_bounds->getFirst(); packet_max = packet_bounds->getMax(); gop_len = 0; audio_len = 0; for (packet_index = packet_min; packet_index < packet_max; packet_index++) { Pack *packet; if (!(packet = (Pack *) packet_list->vector(packet_index))) { printf(_("NULL GOP packet vector?!\n")); goto failure; } if (packet_index == packet_min) gop_start = packet->getStart(); gop_len += packet->getLen(); audio_index = packet->getAudioFirst(); audio_max = packet->getAudioMax(); for (; audio_index < audio_max; audio_index++) { Bounds *audio_bounds; int aes, aes_min, aes_max; Vector *audio_vector; if (!(audio_vector = (Vector *) audio_list->vector(audio_index))) { printf(_("NULL GOP AES vector?!\n")); goto failure; } audio_len += audio_vector->getLen(); } } // display GOP snprintf(datatype, 128, _("GOP (%02d:%02d:%02d.%02d%s%s%s)"), hour, min, sec, pictures, drop ? _(" drop") : _(" keep"), closed ? _(" closed") : _(" open"), broken ? _(" broken") : _(" whole")); snprintf(dataoffset, 128, "%" OFF_T_FORMAT, gop_start); snprintf(datasize, 128, "%" OFF_T_FORMAT, gop_len); gtk_list_store_prepend(GOP_list_store, &iter); gtk_list_store_set(GOP_list_store, &iter, ITEM_DATATYPE, datatype, ITEM_OFFSET, dataoffset, ITEM_SIZE, datasize, -1); // display audio info snprintf(datatype, 128, "%s", _(" Audio")); snprintf(dataoffset, 128, "%" OFF_T_FORMAT, gop_start); snprintf(datasize, 128, "%" OFF_T_FORMAT, audio_len); gtk_list_store_append(GOP_list_store, &iter); gtk_list_store_set(GOP_list_store, &iter, ITEM_DATATYPE, datatype, ITEM_OFFSET, dataoffset, ITEM_SIZE, datasize, -1); // update labels with info GOP->get_sequence_info(&info); snprintf(seqbuf,128,_("%dx%d: %s aspect, %s fps, %s%s"), info.horizontal_size, info.vertical_size, aspect_str[info.aspect_ratio], frame_str[info.frame_rate], speed_str(info.bit_rate * 400), info.constrained ? _(", constrained") : ""); gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info),seqbuf); failure: /* perform any cleanups */ while (0) {}; // keep compiler quiet } #define MAX_FRAME_TYPES 4 const char *frame_type_map[MAX_FRAME_TYPES] = { N_("Bad Frame"), N_("I-Frame"), N_("P-Frame"), N_("B-Frame") }; const char *frame_type_str(int type) { if (type >= MAX_FRAME_TYPES || type < 0) type = 0; return _(frame_type_map[type]); } uint32_t get_GOP_selected() { return (uint32_t) gtk_range_get_value(GTK_RANGE(GOP_selector)); } void set_GOP_selected(uint32_t num) { uint32_t max; uint32_t old; max = mpeg2parser->numGOPs(); // correct our bounds if (num >= max) num = max - 1; if (num < 0) num = 0; // remember where we were old = get_GOP_selected(); gtk_range_set_value(GTK_RANGE(GOP_selector), (gdouble) num); } void handle_rc_load() { if (rc_load(PACKAGE,parsable_items)) fprintf(stderr,"%s",_("Could not load rc file -- using defaults.\n")); /* handle side-effects */ /* synchronize the loaded/default values to the slider */ options.run_speed = options.default_run_speed; gtk_range_set_value(GTK_RANGE(slider_run_speed), options.run_speed); mpeg2parser->set_ignore_endcode(options.ignore_endcode); } void Usage(char *title) { printf("Usage: %s [OPTIONS] [FILE]\n\ \n\ -h, --help This help\n\ -v, --vo DRIVER Choose internal output driver. Use 'help' for a list.\n\ -p, --pipe CMD Use external command for output. Recommended:\n\ 'mplayer -nocache -novm -'\n\ -s, --states Report libmpeg2 states\n\ FILE MPEG2 to load on startup\n\ \n", title); exit(1); } void handle_args(int argc, char *argv[]) { static struct option long_options[] = { { "help", 0, NULL, 'h' }, // 0 { "vo", 1, NULL, 'v' }, { "pipe", 1, NULL, 'p' }, { "states", 0, NULL, 's' }, { "version",0, NULL, 'V' }, { 0, 0, 0, 0 } }; /* do our option handling */ while (1) { int c; int index; c = getopt_long(argc, argv, "Vhsv:p:", long_options, &index); if (c == -1) break; // done with args switch (c) { case 0: // got a matching long argument (with no short equiv) switch (index) { default: fprintf(stderr, _("Unknown long argument index %d found!\n"), index); } break; case 'h': Usage(argv[0]); break; case 'V': printf("GOPchop %s (%s)\n", VERSION, __DATE__); exit(0); break; case 's': opt_show_states=1; break; case 'v': opt_videoout=optarg; if (strcmp(opt_videoout,"help")==0) { // display a list of libvo drivers int i; vo_driver_t const *drivers; printf(_("Video output options:\n")); drivers = vo_drivers(); for (i = 0; drivers[i].name; i++) printf("\t%s\n",drivers[i].name); exit(1); } else { // verify selected libvo driver if (!find_libvo_driver(opt_videoout)) { fprintf(stderr,_("No such video driver: '%s'\n"), opt_videoout); exit(1); } desired_output=GOPCHOP_OUTPUT_LIBVO; } break; case 'p': opt_pipe=optarg; desired_output=GOPCHOP_OUTPUT_PIPE; break; default: fprintf(stderr, _("Unknown short argument '%c' found?!\n"), c); /* fall through to the Usage ... */ case '?': Usage(argv[0]); break; } } } /* * There must be a way to hook up an event watcher to frame_window * directly. Until I figure it out, this will have to do. Overkill, * but it works. */ void gopchop_events(GdkEvent *event, gpointer data) { GdkEventExpose *expose = (GdkEventExpose*)event; if (event) switch (event->type) { // this doesn't catch window movement :( case GDK_VISIBILITY_NOTIFY: // forces repaint of frame window // don't attempt repaint while totally hidden if (((GdkEventVisibility*)event)->state == GDK_VISIBILITY_FULLY_OBSCURED) break; if (display_is_window(engine,((GdkEventAny*)event)->window)) display_redraw(engine); break; /* case GDK_EXPOSE: // forces repaint of frame window if (display_is_window(engine,expose->window)) display_redraw(engine); break; */ } // process events normally gtk_main_do_event(event); return; } int main(int argc, char *argv[]) { /* deal with internationalization support */ #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, PACKAGE_DATA_DIR "/locale"); textdomain(GETTEXT_PACKAGE); #endif gtk_set_locale(); /* pass command line args to gtk (which may modify them) */ gtk_init(&argc, &argv); /* handle our command line args */ handle_args(argc, argv); /* setup the parsing object */ mpeg2parser = new MPEG2Parser; /* set up all the gtk windows and widgets */ setup_gtk_stuff(); /* verify that we're working with largefile support * (report to Gtk window) */ if (sizeof(off_t) < 8) { show_error(_("Type 'off_t' is less than 8 bytes.\n" "Largefile support requires at least 8 bytes.\n")); gtk_widget_show(error_dialog); } /* load any saved options (prior to opening the first file) */ handle_rc_load(); /* attempt to load the filename mentioned on the command line * (requires MPEG2Parser and GTK loaded */ if (optind < argc) open_file(argv[optind]); /* start the event handler */ gdk_event_handler_set(gopchop_events,NULL,NULL); gtk_main(); return 0; } /* vi:set ai ts=4 sw=4 expandtab: */