home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 June / ccd0605.iso / LINUX / gopchop-1.1.7.tar.tar / gopchop-1.1.7.tar / gopchop-1.1.7 / src / Main.cpp < prev    next >
C/C++ Source or Header  |  2005-05-01  |  29KB  |  1,016 lines

  1. /*
  2. #
  3. # Main interface tool for GOPchop.  Implements multiple utility functions.
  4. #
  5. # $Id: Main.cpp,v 1.66 2005/05/01 17:59:23 keescook Exp $
  6. #
  7. # Copyright (C) 2001-2003 Kees Cook
  8. # kees@outflux.net, http://outflux.net/
  9. # This program is free software; you can redistribute it and/or
  10. # modify it under the terms of the GNU General Public License
  11. # as published by the Free Software Foundation; either version 2
  12. # of the License, or (at your option) any later version.
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. # GNU General Public License for more details.
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  20. # http://www.gnu.org/copyleft/gpl.html
  21. #
  22. */
  23. /*
  24.  * Initial main.c file generated by Glade. Edit as required.
  25.  * Glade will not overwrite this file.
  26.  */
  27.  
  28. #include "GOPchop.h"
  29.  
  30. #include <stdio.h>
  31. #include <inttypes.h>
  32. #include <sys/types.h>
  33. #include <sys/time.h>
  34. #include <sys/stat.h>
  35. #include <sys/mman.h>
  36. #include <stdlib.h>
  37. #include <fcntl.h>
  38. #include <unistd.h>
  39. #include <sys/uio.h>
  40. #include <string.h>
  41. #include <errno.h>
  42. #include <time.h>
  43.  
  44. #ifdef ENABLE_NLS
  45. # include <locale.h>
  46. #endif
  47.  
  48. #include "Vector.h"
  49. #include "List.h"
  50. #include "ElementStream.h"
  51. #include "Pack.h"
  52. #include "GroupOfPictures.h"
  53. #include "Picture.h"
  54. #include "MPEG2Parser.h"
  55. #include "mpeg2consts.h"
  56. #include "widgets.h"
  57. #include "FirstPack.h"
  58.  
  59. #include "GOPutils.h"
  60.  
  61. #ifdef __cplusplus
  62. extern "C" {
  63. #endif
  64.  
  65. /* libmpeg2 */
  66. #include "video_out.h"
  67. #include <mpeg2dec/mpeg2.h>
  68.  
  69. #ifdef __cplusplus
  70. }
  71. #endif
  72.  
  73. #include <gtk/gtk.h>
  74. #include <gdk/gdkx.h>
  75. #include <X11/Xlib.h>
  76. #include <math.h>
  77.  
  78. #include "main.h"
  79.  
  80. #include "display.h"
  81.  
  82. #include <getopt.h>
  83.  
  84. /* */
  85. /* globals for everyone to beat on */
  86. /* */
  87. // strings
  88. char *GOPchop_cvsid = "$Id: Main.cpp,v 1.66 2005/05/01 17:59:23 keescook Exp $";
  89.  
  90. // objects
  91. MPEG2Parser *mpeg2parser;
  92.  
  93. // command line options
  94. char * opt_videoout    = NULL; // default to the first libvo driver
  95. char * opt_pipe        = NULL; // default to no command
  96. int    opt_show_states = 0;    // default to not reporting libmpeg2 states
  97.  
  98. // decode variables
  99. int                   desired_output = GOPCHOP_OUTPUT_LIBVO;
  100. /*
  101. static mpeg2dec_t    *mpeg2dec = NULL;
  102. static vo_open_t     *output_open = NULL;
  103. static vo_instance_t *output = NULL;
  104. */
  105. struct display_engine_t * engine = NULL;
  106.  
  107. // mark settings
  108. gint main_clist_count = 0;
  109. uint32_t mark_start = 0;
  110. uint32_t mark_end = 0;
  111.  
  112. // pipe variables
  113. int pipes[2];
  114. int *client_pipe = NULL;
  115.  
  116. /* global preferences */
  117. /*
  118.  * add new options below.
  119.  * If you want it saved to the rc file, add it to the "parsable_items" list.
  120.  * Update the "handle_rc" function below for rc options.
  121.  * Update the menu activation handler to auto-save the rc file.
  122.  */
  123. global_options options={
  124.     run_loop:             0, /* don't wrap arround by default */
  125.     run_speed:            1, /* one frame at a time by default */
  126.     default_run_speed:    1, /* one frame at a time by default */
  127.     auto_refresh:         1, /* show gop position changes by default */
  128.     ignore_errors:        0, /* don't ignore errors by default */
  129.     drop_orphaned_frames: 0, /* orphaned frame dropping can be nasty */
  130.     adjust_timestamps:    1, /* adjust by default */
  131.     video_driver_ptr:     NULL, /* prefered video driver */
  132.     video_driver:         "\0",
  133.     force_system_header:  0, /* force prepended system header pack */
  134.     drop_trailing_pack_with_system_header: 0, /* drop final pack if it has a system header */
  135.     ignore_endcode:       0, /* don't stop parsing when an End Code is seen */
  136. };
  137. rc_parse_item parsable_items[] = {
  138.     { "run_loop",             &options.run_loop,             RC_TYPE_BOOLEAN },
  139.     { "default_run_speed",    &options.default_run_speed,    RC_TYPE_INTEGER },
  140.     { "auto_refresh",         &options.auto_refresh,         RC_TYPE_BOOLEAN },
  141.     { "ignore_errors",        &options.ignore_errors,        RC_TYPE_BOOLEAN },
  142.     { "drop_orphaned_frames", &options.drop_orphaned_frames, RC_TYPE_BOOLEAN },
  143.     { "adjust_timestamps",    &options.adjust_timestamps,    RC_TYPE_BOOLEAN },
  144.     { "video_driver_ptr",     &options.video_driver_ptr,     RC_TYPE_STRING  },
  145.     { "force_system_header",  &options.force_system_header,  RC_TYPE_BOOLEAN },
  146.     { "drop_trailing_pack_with_system_header",  &options.drop_trailing_pack_with_system_header,  RC_TYPE_BOOLEAN },
  147.     { "ignore_endcode",       &options.ignore_endcode,       RC_TYPE_BOOLEAN },
  148.     { NULL, NULL, 0 }
  149. };
  150.  
  151. ///// My various supporting functions
  152. void flush()
  153. {
  154.     while (g_main_iteration(FALSE));
  155.     /*
  156.        while(gtk_events_pending())
  157.        gtk_main_iteration();
  158.      */
  159. }
  160.  
  161. void status_on(const gchar * owner, char *text)
  162. {
  163.     guint context;
  164.  
  165.     context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar),
  166.                                            owner);
  167.     gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), context, text);
  168.     flush();
  169. }
  170.  
  171. void status_off(const gchar * owner)
  172. {
  173.     guint context;
  174.  
  175.     context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar),
  176.                                            owner);
  177.     gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), context);
  178.     flush();
  179.     //gtk_main_iteration_do(0);
  180.  
  181. }
  182.  
  183. const gchar *fileops = "fileops";
  184.  
  185. void progress(char *task, float done)
  186. {
  187.     static gchar *lasttask = NULL;
  188.     static float lastdone = 0.0;
  189.     float diff;
  190.  
  191.     int tasksdiffer;
  192.     gint context;
  193.  
  194.     context = gtk_statusbar_get_context_id(GTK_STATUSBAR(main_statusbar),
  195.                                            fileops);
  196.  
  197.     if (lasttask)
  198.     {
  199.         tasksdiffer = strcmp(lasttask, task);
  200.     }
  201.     else
  202.     {
  203.         tasksdiffer = 1;
  204.     }
  205.  
  206.     if (tasksdiffer)
  207.     {
  208.         if (lasttask)
  209.             gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), context);
  210.  
  211.         lasttask = (char *) g_realloc(lasttask, strlen(task) + 1);
  212.         strcpy(lasttask, task);
  213.  
  214.         gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), context, lasttask);
  215.         flush();
  216.     }
  217.  
  218.     diff = fabs(done - lastdone);
  219.  
  220.     if (!tasksdiffer && diff < 0.01)
  221.         return;
  222.  
  223.     lastdone = done;
  224.  
  225.     gtk_progress_set_percentage(GTK_PROGRESS(main_progressbar), lastdone);
  226.  
  227.     /*   sending to stderr instead...
  228.        if (mpeg2parser->getError())
  229.        show_error(mpeg2parser->getError());
  230.      */
  231.  
  232.     flush();
  233. }
  234.  
  235. void show_error(const gchar *text)
  236. {
  237.     GtkTextIter iter;
  238.     GtkTextBuffer *buffer;
  239.  
  240.     buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(error_text_why));
  241.     gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(buffer),&iter);
  242.     gtk_text_iter_forward_to_end(&iter);
  243.     gtk_text_buffer_insert(buffer,&iter,text,-1);
  244.  
  245. }
  246.  
  247.  
  248. void get_clips_from_list(GtkTreeModel *model,
  249.                          GtkTreeIter  *iter,
  250.                          struct clip_list_t * clips)
  251. {
  252.     GValue list_GOP_first;
  253.     GValue list_GOP_last;
  254.  
  255.     if (!model || !iter || !clips) return;
  256.  
  257.     memset(&list_GOP_first,0,sizeof(list_GOP_first));
  258.     memset(&list_GOP_last, 0,sizeof(list_GOP_last));
  259.  
  260.     gtk_tree_model_get_value(GTK_TREE_MODEL(model), iter,
  261.                              ITEM_GOP_FIRST, &list_GOP_first);
  262.     gtk_tree_model_get_value(GTK_TREE_MODEL(model), iter,
  263.                              ITEM_GOP_LAST,  &list_GOP_last);
  264.  
  265.     clips->GOP_first = g_value_get_uint(&list_GOP_first);
  266.     clips->GOP_last =  g_value_get_uint(&list_GOP_last);
  267.  
  268.     g_value_unset(&list_GOP_first);
  269.     g_value_unset(&list_GOP_last);
  270. }
  271.  
  272.  
  273. /* "foreach" functions used to walk the main_clist */
  274. gboolean foreach_count_GOPs(GtkTreeModel *model,
  275.                             GtkTreePath *path,
  276.                             GtkTreeIter *iter,
  277.                             gpointer data)
  278. {
  279.     uint32_t * count = (uint32_t *)data;
  280.     struct clip_list_t clips;
  281.  
  282.     if (!count) return TRUE;
  283.  
  284.     get_clips_from_list(model,iter,&clips);
  285.  
  286.     *count += clips.GOP_last - clips.GOP_first + 1;
  287.  
  288.     return FALSE; /* keep traversing */
  289. }
  290.  
  291.  
  292. gboolean foreach_save_clips_to_file(GtkTreeModel *model,
  293.                                     GtkTreePath *path,
  294.                                     GtkTreeIter *iter,
  295.                                     gpointer data)
  296. {
  297.     struct clip_list_t clips;
  298.     uint32_t i;
  299.     struct save_clip_info_t * clip_info = (struct save_clip_info_t*)data;
  300.  
  301.     if (!clip_info) return TRUE; /* abort */
  302.  
  303.     get_clips_from_list(model,iter,&clips);
  304.  
  305.  
  306.     for (i = clips.GOP_first; i <= clips.GOP_last; i++) {
  307.         progress(clip_info->progress_task,
  308.                  (float)( (float) clip_info->count++ /
  309.                           (float) clip_info->total     ) );
  310.         
  311.         clip_info->written_bytes +=
  312.             write_GOP(clip_info->file, mpeg2parser, i,
  313.                     ((clip_info->GOP_previous + 1) == i),
  314.                     options.drop_orphaned_frames,
  315.                     options.adjust_timestamps,
  316.                     &clip_info->written_pictures,
  317.                     //TI!
  318.                     (clip_info->total - clip_info->count)==0,
  319.                     options.drop_trailing_pack_with_system_header);
  320.         clip_info->GOP_previous = i;
  321.  
  322.     }
  323.  
  324.     return FALSE; /* keep on steppin */
  325. }
  326.  
  327.  
  328.  
  329.  
  330. void save_file(char *filename)
  331. {
  332.     char buf[1024];
  333.     FILE *mpeg2;
  334.     struct save_clip_info_t clip_info;
  335.  
  336.     clip_info.progress_task = _("Saving MPEG2");
  337.  
  338.     if (!(mpeg2 = fopen(filename, "w")))
  339.     {
  340.         snprintf(buf, 1024, "%s: %s\n", filename, strerror(errno));
  341.         show_error(buf);
  342.     }
  343.     else
  344.     {
  345.         time_t begin, finish;
  346.         char report[REPORT_LENGTH];
  347.  
  348.         time(&begin);
  349.         clip_info.total=0;
  350.  
  351.         /* count up how many GOPs there are to write */
  352.         gtk_tree_model_foreach(GTK_TREE_MODEL(main_list_store),
  353.                                foreach_count_GOPs,
  354.                                &clip_info.total);
  355.  
  356.         clip_info.file=mpeg2;
  357.         clip_info.count=0;
  358.         clip_info.written_pictures=0;
  359.         clip_info.written_bytes=0;
  360.         // make sure the first GOP is closed
  361.         clip_info.GOP_previous = (uint32_t) -2;
  362.         
  363.         //TI! save first dummy pack with valid system header
  364.         if (options.force_system_header)
  365.         {
  366.             clip_info.written_bytes+=(mpeg2parser->getFirstPack())->saveFirstPack(mpeg2);
  367.         }
  368.         
  369.         /* save each clip in the list */
  370.         gtk_tree_model_foreach(GTK_TREE_MODEL(main_list_store),
  371.                                foreach_save_clips_to_file,
  372.                                &clip_info);
  373.  
  374.         progress(clip_info.progress_task, 1.0);
  375.         status_off(fileops);
  376.  
  377.         // write an end-of-PS marker
  378.         fwrite(eos_buf, sizeof(eos_buf), 1, mpeg2);
  379.         fclose(mpeg2);
  380.  
  381.         time(&finish);
  382.         finish -= begin;
  383.  
  384.         clip_info.written_bytes /= 1048576;
  385.         if (finish > 0)
  386.             clip_info.written_bytes /= finish;
  387.         if (clip_info.written_bytes == 0)
  388.             clip_info.written_bytes = 1;
  389.  
  390.         // FIXME: bad pluralization
  391.         snprintf(report,REPORT_LENGTH,finish == 1
  392.                 ? _("Wrote %s (GOPs: %u, pictures: %u) in %d second (%%%sMB/s)\n")
  393.                 : _("Wrote %s (GOPs: %u, pictures: %u) in %d seconds (%%%sMB/s)\n"),
  394.                 filename, clip_info.total, clip_info.written_pictures, finish, OFF_T_FORMAT);
  395.  
  396.         fprintf(stderr, report, clip_info.written_bytes);
  397.     }
  398. }
  399.  
  400. void open_file(char *filename)
  401. {
  402.     time_t begin, finish;
  403.     char buf[128];
  404.     off_t speed;
  405.     char report[REPORT_LENGTH];
  406.     char * gop_cache_filename;
  407.  
  408.     time(&begin);
  409.     if (!mpeg2parser->init(filename, progress))
  410.     {
  411.         show_error(mpeg2parser->getError());
  412.     }
  413.     else
  414.     {
  415.         mpeg2parser->parse(options.ignore_errors);
  416.         if (mpeg2parser->getError())
  417.             show_error(mpeg2parser->getError());
  418.  
  419.         // make sure we actually HAVE something...
  420.         List *GOPs = mpeg2parser->getGOPs();
  421.         if (GOPs && GOPs->getNum())
  422.             file_is_loaded();
  423.         else
  424.             mpeg2parser->reset();
  425.     }
  426.  
  427.     // load-time calculation
  428.     time(&finish);
  429.     finish -= begin;
  430.     speed = mpeg2parser->getSize() / 1048576;   // max speed is max size
  431.     if (finish > 0)
  432.         speed /= finish;
  433.     if (speed == 0)
  434.         speed = 1;              // claim at least 1MB/s
  435.  
  436.     snprintf(report,REPORT_LENGTH,finish == 1
  437.             ? _("Loaded %s in %d second (%%%sMB/s)\n")
  438.             : _("Loaded %s in %d seconds (%%%sMB/s)\n"),
  439.             filename, finish, OFF_T_FORMAT);
  440.  
  441.     fprintf(stderr, report, speed);
  442.  
  443.     progress(_("Caching"), 1.0);
  444.     /* write out the offset map */
  445.     if (!(gop_cache_filename=(char*)malloc(strlen(filename)+5))) {
  446.         perror("malloc");
  447.     }
  448.     else {
  449.         sprintf(gop_cache_filename,"%s.gop",filename);
  450.         if (write_gop_cache(gop_cache_filename,mpeg2parser)) {
  451.             printf("%s",_("Skipping offset cache writing.\n"));
  452.         }
  453.     }
  454.  
  455.     progress(_("Done"), 1.0);
  456.     status_off(fileops);
  457. }
  458.  
  459. /* gtk stupid: no way to get row count from a clist */
  460. void update_GOP_slice_count(int count) {
  461.     gchar buf[128];
  462.  
  463.     /* update the count of clips */
  464.     sprintf(buf, _("Clips: %d"), count);
  465.     gtk_label_set_text(GTK_LABEL(main_label_clips), buf);
  466. }
  467.  
  468. void add_GOP_slice(uint32_t start, uint32_t end)
  469. {
  470.     GtkTreeIter iter;
  471.     gchar *pathname;
  472.  
  473.     pathname = mpeg2parser->getFilename();
  474.     if (!pathname)
  475.         pathname = _("unknown pathname");
  476.  
  477.     gtk_list_store_append (main_list_store, &iter);
  478.     gtk_list_store_set (main_list_store, &iter,
  479.                         ITEM_FILENAME,  pathname,
  480.                         ITEM_GOP_FIRST, start,
  481.                         ITEM_GOP_LAST,  end,
  482.                         -1);
  483.  
  484.     main_clist_count++;
  485.     update_GOP_slice_count(main_clist_count);
  486. }
  487.  
  488. void clear_GOP_slices()
  489. {
  490.     gchar buf[128];
  491.  
  492.     gtk_list_store_clear(main_list_store);
  493.  
  494.     main_clist_count=0;
  495.     update_GOP_slice_count(main_clist_count);
  496. }
  497.  
  498. void remove_GOP_slice(GtkTreeIter *iter)
  499. {
  500.     if (!iter) return;
  501.  
  502.     gtk_list_store_remove(main_list_store,iter);
  503.  
  504.     main_clist_count--;
  505.     update_GOP_slice_count(main_clist_count);
  506. }
  507.  
  508. GdkFilterReturn filter_event(GdkXEvent *xevent,
  509.                              GdkEvent  *event,
  510.                              gpointer   data)
  511. {
  512.     //report_event(event);
  513.     printf("got event\n");
  514.     return GDK_FILTER_CONTINUE;
  515. }
  516.  
  517. const mpeg2_info_t *parser_info = NULL;
  518.  
  519. // decode_start
  520. // decode_stop
  521.  
  522. void clear_GOP_info()
  523. {
  524.     gtk_list_store_clear(GOP_list_store);
  525.     gtk_label_set_text(GTK_LABEL(GOP_label_filename), "");
  526.     gtk_label_set_text(GTK_LABEL(GOP_label_GOP), _("No GOP selected"));
  527.     gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info),
  528.         _("No Sequence Info Found") );
  529. }
  530.  
  531. void update_GOP_info(uint32_t num)
  532. {
  533.     int i;
  534.     GroupOfPictures *GOP;
  535.     Bounds *picture_bounds;
  536.     Bounds *packet_bounds;
  537.     Picture *picture;
  538.     List *GOPs;
  539.     List *picture_list;
  540.     List *packet_list;
  541.     List *audio_list;
  542.     List *video_list;
  543.     Vector *head_vector;
  544.     uint32_t picture_index, picture_max;
  545.     uint32_t packet_min, packet_index, packet_max;
  546.     uint32_t audio_index, audio_max;
  547.     off_t gop_len, gop_start;
  548.     off_t audio_len;
  549.     //int   aindex, amax;
  550.     uint8_t *head;
  551.     int drop, hour, min, sec, pictures, closed, broken;
  552.  
  553.     GtkTreeIter iter;
  554.     gchar datatype[128];
  555.     gchar dataoffset[128];
  556.     gchar datasize[128];
  557.  
  558.     gchar seqbuf[128];
  559.     struct sequence_info info;
  560.  
  561.     /* wipe out the old list */
  562.     gtk_list_store_clear(GOP_list_store);
  563.     // wipe out labels
  564.     sprintf(seqbuf,_("GOP %u information:"),num);
  565.     gtk_label_set_text(GTK_LABEL(GOP_label_GOP), seqbuf);
  566.     gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info),
  567.                        _("No Sequence Info Found"));
  568.  
  569.     if (!(GOPs = mpeg2parser->getGOPs()))
  570.     {
  571.         printf(_("no GOPs ?!\n"));
  572.         goto failure;
  573.     }
  574.     if (!(packet_list = mpeg2parser->getPackets()))
  575.     {
  576.         printf(_("NULL picture list ?!\n"));
  577.         goto failure;
  578.     }
  579.     if (!(picture_list = mpeg2parser->getPictures()))
  580.     {
  581.         printf(_("NULL picture list ?!\n"));
  582.         goto failure;
  583.     }
  584.     if (!(audio_list = mpeg2parser->getAudio()))
  585.     {
  586.         printf(_("NULL audio list ?!\n"));
  587.         goto failure;
  588.     }
  589.     if (!(video_list = mpeg2parser->getVideo()))
  590.     {
  591.         printf(_("NULL video list ?!\n"));
  592.         goto failure;
  593.     }
  594.     if (!(GOP = (GroupOfPictures *) GOPs->vector(num)))
  595.     {
  596.         printf(_("no such GOP %d ?!\n"), num);
  597.         goto failure;
  598.     }
  599.     if (!(head_vector = GOP->getHeader()))
  600.     {
  601.         printf(_("GOP header missing?!\n"));
  602.         goto failure;
  603.     }
  604.     if (!(head = mpeg2parser->bytesAvail(head_vector->getStart(), 8)))
  605.     {
  606.         printf(_("GOP header not available?!\n"));
  607.         goto failure;
  608.     }
  609.  
  610.     // calculate GOP information
  611.     /*
  612.        4         5         6        7
  613.        |         |         |        |
  614.        7 65432 107654 3 210765 432107 6 543210
  615.        1 11111 111111 1 111111 111111 1 1
  616.        d hour  min    m sec    pic    c b
  617.        r              a               l roken
  618.        o              r               osed
  619.        p              k
  620.      */
  621.     drop = ((head[4] & 0x80) > 0);
  622.     hour = ((head[4] & 0x7C) >> 2);
  623.     min = ((head[4] & 0x3) << 4) | ((head[5] & 0xF0) >> 4);
  624.  
  625.     // sec/picture decoding corrected, care of Scott Smith
  626.     sec = ((head[5] & 0x7) << 3) | ((head[6] & 0xE0) >> 5);
  627.     pictures = ((head[6] & 0x1F) << 1) | ((head[7] & 0x80) >> 7);
  628.  
  629.     closed = ((head[7] & 0x40) > 0);
  630.     broken = ((head[7] & 0x20) > 0);
  631.  
  632.     if (!(picture_bounds = GOP->getPictureBounds()))
  633.     {
  634.         printf(_("NULL GOP picture bounds?!\n"));
  635.         goto failure;
  636.     }
  637.  
  638.     // FIXME: technically, this loop does a lot of needless
  639.     //        reassigning of values.  However, this loop doesn't
  640.     //        happen often, and it's the easiest way I could think
  641.     //        of to sort by offset.
  642.  
  643.     // get picture bounds
  644.     picture_index = picture_bounds->getFirst();
  645.     picture_max = picture_bounds->getMax();
  646.  
  647.     for (; picture_index < picture_max; picture_index++)
  648.     {
  649.         Bounds *pic_bounds;
  650.         uint32_t ves, ves_min, ves_max;
  651.         Vector *pic_vector;
  652.         off_t pic_start;
  653.         off_t pic_len;
  654.  
  655.         if (!(picture = (Picture *) picture_list->vector(picture_index)))
  656.         {
  657.             printf(_("NULL picture?!\n"));
  658.             goto failure;
  659.         }
  660.  
  661.         if (!(pic_bounds = picture->getVideoBounds()))
  662.         {
  663.             printf(_("NULL picture bounds?!\n"));
  664.             goto failure;
  665.         }
  666.         ves_min = pic_bounds->getFirst();
  667.         ves_max = pic_bounds->getMax();
  668.         pic_len = 0;
  669.         for (ves = ves_min; ves < ves_max; ves++)
  670.         {
  671.             if (!(pic_vector = (Vector *) video_list->vector(ves)))
  672.             {
  673.                 printf(_("NULL picture VES vector?!\n"));
  674.                 goto failure;
  675.             }
  676.             if (ves == ves_min)
  677.                 pic_start = pic_vector->getStart();
  678.             pic_len += pic_vector->getLen();
  679.         }
  680.  
  681.         snprintf(datatype,   128, _("  Picture (%s: %02d)"),
  682.                 frame_type_str(picture->getType()), picture->getTime());
  683.         snprintf(dataoffset, 128, "%" OFF_T_FORMAT, pic_start);
  684.         snprintf(datasize,   128, "%" OFF_T_FORMAT, pic_len);
  685.         gtk_list_store_append(GOP_list_store, &iter);
  686.         gtk_list_store_set(GOP_list_store, &iter,
  687.                 ITEM_DATATYPE, datatype,
  688.                 ITEM_OFFSET,   dataoffset,
  689.                 ITEM_SIZE,     datasize,
  690.                 -1);
  691.     }
  692.  
  693.     // process GOP packets
  694.     if (!(packet_bounds = GOP->getPacketBounds()))
  695.     {
  696.         printf(_("NULL GOP packet bounds?!\n"));
  697.         goto failure;
  698.     }
  699.     packet_min = packet_bounds->getFirst();
  700.     packet_max = packet_bounds->getMax();
  701.     gop_len = 0;
  702.     audio_len = 0;
  703.     for (packet_index = packet_min; packet_index < packet_max; packet_index++)
  704.     {
  705.         Pack *packet;
  706.  
  707.         if (!(packet = (Pack *) packet_list->vector(packet_index)))
  708.         {
  709.             printf(_("NULL GOP packet vector?!\n"));
  710.             goto failure;
  711.         }
  712.         if (packet_index == packet_min)
  713.             gop_start = packet->getStart();
  714.         gop_len += packet->getLen();
  715.  
  716.         audio_index = packet->getAudioFirst();
  717.         audio_max = packet->getAudioMax();
  718.  
  719.         for (; audio_index < audio_max; audio_index++)
  720.         {
  721.             Bounds *audio_bounds;
  722.             int aes, aes_min, aes_max;
  723.             Vector *audio_vector;
  724.  
  725.             if (!(audio_vector = (Vector *) audio_list->vector(audio_index)))
  726.             {
  727.                 printf(_("NULL GOP AES vector?!\n"));
  728.                 goto failure;
  729.             }
  730.  
  731.             audio_len += audio_vector->getLen();
  732.  
  733.         }
  734.     }
  735.  
  736.     // display GOP
  737.     snprintf(datatype,   128, _("GOP (%02d:%02d:%02d.%02d%s%s%s)"),
  738.             hour, min, sec, pictures,
  739.             drop ? _(" drop") : _(" keep"),
  740.             closed ? _(" closed") : _(" open"),
  741.             broken ? _(" broken") : _(" whole"));
  742.     snprintf(dataoffset, 128, "%" OFF_T_FORMAT, gop_start);
  743.     snprintf(datasize,   128, "%" OFF_T_FORMAT, gop_len);
  744.     gtk_list_store_prepend(GOP_list_store, &iter);
  745.     gtk_list_store_set(GOP_list_store, &iter,
  746.             ITEM_DATATYPE, datatype,
  747.             ITEM_OFFSET,   dataoffset,
  748.             ITEM_SIZE,     datasize,
  749.             -1);
  750.  
  751.     // display audio info
  752.     snprintf(datatype,   128, "%s", _("  Audio"));
  753.     snprintf(dataoffset, 128, "%" OFF_T_FORMAT, gop_start);
  754.     snprintf(datasize,   128, "%" OFF_T_FORMAT, audio_len);
  755.     gtk_list_store_append(GOP_list_store, &iter);
  756.     gtk_list_store_set(GOP_list_store, &iter,
  757.             ITEM_DATATYPE, datatype,
  758.             ITEM_OFFSET,   dataoffset,
  759.             ITEM_SIZE,     datasize,
  760.             -1);
  761.  
  762.     // update labels with info
  763.     GOP->get_sequence_info(&info);    
  764.     snprintf(seqbuf,128,_("%dx%d: %s aspect, %s fps, %s%s"),
  765.         info.horizontal_size, info.vertical_size,
  766.         aspect_str[info.aspect_ratio],
  767.         frame_str[info.frame_rate],
  768.         speed_str(info.bit_rate * 400),
  769.         info.constrained ? _(", constrained") : "");
  770.     gtk_label_set_text(GTK_LABEL(GOP_label_sequence_info),seqbuf);
  771.  
  772.   failure:
  773.     /* perform any cleanups */
  774.     while (0) {}; // keep compiler quiet
  775. }
  776.  
  777. #define MAX_FRAME_TYPES 4
  778. const char *frame_type_map[MAX_FRAME_TYPES] = {
  779.     N_("Bad Frame"),
  780.     N_("I-Frame"),
  781.     N_("P-Frame"),
  782.     N_("B-Frame")
  783. };
  784. const char *frame_type_str(int type)
  785. {
  786.     if (type >= MAX_FRAME_TYPES || type < 0)
  787.         type = 0;
  788.     return _(frame_type_map[type]);
  789. }
  790.  
  791.  
  792. uint32_t get_GOP_selected()
  793. {
  794.     return (uint32_t) gtk_range_get_value(GTK_RANGE(GOP_selector));
  795. }
  796.  
  797. void set_GOP_selected(uint32_t num)
  798. {
  799.     uint32_t max;
  800.     uint32_t old;
  801.  
  802.     max = mpeg2parser->numGOPs();
  803.     // correct our bounds
  804.     if (num >= max)
  805.         num = max - 1;
  806.     if (num < 0)
  807.         num = 0;
  808.  
  809.     // remember where we were
  810.     old = get_GOP_selected();
  811.  
  812.     gtk_range_set_value(GTK_RANGE(GOP_selector), (gdouble) num);
  813. }
  814.  
  815. void handle_rc_load()
  816. {
  817.     if (rc_load(PACKAGE,parsable_items))
  818.         fprintf(stderr,"%s",_("Could not load rc file -- using defaults.\n"));
  819.  
  820.     /* handle side-effects */
  821.  
  822.     /* synchronize the loaded/default values to the slider */
  823.     options.run_speed = options.default_run_speed;
  824.     gtk_range_set_value(GTK_RANGE(slider_run_speed), options.run_speed);
  825.  
  826.     mpeg2parser->set_ignore_endcode(options.ignore_endcode);
  827. }
  828.  
  829. void Usage(char *title)
  830. {
  831.     printf("Usage: %s [OPTIONS] [FILE]\n\
  832. \n\
  833.   -h, --help         This help\n\
  834.   -v, --vo DRIVER    Choose internal output driver.  Use 'help' for a list.\n\
  835.   -p, --pipe CMD     Use external command for output.  Recommended:\n\
  836.                         'mplayer -nocache -novm -'\n\
  837.   -s, --states       Report libmpeg2 states\n\
  838.   FILE               MPEG2 to load on startup\n\
  839. \n", title);
  840.  
  841.     exit(1);
  842. }
  843.  
  844. void handle_args(int argc, char *argv[])
  845. {
  846.     static struct option long_options[] = {
  847.         { "help",   0, NULL, 'h' }, // 0
  848.         { "vo",     1, NULL, 'v' },
  849.         { "pipe",   1, NULL, 'p' },
  850.         { "states", 0, NULL, 's' },
  851.         { "version",0, NULL, 'V' },
  852.         { 0,        0, 0,    0 }
  853.     };
  854.  
  855.     /* do our option handling */
  856.     while (1)
  857.     {
  858.         int c;
  859.         int index;
  860.  
  861.         c = getopt_long(argc, argv, "Vhsv:p:", long_options, &index);
  862.  
  863.         if (c == -1)
  864.             break; // done with args
  865.  
  866.         switch (c)
  867.         {
  868.             case 0:
  869.                 // got a matching long argument (with no short equiv)
  870.                 switch (index)
  871.                 {
  872.                     default:
  873.                         fprintf(stderr,
  874.                                 _("Unknown long argument index %d found!\n"),
  875.                                 index);
  876.                 }
  877.                 break;
  878.             case 'h':
  879.                 Usage(argv[0]);
  880.                 break;
  881.             case 'V':
  882.                 printf("GOPchop %s (%s)\n", VERSION, __DATE__);
  883.                 exit(0);
  884.                 break;
  885.             case 's':
  886.                 opt_show_states=1;
  887.                 break;
  888.             case 'v':
  889.                 opt_videoout=optarg;
  890.                 if (strcmp(opt_videoout,"help")==0)
  891.                 {
  892.                     // display a list of libvo drivers
  893.                     int i;
  894.                     vo_driver_t const *drivers;
  895.  
  896.                     printf(_("Video output options:\n"));
  897.                     drivers = vo_drivers();
  898.                     for (i = 0; drivers[i].name; i++)
  899.                         printf("\t%s\n",drivers[i].name);
  900.                     exit(1);
  901.                 }
  902.                 else
  903.                 {
  904.                     // verify selected libvo driver
  905.                     if (!find_libvo_driver(opt_videoout))
  906.                     {
  907.                         fprintf(stderr,_("No such video driver: '%s'\n"),
  908.                                 opt_videoout);
  909.                         exit(1);
  910.                     }
  911.                     desired_output=GOPCHOP_OUTPUT_LIBVO;
  912.                 }
  913.                 break;
  914.             case 'p':
  915.                 opt_pipe=optarg;
  916.                 desired_output=GOPCHOP_OUTPUT_PIPE;
  917.                 break;
  918.             default:
  919.                 fprintf(stderr, _("Unknown short argument '%c' found?!\n"), c);
  920.                 /* fall through to the Usage ... */
  921.             case '?':
  922.                 Usage(argv[0]);
  923.                 break;
  924.         }
  925.     }
  926. }
  927.  
  928. /*
  929.  * There must be a way to hook up an event watcher to frame_window
  930.  * directly.  Until I figure it out, this will have to do.  Overkill,
  931.  * but it works.
  932.  */
  933. void gopchop_events(GdkEvent *event, gpointer data)
  934. {
  935.     GdkEventExpose *expose = (GdkEventExpose*)event;
  936.     if (event)
  937.         switch (event->type)
  938.         {
  939.             // this doesn't catch window movement :(
  940.             case GDK_VISIBILITY_NOTIFY: // forces repaint of frame window
  941.                 // don't attempt repaint while totally hidden
  942.                 if (((GdkEventVisibility*)event)->state == GDK_VISIBILITY_FULLY_OBSCURED)
  943.                     break;
  944.  
  945.                 if (display_is_window(engine,((GdkEventAny*)event)->window))
  946.                     display_redraw(engine);
  947.  
  948.                 break;
  949.             /*
  950.             case GDK_EXPOSE: // forces repaint of frame window
  951.                 if (display_is_window(engine,expose->window))
  952.                     display_redraw(engine);
  953.  
  954.                 break;
  955.                 */
  956.         }
  957.     
  958.     // process events normally
  959.     gtk_main_do_event(event);
  960.     return;
  961. }
  962.  
  963. int main(int argc, char *argv[])
  964. {
  965.  
  966.  
  967.     /* deal with internationalization support */
  968. #ifdef ENABLE_NLS
  969.     setlocale(LC_ALL, "");
  970.     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_DATA_DIR "/locale");
  971.     textdomain(GETTEXT_PACKAGE);
  972. #endif
  973.     gtk_set_locale();
  974.  
  975.     /* pass command line args to gtk (which may modify them) */
  976.     gtk_init(&argc, &argv);
  977.     /* handle our command line args */
  978.     handle_args(argc, argv);
  979.  
  980.     /* setup the parsing object */
  981.     mpeg2parser = new MPEG2Parser;
  982.  
  983.     /* set up all the gtk windows and widgets */
  984.     setup_gtk_stuff();
  985.  
  986.     /* verify that we're working with largefile support
  987.      * (report to Gtk window)
  988.      */
  989.     if (sizeof(off_t) < 8)
  990.     {
  991.         show_error(_("Type 'off_t' is less than 8 bytes.\n"
  992.                      "Largefile support requires at least 8 bytes.\n"));
  993.         gtk_widget_show(error_dialog);
  994.     }
  995.  
  996.     /* load any saved options (prior to opening the first file) */
  997.     handle_rc_load();
  998.  
  999.     /* attempt to load the filename mentioned on the command line 
  1000.      * (requires MPEG2Parser and GTK loaded
  1001.      */
  1002.     if (optind < argc)
  1003.         open_file(argv[optind]);
  1004.  
  1005.     /* start the event handler */
  1006.     gdk_event_handler_set(gopchop_events,NULL,NULL);
  1007.     gtk_main();
  1008.     return 0;
  1009. }
  1010.  
  1011.  
  1012. /* vi:set ai ts=4 sw=4 expandtab: */
  1013.