LCOV - code coverage report
Current view: top level - src/elements/gstgpactf.c (source / functions) Coverage Total Hit
Test: coverage.filtered.info Lines: 78.8 % 665 524
Test Date: 2026-03-11 21:42:04 Functions: 90.0 % 30 27

            Line data    Source code
       1              : /*
       2              :  *                      GPAC - Multimedia Framework C SDK
       3              :  *
       4              :  *                      Authors: Deniz Ugur, Romain Bouqueau, Sohaib Larbi
       5              :  *                      Copyright (c) Motion Spell
       6              :  *                              All rights reserved
       7              :  *
       8              :  *  This file is part of the GPAC/GStreamer wrapper
       9              :  *
      10              :  *  This GPAC/GStreamer wrapper is free software; you can redistribute it
      11              :  *  and/or modify it under the terms of the GNU Affero General Public License
      12              :  *  as published by the Free Software Foundation; either version 3, or (at
      13              :  *  your option) any later version.
      14              :  *
      15              :  *  This GPAC/GStreamer wrapper is distributed in the hope that it will be
      16              :  *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
      17              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18              :  *  GNU Affero General Public License for more details.
      19              :  *
      20              :  *  You should have received a copy of the GNU Affero General Public
      21              :  *  License along with this library; see the file LICENSE.  If not, write to
      22              :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23              :  *
      24              :  */
      25              : 
      26              : #include "elements/gstgpactf.h"
      27              : #include "elements/gstgpacsink.h"
      28              : 
      29              : GST_DEBUG_CATEGORY_STATIC(gst_gpac_tf_debug);
      30              : #define GST_CAT_DEFAULT gst_gpac_tf_debug
      31              : 
      32              : // #MARK: Pad Class
      33        27897 : G_DEFINE_TYPE(GstGpacTransformPad, gst_gpac_tf_pad, GST_TYPE_AGGREGATOR_PAD);
      34              : 
      35              : enum
      36              : {
      37              :   GPAC_PROP_PAD_0,
      38              :   GPAC_PROP_PAD_PID,
      39              : };
      40              : 
      41              : // #MARK: [P] Properties
      42              : static void
      43           46 : gst_gpac_tf_pad_set_property(GObject* object,
      44              :                              guint prop_id,
      45              :                              const GValue* value,
      46              :                              GParamSpec* pspec)
      47              : {
      48           46 :   GstGpacTransformPad* pad = GST_GPAC_TF_PAD(object);
      49           46 :   g_return_if_fail(GST_IS_GPAC_TF_PAD(object));
      50              : 
      51           46 :   switch (prop_id) {
      52           46 :     case GPAC_PROP_PAD_PID:
      53           46 :       pad->pid = g_value_get_pointer(value);
      54           46 :       break;
      55              : 
      56            0 :     default:
      57            0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      58            0 :       break;
      59              :   }
      60              : }
      61              : 
      62              : static void
      63        13890 : gst_gpac_tf_pad_get_property(GObject* object,
      64              :                              guint prop_id,
      65              :                              GValue* value,
      66              :                              GParamSpec* pspec)
      67              : {
      68        13890 :   GstGpacTransformPad* pad = GST_GPAC_TF_PAD(object);
      69        13890 :   g_return_if_fail(GST_IS_GPAC_TF_PAD(object));
      70              : 
      71        13890 :   switch (prop_id) {
      72        13890 :     case GPAC_PROP_PAD_PID:
      73        13890 :       g_value_set_pointer(value, pad->pid);
      74        13890 :       break;
      75              : 
      76            0 :     default:
      77            0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      78            0 :       break;
      79              :   }
      80              : }
      81              : 
      82              : // #MARK: [P] Initialization
      83              : static void
      84           20 : gst_gpac_tf_pad_init(GstGpacTransformPad* pad)
      85              : {
      86           20 :   GpacPadPrivate* priv = g_new0(GpacPadPrivate, 1);
      87           20 :   priv->self = GST_PAD(pad);
      88           20 :   priv->idr_period = GST_CLOCK_TIME_NONE;
      89           20 :   priv->idr_last = GST_CLOCK_TIME_NONE;
      90           20 :   priv->idr_next = GST_CLOCK_TIME_NONE;
      91           20 :   gst_pad_set_element_private(GST_PAD(pad), priv);
      92              : 
      93           20 :   pad->pid = NULL;
      94           20 : };
      95              : 
      96              : static void
      97            0 : gst_gpac_tf_pad_finalize(GObject* object)
      98              : {
      99            0 :   GstGpacTransformPad* pad = GST_GPAC_TF_PAD(object);
     100            0 :   GpacPadPrivate* priv = gst_pad_get_element_private(GST_PAD(pad));
     101              : 
     102            0 :   if (priv) {
     103            0 :     if (priv->caps)
     104            0 :       gst_caps_unref(priv->caps);
     105            0 :     if (priv->segment)
     106            0 :       gst_segment_free(priv->segment);
     107            0 :     if (priv->tags)
     108            0 :       gst_tag_list_unref(priv->tags);
     109            0 :     if (priv->gpac_caps)
     110            0 :       g_list_free_full(priv->gpac_caps, (GDestroyNotify)g_free);
     111            0 :     g_free(priv);
     112            0 :     gst_pad_set_element_private(GST_PAD(pad), NULL);
     113              :   }
     114              : 
     115            0 :   G_OBJECT_CLASS(gst_gpac_tf_pad_parent_class)->finalize(object);
     116            0 : }
     117              : 
     118              : static void
     119            1 : gst_gpac_tf_pad_class_init(GstGpacTransformPadClass* klass)
     120              : {
     121            1 :   GObjectClass* gobject_class = (GObjectClass*)klass;
     122              : 
     123            1 :   gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_gpac_tf_pad_finalize);
     124            1 :   gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_gpac_tf_pad_set_property);
     125            1 :   gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_gpac_tf_pad_get_property);
     126              : 
     127            1 :   g_object_class_install_property(
     128              :     gobject_class,
     129              :     GPAC_PROP_PAD_PID,
     130              :     g_param_spec_pointer("pid",
     131              :                          "GPAC's Filter PID",
     132              :                          "The PID of the filter in the GPAC session",
     133              :                          G_PARAM_READWRITE));
     134            1 : };
     135              : 
     136              : // #MARK: Element Class
     137              : #define gst_gpac_tf_parent_class parent_class
     138        12451 : G_DEFINE_TYPE(GstGpacTransform, gst_gpac_tf, GST_TYPE_AGGREGATOR);
     139              : 
     140              : // #MARK: Properties
     141              : static void
     142           14 : gst_gpac_tf_set_property(GObject* object,
     143              :                          guint prop_id,
     144              :                          const GValue* value,
     145              :                          GParamSpec* pspec)
     146              : {
     147           14 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(object);
     148           14 :   g_return_if_fail(GST_IS_GPAC_TF(object));
     149           14 :   if (gpac_set_property(GPAC_PROP_CTX(GPAC_CTX), prop_id, value, pspec))
     150           12 :     return;
     151              : 
     152              :   // Handle the element properties
     153            2 :   if (IS_ELEMENT_PROPERTY(prop_id)) {
     154            2 :     switch (prop_id) {
     155            2 :       case GPAC_PROP_SEGDUR:
     156            2 :         gpac_tf->global_idr_period =
     157            2 :           ((guint64)g_value_get_float(value)) * GST_SECOND;
     158            2 :         GST_DEBUG_OBJECT(object,
     159              :                          "Set global IDR period to %" GST_TIME_FORMAT,
     160              :                          GST_TIME_ARGS(gpac_tf->global_idr_period));
     161            2 :         break;
     162              : 
     163            0 :       default:
     164            0 :         break;
     165              :     }
     166            2 :     return;
     167              :   }
     168              : 
     169            0 :   G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     170              : }
     171              : 
     172              : static void
     173            0 : gst_gpac_tf_get_property(GObject* object,
     174              :                          guint prop_id,
     175              :                          GValue* value,
     176              :                          GParamSpec* pspec)
     177              : {
     178            0 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(object);
     179            0 :   g_return_if_fail(GST_IS_GPAC_TF(object));
     180            0 :   if (gpac_get_property(GPAC_PROP_CTX(GPAC_CTX), prop_id, value, pspec))
     181            0 :     return;
     182              : 
     183              :   // Handle the element properties
     184            0 :   if (IS_ELEMENT_PROPERTY(prop_id)) {
     185            0 :     switch (prop_id) {
     186            0 :       case GPAC_PROP_SEGDUR:
     187            0 :         g_value_set_float(value,
     188            0 :                           ((float)gpac_tf->global_idr_period) / GST_SECOND);
     189            0 :         break;
     190              : 
     191            0 :       default:
     192            0 :         break;
     193              :     }
     194            0 :     return;
     195              :   }
     196              : 
     197            0 :   G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     198              : }
     199              : 
     200              : // #MARK: Helper Functions
     201              : static gboolean
     202         2522 : gpac_prepare_pids(GstElement* element)
     203              : {
     204         2522 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(element);
     205              :   GstIterator* pad_iter;
     206         2522 :   GValue item = G_VALUE_INIT;
     207         2522 :   gboolean done = FALSE;
     208         2522 :   gboolean ret = FALSE;
     209         2522 :   gboolean caps_changed = FALSE;
     210              : 
     211              :   // Track configuration status
     212              :   GHashTable* pids_before_reset =
     213         2522 :     g_hash_table_new(g_direct_hash, g_direct_equal);
     214         2522 :   GHashTable* pids_seen = g_hash_table_new(g_direct_hash, g_direct_equal);
     215              : 
     216              :   // Iterate over the pads
     217         2522 :   pad_iter = gst_element_iterate_sink_pads(element);
     218        12370 :   while (!done) {
     219         9848 :     switch (gst_iterator_next(pad_iter, &item)) {
     220         7326 :       case GST_ITERATOR_OK: {
     221         7326 :         GstPad* pad = g_value_get_object(&item);
     222         7326 :         GstAggregatorPad* agg_pad = GST_AGGREGATOR_PAD(pad);
     223         7326 :         GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     224              : 
     225              :         // Get the PID
     226         7326 :         GF_FilterPid* pid = NULL;
     227         7326 :         g_object_get(agg_pad, "pid", &pid, NULL);
     228              : 
     229              :         // Create the PID if necessary
     230         7326 :         if (pid == NULL) {
     231           23 :           pid = gpac_pid_new(GPAC_SESS_CTX(GPAC_CTX));
     232           23 :           if (G_UNLIKELY(pid == NULL)) {
     233            0 :             GST_ELEMENT_ERROR(
     234              :               element, STREAM, FAILED, (NULL), ("Failed to create PID"));
     235            0 :             goto fail;
     236              :           }
     237           23 :           g_object_set(agg_pad, "pid", pid, NULL);
     238              : 
     239              :           // Share the pad private data
     240           23 :           gf_filter_pid_set_udta(pid, priv);
     241              :         }
     242              : 
     243         7326 :         if (priv->flags) {
     244           22 :           if (G_UNLIKELY(!gpac_pid_reconfigure(element, priv, pid))) {
     245            0 :             GST_ELEMENT_ERROR(
     246              :               element, STREAM, FAILED, (NULL), ("Failed to reconfigure PID"));
     247            0 :             goto fail;
     248              :           }
     249           22 :           priv->flags = 0;
     250           22 :           caps_changed |= priv->caps_changed;
     251              :         }
     252              : 
     253              :         // Track the PID
     254         7326 :         g_hash_table_insert(pids_seen, pid, GINT_TO_POINTER(1));
     255              : 
     256         7326 :         g_value_reset(&item);
     257         7326 :         break;
     258              :       }
     259            0 :       case GST_ITERATOR_RESYNC:
     260            0 :         gst_iterator_resync(pad_iter);
     261              : 
     262              :         // Add seen PIDs to the before reset list
     263            0 :         g_hash_table_foreach(
     264              :           pids_seen, (GHFunc)g_hash_table_insert, pids_before_reset);
     265            0 :         g_hash_table_remove_all(pids_seen);
     266              : 
     267            0 :         break;
     268            0 :       case GST_ITERATOR_ERROR:
     269            0 :         GST_ELEMENT_ERROR(
     270              :           element, STREAM, FAILED, (NULL), ("Failed to iterate over pads"));
     271            0 :         goto fail;
     272         2522 :       case GST_ITERATOR_DONE:
     273         2522 :         done = TRUE;
     274         2522 :         ret = TRUE;
     275         2522 :         break;
     276              :     }
     277              :   }
     278              : 
     279              :   // If caps changed, re-iterate over the pads to build the filter caps again
     280         2522 :   if (caps_changed) {
     281           13 :     GF_Filter* memin = GPAC_SESS_CTX(GPAC_CTX)->memin;
     282           13 :     g_assert(memin != NULL);
     283           13 :     gf_filter_override_caps(memin, NULL, 0);
     284           13 :     pad_iter = gst_element_iterate_sink_pads(element);
     285           13 :     done = FALSE;
     286           46 :     while (!done) {
     287           33 :       switch (gst_iterator_next(pad_iter, &item)) {
     288           20 :         case GST_ITERATOR_OK: {
     289           20 :           GstPad* pad = g_value_get_object(&item);
     290           20 :           GstAggregatorPad* agg_pad = GST_AGGREGATOR_PAD(pad);
     291           20 :           GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     292              : 
     293              :           // Get the PID
     294           20 :           GF_FilterPid* pid = NULL;
     295           20 :           g_object_get(agg_pad, "pid", &pid, NULL);
     296           20 :           if (pid == NULL)
     297            0 :             continue;
     298              : 
     299              :           // Start a new caps bundle
     300           80 :           for (GList* l = priv->gpac_caps; l != NULL; l = l->next) {
     301           60 :             GF_FilterCapability* cap = l->data;
     302           60 :             gf_filter_push_caps(
     303              :               memin, cap->code, &cap->val, NULL, GF_CAPS_OUTPUT, 0);
     304              :           }
     305           20 :           gf_filter_push_caps(memin, 0, NULL, NULL, 0, 0);
     306              : 
     307           20 :           priv->caps_changed = FALSE;
     308           20 :           g_value_reset(&item);
     309           20 :           break;
     310              :         }
     311            0 :         case GST_ITERATOR_RESYNC:
     312            0 :           gst_iterator_resync(pad_iter);
     313            0 :           break;
     314            0 :         case GST_ITERATOR_ERROR:
     315            0 :           GST_ELEMENT_ERROR(
     316              :             element, STREAM, FAILED, (NULL), ("Failed to iterate over pads"));
     317            0 :           goto fail;
     318           13 :         case GST_ITERATOR_DONE:
     319           13 :           done = TRUE;
     320           13 :           break;
     321              :       }
     322              :     }
     323              :   }
     324              : 
     325              :   // Check for PIDs that are no longer needed
     326              :   GHashTableIter iter;
     327              :   gpointer key, value;
     328         2522 :   g_hash_table_iter_init(&iter, pids_before_reset);
     329         5044 :   while (g_hash_table_iter_next(&iter, &key, &value)) {
     330            0 :     if (!g_hash_table_contains(pids_seen, key)) {
     331            0 :       GF_FilterPid* pid = key;
     332            0 :       gpac_pid_del(pid);
     333              :     }
     334              :   }
     335              : 
     336         2522 : fail:
     337              :   // Clean up
     338         2522 :   g_value_unset(&item);
     339         2522 :   gst_iterator_free(pad_iter);
     340         2522 :   g_hash_table_destroy(pids_before_reset);
     341         2522 :   g_hash_table_destroy(pids_seen);
     342         2522 :   return ret;
     343              : }
     344              : 
     345              : // #MARK: Aggregator
     346              : GstFlowReturn
     347         2506 : gst_gpac_tf_consume(GstAggregator* agg, Bool is_eos)
     348              : {
     349         2506 :   GstFlowReturn flow_ret = GST_FLOW_OK;
     350         2506 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(agg);
     351         2506 :   GstSegment* segment = &GST_AGGREGATOR_PAD(agg->srcpad)->segment;
     352              : 
     353         2506 :   GST_DEBUG_OBJECT(agg, "Consuming output...");
     354              : 
     355              :   void* output;
     356              :   GPAC_FilterPPRet ret;
     357         5042 :   while ((ret = gpac_memio_consume(GPAC_SESS_CTX(GPAC_CTX), &output))) {
     358         2536 :     if (ret & GPAC_FILTER_PP_RET_ERROR) {
     359              :       // An error occurred, stop processing
     360            0 :       goto error;
     361              :     }
     362              : 
     363         2536 :     if (ret == GPAC_FILTER_PP_RET_EMPTY) {
     364              :       // No data available
     365          906 :       GST_DEBUG_OBJECT(agg, "No more data available, exiting");
     366          906 :       return is_eos ? GST_FLOW_EOS : GST_FLOW_OK;
     367              :     }
     368              : 
     369         1630 :     gboolean had_signal = (ret & GPAC_FILTER_PP_RET_SIGNAL) != 0;
     370         1630 :     if (ret > GPAC_MAY_HAVE_BUFFER) {
     371         1630 :       if (output) {
     372              :         // Notify the selected sample
     373           45 :         gst_aggregator_selected_samples(agg,
     374           45 :                                         GST_BUFFER_PTS(output),
     375           45 :                                         GST_BUFFER_DTS(output),
     376           45 :                                         GST_BUFFER_DURATION(output),
     377              :                                         NULL);
     378              :       }
     379              : 
     380         1630 :       if (HAS_FLAG(ret, GPAC_FILTER_PP_RET_BUFFER)) {
     381              :         // Send the buffer
     382            0 :         GST_DEBUG_OBJECT(agg, "Sending buffer");
     383            0 :         flow_ret = gst_aggregator_finish_buffer(agg, GST_BUFFER(output));
     384            0 :         GST_DEBUG_OBJECT(agg, "Buffer sent!");
     385         1630 :       } else if (HAS_FLAG(ret, GPAC_FILTER_PP_RET_BUFFER_LIST)) {
     386              :         // Send the buffer list
     387           30 :         GST_DEBUG_OBJECT(agg, "Sending buffer list");
     388           30 :         GstBufferList* buffer_list = GST_BUFFER_LIST(output);
     389              : 
     390              :         // Show in debug the buffer list pts and dts (running time) and flags
     391         1109 :         for (guint i = 0; i < gst_buffer_list_length(buffer_list); i++) {
     392         1079 :           GstBuffer* buffer = gst_buffer_list_get(buffer_list, i);
     393              :           // Calculate pts and dts in running time
     394         1079 :           guint64 pts = gst_segment_to_running_time(
     395              :             segment, GST_FORMAT_TIME, GST_BUFFER_PTS(buffer));
     396         1079 :           guint64 dts = gst_segment_to_running_time(
     397              :             segment, GST_FORMAT_TIME, GST_BUFFER_DTS(buffer));
     398         1079 :           guint64 duration = gst_segment_to_running_time(
     399              :             segment, GST_FORMAT_TIME, GST_BUFFER_DURATION(buffer));
     400         1079 :           GST_DEBUG_OBJECT(
     401              :             agg,
     402              :             "Buffer %d: PTS: %" GST_TIME_FORMAT ", DTS: %" GST_TIME_FORMAT
     403              :             ", Duration: %" GST_TIME_FORMAT ", IsHeader: %s",
     404              :             i,
     405              :             GST_TIME_ARGS(pts),
     406              :             GST_TIME_ARGS(dts),
     407              :             GST_TIME_ARGS(duration),
     408              :             GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_HEADER) ? "Yes"
     409              :                                                                    : "No");
     410              :         }
     411              : 
     412           30 :         flow_ret = gst_aggregator_finish_buffer_list(agg, buffer_list);
     413           30 :         GST_DEBUG_OBJECT(agg, "Buffer list sent!");
     414         1600 :       } else if (HAS_FLAG(ret, GPAC_FILTER_PP_RET_NULL)) {
     415              :         // If we had signals, consume all of them first
     416         1600 :         if (had_signal)
     417            0 :           continue;
     418              : 
     419         1600 :         if (is_eos)
     420            5 :           return GST_FLOW_EOS;
     421              : 
     422         1595 :         GST_DEBUG_OBJECT(agg,
     423              :                          "Sending sync buffer, possibly connected to fakesink");
     424              : 
     425              :         // We send only one buffer regardless of potential pending buffers
     426         1595 :         GstBuffer* buffer = gpac_tf->sync_buffer;
     427         1595 :         if (!buffer)
     428          389 :           buffer = gst_buffer_new();
     429              : 
     430              :         // Send the sync buffer
     431         1595 :         flow_ret = gst_aggregator_finish_buffer(agg, buffer);
     432              : 
     433              :         // Buffer is transferred to the aggregator, so we set it to NULL
     434         1595 :         if (gpac_tf->sync_buffer)
     435         1206 :           gpac_tf->sync_buffer = NULL;
     436              : 
     437         1595 :         return flow_ret;
     438              :       } else {
     439            0 :         GST_ELEMENT_WARNING(
     440              :           agg, STREAM, FAILED, (NULL), ("Unknown return value: %d", ret));
     441            0 :         g_warn_if_reached();
     442              :       }
     443              : 
     444           30 :       if (flow_ret != GST_FLOW_OK) {
     445            0 :         GST_ELEMENT_ERROR(agg,
     446              :                           STREAM,
     447              :                           FAILED,
     448              :                           (NULL),
     449              :                           ("Failed to finish buffer, ret: %d", flow_ret));
     450            0 :         return flow_ret;
     451              :       }
     452              :     }
     453              :   }
     454              : 
     455            0 : error:
     456            0 :   GST_ELEMENT_ERROR(agg, STREAM, FAILED, (NULL), ("Failed to consume output"));
     457            0 :   return GST_FLOW_ERROR;
     458              : }
     459              : 
     460              : static gboolean
     461          139 : gst_gpac_tf_sink_event(GstAggregator* agg,
     462              :                        GstAggregatorPad* pad,
     463              :                        GstEvent* event)
     464              : {
     465          139 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(GST_ELEMENT(agg));
     466          139 :   GpacPadPrivate* priv = gst_pad_get_element_private(GST_PAD(pad));
     467              : 
     468          139 :   switch (GST_EVENT_TYPE(event)) {
     469           26 :     case GST_EVENT_CAPS: {
     470           26 :       if (priv->caps)
     471            6 :         gst_caps_unref(priv->caps);
     472              : 
     473              :       GstCaps* caps;
     474           26 :       gst_event_parse_caps(event, &caps);
     475           26 :       priv->caps = gst_caps_ref(caps);
     476           26 :       priv->flags |= GPAC_PAD_CAPS_SET;
     477           26 :       break;
     478              :     }
     479              : 
     480           23 :     case GST_EVENT_SEGMENT: {
     481           23 :       if (priv->segment)
     482            3 :         gst_segment_free(priv->segment);
     483              : 
     484              :       const GstSegment* segment;
     485           23 :       gst_event_parse_segment(event, &segment);
     486           23 :       priv->segment = gst_segment_copy(segment);
     487           23 :       priv->flags |= GPAC_PAD_SEGMENT_SET;
     488           23 :       priv->dts_offset_set = FALSE;
     489              : 
     490           23 :       gboolean is_video_pad = gst_pad_get_pad_template(GST_PAD(pad)) ==
     491           23 :                               gst_gpac_get_sink_template(GPAC_TEMPLATE_VIDEO);
     492           23 :       gboolean is_only_pad = g_list_length(GST_ELEMENT(agg)->sinkpads) == 1;
     493              : 
     494              :       // Update the segment and global offset only if video pad or the only pad
     495           23 :       if (is_video_pad || is_only_pad) {
     496           20 :         gst_aggregator_update_segment(agg, gst_segment_copy(segment));
     497           20 :         gpac_memio_set_global_offset(GPAC_SESS_CTX(GPAC_CTX), segment);
     498              :       }
     499              : 
     500              :       // Check if playback rate is equal to 1.0
     501           23 :       if (segment->rate != 1.0)
     502            0 :         GST_FIXME_OBJECT(agg,
     503              :                          "Handling of non-1.0 playback rate is not "
     504              :                          "implemented yet");
     505              : 
     506           23 :       break;
     507              :     }
     508              : 
     509           25 :     case GST_EVENT_TAG: {
     510           25 :       if (priv->tags)
     511            6 :         gst_tag_list_unref(priv->tags);
     512              : 
     513              :       GstTagList* tags;
     514           25 :       gst_event_parse_tag(event, &tags);
     515           25 :       priv->tags = gst_tag_list_ref(tags);
     516           25 :       priv->flags |= GPAC_PAD_TAGS_SET;
     517           25 :       break;
     518              :     }
     519              : 
     520           20 :     case GST_EVENT_EOS: {
     521              :       // Set this pad as EOS
     522           20 :       GF_FilterPid* pid = NULL;
     523           20 :       g_object_get(GST_AGGREGATOR_PAD(pad), "pid", &pid, NULL);
     524           20 :       g_assert(pid != NULL);
     525           20 :       gpac_memio_set_eos(GPAC_SESS_CTX(GPAC_CTX), pid);
     526           20 :       priv->eos = TRUE;
     527              : 
     528              :       // Are all pads EOS?
     529           20 :       gboolean all_eos = TRUE;
     530           20 :       GList* sinkpads = GST_ELEMENT(agg)->sinkpads;
     531           53 :       for (GList* l = sinkpads; l; l = l->next) {
     532           40 :         GstAggregatorPad* agg_pad = GST_AGGREGATOR_PAD(l->data);
     533           40 :         GpacPadPrivate* priv = gst_pad_get_element_private(GST_PAD(agg_pad));
     534           40 :         if (!priv->eos) {
     535            7 :           all_eos = FALSE;
     536            7 :           break;
     537              :         }
     538              :       }
     539              : 
     540           20 :       if (!all_eos) {
     541            7 :         GST_DEBUG_OBJECT(agg, "Not all pads are EOS, not sending EOS to GPAC");
     542            7 :         break;
     543              :       }
     544              : 
     545              :       // If all pads are EOS, send EOS to the source
     546           13 :       GST_DEBUG_OBJECT(agg, "All pads are EOS, sending EOS to GPAC");
     547           13 :       gpac_session_run(GPAC_SESS_CTX(GPAC_CTX), TRUE);
     548           13 :       gst_gpac_tf_consume(agg, GST_EVENT_TYPE(event) == GST_EVENT_EOS);
     549           13 :       break;
     550              :     }
     551              : 
     552            0 :     case GST_EVENT_FLUSH_START:
     553            0 :       gpac_session_run(GPAC_SESS_CTX(GPAC_CTX), TRUE);
     554            0 :       gst_gpac_tf_consume(agg, GST_EVENT_TYPE(event) == GST_EVENT_EOS);
     555            0 :       break;
     556              : 
     557           45 :     default:
     558           45 :       break;
     559              :   }
     560              : 
     561          139 :   return GST_AGGREGATOR_CLASS(parent_class)->sink_event(agg, pad, event);
     562              : }
     563              : 
     564              : gboolean
     565           14 : gst_gpac_tf_negotiated_src_caps(GstAggregator* agg, GstCaps* caps)
     566              : {
     567           14 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(GST_ELEMENT(agg));
     568           14 :   GstElement* element = GST_ELEMENT(agg);
     569           14 :   GObjectClass* klass = G_OBJECT_CLASS(G_TYPE_INSTANCE_GET_CLASS(
     570              :     G_OBJECT(element), GST_TYPE_GPAC_TF, GstGpacTransformClass));
     571           14 :   GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
     572              : 
     573              :   // Check if this is element is inside our sink bin
     574           14 :   GstObject* sink_bin = gst_element_get_parent(element);
     575           14 :   if (GST_IS_GPAC_SINK(sink_bin)) {
     576              :     // We might already have a destination set
     577            5 :     if ((params->is_single && params->info->destination) ||
     578            4 :         GPAC_PROP_CTX(GPAC_CTX)->destination) {
     579            2 :       return TRUE;
     580              :     }
     581              :   }
     582              : 
     583           12 :   return gpac_memio_set_gst_caps(GPAC_SESS_CTX(GPAC_CTX), caps);
     584              : }
     585              : 
     586              : void
     587         4637 : gst_gpac_request_idr(GstAggregator* agg, GstPad* pad, GstBuffer* buffer)
     588              : {
     589         4637 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(GST_ELEMENT(agg));
     590         4637 :   GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     591              :   GstEvent* gst_event;
     592              : 
     593              :   // Skip if this is not a key frame
     594         4637 :   if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT))
     595         4538 :     return;
     596              : 
     597              :   // Skip if we don't have a valid PTS
     598           99 :   if (!GST_BUFFER_PTS_IS_VALID(buffer))
     599            0 :     return;
     600              : 
     601              :   // Decide on which IDR period to use
     602           99 :   guint64 idr_period = GST_CLOCK_TIME_NONE;
     603           99 :   if (priv->idr_period != GST_CLOCK_TIME_NONE) {
     604           79 :     idr_period = priv->idr_period;
     605              :     // Preserve the IDR period sent by gpac
     606           79 :     gpac_tf->gpac_idr_period = idr_period;
     607              :   }
     608              : 
     609              :   // Use the global IDR period if available
     610           99 :   if (gpac_tf->global_idr_period)
     611            4 :     idr_period = gpac_tf->global_idr_period;
     612              : 
     613              :   // Skip if we don't have an IDR period
     614           99 :   if (idr_period == GST_CLOCK_TIME_NONE)
     615           16 :     return;
     616              : 
     617          166 :   priv->idr_last = gst_segment_to_running_time(
     618           83 :     priv->segment, GST_FORMAT_TIME, GST_BUFFER_PTS(buffer));
     619              : 
     620           83 :   GST_DEBUG_OBJECT(agg,
     621              :                    "Key frame received at %" GST_TIME_FORMAT,
     622              :                    GST_TIME_ARGS(priv->idr_last));
     623              : 
     624              :   // If this is the first IDR, request it immediately
     625           83 :   if (priv->idr_next == GST_CLOCK_TIME_NONE) {
     626            8 :     priv->idr_next = priv->idr_last + idr_period;
     627            8 :     goto request;
     628              :   }
     629              : 
     630              :   // If this IDR arrived before the next scheduled IDR, ignore
     631           75 :   if (priv->idr_last < priv->idr_next) {
     632           56 :     guint64 diff = priv->idr_next - priv->idr_last;
     633           56 :     GST_DEBUG_OBJECT(agg,
     634              :                      "IDR arrived %" GST_TIME_FORMAT
     635              :                      " before the next IDR on pad %s",
     636              :                      GST_TIME_ARGS(diff),
     637              :                      GST_PAD_NAME(pad));
     638           56 :     return;
     639              :   }
     640              : 
     641              :   // Check if this IDR was on time
     642           19 :   guint64 diff = priv->idr_last - priv->idr_next;
     643           19 :   if (diff)
     644            0 :     GST_ELEMENT_WARNING(agg,
     645              :                         STREAM,
     646              :                         FAILED,
     647              :                         ("IDR was late by %" GST_TIME_FORMAT
     648              :                          " on pad %s, reconsider encoding options",
     649              :                          GST_TIME_ARGS(diff),
     650              :                          GST_PAD_NAME(pad)),
     651              :                         (NULL));
     652              : 
     653              :   // Schedule the next IDR at the desired time, regardless of whether current
     654              :   // one was late or not
     655           19 :   priv->idr_next += idr_period;
     656              : 
     657           27 : request:
     658              :   // Send the next IDR request
     659              :   gst_event =
     660           27 :     gst_video_event_new_upstream_force_key_unit(priv->idr_next, TRUE, 1);
     661           27 :   GST_DEBUG_OBJECT(
     662              :     agg, "Requesting IDR at %" GST_TIME_FORMAT, GST_TIME_ARGS(priv->idr_next));
     663           27 :   if (!gst_pad_push_event(pad, gst_event))
     664            0 :     GST_ELEMENT_WARNING(
     665              :       agg, STREAM, FAILED, (NULL), ("Failed to push the force key unit event"));
     666              : }
     667              : 
     668              : static GstFlowReturn
     669         2506 : gst_gpac_tf_aggregate(GstAggregator* agg, gboolean timeout)
     670              : {
     671         2506 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(GST_ELEMENT(agg));
     672              :   GstIterator* pad_iter;
     673         2506 :   GValue item = G_VALUE_INIT;
     674         2506 :   gboolean done = FALSE;
     675         2506 :   gboolean has_buffers = TRUE;
     676              : 
     677              :   // Check and create PIDs if necessary
     678         2506 :   if (!gpac_prepare_pids(GST_ELEMENT(agg))) {
     679            0 :     GST_ELEMENT_ERROR(agg, STREAM, FAILED, (NULL), ("Failed to prepare PIDs"));
     680            0 :     return GST_FLOW_ERROR;
     681              :   }
     682              : 
     683         2506 :   GST_DEBUG_OBJECT(agg, "Aggregating buffers");
     684              : 
     685              :   // Create the temporary queue
     686         2506 :   GQueue* queue = g_queue_new();
     687              : 
     688              :   // Keep consuming buffers until all pads are drained
     689         8108 :   while (has_buffers) {
     690         5602 :     has_buffers = FALSE;
     691         5602 :     done = FALSE;
     692              : 
     693              :     // Iterate over the pads
     694         5602 :     pad_iter = gst_element_iterate_sink_pads(GST_ELEMENT(agg));
     695        27772 :     while (!done) {
     696        22170 :       switch (gst_iterator_next(pad_iter, &item)) {
     697        16568 :         case GST_ITERATOR_OK: {
     698        16568 :           GF_FilterPid* pid = NULL;
     699        16568 :           GstPad* pad = g_value_get_object(&item);
     700        16568 :           GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     701              :           GstBuffer* buffer =
     702        16568 :             gst_aggregator_pad_pop_buffer(GST_AGGREGATOR_PAD(pad));
     703              : 
     704              :           // Continue if no buffer is available
     705        16568 :           if (!buffer) {
     706        10044 :             GST_DEBUG_OBJECT(
     707              :               agg, "No buffer available on pad %s", GST_PAD_NAME(pad));
     708        10044 :             goto next;
     709              :           }
     710              : 
     711              :           // We found at least one buffer, continue the outer loop
     712         6524 :           has_buffers = TRUE;
     713              : 
     714              :           // Skip droppable/gap buffers
     715         6524 :           if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_GAP)) {
     716            0 :             GST_DEBUG_OBJECT(
     717              :               agg, "Gap buffer received on pad %s", GST_PAD_NAME(pad));
     718            0 :             goto next;
     719              :           }
     720              : 
     721              :           // Send the key frame request
     722              :           // Only send IDR request for video pads
     723        13048 :           if (gst_pad_get_pad_template(pad) ==
     724         6524 :               gst_gpac_get_sink_template(GPAC_TEMPLATE_VIDEO)) {
     725         4637 :             gst_gpac_request_idr(agg, pad, buffer);
     726              :           }
     727              : 
     728              :           // Get the PID
     729         6524 :           g_object_get(GST_AGGREGATOR_PAD(pad), "pid", &pid, NULL);
     730         6524 :           g_assert(pid);
     731              : 
     732              :           // Create the packet
     733         6524 :           GF_FilterPacket* packet = gpac_pck_new_from_buffer(buffer, priv, pid);
     734         6524 :           if (!packet) {
     735            0 :             GST_ELEMENT_ERROR(agg,
     736              :                               STREAM,
     737              :                               FAILED,
     738              :                               (NULL),
     739              :                               ("Failed to create packet from buffer"));
     740            0 :             goto next;
     741              :           }
     742              : 
     743              :           // Enqueue the packet
     744         6524 :           g_queue_push_tail(queue, packet);
     745              : 
     746              :           // Select the highest PTS for sync buffer
     747         6524 :           gboolean is_video_pad =
     748         6524 :             gst_pad_get_pad_template(GST_PAD(pad)) ==
     749         6524 :             gst_gpac_get_sink_template(GPAC_TEMPLATE_VIDEO);
     750         6524 :           gboolean is_only_pad = g_list_length(GST_ELEMENT(agg)->sinkpads) == 1;
     751         6524 :           if (is_video_pad || is_only_pad) {
     752         4637 :             if (gpac_tf->sync_buffer) {
     753         3423 :               guint64 current_pts = GST_BUFFER_PTS(buffer);
     754         3423 :               guint64 sync_pts = GST_BUFFER_PTS(gpac_tf->sync_buffer);
     755         3423 :               if (current_pts > sync_pts) {
     756          908 :                 gst_buffer_replace(&gpac_tf->sync_buffer, buffer);
     757              :               }
     758              :             } else {
     759              :               // If no sync buffer exists, create one
     760         1214 :               gpac_tf->sync_buffer = gst_buffer_ref(buffer);
     761              :             }
     762              :           }
     763              : 
     764         1887 :         next:
     765        16568 :           if (buffer)
     766         6524 :             gst_buffer_unref(buffer);
     767        16568 :           g_value_reset(&item);
     768        16568 :           break;
     769              :         }
     770            0 :         case GST_ITERATOR_RESYNC:
     771            0 :           gst_iterator_resync(pad_iter);
     772            0 :           GST_ELEMENT_WARNING(agg,
     773              :                               STREAM,
     774              :                               FAILED,
     775              :                               (NULL),
     776              :                               ("Data structure changed during pad iteration, "
     777              :                                "discarding all packets"));
     778            0 :           g_queue_clear_full(queue, (GDestroyNotify)gf_filter_pck_unref);
     779            0 :           break;
     780         5602 :         case GST_ITERATOR_ERROR:
     781              :         case GST_ITERATOR_DONE:
     782         5602 :           done = TRUE;
     783         5602 :           break;
     784              :       }
     785              :     }
     786              :   }
     787              : 
     788              :   // Clean up
     789         2506 :   g_value_unset(&item);
     790         2506 :   gst_iterator_free(pad_iter);
     791              : 
     792              :   // Check if we have any packets to send
     793         2506 :   if (g_queue_is_empty(queue)) {
     794           13 :     GST_DEBUG_OBJECT(agg, "No packets to send, returning EOS");
     795           13 :     g_queue_free(queue);
     796           13 :     return GST_FLOW_EOS;
     797              :   }
     798              : 
     799              :   // Merge the queues
     800         9017 :   while (!g_queue_is_empty(queue)) {
     801         6524 :     gpointer pck = g_queue_pop_head(queue);
     802         6524 :     g_queue_push_tail(gpac_tf->queue, pck);
     803              :   }
     804         2493 :   g_queue_free(queue);
     805              : 
     806              :   // Run the filter session
     807         2493 :   if (gpac_session_run(GPAC_SESS_CTX(GPAC_CTX), FALSE) != GF_OK) {
     808            0 :     GST_ELEMENT_ERROR(
     809              :       agg, STREAM, FAILED, (NULL), ("Failed to run the GPAC session"));
     810            0 :     return GST_FLOW_ERROR;
     811              :   }
     812              : 
     813              :   // Consume the output
     814         2493 :   return gst_gpac_tf_consume(agg, FALSE);
     815              : }
     816              : 
     817              : // #MARK: Pad Management
     818              : static GstAggregatorPad*
     819           20 : gst_gpac_tf_create_new_pad(GstAggregator* element,
     820              :                            GstPadTemplate* templ,
     821              :                            const gchar* pad_name,
     822              :                            const GstCaps* caps)
     823              : {
     824           20 :   GstElementClass* klass = GST_ELEMENT_GET_CLASS(element);
     825           20 :   GstGpacTransform* agg = GST_GPAC_TF(element);
     826              :   gchar* name;
     827              :   gint pad_id;
     828              : 
     829              : #define TEMPLATE_CHECK(prefix, count_field)                                    \
     830              :   if (templ == gst_element_class_get_pad_template(klass, prefix "_%u")) {      \
     831              :     agg->count_field++;                                                        \
     832              :     if (pad_name != NULL && sscanf(pad_name, prefix "_%u", &pad_id) == 1) {    \
     833              :       name = g_strdup(pad_name);                                               \
     834              :     } else {                                                                   \
     835              :       pad_id =                                                                 \
     836              :         agg->audio_pad_count + agg->video_pad_count + agg->subtitle_pad_count; \
     837              :       name = g_strdup_printf(prefix "_%u", pad_id);                            \
     838              :     }                                                                          \
     839              :   } else
     840              : 
     841              :   // Check the pad template and decide the pad name
     842           20 :   TEMPLATE_CHECK("video", video_pad_count)
     843            3 :   TEMPLATE_CHECK("audio", audio_pad_count)
     844            0 :   TEMPLATE_CHECK("subtitle", subtitle_pad_count)
     845              :   {
     846            0 :     GST_ELEMENT_WARNING(
     847              :       agg, STREAM, FAILED, (NULL), ("This is not our template!"));
     848            0 :     return NULL;
     849              :   }
     850              : 
     851              : #undef TEMPLATE_CHECK
     852              : 
     853           20 :   GST_DEBUG_OBJECT(agg, "Creating new pad %s", name);
     854              : 
     855              :   // Create the pad
     856           20 :   GstGpacTransformPad* pad = g_object_new(GST_TYPE_GPAC_TF_PAD,
     857              :                                           "name",
     858              :                                           name,
     859              :                                           "direction",
     860           20 :                                           templ->direction,
     861              :                                           "template",
     862              :                                           templ,
     863              :                                           NULL);
     864           20 :   g_free(name);
     865              : 
     866              :   // Initialize the private data
     867           20 :   GpacPadPrivate* priv = gst_pad_get_element_private(GST_PAD(pad));
     868           20 :   priv->id = pad_id;
     869           20 :   if (caps) {
     870            0 :     priv->caps = gst_caps_copy(caps);
     871            0 :     priv->flags |= GPAC_PAD_CAPS_SET;
     872              :   }
     873              : 
     874           20 :   return GST_AGGREGATOR_PAD(pad);
     875              : }
     876              : 
     877              : // #MARK: Lifecycle
     878              : static void
     879           29 : gst_gpac_tf_reset(GstGpacTransform* tf)
     880              : {
     881           29 :   gboolean done = FALSE;
     882           29 :   GValue item = G_VALUE_INIT;
     883           29 :   GstIterator* pad_iter = gst_element_iterate_sink_pads(GST_ELEMENT(tf));
     884           81 :   while (!done) {
     885           52 :     switch (gst_iterator_next(pad_iter, &item)) {
     886           23 :       case GST_ITERATOR_OK: {
     887           23 :         GF_FilterPid* pid = NULL;
     888           23 :         GstPad* pad = g_value_get_object(&item);
     889           23 :         GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     890              : 
     891              :         // Reset the PID
     892           23 :         g_object_set(GST_AGGREGATOR_PAD(pad), "pid", NULL, NULL);
     893           23 :         break;
     894              :       }
     895           29 :       case GST_ITERATOR_RESYNC:
     896              :       case GST_ITERATOR_ERROR:
     897              :       case GST_ITERATOR_DONE:
     898           29 :         done = TRUE;
     899           29 :         break;
     900              :     }
     901              :   }
     902           29 :   g_value_unset(&item);
     903           29 :   gst_iterator_free(pad_iter);
     904              : 
     905              :   // Empty the queue
     906           29 :   if (tf->queue) {
     907           16 :     if (!g_queue_is_empty(tf->queue))
     908            0 :       GST_ERROR_OBJECT(tf,
     909              :                        "GPAC queue not empty during reset, pipeline error?");
     910           16 :     g_queue_clear(tf->queue);
     911              :   }
     912           29 : }
     913              : 
     914              : static gboolean
     915           16 : gst_gpac_tf_start(GstAggregator* aggregator)
     916              : {
     917           16 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(aggregator);
     918           16 :   GstElement* element = GST_ELEMENT(aggregator);
     919           16 :   GObjectClass* klass = G_OBJECT_CLASS(G_TYPE_INSTANCE_GET_CLASS(
     920              :     G_OBJECT(element), GST_TYPE_GPAC_TF, GstGpacTransformClass));
     921           16 :   GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
     922              :   GstSegment segment;
     923              : 
     924              :   // Check if we have the graph property set
     925           16 :   if (!params->is_single && !GPAC_PROP_CTX(GPAC_CTX)->graph) {
     926            0 :     GST_ELEMENT_ERROR(
     927              :       element, STREAM, FAILED, (NULL), ("Graph property must be set"));
     928            0 :     return FALSE;
     929              :   }
     930              : 
     931              :   // Convert the properties to arguments
     932           16 :   if (!gpac_apply_properties(GPAC_PROP_CTX(GPAC_CTX))) {
     933            0 :     GST_ELEMENT_ERROR(
     934              :       element, LIBRARY, INIT, (NULL), ("Failed to apply properties"));
     935            0 :     return FALSE;
     936              :   }
     937              :   // Initialize the GPAC context
     938           16 :   if (!gpac_init(GPAC_CTX, element)) {
     939            0 :     GST_ELEMENT_ERROR(
     940              :       element, LIBRARY, INIT, (NULL), ("Failed to initialize GPAC context"));
     941            0 :     return FALSE;
     942              :   }
     943              : 
     944              :   // Create the session
     945           16 :   if (!gpac_session_init(GPAC_SESS_CTX(GPAC_CTX), element, params)) {
     946            0 :     GST_ELEMENT_ERROR(
     947              :       element, LIBRARY, INIT, (NULL), ("Failed to initialize GPAC session"));
     948            0 :     return FALSE;
     949              :   }
     950              : 
     951              :   // Create the memory input
     952           16 :   gpac_return_val_if_fail(
     953              :     gpac_memio_new(GPAC_SESS_CTX(GPAC_CTX), GPAC_MEMIO_DIR_IN), FALSE);
     954           16 :   gpac_memio_assign_queue(
     955              :     GPAC_SESS_CTX(GPAC_CTX), GPAC_MEMIO_DIR_IN, gpac_tf->queue);
     956              : 
     957              :   // Open the session
     958           16 :   gchar* graph = NULL;
     959           16 :   if (params->is_single) {
     960           12 :     if (params->info->default_options) {
     961           11 :       GList* props = GPAC_PROP_CTX(GPAC_CTX)->properties;
     962           11 :       GString* options = g_string_new(NULL);
     963              : 
     964              :       // Only override if not set already
     965           34 :       for (guint32 i = 0; params->info->default_options[i].name; i++) {
     966           23 :         gboolean found = FALSE;
     967              : 
     968           44 :         for (GList* l = props; l != NULL; l = l->next) {
     969           21 :           gchar* prop = (gchar*)l->data;
     970              : 
     971           21 :           g_autofree gchar* prefix =
     972           21 :             g_strdup_printf("--%s", params->info->default_options[i].name);
     973           21 :           if (g_str_has_prefix(prop, prefix)) {
     974            0 :             found = TRUE;
     975            0 :             break;
     976              :           }
     977              :         }
     978              : 
     979           23 :         if (!found) {
     980           23 :           const gchar* name = params->info->default_options[i].name;
     981           23 :           const gchar* value = params->info->default_options[i].value;
     982           23 :           g_string_append_printf(options, "%s=%s:", name, value);
     983              :         }
     984              :       }
     985              : 
     986              :       // Remove the trailing colon
     987           11 :       if (options->len > 0)
     988           11 :         g_string_truncate(options, options->len - 1);
     989              : 
     990           11 :       if (options->len > 0)
     991              :         graph =
     992           11 :           g_strdup_printf("%s:%s", params->info->filter_name, options->str);
     993              :       else
     994            0 :         graph = g_strdup(params->info->filter_name);
     995           11 :       g_string_free(options, TRUE);
     996              :     } else {
     997            2 :       graph = g_strdup(params->info->filter_name);
     998              :     }
     999              :   } else {
    1000            8 :     graph = g_strdup(GPAC_PROP_CTX(GPAC_CTX)->graph);
    1001              :   }
    1002              : 
    1003              :   // Set the destination override on session context
    1004           16 :   GPAC_SESS_CTX(GPAC_CTX)->destination = GPAC_PROP_CTX(GPAC_CTX)->destination;
    1005              : 
    1006           16 :   gpac_return_val_if_fail(gpac_session_open(GPAC_SESS_CTX(GPAC_CTX), graph),
    1007              :                           FALSE);
    1008           16 :   g_free(graph);
    1009              : 
    1010              :   // Create the memory output
    1011           16 :   gboolean is_inside_sink = GST_IS_GPAC_SINK(gst_element_get_parent(element));
    1012           16 :   gboolean requires_memout =
    1013           16 :     params->info && GPAC_SE_IS_REQUIRES_MEMOUT(params->info->flags);
    1014           20 :   requires_memout = !is_inside_sink || (is_inside_sink && requires_memout) ||
    1015            4 :                     GPAC_PROP_CTX(GPAC_CTX)->destination;
    1016              : 
    1017           16 :   if (requires_memout) {
    1018           13 :     gpac_return_val_if_fail(
    1019              :       gpac_memio_new(GPAC_SESS_CTX(GPAC_CTX), GPAC_MEMIO_DIR_OUT), FALSE);
    1020              :   }
    1021              : 
    1022              :   // Check if the session has an output
    1023           16 :   if (!gpac_session_has_output(GPAC_SESS_CTX(GPAC_CTX))) {
    1024            0 :     GST_ELEMENT_ERROR(
    1025              :       element, STREAM, FAILED, (NULL), ("Session has no output"));
    1026            0 :     return FALSE;
    1027              :   }
    1028              : 
    1029              :   // Initialize the PIDs for all pads
    1030           16 :   if (!gpac_prepare_pids(element)) {
    1031            0 :     GST_ELEMENT_ERROR(
    1032              :       element, LIBRARY, FAILED, (NULL), ("Failed to prepare PIDs"));
    1033            0 :     return FALSE;
    1034              :   }
    1035           16 :   GST_DEBUG_OBJECT(element, "GPAC session started");
    1036              : 
    1037              :   // Initialize the segment
    1038           16 :   gst_segment_init(&segment, GST_FORMAT_TIME);
    1039           16 :   gst_aggregator_update_segment(aggregator, &segment);
    1040           16 :   return TRUE;
    1041              : }
    1042              : 
    1043              : static gboolean
    1044           16 : gst_gpac_tf_stop(GstAggregator* aggregator)
    1045              : {
    1046           16 :   GstElement* element = GST_ELEMENT(aggregator);
    1047           16 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(aggregator);
    1048           16 :   GObjectClass* klass = G_OBJECT_CLASS(G_TYPE_INSTANCE_GET_CLASS(
    1049              :     G_OBJECT(element), GST_TYPE_GPAC_TF, GstGpacTransformClass));
    1050           16 :   GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
    1051              : 
    1052              :   // Abort the session
    1053           16 :   gpac_memio_set_eos(GPAC_SESS_CTX(GPAC_CTX), NULL);
    1054           16 :   gpac_session_abort(GPAC_SESS_CTX(GPAC_CTX));
    1055              : 
    1056              :   // Close the session
    1057           16 :   if (!gpac_session_close(GPAC_SESS_CTX(GPAC_CTX),
    1058              :                           GPAC_PROP_CTX(GPAC_CTX)->print_stats)) {
    1059            0 :     GST_ELEMENT_ERROR(
    1060              :       element, LIBRARY, SHUTDOWN, (NULL), ("Failed to close GPAC session"));
    1061            0 :     return FALSE;
    1062              :   }
    1063              : 
    1064              :   // Reset the element
    1065           16 :   gst_gpac_tf_reset(gpac_tf);
    1066              : 
    1067              :   // Destroy the GPAC context
    1068           16 :   gpac_destroy(GPAC_CTX);
    1069           16 :   GST_DEBUG_OBJECT(element, "GPAC session stopped");
    1070           16 :   return TRUE;
    1071              : }
    1072              : 
    1073              : static void
    1074            0 : gst_gpac_tf_finalize(GObject* object)
    1075              : {
    1076            0 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(object);
    1077            0 :   GPAC_PropertyContext* ctx = GPAC_PROP_CTX(GPAC_CTX);
    1078              : 
    1079              :   // Free the properties
    1080            0 :   g_list_free(ctx->properties);
    1081            0 :   ctx->properties = NULL;
    1082              : 
    1083            0 :   if (ctx->props_as_argv) {
    1084            0 :     for (u32 i = 0; ctx->props_as_argv[i]; i++)
    1085            0 :       g_free(ctx->props_as_argv[i]);
    1086            0 :     g_free((void*)ctx->props_as_argv);
    1087              :   }
    1088              : 
    1089              :   // Free the queue
    1090            0 :   if (gpac_tf->queue) {
    1091            0 :     g_assert(g_queue_is_empty(gpac_tf->queue));
    1092            0 :     g_queue_free(gpac_tf->queue);
    1093            0 :     gpac_tf->queue = NULL;
    1094              :   }
    1095              : 
    1096            0 :   G_OBJECT_CLASS(parent_class)->finalize(object);
    1097            0 : }
    1098              : 
    1099              : // #MARK: Initialization
    1100              : static void
    1101           13 : gst_gpac_tf_init(GstGpacTransform* tf)
    1102              : {
    1103           13 :   gst_gpac_tf_reset(tf);
    1104           13 :   tf->queue = g_queue_new();
    1105           13 : }
    1106              : 
    1107              : static void
    1108            2 : gst_gpac_tf_class_init(GstGpacTransformClass* klass)
    1109              : {
    1110              :   // Initialize the class
    1111            2 :   GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
    1112            2 :   GstElementClass* gstelement_class = GST_ELEMENT_CLASS(klass);
    1113            2 :   GstAggregatorClass* gstaggregator_class = GST_AGGREGATOR_CLASS(klass);
    1114            2 :   parent_class = g_type_class_peek_parent(klass);
    1115              : 
    1116              :   // Install the pad templates
    1117            2 :   gpac_install_sink_pad_templates(gstelement_class);
    1118              : 
    1119              :   // Set the pad management functions
    1120            2 :   gstaggregator_class->create_new_pad =
    1121            2 :     GST_DEBUG_FUNCPTR(gst_gpac_tf_create_new_pad);
    1122              : 
    1123              :   // Set the aggregator functions
    1124            2 :   gstaggregator_class->sink_event = GST_DEBUG_FUNCPTR(gst_gpac_tf_sink_event);
    1125            2 :   gstaggregator_class->aggregate = GST_DEBUG_FUNCPTR(gst_gpac_tf_aggregate);
    1126            2 :   gstaggregator_class->negotiated_src_caps =
    1127            2 :     GST_DEBUG_FUNCPTR(gst_gpac_tf_negotiated_src_caps);
    1128            2 :   gstaggregator_class->start = GST_DEBUG_FUNCPTR(gst_gpac_tf_start);
    1129            2 :   gstaggregator_class->stop = GST_DEBUG_FUNCPTR(gst_gpac_tf_stop);
    1130              : 
    1131            2 :   gst_type_mark_as_plugin_api(GST_TYPE_GPAC_TF_PAD, 0);
    1132            2 : }
    1133              : 
    1134              : // #MARK: Registration
    1135              : static void
    1136            8 : gst_gpac_tf_subclass_init(GstGpacTransformClass* klass)
    1137              : {
    1138            8 :   GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
    1139            8 :   GstElementClass* gstelement_class = GST_ELEMENT_CLASS(klass);
    1140            8 :   GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
    1141              : 
    1142              :   // Set the finalizer
    1143            8 :   gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_gpac_tf_finalize);
    1144              : 
    1145              :   // Set the property handlers
    1146            8 :   gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_gpac_tf_set_property);
    1147            8 :   gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_gpac_tf_get_property);
    1148            8 :   gpac_install_global_properties(gobject_class);
    1149            8 :   gpac_install_local_properties(
    1150              :     gobject_class, GPAC_PROP_PRINT_STATS, GPAC_PROP_0);
    1151              : 
    1152              :   // Add the subclass-specific properties and pad templates
    1153            8 :   if (params->is_single) {
    1154            6 :     if (params->is_inside_sink) {
    1155            1 :       gst_element_class_add_static_pad_template(gstelement_class,
    1156              :                                                 &internal_pad_template);
    1157              :     } else {
    1158            5 :       gst_element_class_add_static_pad_template(gstelement_class,
    1159            5 :                                                 &params->info->src_template);
    1160              :     }
    1161              : 
    1162              :     // Set property blacklist
    1163            6 :     GList* blacklist = NULL;
    1164            6 :     if (params->info->default_options) {
    1165            3 :       filter_option* opt = params->info->default_options;
    1166           10 :       for (u32 i = 0; opt[i].name; i++) {
    1167            7 :         if (opt[i].forced)
    1168            4 :           blacklist = g_list_append(blacklist, (gpointer)opt[i].name);
    1169              :       }
    1170              :     }
    1171              : 
    1172              :     // Install the filter properties
    1173            6 :     gpac_install_filter_properties(
    1174            6 :       gobject_class, blacklist, params->info->filter_name);
    1175            6 :     g_list_free(blacklist);
    1176              : 
    1177              :     // Install the signals if not inside the sink bin
    1178              :     // They would be installed by the sink bin itself
    1179            6 :     if (!params->is_inside_sink && params->info->signal_presets) {
    1180            0 :       gpac_install_signals_by_presets(gobject_class,
    1181            0 :                                       params->info->signal_presets);
    1182              :     }
    1183              : 
    1184              :     // Check if we have any filter options to expose
    1185            8 :     for (u32 i = 0; i < G_N_ELEMENTS(filter_options); i++) {
    1186            6 :       filter_option_overrides* opts = &filter_options[i];
    1187            6 :       if (g_strcmp0(opts->filter_name, params->info->filter_name) == 0) {
    1188            8 :         for (u32 j = 0; opts->options[j]; j++) {
    1189            4 :           guint32 prop_id = opts->options[j];
    1190            4 :           gpac_install_local_properties(gobject_class, prop_id, GPAC_PROP_0);
    1191              :         }
    1192            4 :         break;
    1193              :       }
    1194              :     }
    1195              :   } else {
    1196            2 :     gpac_install_src_pad_templates(gstelement_class);
    1197            2 :     gpac_install_local_properties(
    1198              :       gobject_class, GPAC_PROP_GRAPH, GPAC_PROP_DESTINATION, GPAC_PROP_0);
    1199              : 
    1200            2 :     if (!params->is_inside_sink)
    1201            1 :       gpac_install_all_signals(gobject_class);
    1202              : 
    1203              :     // We don't know which filters will be used, so we expose all options
    1204            4 :     for (u32 i = 0; i < G_N_ELEMENTS(filter_options); i++) {
    1205            2 :       filter_option_overrides* opts = &filter_options[i];
    1206            4 :       for (u32 j = 0; opts->options[j]; j++) {
    1207            2 :         guint32 prop_id = opts->options[j];
    1208            2 :         gpac_install_local_properties(gobject_class, prop_id, GPAC_PROP_0);
    1209              :       }
    1210              :     }
    1211              :   }
    1212              : 
    1213              :   // Set the metadata
    1214            8 :   const gchar* longname = "gpac transformer";
    1215            8 :   if (params->is_single) {
    1216            6 :     if (!g_strcmp0(params->info->filter_name, params->info->alias_name))
    1217              :       longname =
    1218            2 :         g_strdup_printf("gpac %s transformer", params->info->filter_name);
    1219              :     else
    1220            4 :       longname = g_strdup_printf("gpac %s (%s) transformer",
    1221            4 :                                  params->info->alias_name,
    1222            4 :                                  params->info->filter_name);
    1223              :   }
    1224            8 :   gst_element_class_set_static_metadata(
    1225              :     gstelement_class,
    1226              :     longname,
    1227              :     "Aggregator/Transform",
    1228              :     "Aggregates and transforms incoming data via GPAC",
    1229              :     "Deniz Ugur <deniz.ugur@motionspell.com>");
    1230            8 : }
    1231              : 
    1232              : gboolean
    1233            2 : gst_gpac_tf_register(GstPlugin* plugin)
    1234              : {
    1235              :   GType type;
    1236              :   GstGpacParams* params;
    1237            2 :   GTypeInfo subclass_typeinfo = {
    1238              :     sizeof(GstGpacTransformClass),
    1239              :     NULL, // base_init
    1240              :     NULL, // base_finalize
    1241              :     (GClassInitFunc)gst_gpac_tf_subclass_init,
    1242              :     NULL, // class_finalize
    1243              :     NULL, // class_data
    1244              :     sizeof(GstGpacTransform),
    1245              :     0,
    1246              :     NULL, // instance_init
    1247              :   };
    1248              : 
    1249            2 :   GST_DEBUG_CATEGORY_INIT(gst_gpac_tf_debug, "gpactf", 0, "GPAC Transform");
    1250              : 
    1251              :   // Register the regular transform element
    1252            2 :   GST_LOG("Registering regular gpac transform element");
    1253            2 :   params = g_new0(GstGpacParams, 1);
    1254            2 :   params->is_single = FALSE;
    1255            2 :   params->is_inside_sink = FALSE;
    1256            2 :   type = g_type_register_static(
    1257              :     GST_TYPE_GPAC_TF, "GstGpacTransformRegular", &subclass_typeinfo, 0);
    1258            2 :   g_type_set_qdata(type, GST_GPAC_PARAMS_QDATA, params);
    1259            2 :   if (!gst_element_register(plugin, "gpactf", GST_RANK_PRIMARY, type)) {
    1260            0 :     GST_ERROR_OBJECT(plugin,
    1261              :                      "Failed to register regular gpac transform element");
    1262            0 :     return FALSE;
    1263              :   }
    1264              : 
    1265              :   // Register subelements
    1266           10 :   for (u32 i = 0; i < G_N_ELEMENTS(subelements); i++) {
    1267            8 :     subelement_info* info = &subelements[i];
    1268              : 
    1269            8 :     if (info->flags & GPAC_SE_SINK_ONLY) {
    1270            2 :       GST_DEBUG_OBJECT(plugin,
    1271              :                        "Subelement %s is a sink only element, skipping",
    1272              :                        info->alias_name);
    1273            2 :       continue;
    1274              :     }
    1275              : 
    1276              :     // Register the sub transform element
    1277            6 :     GST_LOG("Registering %s transform subelement", info->filter_name);
    1278            6 :     params = g_new0(GstGpacParams, 1);
    1279            6 :     params->is_single = TRUE;
    1280            6 :     params->is_inside_sink = FALSE;
    1281            6 :     params->info = info;
    1282            6 :     const gchar* name = g_strdup_printf("gpac%s", info->alias_name);
    1283              :     const gchar* type_name =
    1284            6 :       g_strdup_printf("GstGpacTransform%c%s",
    1285            6 :                       g_ascii_toupper(info->alias_name[0]),
    1286            6 :                       info->alias_name + 1);
    1287              : 
    1288            6 :     type = g_type_register_static(
    1289              :       GST_TYPE_GPAC_TF, type_name, &subclass_typeinfo, 0);
    1290            6 :     g_type_set_qdata(type, GST_GPAC_PARAMS_QDATA, params);
    1291            6 :     if (!gst_element_register(plugin, name, GST_RANK_SECONDARY, type)) {
    1292            0 :       GST_ERROR_OBJECT(
    1293              :         plugin, "Failed to register %s transform subelement", info->alias_name);
    1294            0 :       return FALSE;
    1295              :     }
    1296              :   }
    1297              : 
    1298            2 :   return TRUE;
    1299              : }
    1300              : 
    1301            2 : GST_ELEMENT_REGISTER_DEFINE_CUSTOM(gpac_tf, gst_gpac_tf_register);
    1302              : 
    1303              : // #MARK: Private registration
    1304              : GType
    1305            4 : gst_gpac_tf_register_custom(subelement_info* se_info,
    1306              :                             gboolean is_inside_sink,
    1307              :                             gboolean is_single)
    1308              : {
    1309              :   const gchar* type_name =
    1310            4 :     g_strdup_printf("GstGpacTransformPrivate%c%s",
    1311            4 :                     g_ascii_toupper(se_info->alias_name[0]),
    1312            4 :                     se_info->alias_name + 1);
    1313              : 
    1314              :   // Check if the type is already registered
    1315            4 :   if (g_type_from_name(type_name) != G_TYPE_INVALID) {
    1316            0 :     GST_WARNING("Type %s is already registered, returning existing type",
    1317              :                 type_name);
    1318            0 :     return g_type_from_name(type_name);
    1319              :   }
    1320              : 
    1321              :   GType type;
    1322            4 :   GTypeInfo type_info = {
    1323              :     sizeof(GstGpacTransformClass),
    1324              :     NULL, // base_init
    1325              :     NULL, // base_finalize
    1326              :     (GClassInitFunc)gst_gpac_tf_subclass_init,
    1327              :     NULL, // class_finalize
    1328              :     NULL, // class_data
    1329              :     sizeof(GstGpacTransform),
    1330              :     0,
    1331              :     NULL,
    1332              :   };
    1333              : 
    1334            4 :   GST_DEBUG_CATEGORY_INIT(gst_gpac_tf_debug, "gpactf", 0, "GPAC Transform");
    1335              : 
    1336              :   // Register the custom transform element
    1337            4 :   GST_LOG("Creating a private gpac transform element with subelement info: %p",
    1338              :           se_info);
    1339              : 
    1340            4 :   GstGpacParams* params = g_new0(GstGpacParams, 1);
    1341            4 :   params->is_single = is_single;
    1342            4 :   params->is_inside_sink = is_inside_sink;
    1343            4 :   params->info = se_info;
    1344              : 
    1345            4 :   type = g_type_register_static(GST_TYPE_GPAC_TF, type_name, &type_info, 0);
    1346            4 :   g_type_set_qdata(type, GST_GPAC_PARAMS_QDATA, params);
    1347            4 :   return type;
    1348              : }
        

Generated by: LCOV version 2.0-1