/* This file is part of libextractor. Copyright (C) 2021 Christian Grothoff libextractor 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 3, or (at your option) any later version. libextractor 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 libextractor; see the file COPYING. If not, write to the 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 * @brief plugin to extract metadata using libvlc * @author Christian Grothoff */ #include "platform.h" #include "extractor.h" #include #include /** * 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, 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; } /** * 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, unsigned char *buf, size_t len) { struct EXTRACTOR_ExtractContext *ec = opaque; void *data; 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); 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, uint64_t offset) { struct EXTRACTOR_ExtractContext *ec = opaque; 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)) { fprintf (stderr, "Seek failed!\n"); return -1; } return 0; } /** * Callback prototype to close a custom bitstream input media. * * @param opaque our `struct EXTRACTOR_ExtractContext` */ static void close_cb (void *opaque) { /* intentionally empty */ fprintf (stderr, "Close called\n"); } static void extract (struct EXTRACTOR_ExtractContext *ec, libvlc_media_t *media) { struct { enum libvlc_meta_t vt; enum EXTRACTOR_MetaType mt; } map[] = { { libvlc_meta_Title, EXTRACTOR_METATYPE_TITLE }, { libvlc_meta_Artist, EXTRACTOR_METATYPE_ARTIST }, { libvlc_meta_Genre, EXTRACTOR_METATYPE_GENRE }, { libvlc_meta_Copyright, EXTRACTOR_METATYPE_COPYRIGHT }, { libvlc_meta_Album, EXTRACTOR_METATYPE_ALBUM }, { libvlc_meta_TrackNumber, EXTRACTOR_METATYPE_TRACK_NUMBER }, { libvlc_meta_Description, EXTRACTOR_METATYPE_DESCRIPTION }, { libvlc_meta_Rating, EXTRACTOR_METATYPE_RATING }, { libvlc_meta_Date, EXTRACTOR_METATYPE_CREATION_TIME }, { libvlc_meta_Setting, EXTRACTOR_METATYPE_UNKNOWN }, { libvlc_meta_URL, EXTRACTOR_METATYPE_URL }, { libvlc_meta_Language, EXTRACTOR_METATYPE_LANGUAGE }, { libvlc_meta_NowPlaying, EXTRACTOR_METATYPE_UNKNOWN }, { libvlc_meta_Publisher, EXTRACTOR_METATYPE_PUBLISHER }, { libvlc_meta_EncodedBy, EXTRACTOR_METATYPE_ENCODED_BY }, { libvlc_meta_ArtworkURL, EXTRACTOR_METATYPE_URL }, { libvlc_meta_TrackID, EXTRACTOR_METATYPE_TRACK_NUMBER }, { libvlc_meta_TrackTotal, EXTRACTOR_METATYPE_UNKNOWN }, { libvlc_meta_Director, EXTRACTOR_METATYPE_MOVIE_DIRECTOR }, { libvlc_meta_Season, EXTRACTOR_METATYPE_SHOW_SEASON_NUMBER }, { libvlc_meta_Episode, EXTRACTOR_METATYPE_SHOW_EPISODE_NUMBER }, { libvlc_meta_ShowName, EXTRACTOR_METATYPE_SHOW_NAME }, { libvlc_meta_Actors, EXTRACTOR_METATYPE_PERFORMER }, { libvlc_meta_AlbumArtist, EXTRACTOR_METATYPE_ARTIST }, { libvlc_meta_DiscNumber, EXTRACTOR_METATYPE_DISC_NUMBER }, { libvlc_meta_DiscTotal, EXTRACTOR_METATYPE_UNKNOWN }, { 0, 0 } }; for (unsigned int i = 0; EXTRACTOR_METATYPE_RESERVED != map[i].mt; i++) { char *meta; fprintf (stderr, "."); meta = libvlc_media_get_meta (media, map[i].vt); if (NULL == meta) continue; ec->proc (ec->cls, "vlc", map[i].mt, EXTRACTOR_METAFORMAT_UTF8, /* ??? */ "text/plain", meta, strlen (meta) + 1); free (meta); } } 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) { vfprintf (stderr, fmt, args); fprintf (stderr, "\n"); } /** * Extract information using libvlc * * @param ec extraction context */ void EXTRACTOR_vlc_extract_method (struct EXTRACTOR_ExtractContext *ec) { libvlc_instance_t *vlc; libvlc_media_t *media; libvlc_event_manager_t *em; { sigset_t set; signal (SIGCHLD, SIG_DFL); sigemptyset (&set); sigaddset (&set, SIGPIPE); pthread_sigmask (SIG_BLOCK, &set, NULL); } { const char *argv[] = { "-v", "3", NULL }; vlc = libvlc_new (2, argv); } if (NULL == vlc) return; libvlc_log_set (vlc, &my_logger, NULL); if (1) { fprintf (stderr, "Opening file `%s'\n", "testdata/matroska_flame.mkv"); media = libvlc_media_new_path (vlc, "testdata/matroska_flame.mkv"); if (NULL == media) { fprintf (stderr, "Open failed\n"); libvlc_release (vlc); return; } } else if (0) { fprintf (stderr, "Opening file `%s'\n", "testdata/matroska_flame.mkv"); int fd = open ("testdata/matroska_flame.mkv", O_RDONLY); if (-1 == fd) { fprintf (stderr, "Open failed: %s\n", strerror (errno)); libvlc_release (vlc); return; } media = libvlc_media_new_fd (vlc, fd); } else if (0) { fprintf (stderr, "Reading via IPC\n"); media = libvlc_media_new_callbacks (vlc, &open_cb, &read_cb, &seek_cb, &close_cb, ec); } else abort (); if (NULL == media) { libvlc_release (vlc); return; } em = libvlc_media_event_manager (media); libvlc_event_attach (em, libvlc_MediaParsedChanged, &media_ready, ec); fprintf (stderr, "Triggering parser\n"); { int status; status = libvlc_media_parse_with_options (media, libvlc_media_fetch_local | libvlc_media_parse_network | libvlc_media_fetch_network, 30000); /* 30s timeout */ fprintf (stderr, "Status: %d\n", status); } fprintf (stderr, "Sleeping\n"); sleep (10); extract (ec, media); libvlc_media_release (media); libvlc_release (vlc); } /* end of vlc_extractor.c */