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

Generated by: LCOV version 2.0-1