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:
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;