/* # # Main interface tool for GOPchop. Implements multiple utility functions. # # $Id: Main.cpp,v 1.42 2004/02/02 02:02:07 nemies Exp $ # # Copyright (C) 2001-2003 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 #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 "GOPutils.h" #ifdef __cplusplus extern "C" { #endif /* libmpeg2 */ #include "video_out.h" #include #ifdef __cplusplus } #endif #include #include #ifdef __cplusplus extern "C" { #endif #include "interface.h" #include "support.h" #ifdef __cplusplus } #endif #include "main.h" #include // display writing locations to stderr #undef MPEG2_CLIPS_TO_STDERR /* */ /* globals for everyone to beat on */ /* */ // strings char *GOPchop_cvsid = "$Id: Main.cpp,v 1.42 2004/02/02 02:02:07 nemies Exp $"; char *GOPchop_version = VERSION; // objects MPEG2Parser *mpeg2parser; // windows GtkWidget *main_window = NULL; GtkWidget *about_window = NULL; GtkWidget *fileselection_window = NULL; GtkWidget *saveselection_window = NULL; GtkWidget *error_dialog = NULL; GtkWidget *overwrite_dialog = NULL; GtkWidget *GOP_window = NULL; // the menus and buttons GtkWidget *menu_open = NULL; GtkWidget *menu_save = NULL; GtkWidget *menu_close = NULL; GtkWidget *button_run = NULL; GtkWidget *button_refresh = NULL; GtkWidget *button_prev = NULL; GtkWidget *button_next = NULL; GtkWidget *button_start_mark = NULL; GtkWidget *button_end_mark = NULL; GtkWidget *main_popup = NULL; popup_info_t main_popup_info; gint main_clist_row = -1; /* FIXME: I should move these into a structure of the global options */ GtkWidget *menu_options_run_loop = NULL; GtkWidget *menu_options_auto_refresh = NULL; GtkWidget *menu_options_ignore_errors = NULL; GtkWidget *menu_options_drop_orphaned_frames = NULL; // labels and things GtkWidget *main_progressbar = NULL; GtkWidget *main_statusbar = NULL; GtkWidget *main_clist = NULL; GtkWidget *main_label_clips = NULL; GtkWidget *main_label_mark = NULL; GtkWidget *error_text_why = NULL; GtkWidget *GOP_selector = NULL; GtkWidget *slider_run_speed = NULL; GtkWidget *GOP_label_filename = NULL; GtkWidget *GOP_label_sequence_info = NULL; GtkWidget *overwrite_label_filename = NULL; GtkWidget *GOP_clist = NULL; // command line options char * opt_videoout = NULL; // default to the first libvo driver char * opt_pipe = NULL; // default to no command #define GOPCHOP_OUTPUT_LIBVO 0 #define GOPCHOP_OUTPUT_PIPE 1 // 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; // writer variables uint8_t *GOPareaPtr = NULL; size_t GOPareaSize = 0; // mark settings 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, run_speed: 1, auto_refresh: 1, ignore_errors: 0, drop_orphaned_frames: 1, }; rc_parse_item parsable_items[] = { { "run_loop", &options.run_loop, RC_TYPE_BOOLEAN }, { "run_speed", &options.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 }, { NULL, NULL, 0 } }; ///// Callbacks that glade doesn't know about void on_GOP_selector_value_changed(GtkAdjustment * adjustment, gpointer user_data) { uint32_t GOP; GOP = (uint32_t) GTK_ADJUSTMENT(adjustment)->value; // only do something if we have output, and auto refreshing if (options.auto_refresh) show_GOP(GOP); // make sure the parser is loaded if (mpeg2parser->getParsingState()==PARSER_STATE_FINISHED) update_GOP_info(GOP); // handle start/end button availability if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button_start_mark))) { gtk_widget_set_sensitive(button_end_mark, (GOP >= mark_start)); } } void on_slider_run_speed_value_changed(GtkAdjustment * adjustment, gpointer user_data) { options.run_speed = (int) GTK_ADJUSTMENT(adjustment)->value; rc_save(PACKAGE,parsable_items); } ///// 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(); } 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(char *text) { static gchar *str = NULL; // text needs a static area, I think... ? (void *) str = g_realloc(str, strlen(text) + 1); strcpy(str, text); gtk_text_freeze(GTK_TEXT(error_text_why)); gtk_text_set_point(GTK_TEXT(error_text_why), 0); gtk_text_forward_delete(GTK_TEXT(error_text_why), gtk_text_get_length(GTK_TEXT(error_text_why))); gtk_text_insert(GTK_TEXT(error_text_why), NULL, NULL, NULL, (str), strlen(str)); gtk_text_set_point(GTK_TEXT(error_text_why), gtk_text_get_length(GTK_TEXT(error_text_why))); gtk_text_thaw(GTK_TEXT(error_text_why)); gtk_widget_show(GTK_WIDGET(error_dialog)); } // file: stream to write to // num: which GOP to write // continuous: if the GOP prior was actually the previous GOP // (continuous==FALSE if this GOP is a follows a splice point) off_t write_GOP(FILE * file, uint32_t num, int continuous) { List *GOPs; GroupOfPictures *GOP; Vector *GOPheader; List *packets; Bounds *packet_bounds; Vector *pes_vector; Pack *packet; uint32_t pes, pes_max; uint8_t old_bits; uint8_t *header; uint8_t *area; int GOP_corrected; int P_Frame_seen; int open_GOP; off_t written_bytes; size_t GOPlen; size_t GOPoffset; written_bytes = 0; GOP_corrected = 0; P_Frame_seen = 0; open_GOP = 0; if (!(GOPs = mpeg2parser->getGOPs())) { printf("%s", _("NULL GOP list?!\n")); return 0; } if (!(packets = mpeg2parser->getPackets())) { printf("%s", _("NULL packet list?!\n")); return 0; } if (!(GOP = (GroupOfPictures *) GOPs->vector(num))) { printf("%s", _("NULL GOP?!\n")); return 0; } if (!(GOPheader = (Vector *) GOP->getHeader())) { printf("%s", _("NULL GOP header?!\n")); return 0; } if (!(packet_bounds = GOP->getPacketBounds())) { printf("%s", _("NULL packet bounds?!\n")); return 0; } // instead of writing one packet at a time, let's try // writing whole GOPs at once. // figure out size of GOP in memory GOPlen = 0; pes_max = packet_bounds->getMax(); for (pes = packet_bounds->getFirst(); pes < pes_max; pes++) { if (!(packet = (Pack *) packets->vector(pes))) { printf("%s", _("NULL PES?!\n")); return 0; } GOPlen += packet->getLen(); } if (GOPlen > GOPareaSize) { // let's not realloc because we don't need to keep the // contents if (GOPareaPtr) free(GOPareaPtr); if (!(GOPareaPtr = (uint8_t *) malloc(GOPlen))) { printf("%s", _("Ran out of memory!?\n")); return 0; } GOPareaSize = GOPlen; } GOPoffset = 0; pes_max = packet_bounds->getMax(); for (pes = packet_bounds->getFirst(); pes < pes_max; pes++) { size_t len; int undo; int okay_to_write; // assume we can write this packet... okay_to_write = 1; //printf("writing PES %d\n",pes); undo = 0; if (!(packet = (Pack *) packets->vector(pes))) { printf("%s", _("NULL PES?!\n")); goto abort; } len = packet->getLen(); if (!(area = mpeg2parser->bytesAvail(packet->getStart(), len))) { printf("%s", _("unavailable memory?!\n")); goto abort; } if (!continuous && !GOP_corrected && GOPheader->getStart() > packet->getStart() && GOPheader->getStart() + GOPheader->getLen() < packet->getStart() + packet->getLen()) { // GOP header is in this packet header = area + (GOPheader->getStart() - packet->getStart()); // do we have an open GOP? open_GOP = ((header[7] & 0x40) == 0); if (open_GOP) { // need to set the "broken" flag old_bits = header[7]; header[7] |= GOP_FLAG_BROKEN; // an editor has been here if (options.drop_orphaned_frames) header[7] |= GOP_FLAG_CLOSED; // but most decoders don't care /* printf("GOP: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n", header[0],header[1],header[2],header[3], header[4],header[5],header[6],header[7]); */ GOP_corrected = 1; undo = 1; } } // handle B-frame dropping if (options.drop_orphaned_frames && open_GOP && !P_Frame_seen) { Vector *pic_header; // if this packet has a B-frame, skip it if ((pic_header = mpeg2parser->findCode((Vector *) packet, picture_start_code, 6))) { int pic_type; pic_type = (area[pic_header->getStart() - packet->getStart() + 5] & PICTURE_CODING_MASK) >> 3; if (pic_type == PICTURE_CODING_BIDIRECTIONAL) { //printf("Dropping open GOP B-frame on splice\n"); okay_to_write = 0; } if (pic_type == PICTURE_CODING_PREDICTIVE) { //printf("Keeping rest of frames in open GOP splice\n"); P_Frame_seen = 1; } } } if (okay_to_write) { #ifdef MPEG2_CLIPS_TO_STDERR fprintf(stderr, _("\tWriting @ %llu for %d bytes\n"), packet->getStart(), len); #endif /* if (fwrite(area,sizeof(uint8_t),len,file)!=len) { printf("write failed\n"); goto abort; } written_bytes+=len; */ memcpy(GOPareaPtr + GOPoffset, area, len); GOPoffset += len; } // put the GOP header bits back if (undo) { header[7] = old_bits; } } if (GOPoffset > 0) { // flush to disk if (fwrite(GOPareaPtr, sizeof(uint8_t), GOPoffset, file) != GOPoffset) { printf("%s", _("write failed\n")); goto abort; } written_bytes += GOPoffset; } abort: return written_bytes; } void save_file(char *filename) { char buf[1024]; FILE *mpeg2; uint8_t end_buf[4] = { 0x0, 0x0, 0x1, 0xB9 }; char *task = _("Saving MPEG2"); if (!(mpeg2 = fopen(filename, "w"))) { snprintf(buf, 1024, "%s: %s\n", filename, strerror(errno)); show_error(buf); } else { gint row; int i; gchar *starts; gchar *ends; int start, end; int clistrows; int totalGOPs = 0; int count; //int prev_GOP=-2; // make sure the first GOP is closed int prev_GOP = -1; time_t begin, finish; off_t written_bytes; char report[REPORT_LENGTH]; time(&begin); written_bytes = 0; // FIXME: use the managed global count instead? for (row = 0; gtk_clist_get_text(GTK_CLIST(main_clist), row, 1, &starts); row++) { } clistrows = row; for (row = 0; row < clistrows; row++) { gtk_clist_get_text(GTK_CLIST(main_clist), row, 1, &starts); gtk_clist_get_text(GTK_CLIST(main_clist), row, 2, &ends); start = atoi(starts); end = atoi(ends); totalGOPs += end - start + 1; } if (!totalGOPs) goto abort; count = 0; for (row = 0; row < clistrows; row++) { gtk_clist_get_text(GTK_CLIST(main_clist), row, 1, &starts); gtk_clist_get_text(GTK_CLIST(main_clist), row, 2, &ends); start = atoi(starts); end = atoi(ends); for (i = start; i <= end; i++) { progress(task, (float) ((float) count++ / (float) totalGOPs)); written_bytes += write_GOP(mpeg2, i, (prev_GOP + 1) == i); prev_GOP = i; } } abort: progress(task, 1.0); status_off(fileops); // write an end-of-PS marker fwrite(end_buf, sizeof(uint8_t), 4, mpeg2); fclose(mpeg2); time(&finish); finish -= begin; written_bytes /= 1048576; if (finish > 0) written_bytes /= finish; if (written_bytes == 0) written_bytes = 1; // FIXME: bad pluralization snprintf(report,REPORT_LENGTH,finish == 1 ? _("Wrote %s (GOPs: %u) in %d second (%%%sMB/s)\n") : _("Wrote %s (GOPs: %u) in %d seconds (%%%sMB/s)\n"), filename, totalGOPs, finish, OFF_T_FORMAT); fprintf(stderr, report, 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); } int main_clist_count = 0; void add_GOP_slice(uint32_t start, uint32_t end) { gchar *pathname; gchar first[128]; gchar last[128]; gchar *data[3]; gchar buf[128]; pathname = mpeg2parser->getFilename(); if (!pathname) pathname = _("unknown pathname"); data[0] = pathname; data[1] = first; data[2] = last; sprintf(first, "%d", start); sprintf(last, "%d", end); gtk_clist_append(GTK_CLIST(main_clist), data); main_clist_count++; update_GOP_slice_count(main_clist_count); } void clear_GOP_slices() { gchar buf[128]; main_clist_count=0; update_GOP_slice_count(main_clist_count); gtk_clist_clear(GTK_CLIST(main_clist)); main_clist_row = -1; } void remove_GOP_slice(int row) { if (row!=-1 && rowsequence) { fprintf(stderr,"%s",_("Got STATE_SEQUENCE without a sequence!?\n")); break; } /* printf("saw sequence (%dx%d chroma: %dx%d)\n", info->sequence->width, info->sequence->height, info->sequence->chroma_width, info->sequence->chroma_height ); */ /* sequence started, so initialize output window */ if (output->setup(output, info->sequence->width, info->sequence->height, info->sequence->chroma_width, info->sequence->chroma_height, &setup_result)) { fprintf(stderr, "%s", _("Failed to setup display\n")); return; } /* does the output need to be converted? */ if (setup_result.convert) mpeg2_convert(mpeg2dec, setup_result.convert, NULL); /* can the output provide an fbuf area? */ if (output->set_fbuf) { uint8_t *buf[3]; void *id; mpeg2_custom_fbuf(mpeg2dec, 1); output->set_fbuf(output, buf, &id); mpeg2_set_buf(mpeg2dec, buf, id); output->set_fbuf(output, buf, &id); mpeg2_set_buf(mpeg2dec, buf, id); } else if (output->setup_fbuf) { uint8_t *buf[3]; void *id; output->setup_fbuf(output, buf, &id); mpeg2_set_buf(mpeg2dec, buf, id); output->setup_fbuf(output, buf, &id); mpeg2_set_buf(mpeg2dec, buf, id); output->setup_fbuf(output, buf, &id); mpeg2_set_buf(mpeg2dec, buf, id); } break; case STATE_PICTURE: //printf("picture starting\n"); /* picture is starting */ if (output->set_fbuf) { uint8_t *buf[3]; void *id; output->set_fbuf(output, buf, &id); mpeg2_set_buf(mpeg2dec, buf, id); } if (output->start_fbuf) { if (info->current_fbuf) { output->start_fbuf(output, info->current_fbuf->buf, info->current_fbuf->id); } else { fprintf(stderr,"%s",_("Got STATE_PICTURE without a current_fbuf!?\n")); } } break; case STATE_SLICE: case STATE_END: /* if (info->display_picture) { switch (info->display_picture->flags & PIC_MASK_CODING_TYPE) { case PIC_FLAG_CODING_TYPE_I: printf("drawing I-frame (pts %u)\n", info->display_picture->pts); break; case PIC_FLAG_CODING_TYPE_P: printf("drawing P-frame (pts %u)\n", info->display_picture->pts); break; case PIC_FLAG_CODING_TYPE_B: printf("drawing B-frame (pts %u)\n", info->display_picture->pts); break; case PIC_FLAG_CODING_TYPE_D: printf("drawing D-frame (!?) (pts %u)\n", info->display_picture->pts); break; default: printf("drawing unknown picture type (pts %u)\n", info->display_picture->pts); break; } } else { printf("display_picture flags not available!?\n"); } */ /* draw current picture */ if (output->draw && info->display_fbuf) { output->draw(output, info->display_fbuf->buf, info->display_fbuf->id); } if (output->discard && info->discard_fbuf) output->discard(output, info->discard_fbuf->buf, info->discard_fbuf->id); break; case STATE_INVALID: /* report some other error */ fprintf(stderr, "%s", _("Decoder threw 'STATE_INVALID'. This needs to be handled better!\n")); case STATE_INVALID_END: fprintf(stderr, "%s", _("Decoder threw 'STATE_INVALID_END'. This needs to be handled better!\n")); break; } } } void decode_stop() { //printf("decode stop\n"); if (output) { mpeg2_close(mpeg2dec); mpeg2dec = NULL; if (output->close) output->close(output); output = NULL; output_open = NULL; } if (client_pipe) { close(*client_pipe); client_pipe = NULL; } } vo_open_t * find_libvo_driver(char * desired_driver) { int i; vo_driver_t const *drivers; drivers = vo_drivers(); for (i = 0; drivers[i].name; i++) if (!desired_driver || !strcmp(drivers[i].name, desired_driver)) return drivers[i].open; return NULL; } void decode_start() { if (desired_output==GOPCHOP_OUTPUT_LIBVO) { // decoding uint32_t accel = 0; //printf("decode start\n"); if (!(output_open=find_libvo_driver(opt_videoout))) { fprintf(stderr, _("Can't find output driver '%s'\n"), opt_videoout); exit(1); } if (!(output = output_open())) { fprintf(stderr, _("Can't open output driver '%s'\n"), opt_videoout); } accel = mpeg2_accel(MPEG2_ACCEL_DETECT); #ifdef ARCH_X86 if (accel & MPEG2_ACCEL_X86_MMXEXT) printf("%s", _("Using x86 MMXext acceleration\n")); else if (accel & MPEG2_ACCEL_X86_3DNOW) printf("%s", _("Using x86 3DNow acceleration\n")); else if (accel & MPEG2_ACCEL_X86_MMX) printf("%s", _("Using x86 MMX acceleration\n")); #endif #ifdef ARCH_PPC if (accel & MPEG2_ACCEL_PPC_ALTIVEC) printf("%s", _("Using PowerPC Altivec acceleration\n")); #endif #ifdef ARCH_ALPHA if (accel & MPEG2_ACCEL_ALPHA_MVI) printf("%s", _("Using Alpha MVI acceleration\n")); else if (accel & MPEG2_ACCEL_ALPHA) printf("%s", _("Using Alpha acceleration\n")); #endif #ifdef ARCH_SPARC if (accel & MPEG2_ACCEL_SPARC_VIS) printf("%s", _("Using Sparc VIS acceleration\n")); else if (accel & MPEG2_ACCEL_SPARC_VIS2) printf("%s", _("Using Sparc VIS2 acceleration\n")); #endif else printf("%s", _("Using no special acceleration\n")); if (!(mpeg2dec = mpeg2_init())) { fprintf(stderr, "%s", _("Cannot initialize mpeg2 decoder!\n")); exit(1); } } else // GOPCHOP_OUTPUT_PIPE { // try a piped command instead if (!opt_pipe) { fprintf(stderr, "%s", _("Pipe command required. Try --pipe\n")); exit(1); } // fork off a command if (pipe(pipes) < 0) { perror("pipe"); exit(1); } pid_t pid = fork(); // failure if (pid < 0) { perror("fork"); exit(1); } // child if (pid == 0) { char * shell; close(pipes[1]); if (dup2(pipes[0], STDIN_FILENO) < 0) { perror("dup2"); exit(1); } // get the shell to use if (!(shell=getenv("SHELL"))) { fprintf(stderr,"%s", _("Cannot figure out from environment what shell to exec.\n")); exit(1); } execl(shell,shell,"-c", opt_pipe, NULL); perror("execl"); exit(1); } // parent client_pipe = &pipes[1]; close(pipes[0]); } } void clear_GOP_info() { gtk_clist_clear(GTK_CLIST(GOP_clist)); gtk_label_set_text(GTK_LABEL(GOP_label_filename), ""); gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info),""); } 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; gchar datatype[128]; gchar dataoffset[128]; gchar datasize[128]; gchar *data[3] = { datatype, dataoffset, datasize }; gchar seqbuf[128]; struct sequence_info info; gtk_clist_freeze(GTK_CLIST(GOP_clist)); gtk_clist_clear(GTK_CLIST(GOP_clist)); // wipe out label 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(); } sprintf(datatype, _(" Picture (%s: %02d)"), frame_type_str(picture->getType()), picture->getTime()); sprintf(dataoffset, "%" OFF_T_FORMAT, pic_start); sprintf(datasize, "%" OFF_T_FORMAT, pic_len); gtk_clist_append(GTK_CLIST(GOP_clist), data); } // 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 sprintf(datatype, _("GOP (%02d:%02d:%02d.%02d%s%s%s)"), hour, min, sec, pictures, drop ? _(" drop") : _(" keep"), closed ? _(" closed") : _(" open"), broken ? _(" broken") : _(" whole")); sprintf(dataoffset, "%" OFF_T_FORMAT, gop_start); sprintf(datasize, "%" OFF_T_FORMAT, gop_len); gtk_clist_prepend(GTK_CLIST(GOP_clist), data); // display audio info sprintf(datatype, "%s", _(" Audio")); sprintf(dataoffset, "%" OFF_T_FORMAT, gop_start); sprintf(datasize, "%" OFF_T_FORMAT, audio_len); gtk_clist_append(GTK_CLIST(GOP_clist), data); // update labels with info GOP->get_sequence_info(&info); sprintf(seqbuf,_("%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: gtk_clist_thaw(GTK_CLIST(GOP_clist)); } #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]); } void show_GOP(uint32_t wantedGOP) { static int initdone = 0; List *packets; Pack *packet; List *GOPs; GroupOfPictures *GOP; Bounds *bounds; ElementStream *ves; int start, stop; uint8_t *loc; size_t len; // get our GOP list if (!(GOPs = mpeg2parser->getGOPs())) { printf("%s", _("\tNo GOPs\n")); return; } // get our packet list if (!(packets = mpeg2parser->getPackets())) { printf("%s", _("\tNo packets\n")); return; } // get GOP we're after if (!(GOP = (GroupOfPictures *) GOPs->vector(wantedGOP))) { printf(_("\tGOP %d missing\n"), wantedGOP); return; } // get our GOP's range of packets bounds = GOP->getPacketBounds(); // remember our start and finish points start = bounds->getFirst(); stop = bounds->getMax(); for (uint32_t j = start; j < stop; j++) { if (!(packet = (Pack *) packets->vector(j))) { printf("%s", _("NULL packet?!\n")); exit(1); } if (desired_output==GOPCHOP_OUTPUT_LIBVO && output) { uint32_t ves_start, ves_stop; ves_start = packet->getVideoFirst(); ves_stop = packet->getVideoMax(); for (uint32_t k = ves_start; k < ves_stop; k++) { if (!(ves = (ElementStream *)mpeg2parser->getVideo()->vector(k))) { printf("%s", _("NULL VES?!\n")); exit(1); } len = ves->getLen(); (void *)loc = mpeg2parser->bytesAvail(ves->getStart(), len); /* fprintf(stderr,_("\t\tVES: %d @ %llu (%d): 0x%08x\n"), k,ves->getStart(),len, (unsigned int)loc); */ if (loc && len) decode(loc, len); } } else if (desired_output==GOPCHOP_OUTPUT_PIPE && client_pipe) { // send entire packet to pipe len = packet->getLen(); (void *)loc = mpeg2parser->bytesAvail(packet->getStart(), len); if (loc && client_pipe) { int written; // stuff things into the pipe while (len > 0) { if ((written = write(*client_pipe, loc, len)) < 0) { perror("write"); exit(1); } loc += written; len -= written; } } } } } uint32_t get_GOP_selected() { uint32_t GOP; GOP = (uint32_t) GTK_ADJUSTMENT(gtk_range_get_adjustment(GTK_RANGE(GOP_selector)))-> value; return GOP; } 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_adjustment_set_value(GTK_ADJUSTMENT (gtk_range_get_adjustment (GTK_RANGE(GOP_selector))), num); // if we're in the same place, force a refresh, damnit if (num == old) gtk_adjustment_value_changed((GTK_ADJUSTMENT (gtk_range_get_adjustment (GTK_RANGE(GOP_selector))))); } void handle_rc_load() { if (rc_load(PACKAGE,parsable_items)) fprintf(stderr,"%s",_("Could not load rc file -- using defaults.\n")); /* synchronize the loaded/default values to the menus */ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_options_run_loop),options.run_loop); gtk_adjustment_set_value(GTK_ADJUSTMENT(gtk_range_get_adjustment(GTK_RANGE(slider_run_speed))), options.run_speed); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_options_ignore_errors),options.ignore_errors); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_options_auto_refresh),options.auto_refresh); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_options_drop_orphaned_frames),options.drop_orphaned_frames); } void setup_gtk_stuff() { const gchar *mainstatus = "main"; GtkAdjustment *scale; /* * FIXME: I really ought to do error checking on each of these * GTK_WIDGET lines */ /* figure out where our pixmaps are */ add_pixmap_directory(PACKAGE_DATA_DIR "/pixmaps"); add_pixmap_directory(PACKAGE_SOURCE_DIR "/pixmaps"); /* create our main window */ main_window = create_main_window(); // create our error dialog now error_dialog = create_error_dialog(); overwrite_dialog = create_overwrite_dialog(); // create our GOP window now GOP_window = create_GOP_window(); // create our popup main_popup = create_main_popup(); main_popup_info.x = 0; main_popup_info.y = 0; // locate menus and buttons menu_open = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "menu_file_open")); menu_close = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "menu_file_close")); menu_save = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "menu_file_save")); menu_options_run_loop = GTK_WIDGET(lookup_widget (GTK_WIDGET(main_window), "menu_options_run_loop")); menu_options_auto_refresh = GTK_WIDGET(lookup_widget (GTK_WIDGET(main_window), "menu_options_auto_refresh")); menu_options_ignore_errors = GTK_WIDGET(lookup_widget (GTK_WIDGET(main_window), "menu_options_ignore_errors")); menu_options_drop_orphaned_frames = GTK_WIDGET(lookup_widget (GTK_WIDGET(main_window), "menu_options_drop_orphaned_frames")); button_run = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "button_run")); button_refresh = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "button_refresh")); button_prev = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "button_prev")); button_next = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "button_next")); button_start_mark = GTK_WIDGET(lookup_widget (GTK_WIDGET(main_window), "button_start_mark")); button_end_mark = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "button_end_mark")); // locate things main_progressbar = GTK_WIDGET(lookup_widget (GTK_WIDGET(main_window), "main_progressbar")); main_statusbar = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "main_statusbar")); GOP_selector = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "GOP_selector")); slider_run_speed = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "slider_run_speed")); main_label_clips = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "main_label_clips")); main_label_mark = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "main_label_mark")); main_clist = GTK_WIDGET(lookup_widget(GTK_WIDGET(main_window), "main_clist")); main_clist_row = -1; // nothing selected GOP_clist = GTK_WIDGET(lookup_widget(GTK_WIDGET(GOP_window), "GOP_clist")); GOP_label_filename = GTK_WIDGET(lookup_widget (GTK_WIDGET(GOP_window), "GOP_label_filename")); GOP_label_sequence_info = GTK_WIDGET(lookup_widget(GTK_WIDGET(GOP_window), "GOP_label_sequence_info")); overwrite_label_filename = GTK_WIDGET(lookup_widget (GTK_WIDGET(overwrite_dialog), "overwrite_label_filename")); error_text_why = GTK_WIDGET(lookup_widget(GTK_WIDGET(error_dialog), "error_text_why")); //////////////////// // do things that glade doesn't handle /////////////////// // set up ranges scale = gtk_range_get_adjustment(GTK_RANGE(GOP_selector)); scale->step_increment = 1; scale->page_increment = 1; scale->page_size = 1; gtk_signal_connect(GTK_OBJECT(scale), "value-changed", GTK_SIGNAL_FUNC(on_GOP_selector_value_changed), NULL); scale = gtk_range_get_adjustment(GTK_RANGE(slider_run_speed)); scale->lower = 1; scale->upper = 100; scale->step_increment = 1; scale->page_increment = 1; scale->page_size = 1; gtk_signal_connect(GTK_OBJECT(scale), "value-changed", GTK_SIGNAL_FUNC(on_slider_run_speed_value_changed), NULL); // column justification gtk_clist_set_column_justification(GTK_CLIST(main_clist), 1, GTK_JUSTIFY_RIGHT); gtk_clist_set_column_justification(GTK_CLIST(main_clist), 2, GTK_JUSTIFY_RIGHT); gtk_clist_set_column_justification(GTK_CLIST(GOP_clist), 1, GTK_JUSTIFY_RIGHT); gtk_clist_set_column_justification(GTK_CLIST(GOP_clist), 2, GTK_JUSTIFY_RIGHT); // clist options gtk_clist_set_column_min_width(GTK_CLIST(main_clist), 0, 200); gtk_clist_set_column_auto_resize(GTK_CLIST(main_clist), 0, TRUE); gtk_clist_set_column_auto_resize(GTK_CLIST(main_clist), 1, TRUE); gtk_clist_set_column_auto_resize(GTK_CLIST(main_clist), 2, TRUE); gtk_clist_set_reorderable(GTK_CLIST(main_clist), TRUE); gtk_clist_set_use_drag_icons(GTK_CLIST(main_clist), FALSE); gtk_clist_set_column_min_width(GTK_CLIST(GOP_clist), 0, 200); gtk_clist_set_column_auto_resize(GTK_CLIST(GOP_clist), 0, TRUE); gtk_clist_set_column_auto_resize(GTK_CLIST(GOP_clist), 1, TRUE); gtk_clist_set_column_auto_resize(GTK_CLIST(GOP_clist), 2, TRUE); // word wrap gtk_text_set_word_wrap(GTK_TEXT(error_text_why), TRUE); // further initialization status_on(mainstatus, _("Ready")); /* display our main window and GOP information window */ gtk_widget_show(main_window); gtk_widget_show(GOP_window); } 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\ 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' }, { 0, 0, 0, 0 } }; /* do our option handling */ while (1) { int c; int index; c = getopt_long(argc, argv, "hv: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': 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; } } } int main(int argc, char *argv[]) { /* deal with internationalization support */ #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, PACKAGE_LOCALE_DIR); textdomain(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 */ gtk_main(); return 0; } /* vi:set ai ts=4 sw=4 expandtab: */