libextractor

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

mediagst.c (7588B)


      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/mediagst.c
     22  * @brief plumbing between an extraction context and a GStreamer pipeline
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "mediagst.h"
     27 
     28 #include <gst/app/gstappsrc.h>
     29 
     30 
     31 /**
     32  * Implementation of GstAppSrc's "need-data" callback.  Reads data from
     33  * the extraction context and passes it to GStreamer.
     34  *
     35  * @param appsrc the source that ran out of data
     36  * @param size number of bytes requested
     37  * @param io our execution context
     38  */
     39 static void
     40 feed_data (GstElement *appsrc,
     41            guint size,
     42            struct EXTRACTOR_GstIO *io)
     43 {
     44   GstBuffer *buffer;
     45   GstMemory *mem;
     46   GstMapInfo mi;
     47   guint accumulated;
     48   ssize_t data_len;
     49   uint8_t *le_data;
     50 
     51   if ( (0 != io->length) &&
     52        (io->offset >= io->length) )
     53   {
     54     gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
     55     return;
     56   }
     57   if ( (0 != io->length) &&
     58        (io->offset + size > io->length) )
     59     size = io->length - io->offset;
     60   if (0 == size)
     61   {
     62     gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
     63     return;
     64   }
     65   mem = gst_allocator_alloc (NULL, size, NULL);
     66   if (! gst_memory_map (mem, &mi, GST_MAP_WRITE))
     67   {
     68     gst_memory_unref (mem);
     69     gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
     70     return;
     71   }
     72   accumulated = 0;
     73   data_len = 1;
     74   while ( (accumulated < size) &&
     75           (data_len > 0) )
     76   {
     77     data_len = io->ec->read (io->ec->cls,
     78                              (void **) &le_data,
     79                              size - accumulated);
     80     if (data_len > 0)
     81     {
     82       memcpy (&mi.data[accumulated],
     83               le_data,
     84               data_len);
     85       accumulated += data_len;
     86     }
     87   }
     88   gst_memory_unmap (mem, &mi);
     89   if (0 == accumulated)
     90   {
     91     gst_memory_unref (mem);
     92     gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
     93     return;
     94   }
     95   gst_memory_resize (mem, 0, accumulated);
     96   buffer = gst_buffer_new ();
     97   gst_buffer_append_memory (buffer, mem);
     98   /* the offsets matter: they are what makes random access work */
     99   GST_BUFFER_OFFSET (buffer) = io->offset;
    100   GST_BUFFER_OFFSET_END (buffer) = io->offset + accumulated;
    101   io->offset += accumulated;
    102   gst_app_src_push_buffer (GST_APP_SRC (appsrc), buffer);
    103 }
    104 
    105 
    106 /**
    107  * Implementation of GstAppSrc's "seek-data" callback.
    108  *
    109  * @param appsrc the source that wants to seek
    110  * @param position new desired absolute position in the file
    111  * @param io our execution context
    112  * @return TRUE if seeking succeeded
    113  */
    114 static gboolean
    115 seek_data (GstElement *appsrc,
    116            guint64 position,
    117            struct EXTRACTOR_GstIO *io)
    118 {
    119   int64_t ret;
    120 
    121   (void) appsrc;
    122   ret = io->ec->seek (io->ec->cls,
    123                       position,
    124                       SEEK_SET);
    125   if (0 > ret)
    126     return FALSE;
    127   io->offset = (uint64_t) ret;
    128   return io->offset == position;
    129 }
    130 
    131 
    132 /**
    133  * Implementation of playbin's "source-setup" signal: configure the
    134  * appsrc it created for our "appsrc://" URI.
    135  *
    136  * @param playbin the pipeline
    137  * @param source the appsrc to configure
    138  * @param io our execution context
    139  */
    140 static void
    141 source_setup (GstElement *playbin,
    142               GstElement *source,
    143               struct EXTRACTOR_GstIO *io)
    144 {
    145   (void) playbin;
    146   if (0 != io->length)
    147     g_object_set (source,
    148                   "size", (gint64) io->length,
    149                   "stream-type", GST_APP_STREAM_TYPE_RANDOM_ACCESS,
    150                   NULL);
    151   else
    152     g_object_set (source,
    153                   "size", (gint64) - 1,
    154                   "stream-type", GST_APP_STREAM_TYPE_STREAM,
    155                   NULL);
    156   g_object_set (source,
    157                 "format", GST_FORMAT_BYTES,
    158                 NULL);
    159   g_signal_connect (source,
    160                     "need-data",
    161                     G_CALLBACK (&feed_data),
    162                     io);
    163   g_signal_connect (source,
    164                     "seek-data",
    165                     G_CALLBACK (&seek_data),
    166                     io);
    167 }
    168 
    169 
    170 GstElement *
    171 EXTRACTOR_gst_playbin_ (struct EXTRACTOR_GstIO *io,
    172                         struct EXTRACTOR_ExtractContext *ec,
    173                         unsigned int flags,
    174                         GstElement *video_sink,
    175                         GstElement *audio_sink)
    176 {
    177   GstElement *pipeline;
    178   uint64_t size;
    179 
    180   if (! gst_is_initialized ())
    181     gst_init (NULL, NULL);
    182   io->ec = ec;
    183   io->offset = 0;
    184   size = ec->get_size (ec->cls);
    185   io->length = (UINT64_MAX == size) ? 0 : size;
    186   pipeline = gst_element_factory_make ("playbin", NULL);
    187   if ( (NULL == pipeline) ||
    188        (NULL == video_sink) ||
    189        (NULL == audio_sink) )
    190   {
    191     /* gst-plugins-base is not installed, or not completely */
    192     if (NULL != video_sink)
    193       gst_object_unref (video_sink);
    194     if (NULL != audio_sink)
    195       gst_object_unref (audio_sink);
    196     if (NULL != pipeline)
    197       gst_object_unref (pipeline);
    198     return NULL;
    199   }
    200   g_object_set (pipeline,
    201                 "uri", "appsrc://",
    202                 "video-sink", video_sink,
    203                 "audio-sink", audio_sink,
    204                 "flags", flags,
    205                 NULL);
    206   g_signal_connect (pipeline,
    207                     "source-setup",
    208                     G_CALLBACK (&source_setup),
    209                     io);
    210   return pipeline;
    211 }
    212 
    213 
    214 int
    215 EXTRACTOR_gst_wait_async_ (GstElement *pipeline,
    216                            GstClockTime timeout)
    217 {
    218   GstBus *bus;
    219   GstMessage *msg;
    220   int ret = 0;
    221 
    222   if (0 == timeout)
    223     return 0;
    224   if (NULL == (bus = gst_element_get_bus (pipeline)))
    225     return 0;
    226   while (1)
    227   {
    228     msg = gst_bus_timed_pop_filtered (bus,
    229                                       timeout,
    230                                       GST_MESSAGE_ASYNC_DONE
    231                                       | GST_MESSAGE_ERROR
    232                                       | GST_MESSAGE_EOS);
    233     if (NULL == msg)
    234       break;   /* timed out */
    235     if (GST_MESSAGE_ASYNC_DONE != GST_MESSAGE_TYPE (msg))
    236     {
    237       /* an error, or the file ended before we ever saw a frame */
    238       gst_message_unref (msg);
    239       break;
    240     }
    241     if (GST_OBJECT (pipeline) == GST_MESSAGE_SRC (msg))
    242     {
    243       gst_message_unref (msg);
    244       ret = 1;
    245       break;
    246     }
    247     /* ASYNC_DONE from some element inside the pipeline; keep waiting */
    248     gst_message_unref (msg);
    249   }
    250   gst_object_unref (bus);
    251   return ret;
    252 }
    253 
    254 
    255 gint64
    256 EXTRACTOR_gst_sample_position_ (GstElement *pipeline,
    257                                 unsigned int offset,
    258                                 gint64 *duration)
    259 {
    260   gint64 target;
    261   gint64 dur = 0;
    262 
    263   target = ((gint64) offset) * GST_SECOND;
    264   if (! gst_element_query_duration (pipeline,
    265                                     GST_FORMAT_TIME,
    266                                     &dur))
    267     dur = 0;
    268   if (dur < 0)
    269     dur = 0;
    270   if ( (0 != dur) &&
    271        (target > dur / 3) )
    272     target = dur / 3;
    273   *duration = dur;
    274   return target;
    275 }
    276 
    277 
    278 /* end of mediagst.c */