libextractor

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

thumbnailgst_extractor.c (7817B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2026 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/thumbnailgst_extractor.c
     22  * @author Christian Grothoff
     23  * @brief this extractor produces a binary encoded thumbnail of a frame
     24  *        taken from a video, using GStreamer
     25  *
     26  * This is the GStreamer counterpart of thumbnailffmpeg: it seeks about
     27  * 30s into a video and encodes the frame it finds there as a small JPEG
     28  * (or PNG).  Which of the two is built depends on what was found at
     29  * configure time; if both are installed, both will report a thumbnail and
     30  * it is up to the application to disable one of them in its plugin
     31  * configuration string.
     32  *
     33  * The decoders and the image encoder are themselves GStreamer plugins,
     34  * resolved at run time, so a missing "jpeg" or "playback" plugin cannot
     35  * be detected when libextractor is built.  We simply report nothing.
     36  */
     37 #include "platform.h"
     38 #include "extractor.h"
     39 #include "mediautil.h"
     40 #include "mediagst.h"
     41 
     42 #include <gst/video/video.h>
     43 
     44 
     45 /**
     46  * Scale and encode the raw video @a sample.
     47  *
     48  * @param sample raw frame to convert
     49  * @param opt plugin options (target size and format)
     50  * @param timeout how long the conversion may take, in nanoseconds
     51  * @return the encoded image, NULL on error
     52  */
     53 static GstSample *
     54 encode_thumbnail (GstSample *sample,
     55                   const struct EXTRACTOR_MediaOptions *opt,
     56                   GstClockTime timeout)
     57 {
     58   GstCaps *caps;
     59   GstCaps *to_caps;
     60   GstStructure *s;
     61   GstSample *out;
     62   gint width;
     63   gint height;
     64   unsigned int tw;
     65   unsigned int th;
     66 
     67   if (NULL == (caps = gst_sample_get_caps (sample)))
     68     return NULL;
     69   if (NULL == (s = gst_caps_get_structure (caps, 0)))
     70     return NULL;
     71   if ( (! gst_structure_get_int (s, "width", &width)) ||
     72        (! gst_structure_get_int (s, "height", &height)) ||
     73        (0 >= width) ||
     74        (0 >= height) )
     75     return NULL;
     76   if (! EXTRACTOR_media_thumbnail_size_ ((unsigned int) width,
     77                                          (unsigned int) height,
     78                                          opt->size,
     79                                          &tw,
     80                                          &th))
     81     return NULL;
     82   to_caps = gst_caps_new_simple (opt->png ? "image/png" : "image/jpeg",
     83                                  "width", G_TYPE_INT, (gint) tw,
     84                                  "height", G_TYPE_INT, (gint) th,
     85                                  NULL);
     86   out = gst_video_convert_sample (sample,
     87                                   to_caps,
     88                                   timeout,
     89                                   NULL);
     90   gst_caps_unref (to_caps);
     91   return out;
     92 }
     93 
     94 
     95 /**
     96  * Main method for the gstreamer-thumbnailer plugin.
     97  *
     98  * @param ec extraction context
     99  */
    100 void
    101 EXTRACTOR_thumbnailgst_extract_method (struct EXTRACTOR_ExtractContext *ec);
    102 
    103 void
    104 EXTRACTOR_thumbnailgst_extract_method (struct EXTRACTOR_ExtractContext *ec)
    105 {
    106   struct EXTRACTOR_MediaOptions opt;
    107   struct EXTRACTOR_MediaDeadline deadline;
    108   struct EXTRACTOR_GstIO io;
    109   GstElement *pipeline;
    110   GstElement *sink;
    111   GstSample *sample = NULL;
    112   GstSample *thumbnail = NULL;
    113   GstBuffer *buffer;
    114   GstMapInfo mi;
    115   GstClockTime timeout;
    116   gint64 duration;
    117   gint64 target;
    118 
    119   EXTRACTOR_media_options_parse_ (ec->config,
    120                                   &opt);
    121   EXTRACTOR_media_deadline_start_ (&deadline,
    122                                    opt.deadline);
    123   if (! EXTRACTOR_media_is_media_ (ec, 0))
    124     return;
    125   if (! gst_is_initialized ())
    126     gst_init (NULL, NULL);
    127   sink = gst_element_factory_make ("fakesink", NULL);
    128   if (NULL != sink)
    129     g_object_set (sink,
    130                   "sync", FALSE,
    131                   NULL);
    132   if (NULL == (pipeline =
    133                  EXTRACTOR_gst_playbin_ (&io,
    134                                          ec,
    135                                          EXTRACTOR_GST_PLAY_FLAG_VIDEO,
    136                                          sink,
    137                                          gst_element_factory_make ("fakesink",
    138                                                                    NULL))))
    139     return;
    140   if (GST_STATE_CHANGE_FAILURE ==
    141       gst_element_set_state (pipeline,
    142                              GST_STATE_PAUSED))
    143     goto cleanup;
    144   if (! EXTRACTOR_gst_wait_async_ (pipeline,
    145                                    EXTRACTOR_media_deadline_remaining_ (
    146                                      &deadline) * GST_MSECOND))
    147     goto cleanup;
    148   target = EXTRACTOR_gst_sample_position_ (pipeline,
    149                                            opt.offset,
    150                                            &duration);
    151   if (target >= GST_SECOND)
    152   {
    153     /* a key frame is good enough for a thumbnail and much cheaper than
    154        decoding forward to an exact position */
    155     if (gst_element_seek_simple (pipeline,
    156                                  GST_FORMAT_TIME,
    157                                  GST_SEEK_FLAG_FLUSH
    158                                  | GST_SEEK_FLAG_KEY_UNIT,
    159                                  target))
    160       (void) EXTRACTOR_gst_wait_async_ (pipeline,
    161                                         EXTRACTOR_media_deadline_remaining_ (
    162                                           &deadline) * GST_MSECOND);
    163   }
    164   g_object_get (sink,
    165                 "last-sample",
    166                 &sample,
    167                 NULL);
    168   if (NULL == sample)
    169     goto cleanup;
    170   timeout = EXTRACTOR_media_deadline_remaining_ (&deadline) * GST_MSECOND;
    171   if (0 == timeout)
    172     goto cleanup;
    173   if (NULL == (thumbnail = encode_thumbnail (sample,
    174                                              &opt,
    175                                              timeout)))
    176     goto cleanup;
    177   if (NULL == (buffer = gst_sample_get_buffer (thumbnail)))
    178     goto cleanup;
    179   if (! gst_buffer_map (buffer, &mi, GST_MAP_READ))
    180     goto cleanup;
    181   if ( (0 < mi.size) &&
    182        (EXTRACTOR_MEDIA_MAX_THUMBNAIL >= mi.size) )
    183     ec->proc (ec->cls,
    184               "thumbnailgst",
    185               EXTRACTOR_METATYPE_THUMBNAIL,
    186               EXTRACTOR_METAFORMAT_BINARY,
    187               opt.png ? "image/png" : "image/jpeg",
    188               (const char *) mi.data,
    189               mi.size);
    190   gst_buffer_unmap (buffer, &mi);
    191 cleanup:
    192   if (NULL != thumbnail)
    193     gst_sample_unref (thumbnail);
    194   if (NULL != sample)
    195     gst_sample_unref (sample);
    196   gst_element_set_state (pipeline,
    197                          GST_STATE_NULL);
    198   gst_object_unref (pipeline);
    199 }
    200 
    201 
    202 /**
    203  * Keep the diagnostics of the media framework out of the output of the
    204  * calling application; a decoder complaining about a broken file is not
    205  * something the application asked to see.
    206  *
    207  * ("force-kill" would be tempting here, to get a fresh process for every
    208  * file, but the host cannot know about that special without loading the
    209  * plugin in-process: it only finds out that the child is gone once it has
    210  * already handed it the next file, which is then silently lost.)
    211  *
    212  * @return special options for this plugin
    213  */
    214 const char *
    215 EXTRACTOR_thumbnailgst_options (void);
    216 
    217 const char *
    218 EXTRACTOR_thumbnailgst_options ()
    219 {
    220   return "close-stderr";
    221 }
    222 
    223 
    224 /* end of thumbnailgst_extractor.c */