libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

commit 00d3bf2405ad5a2b3bd76772f22afe76ab27c356
parent 5043ac8e83db6a1e12de8bb7662e027d996fa774
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 20 Dec 2009 19:55:40 +0000

ffmpeg and related fixes

Diffstat:
MTODO | 5+++++
Mconfigure.ac | 2+-
Msrc/include/extractor.h | 43++++++++++++++++---------------------------
Msrc/main/extract.c | 40++++++++++------------------------------
Msrc/main/extractor.c | 160+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/plugins/Makefile.am | 20--------------------
Msrc/plugins/mime_extractor.c | 2+-
Msrc/plugins/thumbnailffmpeg_extractor.c | 677+++++++++++++++++++++----------------------------------------------------------
8 files changed, 303 insertions(+), 646 deletions(-)

diff --git a/TODO b/TODO @@ -10,6 +10,11 @@ Core: * use "-" in config not to append plugin, but to remove one! * document * port test cases +* document special options; we have: + - force-kill: plugin process committs suicide after each file + - oop-only: plugin is never run in-process + - close-stdout: stdout is closed for the (external) plugin process + - close-stderr: stderr is closed for the (external) plugin process 'Unclean' code: * ASF diff --git a/configure.ac b/configure.ac @@ -505,7 +505,7 @@ AC_CACHE_CHECK([whether -export-symbols-regex works], if test "x$gn_cv_export_symbols_regex_works" = "xyes" then LE_LIB_LDFLAGS="$LE_LIB_LDFLAGS -export-symbols-regex \"EXTRACTOR_@<:@a-zA-Z0-9_@:>@*\"" - LE_PLUGIN_LDFLAGS="$LE_PLUGIN_LDFLAGS -export-symbols-regex \"EXTRACTOR_@<:@a-zA-Z0-9_@:>@*_extract\"" + LE_PLUGIN_LDFLAGS="$LE_PLUGIN_LDFLAGS -export-symbols-regex \"EXTRACTOR_@<:@a-zA-Z0-9_@:>@*_.......\"" fi AC_SUBST(LE_LIB_LDFLAGS) AC_SUBST(LE_PLUGIN_LDFLAGS) diff --git a/src/include/extractor.h b/src/include/extractor.h @@ -42,26 +42,32 @@ extern "C" { */ enum EXTRACTOR_Options { + /** - * Run plugins in-process. + * Run plugin out-of-process, starting the process once the plugin + * is to be run. If a plugin crashes, automatically restart the + * respective process for the same file and try once more + * (since the crash may be caused by the previous file). If + * the process crashes immediately again, it is not restarted + * until the next file. */ - EXTRACTOR_OPTION_NONE = 0, + EXTRACTOR_OPTION_DEFAULT_POLICY = 0, /** * Run plugins out-of-process, starting the process * once at the time the plugin is loaded. This will * prevent the main process crashing if a plugin dies. * Ignored on platforms where out-of-process starts - * are not supported. + * are not supported (in-process execution will be + * attempted, unless the plugin itself forbids it). */ - EXTRACTOR_OPTION_OUT_OF_PROCESS = 1, + EXTRACTOR_OPTION_OUT_OF_PROCESS_NO_RESTART = 1, /** - * If a plugin crashes, automatically restart the respective - * process for the next file. Implies - * EXTRACTOR_OPTION_OUT_OF_PROCESS. + * Run plugins in-process. Unsafe, not recommended, + * can be nice for debugging. */ - EXTRACTOR_OPTION_AUTO_RESTART = 2, + EXTRACTOR_OPTION_IN_PROCESS = 2, /** * Internal value for plugins that have been disabled. @@ -478,23 +484,6 @@ EXTRACTOR_plugin_add (struct EXTRACTOR_PluginList * prev, const char *options, enum EXTRACTOR_Options flags); - -/** - * Add a library for keyword extraction at the END of the list. - * @param prev the previous list of libraries, may be NULL - * @param library the name of the library (full path) - * @param options options to give to the library - * @param flags options to use - * @return the new list of libraries, always equal to prev - * except if prev was NULL and no error occurs - */ -struct EXTRACTOR_PluginList * -EXTRACTOR_plugin_add_last(struct EXTRACTOR_PluginList *prev, - const char *library, - const char *options, - enum EXTRACTOR_Options flags); - - /** * Load multiple libraries as specified by the user. * @@ -503,8 +492,8 @@ EXTRACTOR_plugin_add_last(struct EXTRACTOR_PluginList *prev, * "[[-]LIBRARYNAME[(options)][:[-]LIBRARYNAME[(options)]]]*". * For example, 'mp3:ogg' loads the * mp3 and the ogg plugins. The '-' before the LIBRARYNAME - * indicates that the library should be added to the end - * of the library list (addLibraryLast). + * indicates that the library should be removed from + * the library list. * @param prev the previous list of libraries, may be NULL * @param flags options to use * @return the new list of libraries, equal to prev iff an error occured diff --git a/src/main/extract.c b/src/main/extract.c @@ -170,8 +170,6 @@ printHelp () static Help help[] = { { 'b', "bibtex", NULL, gettext_noop("print output in bibtex format") }, - { 'B', "binary", "LANG", - gettext_noop("use the generic plaintext extractor for the language with the 2-letter language code LANG") }, { 'g', "grep-friendly", NULL, gettext_noop("produce grep-friendly output (all results on one line per file)") }, { 'h', "help", NULL, @@ -557,7 +555,6 @@ main (int argc, char *argv[]) int defaultAll = YES; int bibtex = NO; int grepfriendly = NO; - char * binary = NULL; char * name; int ret = 0; EXTRACTOR_MetaDataProcessor processor = NULL; @@ -574,7 +571,6 @@ main (int argc, char *argv[]) while (1) { static struct option long_options[] = { - {"binary", 1, 0, 'B'}, {"bibtex", 0, 0, 'b'}, {"grep-friendly", 0, 0, 'g'}, {"help", 0, 0, 'h'}, @@ -610,9 +606,6 @@ main (int argc, char *argv[]) } processor = &print_bibtex; break; - case 'B': - binary = optarg; - break; case 'g': grepfriendly = YES; if (processor != NULL) @@ -729,40 +722,27 @@ main (int argc, char *argv[]) /* build list of libraries */ if (nodefault == NO) plugins = EXTRACTOR_plugin_add_defaults (in_process - ? EXTRACTOR_OPTION_NONE - : EXTRACTOR_OPTION_AUTO_RESTART); + ? EXTRACTOR_OPTION_IN_PROCESS + : EXTRACTOR_OPTION_DEFAULT_POLICY); else plugins = NULL; if (libraries != NULL) plugins = EXTRACTOR_plugin_add_config (plugins, libraries, in_process - ? EXTRACTOR_OPTION_NONE - : EXTRACTOR_OPTION_AUTO_RESTART); - if (binary != NULL) - { - name = malloc(strlen(binary) + strlen("printable_") + 1); - strcpy(name, "libextractor_printable_"); - strcat(name, binary); - plugins = EXTRACTOR_plugin_add_last(plugins, - name, - NULL, - in_process - ? EXTRACTOR_OPTION_NONE - : EXTRACTOR_OPTION_AUTO_RESTART); - free(name); - } + ? EXTRACTOR_OPTION_IN_PROCESS + : EXTRACTOR_OPTION_DEFAULT_POLICY); if (hash != NULL) { name = malloc(strlen(hash) + strlen("hash_") + 1); strcpy(name, "libextractor_hash_"); strcat(name, hash); - plugins = EXTRACTOR_plugin_add_last(plugins, - name, - NULL, - in_process - ? EXTRACTOR_OPTION_NONE - : EXTRACTOR_OPTION_AUTO_RESTART); + plugins = EXTRACTOR_plugin_add(plugins, + name, + NULL, + in_process + ? EXTRACTOR_OPTION_IN_PROCESS + : EXTRACTOR_OPTION_DEFAULT_POLICY); free(name); } diff --git a/src/main/extractor.c b/src/main/extractor.c @@ -105,6 +105,13 @@ struct EXTRACTOR_PluginList char * plugin_options; /** + * Special options for the plugin + * (as returned by the plugin's "options" method; + * typically NULL). + */ + const char *specials; + + /** * Flags to control how the plugin is executed. */ enum EXTRACTOR_Options flags; @@ -587,18 +594,22 @@ EXTRACTOR_plugin_add_defaults(enum EXTRACTOR_Options flags) * @param lib_handle library to search for the symbol * @param prefix prefix to add * @param sym_name base name for the symbol + * @param options set to special options requested by the plugin * @return NULL on error, otherwise pointer to the symbol */ static void * get_symbol_with_prefix(void *lib_handle, - const char *prefix) + const char *prefix, + const char **options) { char *name; void *symbol; const char *sym_name; char *sym; char *dot; + const char *(*opt_fun)(void); + *options = NULL; sym_name = strstr (prefix, "_"); if (sym_name == NULL) return NULL; @@ -611,7 +622,6 @@ get_symbol_with_prefix(void *lib_handle, sprintf(name, "_EXTRACTOR_%s_extract", sym); - free (sym); /* try without '_' first */ symbol = lt_dlsym(lib_handle, name + 1); if (symbol==NULL) @@ -636,7 +646,23 @@ get_symbol_with_prefix(void *lib_handle, free(first_error); #endif } + + if (symbol != NULL) + { + /* get special options */ + sprintf(name, + "_EXTRACTOR_%s_options", + sym); + /* try without '_' first */ + opt_fun = lt_dlsym(lib_handle, name + 1); + if (opt_fun == NULL) + opt_fun = lt_dlsym(lib_handle, name); + if (opt_fun != NULL) + *options = opt_fun (); + } + free (sym); free(name); + return symbol; } @@ -652,7 +678,8 @@ plugin_load (struct EXTRACTOR_PluginList *plugin) { lt_dladvise advise; - plugin->libname = find_plugin (plugin->short_libname); + if (plugin->libname == NULL) + plugin->libname = find_plugin (plugin->short_libname); if (plugin->libname == NULL) { #if DEBUG @@ -683,7 +710,8 @@ plugin_load (struct EXTRACTOR_PluginList *plugin) return -1; } plugin->extractMethod = get_symbol_with_prefix (plugin->libraryHandle, - plugin->libname); + plugin->libname, + &plugin->specials); if (plugin->extractMethod == NULL) { #if DEBUG @@ -738,47 +766,6 @@ EXTRACTOR_plugin_add (struct EXTRACTOR_PluginList * prev, /** - * Add a library for keyword extraction at the END of the list. - * @param prev the previous list of libraries, may be NULL - * @param library the name of the library - * @param options options to give to the library - * @param flags options to use - * @return the new list of libraries, always equal to prev - * except if prev was NULL and no error occurs - */ -struct EXTRACTOR_PluginList * -EXTRACTOR_plugin_add_last(struct EXTRACTOR_PluginList *prev, - const char *library, - const char *options, - enum EXTRACTOR_Options flags) -{ - struct EXTRACTOR_PluginList *result; - struct EXTRACTOR_PluginList *pos; - char *libname; - - libname = find_plugin (library); - if (libname == NULL) - return prev; - result = calloc (1, sizeof (struct EXTRACTOR_PluginList)); - result->next = prev; - result->short_libname = strdup (library); - result->libname = libname; - result->flags = flags; - if( options ) - result->plugin_options = strdup (options); - else - result->plugin_options = NULL; - if (prev == NULL) - return result; - pos = prev; - while (pos->next != NULL) - pos = pos->next; - pos->next = result; - return prev; -} - - -/** * Load multiple libraries as specified by the user. * * @param config a string given by the user that defines which @@ -786,8 +773,8 @@ EXTRACTOR_plugin_add_last(struct EXTRACTOR_PluginList *prev, * "[[-]LIBRARYNAME[(options)][:[-]LIBRARYNAME[(options)]]]*". * For example, 'mp3:ogg.so' loads the * mp3 and the ogg library. The '-' before the LIBRARYNAME - * indicates that the library should be added to the end - * of the library list (addLibraryLast). + * indicates that the library should be removed from + * the library list. * @param prev the previous list of libraries, may be NULL * @param flags options to use * @return the new list of libraries, equal to prev iff an error occured @@ -836,10 +823,8 @@ EXTRACTOR_plugin_add_config (struct EXTRACTOR_PluginList * prev, if (cpy[last] == '-') { last++; - prev = EXTRACTOR_plugin_add_last (prev, - &cpy[last], - (lastconf != -1) ? &cpy[lastconf] : NULL, - flags); + prev = EXTRACTOR_plugin_remove (prev, + &cpy[last]); } else { @@ -1049,8 +1034,6 @@ transmit_reply (void *cls, } - - /** * 'main' function of the child process. * Reads shm-filenames from 'in' (line-by-line) and @@ -1084,6 +1067,19 @@ process_requests (struct EXTRACTOR_PluginList *plugin, #endif return; } + if ( (plugin->specials != NULL) && + (NULL != strstr (plugin->specials, + "close-stderr")) ) + { + close (2); + } + if ( (plugin->specials != NULL) && + (NULL != strstr (plugin->specials, + "close-stdout")) ) + { + close (1); + } + memset (&hdr, 0, sizeof (hdr)); fin = fdopen (in, "r"); while (NULL != fgets (fn, sizeof(fn), fin)) @@ -1109,6 +1105,14 @@ process_requests (struct EXTRACTOR_PluginList *plugin, munmap (ptr, size); if (-1 != shmid) close (shmid); + if ( (plugin->specials != NULL) && + (NULL != strstr (plugin->specials, + "force-kill")) ) + { + /* we're required to die after each file since this + plugin only supports a single file at a time */ + _exit (0); + } } fclose (fin); close (out); @@ -1195,7 +1199,7 @@ extract_oop (struct EXTRACTOR_PluginList *plugin, { stop_process (plugin); plugin->cpid = -1; - if (plugin->flags != EXTRACTOR_OPTION_AUTO_RESTART) + if (plugin->flags != EXTRACTOR_OPTION_DEFAULT_POLICY) plugin->flags = EXTRACTOR_OPTION_DISABLED; return 0; } @@ -1208,7 +1212,7 @@ extract_oop (struct EXTRACTOR_PluginList *plugin, { stop_process (plugin); plugin->cpid = -1; - if (plugin->flags != EXTRACTOR_OPTION_AUTO_RESTART) + if (plugin->flags != EXTRACTOR_OPTION_DEFAULT_POLICY) plugin->flags = EXTRACTOR_OPTION_DISABLED; return 0; } @@ -1221,7 +1225,7 @@ extract_oop (struct EXTRACTOR_PluginList *plugin, { stop_process (plugin); plugin->cpid = -1; - if (plugin->flags != EXTRACTOR_OPTION_AUTO_RESTART) + if (plugin->flags != EXTRACTOR_OPTION_DEFAULT_POLICY) plugin->flags = EXTRACTOR_OPTION_DISABLED; return 0; } @@ -1241,7 +1245,7 @@ extract_oop (struct EXTRACTOR_PluginList *plugin, stop_process (plugin); plugin->cpid = -1; free (data); - if (plugin->flags != EXTRACTOR_OPTION_AUTO_RESTART) + if (plugin->flags != EXTRACTOR_OPTION_DEFAULT_POLICY) plugin->flags = EXTRACTOR_OPTION_DISABLED; return 0; } @@ -1294,19 +1298,19 @@ extract (struct EXTRACTOR_PluginList *plugins, { switch (ppos->flags) { - case EXTRACTOR_OPTION_NONE: - break; - case EXTRACTOR_OPTION_OUT_OF_PROCESS: - if (0 == ppos->cpid) + case EXTRACTOR_OPTION_DEFAULT_POLICY: + if ( (0 == ppos->cpid) || + (-1 == ppos->cpid) ) start_process (ppos); want_shm = 1; break; - case EXTRACTOR_OPTION_AUTO_RESTART: - if ( (0 == ppos->cpid) || - (-1 == ppos->cpid) ) + case EXTRACTOR_OPTION_OUT_OF_PROCESS_NO_RESTART: + if (0 == ppos->cpid) start_process (ppos); want_shm = 1; break; + case EXTRACTOR_OPTION_IN_PROCESS: + break; case EXTRACTOR_OPTION_DISABLED: break; } @@ -1342,13 +1346,30 @@ extract (struct EXTRACTOR_PluginList *plugins, { flags = ppos->flags; if (shmid == -1) - flags = EXTRACTOR_OPTION_NONE; + flags = EXTRACTOR_OPTION_IN_PROCESS; switch (flags) { - case EXTRACTOR_OPTION_NONE: + case EXTRACTOR_OPTION_DEFAULT_POLICY: + if (0 != extract_oop (ppos, fn, proc, proc_cls)) + return; + if (ppos->cpid == -1) + { + start_process (ppos); + if (0 != extract_oop (ppos, fn, proc, proc_cls)) + return; + } + break; + case EXTRACTOR_OPTION_OUT_OF_PROCESS_NO_RESTART: + if (0 != extract_oop (ppos, fn, proc, proc_cls)) + return; + break; + case EXTRACTOR_OPTION_IN_PROCESS: if (NULL == ppos->extractMethod) plugin_load (ppos); - if ( (NULL != ppos->extractMethod) && + if ( ( (ppos->specials == NULL) || + (NULL == strstr (ppos->specials, + "oop-only")) ) && + (NULL != ppos->extractMethod) && (0 != ppos->extractMethod (data, size, proc, @@ -1356,11 +1377,6 @@ extract (struct EXTRACTOR_PluginList *plugins, ppos->plugin_options)) ) return; break; - case EXTRACTOR_OPTION_OUT_OF_PROCESS: - case EXTRACTOR_OPTION_AUTO_RESTART: - if (0 != extract_oop (ppos, fn, proc, proc_cls)) - return; - break; case EXTRACTOR_OPTION_DISABLED: break; } diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am @@ -394,23 +394,3 @@ libextractor_zip_la_LDFLAGS = \ $(PLUGINFLAGS) EXTRA_DIST = template_extractor.c - - -install-exec-hook: - mkdir -p $(DESTDIR)$(plugindir) &> /dev/null || true - rm -f $(DESTDIR)$(plugindir)/libextractor_thumbnail$(LIBEXT) - rm -f $(DESTDIR)$(plugindir)/libextractor_thumbnail.la - if test "$(thumbgtk)" != ""; then \ - $(LN_S) $(plugindir)/libextractor_thumbnailgtk$(LIBEXT) \ - $(DESTDIR)$(plugindir)/libextractor_thumbnail$(LIBEXT); \ - else \ - if test "$(thumbqt)" != ""; then \ - $(LN_S) $(plugindir)/libextractor_thumbnailqt$(LIBEXT) \ - $(DESTDIR)$(plugindir)/libextractor_thumbnail$(LIBEXT); \ - else \ - if test "$(thumbffmpeg)" != ""; then \ - $(LN_S) $(plugindir)/libextractor_thumbnailffmpeg$(LIBEXT) \ - $(DESTDIR)$(plugindir)/libextractor_thumbnail$(LIBEXT); \ - fi \ - fi \ - fi diff --git a/src/plugins/mime_extractor.c b/src/plugins/mime_extractor.c @@ -265,7 +265,7 @@ static Pattern patterns[] = { {"moov", 4, "video/quicktime", DEFAULT}, {"mdat", 4, "video/quicktime", DEFAULT}, {"\x8aMNG", 4, "video/x-mng", DEFAULT}, - {"\x30\x26\xb2\x75\x8e\x66", 6, "video/asf", DEFAULT}, /* same as .wmv ? */ + {"\x30\x26\xb2\x75\x8e\x66", 6, "video/x-ms-asf", DEFAULT}, /* same as .wmv ? */ {"FWS", 3, "application/x-shockwave-flash", DEFAULT}, {"MThd", 4, "audio/midi", DEFAULT}, {"ID3", 3, "audio/mpeg", DEFAULT}, diff --git a/src/plugins/thumbnailffmpeg_extractor.c b/src/plugins/thumbnailffmpeg_extractor.c @@ -44,287 +44,25 @@ #define DEBUG 0 -struct StreamDescriptor -{ - const uint8_t *data; - size_t offset; - size_t size; -}; - - void __attribute__ ((constructor)) ffmpeg_lib_init (void) { -#if DEBUG - printf ("av_register_all()\n"); -#endif av_register_all (); } -static int -stream_read (void *opaque, uint8_t * buf, int buf_size) -{ - struct StreamDescriptor *rs = (struct StreamDescriptor *) opaque; - size_t len; -#if DEBUG - printf ("read_packet: %zu\n", buf_size); -#endif - if (rs) - { - if (rs->data == NULL) - return -1; - if (rs->offset >= rs->size) - return 0; - len = buf_size; - if (rs->offset + len > rs->size) - len = rs->size - rs->offset; - - memcpy (buf, rs->data + rs->offset, len); - rs->offset += len; -#if DEBUG - printf ("read_packet: len: %zu\n", len); -#endif - return len; - } - return -1; -} - -static int64_t -stream_seek (void *opaque, int64_t offset, int whence) -{ - struct StreamDescriptor *rs = (struct StreamDescriptor *) opaque; - int64_t off_abs; -#if DEBUG - printf ("my_seek: %lld %d\n", offset, whence); -#endif - if (rs) - { - if (whence == AVSEEK_SIZE) - return (int64_t) rs->size; - else if (whence == SEEK_CUR) - off_abs = (int64_t) rs->offset + offset; - else if (whence == SEEK_SET) - off_abs = offset; - else if (whence == SEEK_END) - off_abs = (int64_t) rs->size + offset; - else - { - printf ("whence error %d\n", whence); - abort (); - return AVERROR (EINVAL); - } - if (off_abs >= 0 && off_abs < (int64_t) rs->size) - rs->offset = (size_t) off_abs; - else - off_abs = AVERROR (EINVAL); - return off_abs; - } - return -1; -} - -struct MimeToDecoderMapping -{ - const char *mime_type; - enum CodecID codec_id; -}; - -/* map mime image types to a decoder */ -static const struct MimeToDecoderMapping m2d_map[] = { - {"image/x-bmp", CODEC_ID_BMP}, - {"image/gif", CODEC_ID_GIF}, - {"image/jpeg", CODEC_ID_MJPEG}, - {"image/jpeg-proprietary", CODEC_ID_MJPEG}, - {"image/png", CODEC_ID_PNG}, - {"image/x-png", CODEC_ID_PNG}, - {"image/tiff", CODEC_ID_TIFF}, - {"image/x-portable-pixmap", CODEC_ID_PPM}, -#if DOES_THIS_WORK - { "video/mpeg", CODEC_ID_MPEG2VIDEO }, - { "video/x-flv", CODEC_ID_FLV1 }, - { "video/x-msvideo", CODEC_ID_MSVIDEO1 }, - { "video/asf", CODEC_ID_WMV1 /* or is this WMV2? */ }, -#endif - {NULL, CODEC_ID_NONE} -}; - -#define PROBE_MAX (1<<20) -#define BIOBUF_SIZE (64*1024) #define THUMBSIZE 128 /* max dimension in pixels */ #define MAX_THUMB_SIZE (100*1024) /* in bytes */ -/* ******** mime type detection code (copied from mime_extractor) ************* */ - - -/** - * Detect a file-type. - * @param data the contents of the file - * @param len the length of the file - * @param arg closure... - * @return 0 if the file does not match, 1 if it does - **/ -typedef int (*Detector) (const char *data, size_t len, void *arg); - -/** - * Detect a file-type. - * @param data the contents of the file - * @param len the length of the file - * @return always 1 - **/ -static int -defaultDetector (const char *data, size_t len, void *arg) +const char * +EXTRACTOR_thumbnailffmpeg_options () { - return 1; + return "force-kill;oop-only;close-stderr"; } -/** - * Detect a file-type. - * @param data the contents of the file - * @param len the length of the file - * @return always 0 - **/ -static int -disableDetector (const char *data, size_t len, void *arg) +const char * +EXTRACTOR_thumbnail_options () { - return 0; -} - -typedef struct ExtraPattern -{ - int pos; - int len; - const char *pattern; -} ExtraPattern; - -/** - * Define special matching rules for complicated formats... - **/ -static ExtraPattern xpatterns[] = { -#define AVI_XPATTERN 0 - {8, 4, "AVI "}, - {0, 0, NULL}, -#define WAVE_XPATTERN 2 - {8, 4, "WAVE"}, - {0, 0, NULL}, -#define ACON_XPATTERN 12 - {8, 4, "ACON"}, - {0, 0, NULL}, -#define CR2_PATTERN 14 - {8, 3, "CR\x02"}, - {0, 0, NULL}, -}; - -/** - * Detect AVI. A pattern matches if all XPatterns until the next {0, - * 0, NULL} slot match. OR-ing patterns can be achieved using multiple - * entries in the main table, so this "AND" (all match) semantics are - * the only reasonable answer. - **/ -static int -xPatternMatcher (const char *data, size_t len, void *cls) -{ - ExtraPattern *arg = cls; - - while (arg->pattern != NULL) - { - if (arg->pos + arg->len > len) - return 0; - if (0 != memcmp (&data[arg->pos], arg->pattern, arg->len)) - return 0; - arg++; - } - return 1; -} - -/** - * Use this detector, if the simple header-prefix matching is - * sufficient. - */ -#define DEFAULT &defaultDetector, NULL - -/** - * Use this detector, to disable the mime-type (effectively comment it - * out). - */ -#define DISABLED &disableDetector, NULL - -/** - * Select an entry in xpatterns for matching - */ -#define XPATTERN(a) &xPatternMatcher, &xpatterns[(a)] - - -typedef struct Pattern -{ - const char *pattern; - int size; - const char *mimetype; - Detector detector; - void *arg; -} Pattern; - - -static Pattern patterns[] = { - /* FIXME: add patterns for other mime-types - supported by ffmpeg (also add those to - the mime extractor itself!) */ - {"\xFF\xD8", 2, "image/jpeg", DEFAULT}, - {"\211PNG\r\n\032\n", 8, "image/png", DEFAULT}, - {"/* XPM */", 9, "image/x-xpm", DEFAULT}, - {"GIF8", 4, "image/gif", DEFAULT}, - {"P1", 2, "image/x-portable-bitmap", DEFAULT}, - {"P2", 2, "image/x-portable-graymap", DEFAULT}, - {"P3", 2, "image/x-portable-pixmap", DEFAULT}, - {"P4", 2, "image/x-portable-bitmap", DEFAULT}, - {"P5", 2, "image/x-portable-graymap", DEFAULT}, - {"P6", 2, "image/x-portable-pixmap", DEFAULT}, - {"P7", 2, "image/x-portable-anymap", DEFAULT}, - {"BM", 2, "image/x-bmp", DEFAULT}, - {"\x89PNG", 4, "image/x-png", DEFAULT}, - {"hsi1", 4, "image/x-jpeg-proprietary", DEFAULT}, - {"FLV", 3, "video/x-flv", DEFAULT}, - {"\x2E\x52\x4d\x46", 4, "video/real", DEFAULT}, - {"IIN1", 4, "image/tiff", DEFAULT}, - {"MM\x00\x2a", 4, "image/tiff", DEFAULT}, /* big-endian */ - {"II\x2a\x00", 4, "image/tiff", DEFAULT}, /* little-endian */ - {"RIFF", 4, "video/x-msvideo", XPATTERN (AVI_XPATTERN)}, - {"RIFX", 4, "video/x-msvideo", XPATTERN (AVI_XPATTERN)}, - {"\x00\x00\x01\xb3", 4, "video/mpeg", DEFAULT}, - {"\x00\x00\x01\xba", 4, "video/mpeg", DEFAULT}, - - /* FIXME: find out if ffmpeg actually makes sense for those below, - and add those for which it does make sense to the m2d_map! */ - {"moov", 4, "video/quicktime", DEFAULT}, - {"mdat", 4, "video/quicktime", DEFAULT}, - {"\x8aMNG", 4, "video/x-mng", DEFAULT}, - {"\x30\x26\xb2\x75\x8e\x66", 6, "video/asf", DEFAULT}, /* same as .wmv ? */ - {"FWS", 3, "application/x-shockwave-flash", DEFAULT}, - {NULL, 0, NULL, DISABLED} -}; - - - -static const char * -find_mime (const char *data, - size_t size) -{ - int i; - - i = 0; - while (patterns[i].pattern != NULL) - { - if (size < patterns[i].size) - { - i++; - continue; - } - if (0 == memcmp (patterns[i].pattern, data, patterns[i].size)) - { - if (patterns[i].detector (data, size, patterns[i].arg)) - return patterns[i].mimetype; - } - i++; - } - return NULL; + return "force-kill;oop-only;close-stderr"; } @@ -335,210 +73,131 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, void *proc_cls, const char *options) { - int score; - AVInputFormat *fmt; - AVProbeData pdat; - ByteIOContext *bio_ctx = NULL; - uint8_t *bio_buffer; - struct StreamDescriptor reader_state; - AVFormatContext *format_ctx = NULL; - AVCodecContext *codec_ctx = NULL; + AVProbeData pd; AVPacket packet; - int video_stream_index; - AVCodec *codec; + AVInputFormat *m_pInputFormat; + ByteIOContext *bio; + struct AVFormatContext *fmt; + AVCodecContext *codec_ctx; + AVCodec *codec = NULL; AVFrame *frame = NULL; AVFrame *thumb_frame = NULL; - int64_t ts; - - struct SwsContext *scaler_ctx; + uint8_t *encoder_output_buffer = NULL; + AVCodecContext *enc_codec_ctx = NULL; + AVCodec *enc_codec = NULL; + const AVFrame *tframe; + struct SwsContext *scaler_ctx = NULL; + uint8_t *thumb_buffer = NULL; int sws_flags = SWS_BILINEAR; - uint8_t *thumb_buffer; - int thumb_width, thumb_height; - int sar_num, sar_den; - - uint8_t *encoder_output_buffer; + int64_t ts; size_t encoder_output_buffer_size; - AVCodecContext *enc_codec_ctx; - AVCodec *enc_codec; - + int video_stream_index; + int thumb_width; + int thumb_height; + int sar_num; + int sar_den; int i; int err; - int ret = 0; int frame_finished; - - const char *mime; - int is_image; - enum CodecID image_codec_id; - - bio_ctx = NULL; - bio_buffer = NULL; - format_ctx = NULL; - codec = NULL; - frame = NULL; - thumb_frame = NULL; - thumb_buffer = NULL; - scaler_ctx = NULL; - encoder_output_buffer = NULL; - enc_codec = NULL; - enc_codec_ctx = NULL; - - is_image = 0; - - mime = find_mime ((const char*) data, size); - if (mime == NULL) - return 0; - if (mime != NULL) - { - i = 0; - while (m2d_map[i].mime_type != NULL) - { - if (!strcmp (m2d_map[i].mime_type, mime)) - { - is_image = 1; - image_codec_id = m2d_map[i].codec_id; - break; - } - i++; - } - } + int ret = 0; #if DEBUG - printf ("is_image: %d codec:%d\n", is_image, image_codec_id); + fprintf (stderr, + "ffmpeg starting\n"); #endif - if (!is_image) + pd.buf_size = size; + pd.buf = (void *) data; + pd.filename = "__url_prot"; + + if (NULL == (m_pInputFormat = av_probe_input_format(&pd, 1))) { - pdat.filename = NULL; - pdat.buf = (unsigned char *) data; - pdat.buf_size = (size > PROBE_MAX) ? PROBE_MAX : size; - - fmt = av_probe_input_format (&pdat, 1); - if (fmt == NULL) - return 0; #if DEBUG - printf ("format %p [%s] [%s]\n", fmt, fmt->name, fmt->long_name); + fprintf (stderr, + "Failed to probe input format\n"); #endif - pdat.buf = (unsigned char *) data; - pdat.buf_size = size > PROBE_MAX ? PROBE_MAX : size; - score = fmt->read_probe (&pdat); -#if DEBUG - printf ("score: %d\n", score); -#endif - /*if (score < 50) return 0; */ + return 0; } - - if (is_image) + m_pInputFormat->flags |= AVFMT_NOFILE; + bio = NULL; + url_open_buf(&bio, pd.buf, pd.buf_size, URL_RDONLY); + bio->is_streamed = 1; + if ((av_open_input_stream(&fmt, bio, pd.filename, m_pInputFormat, NULL)) < 0) { - codec_ctx = avcodec_alloc_context (); - codec = avcodec_find_decoder (image_codec_id); - if (codec != NULL) - { - if (avcodec_open (codec_ctx, codec) != 0) - { -#if DEBUG - printf ("open codec failed\n"); -#endif - codec = NULL; - } - } + url_close_buf (bio); + fprintf (stderr, + "Failed to open input stream\n"); + return 0; } - else + if (0 > av_find_stream_info (fmt)) { - bio_ctx = malloc (sizeof (ByteIOContext)); - bio_buffer = malloc (BIOBUF_SIZE); - - reader_state.data = data; - reader_state.offset = 0; - reader_state.size = size; - - init_put_byte (bio_ctx, bio_buffer, - BIOBUF_SIZE, 0, &reader_state, - stream_read, NULL, stream_seek); - - fmt->flags |= AVFMT_NOFILE; - err = av_open_input_stream (&format_ctx, bio_ctx, "", fmt, NULL); - if (err < 0) - { -#if DEBUG - printf ("couldn't open input stream\n"); -#endif - goto out; - } - - err = av_find_stream_info (format_ctx); - if (err < 0) - { -#if DEBUG - printf ("couldn't find codec params\n"); -#endif - goto out; - } - - for (i = 0; i < format_ctx->nb_streams; i++) - { - codec_ctx = format_ctx->streams[i]->codec; - if (codec_ctx->codec_type == CODEC_TYPE_VIDEO) - { - video_stream_index = i; - codec = avcodec_find_decoder (codec_ctx->codec_id); - if (codec == NULL) - { -#if DEBUG - printf ("find_decoder failed\n"); -#endif - break; - } - err = avcodec_open (codec_ctx, codec); - if (err != 0) - { -#if DEBUG - printf ("failed to open codec\n"); -#endif - codec = NULL; - } - break; - } - } + av_close_input_stream (fmt); + url_close_buf (bio); + fprintf (stderr, + "Failed to read stream info\n"); + return 0; } - if (codec_ctx == NULL || codec == NULL) + codec_ctx = NULL; + for (i=0; i<fmt->nb_streams; i++) { -#if DEBUG - printf ("failed to open codec"); -#endif - goto out; + codec_ctx = fmt->streams[i]->codec; + if (codec_ctx->codec_type != CODEC_TYPE_VIDEO) + continue; + codec = avcodec_find_decoder (codec_ctx->codec_id); + if (codec == NULL) + continue; + err = avcodec_open (codec_ctx, codec); + if (err != 0) + { + codec = NULL; + continue; + } + video_stream_index = i; + break; + } + if ( (codec_ctx == NULL) || + (codec == NULL) || + (codec_ctx->width == 0) || + (codec_ctx->height == 0) ) + { + if (codec_ctx != NULL) + avcodec_close (codec_ctx); + av_close_input_stream (fmt); + url_close_buf (bio); + fprintf (stderr, + "No video codec found\n"); + return 0; } + frame = avcodec_alloc_frame (); if (frame == NULL) { -#if DEBUG - printf ("failed to alloc frame"); -#endif - goto out; + if (codec != NULL) + avcodec_close (codec_ctx); + av_close_input_stream (fmt); + url_close_buf (bio); + fprintf (stderr, + "Failed to allocate frame\n"); + return 0; } - - if (!is_image) - { -#if DEBUG - printf ("duration: %lld\n", format_ctx->duration); - if (format_ctx->duration == AV_NOPTS_VALUE) - printf ("duration unknown\n"); -#endif - /* TODO: if duration is known seek to to some better place(?) */ - ts = 10; // s - ts = ts * AV_TIME_BASE; - err = av_seek_frame (format_ctx, -1, ts, 0); - if (err >= 0) - { - avcodec_flush_buffers (codec_ctx); - } #if DEBUG - else - printf ("seeking failed %d\n", err); + if (fmt->duration == AV_NOPTS_VALUE) + fprintf (stderr, + "duration unknown\n"); + else + fprintf (stderr, + "duration: %lld\n", + fmt->duration); #endif - } - + /* TODO: if duration is known seek to to some better place(?) */ + ts = 10; /* s */ + ts = ts * AV_TIME_BASE; + err = av_seek_frame (fmt, -1, ts, 0); + if (err >= 0) + avcodec_flush_buffers (codec_ctx); frame_finished = 0; - if (is_image) + + if (0 /* is_image */) { avcodec_decode_video (codec_ctx, frame, &frame_finished, data, size); } @@ -546,7 +205,7 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, { while (1) { - err = av_read_frame (format_ctx, &packet); + err = av_read_frame (fmt, &packet); if (err < 0) break; if (packet.stream_index == video_stream_index) @@ -564,9 +223,16 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, av_free_packet (&packet); } } - - if (!frame_finished || codec_ctx->width == 0 || codec_ctx->height == 0) - goto out; + if (!frame_finished) + { + if (frame != NULL) + av_free (frame); + av_close_input_stream (fmt); + free (bio); + fprintf (stderr, + "Failed to seek to frame\n"); + return 0; + } sar_num = codec_ctx->sample_aspect_ratio.num; sar_den = codec_ctx->sample_aspect_ratio.den; @@ -575,61 +241,83 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, sar_num = 1; sar_den = 1; } - if ((codec_ctx->width * sar_num) / sar_den > codec_ctx->height) + if ( (codec_ctx->width < THUMBSIZE) && + (codec_ctx->height < THUMBSIZE) ) { - thumb_width = THUMBSIZE; - thumb_height = (thumb_width * codec_ctx->height) / - ((codec_ctx->width * sar_num) / sar_den); + /* no resize */ + thumb_width = codec_ctx->width; + thumb_height = codec_ctx->height; + tframe = frame; } else { - thumb_height = THUMBSIZE; - thumb_width = (thumb_height * - ((codec_ctx->width * sar_num) / sar_den)) / - codec_ctx->height; - } - if (thumb_width < 8) - thumb_width = 8; - if (thumb_height < 1) - thumb_height = 1; + /* need resize */ + if ((codec_ctx->width * sar_num) / sar_den > codec_ctx->height) + { + thumb_width = THUMBSIZE; + thumb_height = (thumb_width * codec_ctx->height) / + ((codec_ctx->width * sar_num) / sar_den); + } + else + { + thumb_height = THUMBSIZE; + thumb_width = (thumb_height * + ((codec_ctx->width * sar_num) / sar_den)) / + codec_ctx->height; + } + if (thumb_width < 8) + thumb_width = 8; + if (thumb_height < 1) + thumb_height = 1; #if DEBUG - printf ("thumb dim: %d %d\n", thumb_width, thumb_height); + fprintf (stderr, + "thumb dim: %d %d\n", + thumb_width, + thumb_height); #endif - scaler_ctx = - sws_getContext (codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, - thumb_width, thumb_height, PIX_FMT_RGB24, sws_flags, NULL, - NULL, NULL); - if (scaler_ctx == NULL) - { + scaler_ctx = + sws_getContext (codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, + thumb_width, thumb_height, PIX_FMT_RGB24, sws_flags, NULL, + NULL, NULL); + if (scaler_ctx == NULL) + { #if DEBUG - printf ("failed to alloc scaler\n"); + fprintf (stderr, + "failed to alloc scaler\n"); #endif - goto out; - } - thumb_frame = avcodec_alloc_frame (); - thumb_buffer = - av_malloc (avpicture_get_size (PIX_FMT_RGB24, thumb_width, thumb_height)); - if (thumb_frame == NULL || thumb_buffer == NULL) - { + goto out; + } + thumb_frame = avcodec_alloc_frame (); + thumb_buffer = + av_malloc (avpicture_get_size (PIX_FMT_RGB24, thumb_width, thumb_height)); + if (thumb_frame == NULL || thumb_buffer == NULL) + { #if DEBUG - printf ("failed to alloc thumb frame\n"); + fprintf (stderr, + "failed to alloc thumb frame\n"); #endif - goto out; + goto out; + } + avpicture_fill ((AVPicture *) thumb_frame, thumb_buffer, + PIX_FMT_RGB24, thumb_width, thumb_height); + + sws_scale (scaler_ctx, + frame->data, + frame->linesize, + 0, codec_ctx->height, + thumb_frame->data, + thumb_frame->linesize); + tframe = thumb_frame; } - avpicture_fill ((AVPicture *) thumb_frame, thumb_buffer, - PIX_FMT_RGB24, thumb_width, thumb_height); - - sws_scale (scaler_ctx, - frame->data, frame->linesize, - 0, codec_ctx->height, thumb_frame->data, thumb_frame->linesize); encoder_output_buffer_size = MAX_THUMB_SIZE; encoder_output_buffer = av_malloc (encoder_output_buffer_size); if (encoder_output_buffer == NULL) { #if DEBUG - printf ("couldn't alloc encoder output buf\n"); + fprintf (stderr, + "couldn't alloc encoder output buf\n"); #endif goto out; } @@ -638,7 +326,8 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, if (enc_codec == NULL) { #if DEBUG - printf ("couldn't find encoder\n"); + fprintf (stderr, + "couldn't find encoder\n"); #endif goto out; } @@ -650,7 +339,8 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, if (avcodec_open (enc_codec_ctx, enc_codec) < 0) { #if DEBUG - printf ("couldn't open encoder\n"); + fprintf (stderr, + "couldn't open encoder\n"); #endif enc_codec = NULL; goto out; @@ -658,7 +348,7 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, err = avcodec_encode_video (enc_codec_ctx, encoder_output_buffer, - encoder_output_buffer_size, thumb_frame); + encoder_output_buffer_size, tframe); if (err > 0) ret = proc (proc_cls, "thumbnailffmpeg", @@ -676,21 +366,18 @@ out: av_free (encoder_output_buffer); if (scaler_ctx != NULL) sws_freeContext(scaler_ctx); - if (codec != NULL) + if (codec_ctx != NULL) avcodec_close (codec_ctx); - if (format_ctx != NULL) - av_close_input_file (format_ctx); + if (fmt != NULL) + av_close_input_stream (fmt); if (frame != NULL) av_free (frame); if (thumb_buffer != NULL) av_free (thumb_buffer); if (thumb_frame != NULL) av_free (thumb_frame); - if (bio_ctx != NULL) - free (bio_ctx); - if (bio_buffer != NULL) - free (bio_buffer); - + if (bio != NULL) + url_close_buf (bio); return ret; }