You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
10 KiB

/*
* import_im.c
*
* Copyright (C) Thomas Oestreich - June 2001
* port to MagickWand API:
* Copyright (C) Francesco Romani - July 2007
*
* This file is part of transcode, a video stream processing tool
*
* transcode 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, or (at your option)
* any later version.
*
* transcode 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 GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#define MOD_NAME "import_im.so"
#define MOD_VERSION "v0.1.3 (2008-10-07)"
#define MOD_CODEC "(video) RGB"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* Note: because of ImageMagick bogosity, this must be included first, so
* we can undefine the PACKAGE_* symbols it splats into our namespace */
#ifdef HAVE_BROKEN_WAND
#include <wand/magick-wand.h>
#else /* we have a SANE wand header */
#include <wand/MagickWand.h>
#endif /* HAVE_BROKEN_WAND */
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#include "transcode.h"
#include "libtc/optstr.h"
#include <stdlib.h>
#include <stdio.h>
/*%*
*%* DESCRIPTION
*%* This module reads single images from disk using ImageMagick;
*%* a stream of correlated images can be automatically read if
*%* their filenames contains a common prefix and a serial number.
*%* All formats supported by ImageMagick are supported as well.
*%*
*%* BUILD-DEPENDS
*%* libMagick >= 6.2.4.0
*%*
*%* DEPENDS
*%* libMagick >= 6.2.4.0
*%*
*%* PROCESSING
*%* import/demuxer
*%*
*%* MEDIA
*%* video
*%*
*%* #INPUT
*%*
*%* OUTPUT
*%* RGB24
*%*
*%* OPTION
*%* noseq (flag)
*%* disable internal auto loading of images with similar names.
*%*/
static int verbose_flag = TC_QUIET;
static int capability_flag = TC_CAP_RGB|TC_CAP_VID;
#define MOD_PRE im
#include "import_def.h"
#include <time.h>
#include <sys/types.h>
#include <regex.h>
static char *head = NULL, *tail = NULL;
static int first_frame = 0, current_frame = 0, decoded_frame = 0, pad = 0;
static int total_frame = 0;
static int width = 0, height = 0;
static MagickWand *wand = NULL;
static int auto_seq_read = TC_TRUE;
/*
* automagically read further images with filename like the first one
* enabled by default for backward compatibility, but obsoleted
* by core option --multi_input
*/
static int TCHandleMagickError(MagickWand *wand)
{
ExceptionType severity;
const char *description = MagickGetException(wand, &severity);
tc_log_error(MOD_NAME, "%s", description);
MagickRelinquishMemory((void*)description);
return TC_IMPORT_ERROR;
}
/* ------------------------------------------------------------
*
* open stream
*
* ------------------------------------------------------------*/
/* I suspect we have a lot of potential memleaks in here -- FRomani */
MOD_open
{
int result, slen = 0;
char *regex = NULL, *frame = NULL;
regex_t preg;
regmatch_t pmatch[4];
if (param->flag == TC_AUDIO) {
return TC_IMPORT_OK;
}
if (param->flag == TC_VIDEO) {
param->fd = NULL;
// get the frame name and range
regex = "\\([^0-9]\\+[-._]\\?\\)\\?\\([0-9]\\+\\)\\([-._].\\+\\)\\?";
result = regcomp(&preg, regex, 0);
if (result) {
tc_log_perror(MOD_NAME, "ERROR: Regex compile failed.\n");
return TC_IMPORT_ERROR;
}
result = regexec(&preg, vob->video_in_file, 4, pmatch, 0);
if (result) {
tc_log_warn(MOD_NAME, "Regex match failed: no image sequence");
slen = strlen(vob->video_in_file) + 1;
head = tc_malloc(slen);
if (head == NULL) {
tc_log_perror(MOD_NAME, "filename head");
return TC_IMPORT_ERROR;
}
strlcpy(head, vob->video_in_file, slen);
tail = tc_malloc(1); /* URGH -- FRomani */
tail[0] = 0;
first_frame = -1;
} else {
// split the name into head, frame number, and tail
slen = pmatch[1].rm_eo - pmatch[1].rm_so + 1;
head = tc_malloc(slen);
if (head == NULL) {
tc_log_perror(MOD_NAME, "filename head");
return TC_IMPORT_ERROR;
}
strlcpy(head, vob->video_in_file, slen);
slen = pmatch[2].rm_eo - pmatch[2].rm_so + 1;
frame = tc_malloc(slen);
if (frame == NULL) {
tc_log_perror(MOD_NAME, "filename frame");
return TC_IMPORT_ERROR;
}
strlcpy(frame, vob->video_in_file + pmatch[2].rm_so, slen);
// If the frame number is padded with zeros, record how many digits
// are actually being used.
if (frame[0] == '0') {
pad = pmatch[2].rm_eo - pmatch[2].rm_so;
}
first_frame = atoi(frame);
slen = pmatch[3].rm_eo - pmatch[3].rm_so + 1;
tail = tc_malloc(slen);
if (tail == NULL) {
tc_log_perror(MOD_NAME, "filename tail");
return TC_IMPORT_ERROR;
}
strlcpy(tail, vob->video_in_file + pmatch[3].rm_so, slen);
tc_free(frame);
}
if (vob->im_v_string != NULL) {
if (optstr_lookup(vob->im_v_string, "noseq")) {
auto_seq_read = TC_FALSE;
if (verbose > TC_INFO) {
tc_log_info(MOD_NAME, "automagic image sequential read disabled");
}
}
}
current_frame = first_frame;
decoded_frame = 0;
width = vob->im_v_width;
height = vob->im_v_height;
if (total_frame == 0) {
/* only the very first time */
MagickWandGenesis();
}
wand = NewMagickWand();
if (wand == NULL) {
tc_log_error(MOD_NAME, "cannot create magick wand");
return TC_IMPORT_ERROR;
}
return TC_IMPORT_OK;
}
return TC_IMPORT_ERROR;
}
/* ------------------------------------------------------------
*
* decode stream
*
* ------------------------------------------------------------*/
MOD_decode
{
char *frame = NULL, *filename = NULL;
int slen;
MagickBooleanType status;
if (param->flag == TC_AUDIO) {
return TC_IMPORT_OK;
}
if (param->flag == TC_VIDEO) {
if (!auto_seq_read) {
if (decoded_frame > 0) {
return TC_IMPORT_ERROR;
}
filename = tc_strdup(vob->video_in_file);
} else {
// build the filename for the current frame
slen = strlen(head) + pad + strlen(tail) + 1;
filename = tc_malloc(slen);
if (pad) {
char framespec[10];
frame = tc_malloc(pad+1);
tc_snprintf(framespec, 10, "%%0%dd", pad);
tc_snprintf(frame, pad+1, framespec, current_frame);
frame[pad] = '\0';
} else if (first_frame >= 0) {
frame = tc_malloc(10);
tc_snprintf(frame, 10, "%d", current_frame);
}
strlcpy(filename, head, slen);
if (frame != NULL) {
strlcat(filename, frame, slen);
tc_free(frame);
frame = NULL;
}
strlcat(filename, tail, slen);
}
ClearMagickWand(wand);
/*
* This avoids IM to buffer all read images.
* I'm quite sure that this can be done in a smarter way,
* but I haven't yet figured out how. -- FRomani
*/
status = MagickReadImage(wand, filename);
if (status == MagickFalse) {
if (auto_seq_read) {
/* let's assume that image sequence ends here */
return TC_IMPORT_ERROR;
}
return TCHandleMagickError(wand);
}
MagickSetLastIterator(wand);
status = MagickGetImagePixels(wand,
0, 0, width, height,
"RGB", CharPixel,
param->buffer);
/* param->size already set correctly by caller */
if (status == MagickFalse) {
return TCHandleMagickError(wand);
}
param->attributes |= TC_FRAME_IS_KEYFRAME;
total_frame++;
current_frame++;
decoded_frame++;
tc_free(filename);
return TC_IMPORT_OK;
}
return TC_IMPORT_ERROR;
}
/* ------------------------------------------------------------
*
* close stream
*
* ------------------------------------------------------------*/
MOD_close
{
if (param->flag == TC_AUDIO) {
return TC_IMPORT_OK;
}
if (param->flag == TC_VIDEO) {
vob_t *vob = tc_get_vob();
if (param->fd != NULL)
pclose(param->fd);
if (head != NULL)
tc_free(head);
if (tail != NULL)
tc_free(tail);
if (wand != NULL) {
DestroyMagickWand(wand);
wand = NULL;
if (!tc_has_more_video_in_file(vob)) {
/* ...can you hear that? is the sound of the ugliness... */
MagickWandTerminus();
}
}
return TC_IMPORT_OK;
}
return TC_IMPORT_ERROR;
}
/*************************************************************************/
/*
* Local variables:
* c-file-style: "stroustrup"
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
* indent-tabs-mode: nil
* End:
*
* vim: expandtab shiftwidth=4:
*/