libextractor

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

commit 3119b616921f4976264ddf6a9fd9accb2d61e334
parent f5ec62c32156a5bb3aa6cf4f648139d14af55a42
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 20 May 2026 00:54:39 +0200

fix up VLC plugin (#2075)

Diffstat:
Mconfigure.ac | 13+++++++++++++
Msrc/plugins/Makefile.am | 19++++++++++++-------
Msrc/plugins/archive_extractor.c | 3+++
Msrc/plugins/flac_extractor.c | 3+++
Msrc/plugins/gif_extractor.c | 3+++
Msrc/plugins/html_extractor.c | 369++-----------------------------------------------------------------------------
Msrc/plugins/riff_extractor.c | 9+++++++--
Msrc/plugins/s3m_extractor.c | 3+++
Msrc/plugins/sid_extractor.c | 3+++
Msrc/plugins/vlc_extractor.c | 429++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/plugins/wav_extractor.c | 3+++
Msrc/plugins/xm_extractor.c | 3+++
12 files changed, 333 insertions(+), 527 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -345,6 +345,13 @@ AC_CHECK_LIB(rpm, rpmReadPackageFile, AM_CONDITIONAL(HAVE_LIBRPM, false))], AM_CONDITIONAL(HAVE_LIBRPM, false)) +AC_CHECK_LIB(vlc, libvlc_media_player_play, + [AC_CHECK_HEADERS([vlc/vlc.h], + AM_CONDITIONAL(HAVE_LIBVLC, true) + AC_DEFINE(HAVE_LIBVLC,1,[Have libvlc]), + AM_CONDITIONAL(HAVE_LIBVLC, false))], + AM_CONDITIONAL(HAVE_LIBVLC, false)) + AC_CHECK_LIB(mpeg2, mpeg2_init, [AC_CHECK_HEADERS([mpeg2dec/mpeg2.h], AM_CONDITIONAL(HAVE_MPEG2, true) @@ -753,6 +760,12 @@ AS_IF([test "x$HAVE_SMF_TRUE" = "x#"], AS_IF([test "x$HAVE_MPEG2_TRUE" = "x#"], [AC_MSG_NOTICE([NOTICE: libmpeg2 not found, mpeg2 support disabled])]) +AS_IF([test "x$HAVE_VLC_TRUE" = "x#"], + [AC_MSG_NOTICE([NOTICE: libvlc not found, libvlc support disabled])]) + +AS_IF([test "x$HAVE_RPM_TRUE" = "x#"], + [AC_MSG_NOTICE([NOTICE: librpm not found, rpm support disabled])]) + AS_IF([test "x$HAVE_CXX" != "xyes"], [AC_MSG_NOTICE([NOTICE: no C++ compiler found (not compiling plugins that require C++)])]) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am @@ -162,6 +162,10 @@ TEST_ZLIB=test_deb TEST_QT=test_qt endif +if HAVE_LIBVLC +PLUGIN_VLC= \ + libextractor_vlc.la +endif plugin_LTLIBRARIES = \ libextractor_dvi.la \ @@ -192,6 +196,7 @@ plugin_LTLIBRARIES = \ $(PLUGIN_OGG) \ $(PLUGIN_PDF) \ $(PLUGIN_RPM) \ + $(PLUGIN_VLC) \ $(PLUGIN_TIFF) \ $(PLUGIN_ZLIB) @@ -674,13 +679,13 @@ test_tiff_LDADD = \ $(top_builddir)/src/plugins/libtest.la -#libextractor_vlc_la_SOURCES = \ -# vlc_extractor.c -#libextractor_vlc_la_LDFLAGS = \ -# $(PLUGINFLAGS) -#libextractor_vlc_la_LIBADD = \ -# -lvlc \ -# $(XLIB) +libextractor_vlc_la_SOURCES = \ + vlc_extractor.c +libextractor_vlc_la_LDFLAGS = \ + $(PLUGINFLAGS) +libextractor_vlc_la_LIBADD = \ + -lvlc \ + $(XLIB) libextractor_wav_la_SOURCES = \ diff --git a/src/plugins/archive_extractor.c b/src/plugins/archive_extractor.c @@ -89,6 +89,9 @@ skip_cb (struct archive *a, * @param ec extraction context provided to the plugin */ void +EXTRACTOR_archive_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_archive_extract_method (struct EXTRACTOR_ExtractContext *ec) { struct archive *a; diff --git a/src/plugins/flac_extractor.c b/src/plugins/flac_extractor.c @@ -438,6 +438,9 @@ flac_error (const FLAC__StreamDecoder *decoder, * @param ec extraction context provided to the plugin */ void +EXTRACTOR_flac_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_flac_extract_method (struct EXTRACTOR_ExtractContext *ec) { FLAC__StreamDecoder *decoder; diff --git a/src/plugins/gif_extractor.c b/src/plugins/gif_extractor.c @@ -60,6 +60,9 @@ gif_READ_func (GifFileType *ft, * @param ec extraction context provided to the plugin */ void +EXTRACTOR_gif_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_gif_extract_method (struct EXTRACTOR_ExtractContext *ec) { GifFileType *gif_file; diff --git a/src/plugins/html_extractor.c b/src/plugins/html_extractor.c @@ -179,6 +179,9 @@ eof_cb (void *sourceData) * @param ec extraction context provided to the plugin */ void +EXTRACTOR_html_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_html_extract_method (struct EXTRACTOR_ExtractContext *ec) { TidyDoc doc; @@ -318,370 +321,13 @@ CLEANUP: } -#if OLD - - -/* ******************** parser helper functions ************** */ - -static int -tagMatch (const char *tag, const char *s, const char *e) -{ - return (((e - s) == strlen (tag)) && (0 == strncasecmp (tag, s, e - s))); -} - - -static int -lookFor (char c, size_t *pos, const char *data, size_t size) -{ - size_t p = *pos; - - while ((p < size) && (data[p] != c)) - { - if (data[p] == '\0') - return 0; - p++; - } - *pos = p; - return p < size; -} - - -static int -skipWhitespace (size_t *pos, const char *data, size_t size) -{ - size_t p = *pos; - - while ((p < size) && (isspace ( (unsigned char) data[p]))) - { - if (data[p] == '\0') - return 0; - p++; - } - *pos = p; - return p < size; -} - - -static int -skipLetters (size_t *pos, const char *data, size_t size) -{ - size_t p = *pos; - - while ((p < size) && (isalpha ( (unsigned char) data[p]))) - { - if (data[p] == '\0') - return 0; - p++; - } - *pos = p; - return p < size; -} - - -static int -lookForMultiple (const char *c, size_t *pos, const char *data, size_t size) -{ - size_t p = *pos; - - while ((p < size) && (strchr (c, data[p]) == NULL)) - { - if (data[p] == '\0') - return 0; - p++; - } - *pos = p; - return p < size; -} - - -static void -findEntry (const char *key, - const char *start, - const char *end, const char **mstart, const char **mend) -{ - size_t len; - - *mstart = NULL; - *mend = NULL; - len = strlen (key); - while (start < end - len - 1) - { - start++; - if (start[len] != '=') - continue; - if (0 == strncasecmp (start, key, len)) - { - start += len + 1; - *mstart = start; - if ((*start == '\"') || (*start == '\'')) - { - start++; - while ((start < end) && (*start != **mstart)) - start++; - (*mstart)++; /* skip quote */ - } - else - { - while ((start < end) && (! isspace ( (unsigned char) *start))) - start++; - } - *mend = start; - return; - } - } -} - - -/** - * Search all tags that correspond to "tagname". Example: - * If the tag is <meta name="foo" desc="bar">, and - * tagname == "meta", keyname="name", keyvalue="foo", - * and searchname="desc", then this function returns a - * copy (!) of "bar". Easy enough? - * - * @return NULL if nothing is found - */ -static char * -findInTags (struct TagInfo *t, - const char *tagname, - const char *keyname, const char *keyvalue, const char *searchname) -{ - const char *pstart; - const char *pend; - - while (t != NULL) - { - if (tagMatch (tagname, t->tagStart, t->tagEnd)) - { - findEntry (keyname, t->tagEnd, t->dataStart, &pstart, &pend); - if ((pstart != NULL) && (tagMatch (keyvalue, pstart, pend))) - { - findEntry (searchname, t->tagEnd, t->dataStart, &pstart, &pend); - if (pstart != NULL) - { - char *ret = malloc (pend - pstart + 1); - if (ret == NULL) - return NULL; - memcpy (ret, pstart, pend - pstart); - ret[pend - pstart] = '\0'; - return ret; - } - } - } - t = t->next; - } - return NULL; -} - - -/* mimetype = text/html */ -int -EXTRACTOR_html_extract (const char *data, - size_t size, - EXTRACTOR_MetaDataProcessor proc, - void *proc_cls, - const char *options) -{ - size_t xsize; - struct TagInfo *tags; - struct TagInfo *t; - struct TagInfo tag; - size_t pos; - size_t tpos; - int i; - char *charset; - char *tmp; - char *xtmp; - int ret; - - ret = 0; - if (size == 0) - return 0; - /* only scan first 32k */ - if (size > 1024 * 32) - xsize = 1024 * 32; - else - xsize = size; - tags = NULL; - tag.next = NULL; - pos = 0; - while (pos < xsize) - { - if (! lookFor ('<', &pos, data, size)) - break; - tag.tagStart = &data[++pos]; - if (! skipLetters (&pos, data, size)) - break; - tag.tagEnd = &data[pos]; - if (! skipWhitespace (&pos, data, size)) - break; -STEP3: - if (! lookForMultiple (">\"\'", &pos, data, size)) - break; - if (data[pos] != '>') - { - /* find end-quote, ignore escaped quotes (\') */ - do - { - tpos = pos; - pos++; - if (! lookFor (data[tpos], &pos, data, size)) - break; - } - while (data[pos - 1] == '\\'); - pos++; - goto STEP3; - } - pos++; - if (! skipWhitespace (&pos, data, size)) - break; - tag.dataStart = &data[pos]; - if (! lookFor ('<', &pos, data, size)) - break; - tag.dataEnd = &data[pos]; - i = 0; - while (relevantTags[i] != NULL) - { - if ((strlen (relevantTags[i]) == tag.tagEnd - tag.tagStart) && - (0 == strncasecmp (relevantTags[i], - tag.tagStart, tag.tagEnd - tag.tagStart))) - { - t = malloc (sizeof (struct TagInfo)); - if (t == NULL) - return 0; - *t = tag; - t->next = tags; - tags = t; - break; - } - i++; - } - /* abort early if we hit the body tag */ - if (tagMatch ("body", tag.tagStart, tag.tagEnd)) - break; - } - - /* fast exit */ - if (tags == NULL) - return 0; - - charset = NULL; - /* first, try to determine mime type and/or character set */ - tmp = findInTags (tags, "meta", "http-equiv", "content-type", "content"); - if (tmp != NULL) - { - /* ideally, tmp == "test/html; charset=ISO-XXXX-Y" or something like that; - if text/html is present, we take that as the mime-type; if charset= - is present, we try to use that for character set conversion. */ - if (0 == strncasecmp (tmp, "text/html", strlen ("text/html"))) - ret = proc (proc_cls, - "html", - EXTRACTOR_METATYPE_MIMETYPE, - EXTRACTOR_METAFORMAT_UTF8, - "text/plain", - "text/html", - strlen ("text/html") + 1); - charset = strcasestr (tmp, "charset="); - if (charset != NULL) - charset = strdup (&charset[strlen ("charset=")]); - free (tmp); - } - i = 0; - while (tagmap[i].name != NULL) - { - tmp = findInTags (tags, "meta", "name", tagmap[i].name, "content"); - if ( (tmp != NULL) && - (ret == 0) ) - { - if (charset == NULL) - { - ret = proc (proc_cls, - "html", - tagmap[i].type, - EXTRACTOR_METAFORMAT_C_STRING, - "text/plain", - tmp, - strlen (tmp) + 1); - } - else - { - xtmp = EXTRACTOR_common_convert_to_utf8 (tmp, - strlen (tmp), - charset); - if (xtmp != NULL) - { - ret = proc (proc_cls, - "html", - tagmap[i].type, - EXTRACTOR_METAFORMAT_UTF8, - "text/plain", - xtmp, - strlen (xtmp) + 1); - free (xtmp); - } - } - } - if (tmp != NULL) - free (tmp); - i++; - } - while (tags != NULL) - { - t = tags; - if ( (tagMatch ("title", t->tagStart, t->tagEnd)) && - (ret == 0) ) - { - if (charset == NULL) - { - xtmp = malloc (t->dataEnd - t->dataStart + 1); - if (xtmp != NULL) - { - memcpy (xtmp, t->dataStart, t->dataEnd - t->dataStart); - xtmp[t->dataEnd - t->dataStart] = '\0'; - ret = proc (proc_cls, - "html", - EXTRACTOR_METATYPE_TITLE, - EXTRACTOR_METAFORMAT_C_STRING, - "text/plain", - xtmp, - strlen (xtmp) + 1); - free (xtmp); - } - } - else - { - xtmp = EXTRACTOR_common_convert_to_utf8 (t->dataStart, - t->dataEnd - t->dataStart, - charset); - if (xtmp != NULL) - { - ret = proc (proc_cls, - "html", - EXTRACTOR_METATYPE_TITLE, - EXTRACTOR_METAFORMAT_UTF8, - "text/plain", - xtmp, - strlen (xtmp) + 1); - free (xtmp); - } - } - } - tags = t->next; - free (t); - } - if (charset != NULL) - free (charset); - return ret; -} - - -#endif - - /** * Initialize glib and load magic file. */ void __attribute__ ((constructor)) +html_gobject_init (void); + +void __attribute__ ((constructor)) html_gobject_init () { magic = magic_open (MAGIC_MIME_TYPE); @@ -696,6 +342,9 @@ html_gobject_init () * Destructor for the library, cleans up. */ void __attribute__ ((destructor)) +html_ltdl_fini (void); + +void __attribute__ ((destructor)) html_ltdl_fini () { if (NULL != magic) diff --git a/src/plugins/riff_extractor.c b/src/plugins/riff_extractor.c @@ -89,6 +89,9 @@ round_double (double num) * @param ec extraction context provided to the plugin */ void +EXTRACTOR_riff_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_riff_extract_method (struct EXTRACTOR_ExtractContext *ec) { ssize_t xsize; @@ -119,8 +122,10 @@ EXTRACTOR_riff_extract_method (struct EXTRACTOR_ExtractContext *ec) blockLen = fread_le (&xdata[28]); /* begin of AVI header at 32 */ - fps = (unsigned int) round_double ((double) 1.0e6 / fread_le (&xdata[32])); - duration = (unsigned int) round_double ((double) fread_le (&xdata[48]) + fps = (unsigned int) round_double ((double) 1.0e6 / fread_le ( + &xdata[32])); + duration = (unsigned int) round_double ((double) fread_le ( + &xdata[48]) * 1000 / fps); width = fread_le (&xdata[64]); height = fread_le (&xdata[68]); diff --git a/src/plugins/s3m_extractor.c b/src/plugins/s3m_extractor.c @@ -78,6 +78,9 @@ LE_NETWORK_STRUCT_END * @param ec extraction context */ void +EXTRACTOR_s3m_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_s3m_extract_method (struct EXTRACTOR_ExtractContext *ec) { void *data; diff --git a/src/plugins/sid_extractor.c b/src/plugins/sid_extractor.c @@ -167,6 +167,9 @@ sidword (const sidwrd data) * @param ec extraction context */ void +EXTRACTOR_sid_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_sid_extract_method (struct EXTRACTOR_ExtractContext *ec) { unsigned int flags; diff --git a/src/plugins/vlc_extractor.c b/src/plugins/vlc_extractor.c @@ -17,8 +17,6 @@ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -THIS PLUGIN IS NOT WORKING, see #2075! - */ /** * @file plugins/vlc_extractor.c @@ -29,135 +27,222 @@ THIS PLUGIN IS NOT WORKING, see #2075! #include "extractor.h" #include <vlc/vlc.h> #include <signal.h> +#include <semaphore.h> +#include <pthread.h> + +struct IORequest +{ + enum { IO_NONE, IO_READ, IO_SEEK, IO_OPEN, IO_CLOSE, IO_DONE } op; + uint64_t offset; + unsigned char *buf; + size_t len; + ssize_t result; + uint64_t size; + int open_ok; + pthread_mutex_t mu; + pthread_cond_t request_ready; + pthread_cond_t response_ready; + struct EXTRACTOR_ExtractContext *ec; + int io_pending; + int stop_requested; /* player event fired; stop thread should act */ + int player_stopped; /* set when MediaPlayerStopped fires */ + libvlc_media_player_t *player; /* needed by the stop thread */ +}; + + +/* ------------------------------------------------------------------ */ +/* VLC callbacks — these run in VLC's threads. */ +/* They post an I/O request and sleep until the main thread services it */ +/* ------------------------------------------------------------------ */ + -/** - * Function to help VLC open a custom bitstream input media. - * - * The same media item can be opened multiple times. Each time, this callback - * is invoked. It should allocate and initialize any instance-specific - * resources, then store them in *datap. The instance resources can be freed - * in the @ref libvlc_media_close_cb callback. - * - * @param opaque our `struct EXTRACTOR_ExtractContext` - * @param[out] datap storage space for a private data pointer - * @param[out] sizep byte length of the bitstream or UINT64_MAX if unknown - * - * @note For convenience, *datap is initially NULL and *sizep is initially 0. - * - * @return 0 on success, non-zero on error. In case of failure, the other - * callbacks will not be invoked and any value stored in *datap and *sizep is - * discarded. - */ static int -open_cb (void *opaque, +open_cb (void *cls, void **datap, uint64_t *sizep) { - struct EXTRACTOR_ExtractContext *ec = opaque; - - *datap = ec; - *sizep = ec->get_size (ec->cls); - if (UINT64_MAX == *sizep) - { - fprintf (stderr, - "Open failed!\n"); - return 1; - } - fprintf (stderr, - "Open returns %llu file size!\n", - (unsigned long long) *sizep); - return 0; + struct IORequest *io = cls; + int ok; + + pthread_mutex_lock (&io->mu); + io->op = IO_OPEN; + io->io_pending = 1; + pthread_cond_signal (&io->request_ready); + pthread_cond_wait (&io->response_ready, + &io->mu); + *datap = io; + *sizep = io->size; + ok = io->open_ok; + pthread_mutex_unlock (&io->mu); + return ok ? 0 : 1; } -/** - * Function to help VLC read data from a custom bitstream input media. - * - * @param opaque our `struct EXTRACTOR_ExtractContext` - * @param buf start address of the buffer to read data into - * @param len bytes length of the buffer - * @return strictly positive number of bytes read, 0 on end-of-stream, - * or -1 on non-recoverable error - * - * @note If no data is immediately available, then the callback should sleep. - * @warning The application is responsible for avoiding deadlock situations. - * In particular, the callback should return an error if playback is stopped; - * if it does not return, then libvlc_media_player_stop() will never return. - */ static ssize_t -read_cb (void *opaque, +read_cb (void *cls, unsigned char *buf, size_t len) { - struct EXTRACTOR_ExtractContext *ec = opaque; - void *data; + struct IORequest *io = cls; ssize_t ret; - ret = ec->read (ec->cls, - &data, - len); - if (-1 == ret) - { - fprintf (stderr, - "Read failed!\n"); - return -1; - } - memcpy (buf, - data, - ret); - fprintf (stderr, - "Read %u bytes!\n", - (unsigned int) ret); + pthread_mutex_lock (&io->mu); + io->op = IO_READ; + io->buf = buf; + io->len = len; + io->io_pending = 1; + pthread_cond_signal (&io->request_ready); + pthread_cond_wait (&io->response_ready, + &io->mu); + ret = io->result; + pthread_mutex_unlock (&io->mu); return ret; } -/** - * Allow VLC to seek a custom bitstream input media. - * - * @param opaque our `struct EXTRACTOR_ExtractContext` - * @param offset absolute byte offset to seek to - * @return 0 on success, -1 on error. - */ static int -seek_cb (void *opaque, +seek_cb (void *cls, uint64_t offset) { - struct EXTRACTOR_ExtractContext *ec = opaque; + struct IORequest *io = cls; + int ret; - fprintf (stderr, - "Seek to %llu!\n", - (unsigned long long) offset); if (offset > INT64_MAX) - { - fprintf (stderr, - "Excessive seek, impossible with LE!\n"); return -1; - } - if (-1 == - ec->seek (ec->cls, - offset, - SEEK_SET)) + pthread_mutex_lock (&io->mu); + io->op = IO_SEEK; + io->offset = offset; + io->io_pending = 1; + pthread_cond_signal (&io->request_ready); + pthread_cond_wait (&io->response_ready, + &io->mu); + ret = (int) io->result; + pthread_mutex_unlock (&io->mu); + return ret; +} + + +/* close_cb IS the termination signal — no stop() needed */ +static void +close_cb (void *cls) +{ + struct IORequest *io = cls; + + pthread_mutex_lock (&io->mu); + io->op = IO_DONE; + io->io_pending = 1; + pthread_cond_signal (&io->request_ready); + pthread_cond_wait (&io->response_ready, + &io->mu); + pthread_mutex_unlock (&io->mu); +} + + +static void +player_stopped_event (const struct libvlc_event_t *p_event, + void *p_data) +{ + struct IORequest *io = p_data; + + pthread_mutex_lock (&io->mu); + io->player_stopped = 1; + pthread_cond_broadcast (&io->request_ready); + pthread_mutex_unlock (&io->mu); +} + + +/* ------------------------------------------------------------------ */ +/* Main-thread I/O dispatcher loop */ +/* Runs on the calling thread so ec->read / ec->seek are single-thread */ +/* ------------------------------------------------------------------ */ + +static void +run_io_loop (struct IORequest *io) +{ + struct EXTRACTOR_ExtractContext *ec = io->ec; + + pthread_mutex_lock (&io->mu); + while (1) { - fprintf (stderr, - "Seek failed!\n"); - return -1; + /* wait for VLC to post a request */ + while (! io->io_pending) + pthread_cond_wait (&io->request_ready, + &io->mu); + io->io_pending = 0; + + switch (io->op) + { + case IO_OPEN: + { + uint64_t sz = ec->get_size (ec->cls); + + io->size = sz; + io->open_ok = (sz != UINT64_MAX); + io->op = IO_NONE; + pthread_cond_signal (&io->response_ready); + break; + } + case IO_READ: + { + void *data; + ssize_t r = ec->read (ec->cls, + &data, + io->len); + + if (r > 0) + memcpy (io->buf, + data, + (size_t) r); + io->result = r; + io->op = IO_NONE; + pthread_cond_signal (&io->response_ready); + break; + } + case IO_SEEK: + { + io->result = ec->seek (ec->cls, + (int64_t) io->offset, + SEEK_SET); + io->op = IO_NONE; + pthread_cond_signal (&io->response_ready); + break; + } + case IO_CLOSE: + io->op = IO_NONE; + pthread_cond_signal (&io->response_ready); /* ack close */ + break; + + case IO_DONE: + io->op = IO_NONE; + pthread_cond_signal (&io->response_ready); /* ack close_cb */ + pthread_mutex_unlock (&io->mu); + return; + + default: + io->op = IO_NONE; + break; + } } - return 0; } -/** - * Callback prototype to close a custom bitstream input media. - * - * @param opaque our `struct EXTRACTOR_ExtractContext` - */ +/* ------------------------------------------------------------------ */ +/* extract() and my_logger unchanged from your version */ +/* ------------------------------------------------------------------ */ + static void -close_cb (void *opaque) +my_logger (void *data, + int level, + const libvlc_log_t *ctx, + const char *fmt, + va_list args) { - /* intentionally empty */ - fprintf (stderr, - "Close called\n"); +#if 0 + vfprintf (stderr, + fmt, + args); + fprintf (stderr, "\n"); +#endif } @@ -226,7 +311,7 @@ extract (struct EXTRACTOR_ExtractContext *ec, }; for (unsigned int i = 0; - EXTRACTOR_METATYPE_RESERVED != map[i].mt; + 0 != map[i].mt; i++) { char *meta; @@ -249,103 +334,131 @@ extract (struct EXTRACTOR_ExtractContext *ec, } -static void -media_ready (const struct libvlc_event_t *p_event, - void *p_data) -{ - fprintf (stderr, - "media status: %d, %d\n", - p_event->type == libvlc_MediaParsedChanged, - p_event->u.media_parsed_changed.new_status); - if (p_event->u.media_parsed_changed.new_status == - libvlc_media_parsed_status_done) - { - fprintf (stderr, - "media ready\n"); - } -} - - -static void -my_logger (void *data, - int level, - const libvlc_log_t *ctx, - const char *fmt, - va_list args) -{ -#if 0 - vfprintf (stderr, - fmt, - args); - fprintf (stderr, "\n"); -#endif -} - - /** * Extract information using libvlc * * @param ec extraction context */ void +EXTRACTOR_vlc_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_vlc_extract_method (struct EXTRACTOR_ExtractContext *ec) { + struct IORequest io = { + .op = IO_NONE, + .ec = ec, + }; libvlc_instance_t *vlc; libvlc_media_t *media; - libvlc_event_manager_t *em; + libvlc_media_player_t *player; + + pthread_mutex_init (&io.mu, + NULL); + pthread_cond_init (&io.request_ready, + NULL); + pthread_cond_init (&io.response_ready, + NULL); { sigset_t set; - signal (SIGCHLD, SIG_DFL); + signal (SIGCHLD, + SIG_DFL); sigemptyset (&set); - sigaddset (&set, SIGPIPE); - pthread_sigmask (SIG_BLOCK, &set, NULL); + sigaddset (&set, + SIGPIPE); + pthread_sigmask (SIG_BLOCK, + &set, + NULL); } { const char *argv[] = { - "-v", - "3", + "--no-video", + "--no-audio", + "--no-plugins-cache", NULL }; - vlc = libvlc_new (2, argv); + + vlc = libvlc_new (3, + argv); } if (NULL == vlc) - return; + goto cleanup; + libvlc_log_set (vlc, &my_logger, NULL); + + /* Pass &io as closure — callbacks use it to marshal I/O to main thread */ media = libvlc_media_new_callbacks (vlc, &open_cb, &read_cb, &seek_cb, &close_cb, - ec); + &io); if (NULL == media) { libvlc_release (vlc); - return; + goto cleanup; } - em = libvlc_media_event_manager (media); - libvlc_event_attach (em, - libvlc_MediaParsedChanged, - &media_ready, - ec); + /* Create a player — this is what actually drives the input thread */ + player = libvlc_media_player_new_from_media (media); + libvlc_media_release (media); /* player holds its own reference */ + if (NULL == player) { - int status; + libvlc_release (vlc); + goto cleanup; + } + + { + libvlc_event_manager_t *pem = libvlc_media_player_event_manager (player); - status = libvlc_media_parse_with_options (media, - libvlc_media_fetch_local - | libvlc_media_parse_network - | libvlc_media_fetch_network, - 30000); /* 30s timeout */ + libvlc_event_attach (pem, + libvlc_MediaPlayerStopped, + &player_stopped_event, + &io); } - extract (ec, - media); - libvlc_media_release (media); + + /* Mute and suppress output completely */ + libvlc_audio_set_mute (player, + 1); + + /* play() causes VLC to actually open and read the media */ + libvlc_media_player_play (player); + + /* Service I/O until close_cb fires (input thread fully exited) */ + run_io_loop (&io); + + /* Stop immediately — we got what we needed */ + libvlc_media_player_stop (player); + + /* Wait for the player to confirm it has fully stopped */ + pthread_mutex_lock (&io.mu); + while (! io.player_stopped) + pthread_cond_wait (&io.request_ready, &io.mu); + pthread_mutex_unlock (&io.mu); + + /* Get the player's internal media copy to read metadata from */ + { + libvlc_media_t *played_media = libvlc_media_player_get_media (player); + + if (NULL != played_media) + { + extract (ec, + played_media); + libvlc_media_release (played_media); + } + } + libvlc_media_player_release (player); libvlc_release (vlc); + +cleanup: + pthread_cond_destroy (&io.response_ready); + pthread_cond_destroy (&io.request_ready); + pthread_mutex_destroy (&io.mu); } diff --git a/src/plugins/wav_extractor.c b/src/plugins/wav_extractor.c @@ -72,6 +72,9 @@ little_endian_to_host32 (uint32_t in) * 24 4 bytes <sample rate> // Samples per second: e.g., 44100 */ void +EXTRACTOR_wav_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_wav_extract_method (struct EXTRACTOR_ExtractContext *ec) { void *data; diff --git a/src/plugins/xm_extractor.c b/src/plugins/xm_extractor.c @@ -65,6 +65,9 @@ struct Header * @param ec extraction context */ void +EXTRACTOR_xm_extract_method (struct EXTRACTOR_ExtractContext *ec); + +void EXTRACTOR_xm_extract_method (struct EXTRACTOR_ExtractContext *ec) { void *data;