mirror of https://github.com/GNOME/gimp.git
1204 lines
33 KiB
C
1204 lines
33 KiB
C
/* gap_decode_xanim.c
|
|
* 1999.11.22 hof (Wolfgang Hofer)
|
|
*
|
|
* GAP ... Gimp Animation Plugins
|
|
*
|
|
* This Module contains:
|
|
*
|
|
* GIMP/GAP-frontend interface for XANIM exporting edition from loki entertainmaint
|
|
* Call xanim exporting edition (the loki version)
|
|
* To split any xanim supported video into
|
|
* anim frames (single images on disk)
|
|
* Audio can also be extracted.
|
|
*
|
|
* xanim exporting edition is available at:
|
|
* Web: http://heroine.linuxbox.com/toys.html
|
|
* http://www.lokigames.com/development/smjpeg.php3
|
|
* download: http://heroine.linuxbox.com/xanim_exporting_edition.tar.gz
|
|
* http://www.lokigames.com/development/download/smjpeg/xanim2801-loki090899.tar.gz
|
|
* Send comments or questions to smjpeg@lokigames.com.
|
|
*
|
|
* Warning: This Module needs UNIX environment to run.
|
|
* It uses programs and commands that are NOT available
|
|
* on other Operating Systems (Win95, NT ...)
|
|
*
|
|
* - xanim 2.80 exporting edition with extensions from loki entertainment.
|
|
* set environment GAP_XANIM_PROG to configure where to find xanim
|
|
* (default: search xanim in your PATH)
|
|
* - grep (UNIX command)
|
|
* - rm (UNIX command is used to delete by wildcard (expanded by /bin/sh)
|
|
* and to delete a directory with all files
|
|
*/
|
|
/* The GIMP -- an image manipulation program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/* revision history
|
|
* 1.1.29b; 2000/11/30 hof: used g_snprintf
|
|
* 1.1.17b; 2000/02/26 hof: bugfixes
|
|
* 1.1.14a; 1999/11/22 hof: fixed gcc warning (too many arguments for format)
|
|
* 1.1.13a; 1999/11/22 hof: first release
|
|
*/
|
|
|
|
/* SYTEM (UNIX) includes */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
/* GIMP includes */
|
|
#include "gtk/gtk.h"
|
|
#include "config.h"
|
|
#include "libgimp/stdplugins-intl.h"
|
|
#include "libgimp/gimp.h"
|
|
|
|
#ifdef G_OS_WIN32
|
|
#include <io.h>
|
|
# ifndef S_ISDIR
|
|
# define S_ISDIR(m) ((m) & _S_IFDIR)
|
|
# endif
|
|
# ifndef S_ISREG
|
|
# define S_ISREG(m) ((m) & _S_IFREG)
|
|
# endif
|
|
#endif
|
|
|
|
/* GAP includes */
|
|
#include "gap_lib.h"
|
|
#include "gap_arr_dialog.h"
|
|
#include "gap_decode_xanim.h"
|
|
|
|
extern int gap_debug; /* ==0 ... dont print debug infos */
|
|
|
|
static char *global_xanim_input_dir = "input";
|
|
|
|
gchar global_xanim_prog[500];
|
|
gchar *global_errlist = NULL;
|
|
|
|
gint32 global_delete_number;
|
|
|
|
#define MKDIR_MODE (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH )
|
|
|
|
/* ============================================================================
|
|
* p_xanim_info
|
|
* ============================================================================
|
|
*/
|
|
static int
|
|
p_xanim_info(char *errlist)
|
|
{
|
|
t_arr_arg argv[20];
|
|
t_but_arg b_argv[2];
|
|
|
|
int l_idx;
|
|
int l_rc;
|
|
|
|
|
|
l_idx = 0;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = _("Conditions to run the xanim based video split");
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = "";
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = _("1.) xanim 2.80.0 exporting edition (the loki version)");
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = _(" must be installed somewhere in your PATH");
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = _(" you can get xanim exporting edition at");
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = " http://heroine.linuxbox.com/toys.html";
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = " http://www.lokigames.com/development/download/smjpeg/xanim2801-loki090899.tar.gz";
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = "";
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = _("2.) if your xanim exporting edition is not in your PATH or is not named xanim");
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = _(" you have to set Environment variable GAP_XANIM_PROG ");
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = _(" to your xanim exporting program and restart gimp");
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = "";
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = _("An ERROR occured while trying to call xanim:");
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = "--------------------------------------------";
|
|
|
|
|
|
l_idx++;
|
|
p_init_arr_arg(&argv[l_idx], WGT_LABEL_LEFT);
|
|
argv[l_idx].label_txt = errlist;
|
|
|
|
l_idx++;
|
|
|
|
/* the Action Button */
|
|
b_argv[0].but_txt = GTK_STOCK_CANCEL;
|
|
b_argv[0].but_val = -1;
|
|
b_argv[1].but_txt = GTK_STOCK_OK;
|
|
b_argv[1].but_val = 0;
|
|
|
|
l_rc = p_array_std_dialog(_("XANIM Information"),
|
|
"",
|
|
l_idx, argv, /* widget array */
|
|
1, b_argv, /* button array */
|
|
-1);
|
|
|
|
return (l_rc);
|
|
} /* end p_xanim_info */
|
|
|
|
|
|
/* ============================================================================
|
|
* p_xanim_dialog
|
|
* ============================================================================
|
|
*/
|
|
static int
|
|
p_xanim_dialog (gint32 *first_frame,
|
|
gint32 *last_frame,
|
|
char *filename,
|
|
gint32 len_filename,
|
|
char *basename,
|
|
gint32 len_basename,
|
|
t_gap_xa_formats *Format,
|
|
gint32 *extract_video,
|
|
gint32 *extract_audio,
|
|
gint32 *jpeg_quality,
|
|
gint32 *autoload,
|
|
gint32 *run_xanim_asynchron)
|
|
{
|
|
#define XADIALOG_NUM_ARGS 12
|
|
static t_arr_arg argv[XADIALOG_NUM_ARGS];
|
|
static char *radio_args[3] = { "XCF", "PPM", "JPEG" };
|
|
|
|
p_init_arr_arg(&argv[0], WGT_FILESEL);
|
|
argv[0].label_txt = _("Video:");
|
|
argv[0].help_txt = _("Name of a videofile to READ by xanim.\n"
|
|
"Frames are extracted from the videofile\n"
|
|
"and written to separate diskfiles.\n"
|
|
"xanim exporting edition is required.");
|
|
argv[0].text_buf_len = len_filename;
|
|
argv[0].text_buf_ret = filename;
|
|
argv[0].entry_width = 250;
|
|
|
|
p_init_arr_arg(&argv[1], WGT_INT_PAIR);
|
|
argv[1].label_txt = _("From:");
|
|
argv[1].help_txt = _("Framenumber of 1st frame to extract");
|
|
argv[1].constraint = FALSE;
|
|
argv[1].int_min = 0;
|
|
argv[1].int_max = 9999;
|
|
argv[1].int_ret = 0;
|
|
argv[1].umin = 0;
|
|
argv[1].entry_width = 80;
|
|
|
|
p_init_arr_arg(&argv[2], WGT_INT_PAIR);
|
|
argv[2].label_txt = _("To:");
|
|
argv[2].help_txt = _("Framenumber of last frame to extract");
|
|
argv[2].constraint = FALSE;
|
|
argv[2].int_min = 0;
|
|
argv[2].int_max = 9999;
|
|
argv[2].int_ret = 9999;
|
|
argv[2].umin = 0;
|
|
argv[2].entry_width = 80;
|
|
|
|
p_init_arr_arg(&argv[3], WGT_FILESEL);
|
|
argv[3].label_txt = _("Framenames:");
|
|
argv[3].help_txt = _("Basename for the AnimFrames to write on disk\n"
|
|
"(framenumber and extension is added)");
|
|
argv[3].text_buf_len = len_basename;
|
|
argv[3].text_buf_ret = basename;
|
|
argv[3].entry_width = 250;
|
|
|
|
p_init_arr_arg(&argv[4], WGT_OPTIONMENU);
|
|
argv[4].label_txt = _("Format");
|
|
argv[4].help_txt = _("Fileformat for the extracted AnimFrames\n"
|
|
"(xcf is extracted as ppm and converted to xcf)");
|
|
argv[4].radio_argc = 3;
|
|
argv[4].radio_argv = radio_args;
|
|
argv[4].radio_ret = 0;
|
|
|
|
p_init_arr_arg(&argv[5], WGT_TOGGLE);
|
|
argv[5].label_txt = _("Extract Frames");
|
|
argv[5].help_txt = _("Enable extraction of Frames");
|
|
argv[5].int_ret = 1;
|
|
|
|
p_init_arr_arg(&argv[6], WGT_TOGGLE);
|
|
argv[6].label_txt = _("Extract Audio");
|
|
argv[6].help_txt = _("Enable extraction of audio to raw audiofile\n"
|
|
"(frame range limits are ignored for audio)");
|
|
argv[6].int_ret = 0;
|
|
|
|
p_init_arr_arg(&argv[7], WGT_INT_PAIR);
|
|
argv[7].label_txt = _("Jpeg Quality:");
|
|
argv[7].help_txt = _("Quality for resulting Jpeg frames\n"
|
|
"(is ignored when other formats are used)");
|
|
argv[7].constraint = TRUE;
|
|
argv[7].int_min = 0;
|
|
argv[7].int_max = 100;
|
|
argv[7].int_ret = 90;
|
|
|
|
p_init_arr_arg(&argv[8], WGT_LABEL);
|
|
argv[8].label_txt = "";
|
|
|
|
p_init_arr_arg(&argv[9], WGT_TOGGLE);
|
|
argv[9].label_txt = _("Open");
|
|
argv[9].help_txt = _("Open the 1st one of the extracted frames");
|
|
argv[9].int_ret = 1;
|
|
|
|
p_init_arr_arg(&argv[10], WGT_TOGGLE);
|
|
argv[10].label_txt = _("Run asynchronously");
|
|
argv[10].help_txt = _("Run xanim asynchronously and delete unwanted frames\n"
|
|
"(out of the specified range) while xanim is still running");
|
|
argv[10].int_ret = 1;
|
|
|
|
p_init_arr_arg(&argv[11], WGT_LABEL_LEFT);
|
|
argv[11].label_txt = _("\nWarning: xanim 2.80 has only limited MPEG support.\n"
|
|
"Most of the frames (type P and B) will be skipped.");
|
|
|
|
if(TRUE == p_array_dialog(_("Split any Xanim readable Video to Frames"),
|
|
_("Select Frame Range"), XADIALOG_NUM_ARGS, argv))
|
|
{
|
|
if(argv[1].int_ret < argv[2].int_ret )
|
|
{
|
|
*first_frame = (long)(argv[1].int_ret);
|
|
*last_frame = (long)(argv[2].int_ret);
|
|
}
|
|
else
|
|
{
|
|
*first_frame = (long)(argv[2].int_ret);
|
|
*last_frame = (long)(argv[1].int_ret);
|
|
}
|
|
*Format = (t_gap_xa_formats)(argv[4].int_ret);
|
|
*extract_video = (long)(argv[5].int_ret);
|
|
*extract_audio = (long)(argv[6].int_ret);
|
|
*jpeg_quality = (long)(argv[7].int_ret);
|
|
*autoload = (long)(argv[9].int_ret);
|
|
*run_xanim_asynchron = (long)(argv[10].int_ret);
|
|
return (0); /* OK */
|
|
}
|
|
else
|
|
{
|
|
return -1; /* Cancel */
|
|
}
|
|
|
|
|
|
} /* end p_xanim_dialog */
|
|
|
|
|
|
static gint
|
|
p_overwrite_dialog(char *filename, gint overwrite_mode)
|
|
{
|
|
static t_but_arg l_argv[3];
|
|
static t_arr_arg argv[1];
|
|
|
|
if(p_file_exists(filename))
|
|
{
|
|
if (overwrite_mode < 1)
|
|
{
|
|
l_argv[0].but_txt = _("Overwrite Frame");
|
|
l_argv[0].but_val = 0;
|
|
l_argv[1].but_txt = _("Overwrite All");
|
|
l_argv[1].but_val = 1;
|
|
l_argv[2].but_txt = GTK_STOCK_CANCEL;
|
|
l_argv[2].but_val = -1;
|
|
|
|
p_init_arr_arg(&argv[0], WGT_LABEL);
|
|
argv[0].label_txt = filename;
|
|
|
|
return(p_array_std_dialog ( _("GAP Question"),
|
|
_("File already exists"),
|
|
1, argv,
|
|
3, l_argv, -1));
|
|
}
|
|
}
|
|
return (overwrite_mode);
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
p_build_xanim_framename(char *framename, gint32 sizeof_framename, gint32 frame_nr, char *ext)
|
|
{
|
|
g_snprintf(framename, sizeof_framename, "%s/frame%d.%s",
|
|
global_xanim_input_dir,
|
|
(int)frame_nr,
|
|
ext);
|
|
}
|
|
|
|
static void
|
|
p_build_gap_framename(char *framename, gint32 sizeof_framename, gint32 frame_nr, char *basename, char *ext)
|
|
{
|
|
g_snprintf(framename, sizeof_framename, "%s%04d.%s", basename, (int)frame_nr, ext);
|
|
}
|
|
|
|
int
|
|
p_is_directory(char *fname)
|
|
{
|
|
struct stat l_stat_buf;
|
|
|
|
/* get File status */
|
|
if (0 != stat(fname, &l_stat_buf))
|
|
{
|
|
/* stat error (file does not exist) */
|
|
return(0);
|
|
}
|
|
|
|
if(S_ISDIR(l_stat_buf.st_mode))
|
|
{
|
|
return(1);
|
|
}
|
|
|
|
return(0);
|
|
} /* end p_is_directory */
|
|
|
|
void
|
|
p_dirname(char *fname)
|
|
{
|
|
int l_idx;
|
|
|
|
l_idx = strlen(fname) -1;
|
|
while(l_idx > 0)
|
|
{
|
|
if(fname[l_idx] == G_DIR_SEPARATOR)
|
|
{
|
|
fname[l_idx] = '\0';
|
|
return;
|
|
}
|
|
l_idx--;
|
|
}
|
|
*fname = '\0';
|
|
}
|
|
|
|
static void
|
|
p_init_xanim_global_name()
|
|
{
|
|
const gchar *l_env;
|
|
|
|
l_env = g_getenv("GAP_XANIM_PROG");
|
|
|
|
if(l_env != NULL)
|
|
{
|
|
strcpy(global_xanim_prog, l_env);
|
|
return;
|
|
}
|
|
strcpy(global_xanim_prog, "xanim"); /* default name */
|
|
}
|
|
|
|
static int
|
|
p_convert_frames(gint32 frame_from, gint32 frame_to, char *basename, char *ext, char *ext2)
|
|
{
|
|
GimpParam *return_vals;
|
|
int nreturn_vals;
|
|
gint32 l_tmp_image_id;
|
|
char l_first_xa_frame[200];
|
|
|
|
/* load 1st one of those frames generated by xanim */
|
|
p_build_xanim_framename(l_first_xa_frame, sizeof(l_first_xa_frame), frame_from, ext);
|
|
l_tmp_image_id = p_load_image(l_first_xa_frame);
|
|
|
|
/* convert the xanim frames (from ppm) to xcf fileformat
|
|
* (the gap module for range convert is not linked to the frontends
|
|
* main program, therefore i call the convert procedure by PDB-interface)
|
|
*/
|
|
return_vals = gimp_run_procedure ("plug_in_gap_range_convert2",
|
|
&nreturn_vals,
|
|
GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, /* runmode */
|
|
GIMP_PDB_IMAGE, l_tmp_image_id,
|
|
GIMP_PDB_DRAWABLE, 0, /* (unused) */
|
|
GIMP_PDB_INT32, frame_from,
|
|
GIMP_PDB_INT32, frame_to,
|
|
GIMP_PDB_INT32, 0, /* dont flatten */
|
|
GIMP_PDB_INT32, 4444, /* dest type (keep type) */
|
|
GIMP_PDB_INT32, 256, /* colors (unused) */
|
|
GIMP_PDB_INT32, 0, /* no dither (unused) */
|
|
GIMP_PDB_STRING, ext2, /* extension for dest. filetype */
|
|
GIMP_PDB_STRING, basename, /* basename for dest. filetype */
|
|
GIMP_PDB_INT32, 0, /* (unused) */
|
|
GIMP_PDB_INT32, 0, /* (unused) */
|
|
GIMP_PDB_INT32, 0, /* (unused) */
|
|
GIMP_PDB_STRING, "none", /* (unused) palettename */
|
|
GIMP_PDB_END);
|
|
|
|
/* destroy the tmp image */
|
|
gimp_image_delete(l_tmp_image_id);
|
|
|
|
if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS)
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
return(0); /* OK */
|
|
}
|
|
|
|
|
|
static gint32
|
|
p_find_max_xanim_frame(gint32 from_nr, char *ext)
|
|
{
|
|
gint32 l_high;
|
|
gint32 l_max_found;
|
|
gint32 l_nr;
|
|
gint32 l_delta;
|
|
char l_frame[500];
|
|
|
|
l_nr = from_nr;
|
|
l_max_found = 0;
|
|
l_high = 100000;
|
|
|
|
while(1 == 1)
|
|
{
|
|
p_build_xanim_framename(l_frame, sizeof(l_frame), l_nr, ext);
|
|
|
|
if(gap_debug) printf("DEBUG find_MAX :%s\n", l_frame);
|
|
|
|
if(p_file_exists(l_frame))
|
|
{
|
|
l_max_found = l_nr;
|
|
l_delta = (l_high - l_nr) / 2;
|
|
if(l_delta == 0)
|
|
{
|
|
l_delta = 1;
|
|
}
|
|
l_nr = l_max_found + l_delta;
|
|
}
|
|
else
|
|
{
|
|
if(l_nr == from_nr) { return (-1); } /* no frames found */
|
|
|
|
if(l_nr < l_high)
|
|
{
|
|
l_high = l_nr;
|
|
l_nr = l_max_found + 1;
|
|
}
|
|
else
|
|
{
|
|
return(l_max_found);
|
|
}
|
|
}
|
|
}
|
|
} /* end p_find_max_xanim_frame */
|
|
|
|
static int
|
|
p_rename_frames(gint32 frame_from, gint32 frame_to, char *basename, char *ext)
|
|
{
|
|
gint32 l_use_mv;
|
|
gint32 l_frame_nr;
|
|
gint32 l_max_found;
|
|
char l_src_frame[500];
|
|
char l_dst_frame[500];
|
|
gint l_overwrite_mode;
|
|
|
|
|
|
if(gap_debug) printf("p_rename_frames:\n");
|
|
l_use_mv = TRUE;
|
|
l_overwrite_mode = 0;
|
|
|
|
|
|
l_max_found = p_find_max_xanim_frame (frame_from, ext);
|
|
if(l_max_found < 0)
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("can't find any extracted frames,\n%s\nmaybe xanim has failed or was cancelled"),
|
|
l_src_frame);
|
|
return(-1);
|
|
}
|
|
|
|
|
|
l_frame_nr = frame_from;
|
|
|
|
while (l_frame_nr <= frame_to)
|
|
{
|
|
p_build_xanim_framename(l_src_frame, sizeof(l_src_frame), l_frame_nr, ext);
|
|
p_build_gap_framename(l_dst_frame, sizeof(l_dst_frame), l_frame_nr, basename, ext);
|
|
|
|
if(!p_file_exists(l_src_frame))
|
|
{
|
|
break; /* srcfile not found, stop */
|
|
}
|
|
|
|
if (strcmp(l_src_frame, l_dst_frame) != 0)
|
|
{
|
|
/* check overwrite if Destination frame already exsts */
|
|
l_overwrite_mode = p_overwrite_dialog(l_dst_frame, l_overwrite_mode);
|
|
if (l_overwrite_mode < 0)
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("frames are not extracted, because overwrite of %s was cancelled"),
|
|
l_dst_frame);
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
remove(l_dst_frame);
|
|
if (p_file_exists(l_dst_frame))
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("failed to overwrite %s (check permissions ?)"),
|
|
l_dst_frame);
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
if (l_use_mv)
|
|
{
|
|
rename(l_src_frame, l_dst_frame);
|
|
}
|
|
|
|
if (!p_file_exists(l_dst_frame))
|
|
{
|
|
p_file_copy(l_src_frame, l_dst_frame);
|
|
if (p_file_exists(l_dst_frame))
|
|
{
|
|
l_use_mv = FALSE; /* if destination is on another device use copy-remove strategy */
|
|
remove(l_src_frame);
|
|
}
|
|
else
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("failed to write %s (check permissions ?)"),
|
|
l_dst_frame);
|
|
return(-1);
|
|
}
|
|
}
|
|
}
|
|
l_frame_nr++;
|
|
if(l_max_found > 0) gimp_progress_update ((gdouble)l_frame_nr / (gdouble)l_max_found);
|
|
}
|
|
return(0);
|
|
} /* end p_rename_frames */
|
|
|
|
static void
|
|
p_delete_frames(gint32 max_tries, gint32 frame_from, gint32 frame_to, char *ext)
|
|
{
|
|
/* this procedure is performed repeatedly while polling the xanim process
|
|
* and after xanim process has (or was) terminated to clean up unwanted frames.
|
|
*/
|
|
gint32 l_tries;
|
|
gint32 l_next_number;
|
|
char l_framename[500];
|
|
|
|
if(gap_debug) printf("p_delete_frames: cleaning up unwanted frames (max=%d)\n", (int)max_tries);
|
|
|
|
l_tries = 0;
|
|
|
|
while ((global_delete_number < frame_from) && (l_tries < max_tries))
|
|
{
|
|
l_next_number = global_delete_number + 1;
|
|
p_build_xanim_framename(l_framename, sizeof(l_framename), l_next_number, ext);
|
|
|
|
if (p_file_exists(l_framename))
|
|
{
|
|
/* if xanim has already written the next frame
|
|
* we can delete the previous (unwanted) frame now
|
|
*/
|
|
p_build_xanim_framename(l_framename, sizeof(l_framename), global_delete_number, ext);
|
|
if(gap_debug) printf("delete frame: %s\n", l_framename);
|
|
remove(l_framename);
|
|
|
|
global_delete_number = l_next_number;
|
|
}
|
|
l_tries++;
|
|
}
|
|
} /* end p_delete_frames */
|
|
|
|
|
|
static void
|
|
p_poll(pid_t xanim_pid, char *one_past_last_frame, gint32 frame_from, gint32 frame_to, char *ext)
|
|
{
|
|
/* loop as long as the Xanim Process is alive */
|
|
|
|
if(gap_debug) printf("poll started on xanim pid: %d\n", (int)xanim_pid);
|
|
|
|
|
|
/* kill with signal 0 checks only if the process is alive (no signal is sent)
|
|
* returns 0 if alive, 1 if no process with given pid found.
|
|
*/
|
|
while (0 == kill(xanim_pid, 0))
|
|
{
|
|
usleep(100000); /* sleep 1 second, and let xanim write some frames */
|
|
if (p_file_exists(one_past_last_frame))
|
|
{
|
|
/* if the last desired frame is written
|
|
* we can stop (kill with signal 9) the xanim process.
|
|
*/
|
|
kill(xanim_pid, 9);
|
|
break;
|
|
}
|
|
|
|
/* check for max unwanted frames and delete (upto 20 of them) */
|
|
p_delete_frames(20, frame_from, frame_to, ext);
|
|
}
|
|
|
|
if(gap_debug) printf("poll ended on xanim pid: %d\n", (int)xanim_pid);
|
|
} /* end p_poll */
|
|
|
|
|
|
static int
|
|
p_grep(char *pattern, char *file)
|
|
{
|
|
gint l_rc;
|
|
gchar *l_cmd;
|
|
|
|
l_cmd = g_strdup_printf("grep -c '%s' \"%s\" >/dev/null", pattern, file);
|
|
l_rc = system(l_cmd);
|
|
g_free(l_cmd);
|
|
if (l_rc == 0)
|
|
{
|
|
return(0); /* pattern found */
|
|
}
|
|
return(1); /* pattern NOT found */
|
|
}
|
|
|
|
static gint
|
|
p_check_xanim()
|
|
{
|
|
gint l_rc;
|
|
gint l_grep_counter1;
|
|
gint l_grep_counter2;
|
|
gint l_grep_counter3;
|
|
gchar *l_cmd;
|
|
static char *l_xanim_help_output = "tmp_xanim_help.output";
|
|
FILE *l_fp;
|
|
|
|
l_fp = fopen(l_xanim_help_output, "w+");
|
|
if (l_fp == NULL)
|
|
{
|
|
global_errlist = g_strdup_printf("no write permission for current directory");
|
|
return(10);
|
|
}
|
|
fprintf(l_fp, "dummy");
|
|
fclose(l_fp);
|
|
|
|
/* execute xanim with -h option and
|
|
* store its output in a file.
|
|
*/
|
|
l_cmd = g_strdup_printf("%s -h 2>&1 >>%s", global_xanim_prog, l_xanim_help_output);
|
|
l_rc = system(l_cmd);
|
|
|
|
if(gap_debug) printf("DEBUG: executed :%s\n Retcode: %d\n", l_cmd, (int)l_rc);
|
|
g_free(l_cmd);
|
|
|
|
if ((l_rc == 127) || (l_rc == (127 << 8)))
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("could not execute %s (check if xanim is installed)"),
|
|
global_xanim_prog );
|
|
return(10);
|
|
}
|
|
|
|
if(!p_file_exists(l_xanim_help_output))
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("%s does not look like xanim"),
|
|
global_xanim_prog );
|
|
return(10);
|
|
}
|
|
|
|
/* check the help output of xanim (using grep) */
|
|
l_grep_counter1 = 0;
|
|
/* l_grep_counter1 += p_grep("anim", l_xanim_help_output); */
|
|
|
|
/* check for the exporting options */
|
|
l_grep_counter2 = 0;
|
|
l_grep_counter2 += p_grep("Ea", l_xanim_help_output);
|
|
l_grep_counter2 += p_grep("Ee", l_xanim_help_output);
|
|
l_grep_counter2 += p_grep("Eq", l_xanim_help_output);
|
|
|
|
/* check for the loki version that is able to write single frames */
|
|
l_grep_counter3 = 0;
|
|
l_grep_counter3 += p_grep("Write video to input/frameN.EXT", l_xanim_help_output);
|
|
|
|
remove(l_xanim_help_output);
|
|
|
|
if(l_grep_counter2 != 0)
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("The xanim program on your system \"%s\"\ndoes not support the exporting options Ea, Ee, Eq"),
|
|
global_xanim_prog );
|
|
return(10);
|
|
}
|
|
if(l_grep_counter3 != 0)
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("The xanim program on your system \"%s\"\ndoes not support exporting of single frames"),
|
|
global_xanim_prog );
|
|
return(10);
|
|
}
|
|
return (0); /* OK, xanim output looks like expected */
|
|
} /* end p_check_xanim */
|
|
|
|
static pid_t
|
|
p_start_xanim_process(gint32 first_frame, gint32 last_frame,
|
|
char *filename,
|
|
t_gap_xa_formats Format,
|
|
gint32 extract_video,
|
|
gint32 extract_audio,
|
|
gint32 jpeg_quality ,
|
|
char *one_past_last_frame,
|
|
gint32 run_xanim_asynchron)
|
|
{
|
|
gchar l_cmd[500];
|
|
gchar l_buf[40];
|
|
pid_t l_xanim_pid;
|
|
int l_rc;
|
|
FILE *l_fp;
|
|
static char *l_xanim_startscript = "tmp_xanim_startscript.sh";
|
|
static char *l_xanim_pidfile = "tmp_xanim_pidfile";
|
|
|
|
l_xanim_pid = -1;
|
|
|
|
/* allocate and prepare args for the xanim call */
|
|
g_snprintf(l_cmd, sizeof(l_cmd), "%s +f ", global_xanim_prog); /* programname */
|
|
|
|
if (extract_audio)
|
|
{
|
|
strcat(l_cmd, "+Ea ");
|
|
}
|
|
|
|
if (extract_video)
|
|
{
|
|
strcat(l_cmd, "+v "); /* +v is verbose mode */
|
|
|
|
switch(Format)
|
|
{
|
|
case XAENC_PPMRAW:
|
|
strcat(l_cmd, "+Ee ");
|
|
break;
|
|
case XAENC_JPEG:
|
|
g_snprintf(l_buf, sizeof(l_buf), "+Eq%d ", (int)jpeg_quality);
|
|
strcat(l_cmd, l_buf);
|
|
break;
|
|
default:
|
|
strcat(l_cmd, "+Ee ");
|
|
break;
|
|
}
|
|
|
|
/* additional option "Pause after N Frames" is used,
|
|
* to stop xanim exporting frames beyond the requested limit
|
|
*/
|
|
if (run_xanim_asynchron)
|
|
{
|
|
g_snprintf(l_buf, sizeof(l_buf), "+Zp%d ", (int)(last_frame +1));
|
|
strcat(l_cmd, l_buf);
|
|
}
|
|
|
|
}
|
|
|
|
/* add the videofilename as last parameter */
|
|
strcat(l_cmd, filename);
|
|
|
|
if (run_xanim_asynchron)
|
|
{
|
|
/* asynchron start */
|
|
remove(l_xanim_pidfile);
|
|
/* generate a shelscript */
|
|
l_fp = fopen(l_xanim_startscript, "w+");
|
|
if (l_fp != NULL)
|
|
{
|
|
fprintf(l_fp, "#!/bin/sh\n");
|
|
/* fprintf(l_fp, "(%s ; touch %s) &\n" */
|
|
fprintf(l_fp, "%s & # ; touch %s) &\n"
|
|
, l_cmd /* start xanim as background process */
|
|
, one_past_last_frame /* and create a dummy frame when xanim is done */
|
|
);
|
|
fprintf(l_fp, "XANIM_PID=$!\n");
|
|
fprintf(l_fp, "echo \"$XANIM_PID # XANIM_PID\"\n");
|
|
fprintf(l_fp, "echo \"$XANIM_PID # XANIM_PID\" > \"%s\"\n", l_xanim_pidfile);
|
|
|
|
/* we pass the xanim pid in a file,
|
|
* exitcodes are truncated to 8 bit
|
|
* by the system call
|
|
*/
|
|
/* fprintf(l_fp, "exit $XANIM_PID\n"); */
|
|
fclose(l_fp);
|
|
|
|
chmod(l_xanim_startscript, MKDIR_MODE);
|
|
}
|
|
|
|
l_rc = system(l_xanim_startscript);
|
|
|
|
l_fp = fopen(l_xanim_pidfile, "r");
|
|
if (l_fp != NULL)
|
|
{
|
|
fscanf(l_fp, "%d", &l_rc);
|
|
fclose(l_fp);
|
|
l_xanim_pid = (pid_t)l_rc;
|
|
}
|
|
|
|
remove(l_xanim_startscript);
|
|
remove(l_xanim_pidfile);
|
|
|
|
|
|
if(gap_debug) printf("ASYNCHRON CALL: %s\nl_xanim_pid:%d\n", l_cmd, (int)l_xanim_pid);
|
|
}
|
|
else
|
|
{
|
|
/* synchron start (blocks until xanim process has finished */
|
|
l_rc = system(l_cmd);
|
|
if ((l_rc & 0xff) == 0) l_xanim_pid = 0;
|
|
else l_xanim_pid = -1;
|
|
|
|
if(gap_debug) printf("ASYNCHRON CALL: %s\nretcode:%d (%d)\n", l_cmd, (int)l_rc, (int)l_xanim_pid);
|
|
}
|
|
|
|
return(l_xanim_pid);
|
|
} /* end p_start_xanim_process */
|
|
|
|
|
|
#ifdef THIS_IS_A_COMMENT_EXEC_DID_NOT_WORK_AND_LEAVES_A_ZOMBIE_PROCESS
|
|
static pid_t
|
|
p_start_xanim_process_exec(gint32 first_frame, gint32 last_frame,
|
|
char *filename,
|
|
t_gap_xa_formats Format,
|
|
gint32 extract_video,
|
|
gint32 extract_audio,
|
|
gint32 jpeg_quality
|
|
)
|
|
{
|
|
char *args[20];
|
|
char l_buf[40];
|
|
int l_idx;
|
|
pid_t l_xanim_pid;
|
|
|
|
/* allocate and prepare args for the xanim call */
|
|
l_idx = 0;
|
|
args[l_idx] = g_strdup(global_xanim_prog); /* programname */
|
|
|
|
l_idx++;
|
|
args[l_idx] = g_strdup("+f");
|
|
|
|
if (extract_audio)
|
|
{
|
|
l_idx++;
|
|
args[l_idx] = g_strdup("+Ea");
|
|
}
|
|
|
|
if (extract_video)
|
|
{
|
|
l_idx++;
|
|
args[l_idx] = g_strdup("+v"); /* +v is verbose mode */
|
|
|
|
l_idx++;
|
|
switch(Format)
|
|
{
|
|
case XAENC_PPMRAW:
|
|
args[l_idx] = g_strdup("+Ee");
|
|
break;
|
|
case XAENC_JPEG:
|
|
g_snprintf(l_buf, sizeof(l_buf), "+Eq%d", (int)jpeg_quality);
|
|
args[l_idx] = g_strdup(l_buf);
|
|
break;
|
|
default:
|
|
args[l_idx] = g_strdup("+Ee");
|
|
break;
|
|
}
|
|
|
|
/* additional option "Pause after N Frames" is used,
|
|
* to stop xanim exporting frames beyond the requested limit
|
|
*/
|
|
l_idx++;
|
|
g_snprintf(l_buf, sizeof(l_buf), "+Zp%d", (int)(last_frame +1));
|
|
args[l_idx] = g_strdup(l_buf);
|
|
|
|
}
|
|
|
|
/* add the videofilename as last parameter */
|
|
l_idx++;
|
|
args[l_idx] = g_strdup(filename);
|
|
|
|
l_idx++;
|
|
args[l_idx] = NULL; /* terminate args list with a NULL pointer */
|
|
|
|
l_xanim_pid = fork();
|
|
|
|
if(l_xanim_pid == 0)
|
|
{
|
|
/* here we are in the forked child process
|
|
* execute xanim
|
|
*/
|
|
execvp(args[0], args);
|
|
|
|
/* this point should never be reached */
|
|
_exit (1);
|
|
}
|
|
|
|
return(l_xanim_pid);
|
|
} /* end p_start_xanim_process */
|
|
#endif
|
|
|
|
|
|
/* ============================================================================
|
|
* gap_xanim_decode
|
|
* ============================================================================
|
|
*/
|
|
|
|
gint32
|
|
gap_xanim_decode(GimpRunMode run_mode)
|
|
{
|
|
gint32 l_rc;
|
|
gint32 first_frame;
|
|
gint32 last_frame;
|
|
char filename[200];
|
|
char basename[200];
|
|
char extension[20];
|
|
char extension2[20];
|
|
t_gap_xa_formats Format;
|
|
gint32 extract_audio;
|
|
gint32 extract_video;
|
|
gint32 jpeg_quality;
|
|
gint32 autoload;
|
|
gint32 run_xanim_asynchron;
|
|
char l_cmd[300];
|
|
char l_one_past_last_frame[200];
|
|
char l_first_to_laod[200];
|
|
char *l_dst_dir;
|
|
pid_t l_xanim_pid;
|
|
int l_input_dir_created_by_myself;
|
|
|
|
l_rc = 0;
|
|
l_input_dir_created_by_myself = FALSE;
|
|
global_errlist = NULL;
|
|
p_init_xanim_global_name();
|
|
|
|
filename[0] = '\0';
|
|
strcpy(&basename[0], "frame_");
|
|
|
|
l_rc = p_xanim_dialog (&first_frame,
|
|
&last_frame,
|
|
filename, sizeof(filename),
|
|
basename, sizeof(basename),
|
|
&Format,
|
|
&extract_video,
|
|
&extract_audio,
|
|
&jpeg_quality,
|
|
&autoload,
|
|
&run_xanim_asynchron);
|
|
|
|
|
|
if(l_rc != 0)
|
|
{
|
|
return(l_rc);
|
|
}
|
|
|
|
if(!p_file_exists(filename))
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("videofile %s not existent or empty\n"),
|
|
filename);
|
|
l_rc = 10;
|
|
}
|
|
else
|
|
{
|
|
l_rc = p_check_xanim();
|
|
}
|
|
|
|
|
|
if (l_rc == 0)
|
|
{
|
|
switch(Format)
|
|
{
|
|
case XAENC_PPMRAW:
|
|
strcpy(extension, "ppm");
|
|
strcpy(extension2, ".ppm");
|
|
break;
|
|
case XAENC_JPEG:
|
|
strcpy(extension, "jpg");
|
|
strcpy(extension2, ".jpg");
|
|
break;
|
|
default:
|
|
strcpy(extension, "ppm");
|
|
strcpy(extension2, ".xcf");
|
|
break;
|
|
|
|
}
|
|
p_build_xanim_framename(l_one_past_last_frame, sizeof(l_one_past_last_frame), last_frame +1 , extension);
|
|
|
|
if (extract_video)
|
|
{
|
|
/* for the frames we need a directory named "input" */
|
|
if (p_is_directory(global_xanim_input_dir))
|
|
{
|
|
/* the input directory already exists,
|
|
* remove frames
|
|
*/
|
|
g_snprintf(l_cmd, sizeof(l_cmd), "rm -f %s/*.%s", global_xanim_input_dir, extension);
|
|
system(l_cmd);
|
|
}
|
|
else
|
|
{
|
|
/* create input directory (needed by xanim to store the frames) */
|
|
mkdir(global_xanim_input_dir, MKDIR_MODE);
|
|
|
|
if (p_is_directory(global_xanim_input_dir))
|
|
{
|
|
l_input_dir_created_by_myself = TRUE;
|
|
}
|
|
else
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("could not create %s directory\n"
|
|
"(that is required for xanim frame export)"),
|
|
global_xanim_input_dir);
|
|
l_rc = 10;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(l_rc == 0)
|
|
{
|
|
gimp_progress_init (_("extracting frames..."));
|
|
gimp_progress_update (0.1); /* fake some progress */
|
|
/* note:
|
|
* we can't show realistic progress for the extracting process
|
|
* because we know nothing about videofileformat and how much frames
|
|
* are realy stored in the videofile.
|
|
*
|
|
* one guess could assume, that xanim will write 0 upto last_frame
|
|
* to disk, and check for the frames that the xanim process creates.
|
|
* The periodically checking can be done in the poll procedure for asynchron
|
|
* startmode only.
|
|
*/
|
|
|
|
l_xanim_pid = p_start_xanim_process(first_frame, last_frame,
|
|
filename,
|
|
Format,
|
|
extract_video,
|
|
extract_audio,
|
|
jpeg_quality,
|
|
l_one_past_last_frame,
|
|
run_xanim_asynchron);
|
|
|
|
if (l_xanim_pid == -1 )
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("could not start xanim process\n(program=%s)"),
|
|
global_xanim_prog );
|
|
l_rc = -1;
|
|
}
|
|
}
|
|
|
|
if(l_rc == 0)
|
|
{
|
|
if(run_xanim_asynchron)
|
|
{
|
|
p_poll(l_xanim_pid, l_one_past_last_frame, first_frame, last_frame, extension);
|
|
}
|
|
|
|
p_delete_frames(99999, first_frame, last_frame, extension);
|
|
remove(l_one_past_last_frame);
|
|
|
|
gimp_progress_update (1.0);
|
|
|
|
if (p_find_max_xanim_frame (first_frame, extension) < first_frame)
|
|
{
|
|
global_errlist = g_strdup_printf(
|
|
_("can't find any extracted frames,\n"
|
|
"xanim has failed or was cancelled"));
|
|
l_rc = -1;
|
|
}
|
|
else
|
|
{
|
|
/* if destination directorypart does not exist, try to create it */
|
|
l_dst_dir = g_strdup(basename);
|
|
p_dirname(l_dst_dir);
|
|
if (*l_dst_dir != '\0')
|
|
{
|
|
if ( !p_is_directory(l_dst_dir) )
|
|
{
|
|
mkdir (l_dst_dir, MKDIR_MODE);
|
|
}
|
|
}
|
|
|
|
if(strcmp(extension, &extension2[1]) == 0)
|
|
{
|
|
gimp_progress_init (_("renaming frames..."));
|
|
l_rc = p_rename_frames(first_frame, last_frame, basename, extension);
|
|
}
|
|
else
|
|
{
|
|
gimp_progress_init (_("converting frames..."));
|
|
l_rc = p_convert_frames(first_frame, last_frame, basename, extension, extension2);
|
|
}
|
|
|
|
if (l_input_dir_created_by_myself)
|
|
{
|
|
if (strcmp(l_dst_dir, global_xanim_input_dir) != 0)
|
|
{
|
|
/* remove input dir with all files */
|
|
g_snprintf(l_cmd, sizeof(l_cmd), "rm -rf \"%s\"", global_xanim_input_dir);
|
|
system(l_cmd);
|
|
}
|
|
}
|
|
g_free(l_dst_dir);
|
|
gimp_progress_update (1.0);
|
|
}
|
|
|
|
}
|
|
|
|
if(l_rc != 0)
|
|
{
|
|
if(global_errlist == NULL)
|
|
{
|
|
p_xanim_info("ERROR: could not execute xanim");
|
|
}
|
|
else
|
|
{
|
|
p_xanim_info(global_errlist);
|
|
}
|
|
l_rc = -1;
|
|
}
|
|
else
|
|
{
|
|
if(autoload)
|
|
{
|
|
/* load first frame and add a display */
|
|
p_build_gap_framename(l_first_to_laod, sizeof(l_first_to_laod), first_frame, basename, &extension2[1]);
|
|
l_rc = p_load_image(l_first_to_laod);
|
|
|
|
if(l_rc >= 0) gimp_display_new(l_rc);
|
|
}
|
|
}
|
|
|
|
return(l_rc);
|
|
} /* end gap_xanim_decode */
|