LCOV - code coverage report
Current view: top level - src/elements/gstgpactf.c (source / functions) Coverage Total Hit
Test: coverage.filtered.info Lines: 79.0 % 629 497
Test Date: 2025-11-16 07:16:50 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        27907 : 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        13895 : gst_gpac_tf_pad_get_property(GObject* object,
      64              :                              guint prop_id,
      65              :                              GValue* value,
      66              :                              GParamSpec* pspec)
      67              : {
      68        13895 :   GstGpacTransformPad* pad = GST_GPAC_TF_PAD(object);
      69        13895 :   g_return_if_fail(GST_IS_GPAC_TF_PAD(object));
      70              : 
      71        13895 :   switch (prop_id) {
      72        13895 :     case GPAC_PROP_PAD_PID:
      73        13895 :       g_value_set_pointer(value, pad->pid);
      74        13895 :       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        12428 : 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         2515 : gpac_prepare_pids(GstElement* element)
     201              : {
     202         2515 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(element);
     203              :   GstIterator* pad_iter;
     204         2515 :   GValue item = G_VALUE_INIT;
     205         2515 :   gboolean done = FALSE;
     206         2515 :   gboolean ret = FALSE;
     207              : 
     208              :   // Track configuration status
     209              :   GHashTable* pids_before_reset =
     210         2515 :     g_hash_table_new(g_direct_hash, g_direct_equal);
     211         2515 :   GHashTable* pids_seen = g_hash_table_new(g_direct_hash, g_direct_equal);
     212              : 
     213              :   // Iterate over the pads
     214         2515 :   pad_iter = gst_element_iterate_sink_pads(element);
     215        12381 :   while (!done) {
     216         9866 :     switch (gst_iterator_next(pad_iter, &item)) {
     217         7351 :       case GST_ITERATOR_OK: {
     218         7351 :         GstPad* pad = g_value_get_object(&item);
     219         7351 :         GstAggregatorPad* agg_pad = GST_AGGREGATOR_PAD(pad);
     220         7351 :         GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     221              : 
     222              :         // Get the PID
     223         7351 :         GF_FilterPid* pid = NULL;
     224         7351 :         g_object_get(agg_pad, "pid", &pid, NULL);
     225              : 
     226              :         // Create the PID if necessary
     227         7351 :         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         7351 :         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         7351 :         g_hash_table_insert(pids_seen, pid, GINT_TO_POINTER(1));
     251              : 
     252         7351 :         g_value_reset(&item);
     253         7351 :         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         2515 :       case GST_ITERATOR_DONE:
     269         2515 :         done = TRUE;
     270         2515 :         ret = TRUE;
     271         2515 :         break;
     272              :     }
     273              :   }
     274              : 
     275              :   // Check for PIDs that are no longer needed
     276              :   GHashTableIter iter;
     277              :   gpointer key, value;
     278         2515 :   g_hash_table_iter_init(&iter, pids_before_reset);
     279         5030 :   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         2515 : fail:
     287              :   // Clean up
     288         2515 :   g_value_unset(&item);
     289         2515 :   gst_iterator_free(pad_iter);
     290         2515 :   g_hash_table_destroy(pids_before_reset);
     291         2515 :   g_hash_table_destroy(pids_seen);
     292         2515 :   return ret;
     293              : }
     294              : 
     295              : // #MARK: Aggregator
     296              : GstFlowReturn
     297         2499 : gst_gpac_tf_consume(GstAggregator* agg, Bool is_eos)
     298              : {
     299         2499 :   GstFlowReturn flow_ret = GST_FLOW_OK;
     300         2499 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(agg);
     301         2499 :   GstSegment* segment = &GST_AGGREGATOR_PAD(agg->srcpad)->segment;
     302              : 
     303         2499 :   GST_DEBUG_OBJECT(agg, "Consuming output...");
     304              : 
     305              :   void* output;
     306              :   GPAC_FilterPPRet ret;
     307         5028 :   while ((ret = gpac_memio_consume(GPAC_SESS_CTX(GPAC_CTX), &output))) {
     308         2529 :     if (ret & GPAC_FILTER_PP_RET_ERROR) {
     309              :       // An error occurred, stop processing
     310            0 :       goto error;
     311              :     }
     312              : 
     313         2529 :     if (ret == GPAC_FILTER_PP_RET_EMPTY) {
     314              :       // No data available
     315          890 :       GST_DEBUG_OBJECT(agg, "No more data available, exiting");
     316          890 :       return is_eos ? GST_FLOW_EOS : GST_FLOW_OK;
     317              :     }
     318              : 
     319         1639 :     gboolean had_signal = (ret & GPAC_FILTER_PP_RET_SIGNAL) != 0;
     320         1639 :     if (ret > GPAC_MAY_HAVE_BUFFER) {
     321         1639 :       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         1639 :       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         1639 :       } 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         1609 :       } else if (HAS_FLAG(ret, GPAC_FILTER_PP_RET_NULL)) {
     365              :         // If we had signals, consume all of them first
     366         1609 :         if (had_signal)
     367            0 :           continue;
     368              : 
     369         1609 :         if (is_eos)
     370            5 :           return GST_FLOW_EOS;
     371              : 
     372         1604 :         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         1604 :         GstBuffer* buffer = gpac_tf->sync_buffer;
     377         1604 :         if (!buffer)
     378          399 :           buffer = gst_buffer_new();
     379              : 
     380              :         // Send the sync buffer
     381         1604 :         flow_ret = gst_aggregator_finish_buffer(agg, buffer);
     382              : 
     383              :         // Buffer is transferred to the aggregator, so we set it to NULL
     384         1604 :         if (gpac_tf->sync_buffer)
     385         1205 :           gpac_tf->sync_buffer = NULL;
     386              : 
     387         1604 :         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 :       GF_FilterPid* pid = NULL;
     473           20 :       g_object_get(GST_AGGREGATOR_PAD(pad), "pid", &pid, NULL);
     474           20 :       g_assert(pid != NULL);
     475           20 :       gpac_memio_set_eos(GPAC_SESS_CTX(GPAC_CTX), pid);
     476           20 :       priv->eos = TRUE;
     477              : 
     478              :       // Are all pads EOS?
     479           20 :       gboolean all_eos = TRUE;
     480           20 :       GList* sinkpads = GST_ELEMENT(agg)->sinkpads;
     481           52 :       for (GList* l = sinkpads; l; l = l->next) {
     482           39 :         GstAggregatorPad* agg_pad = GST_AGGREGATOR_PAD(l->data);
     483           39 :         GpacPadPrivate* priv = gst_pad_get_element_private(GST_PAD(agg_pad));
     484           39 :         if (!priv->eos) {
     485            7 :           all_eos = FALSE;
     486            7 :           break;
     487              :         }
     488              :       }
     489              : 
     490           20 :       if (!all_eos) {
     491            7 :         GST_DEBUG_OBJECT(agg, "Not all pads are EOS, not sending EOS to GPAC");
     492            7 :         break;
     493              :       }
     494              : 
     495              :       // If all pads are EOS, send EOS to the source
     496           13 :       GST_DEBUG_OBJECT(agg, "All pads are EOS, sending EOS to GPAC");
     497           13 :       gpac_session_run(GPAC_SESS_CTX(GPAC_CTX), TRUE);
     498           13 :       gst_gpac_tf_consume(agg, GST_EVENT_TYPE(event) == GST_EVENT_EOS);
     499           13 :       break;
     500              :     }
     501              : 
     502            0 :     case GST_EVENT_FLUSH_START:
     503            0 :       gpac_session_run(GPAC_SESS_CTX(GPAC_CTX), TRUE);
     504            0 :       gst_gpac_tf_consume(agg, GST_EVENT_TYPE(event) == GST_EVENT_EOS);
     505            0 :       break;
     506              : 
     507           44 :     default:
     508           44 :       break;
     509              :   }
     510              : 
     511          137 :   return GST_AGGREGATOR_CLASS(parent_class)->sink_event(agg, pad, event);
     512              : }
     513              : 
     514              : gboolean
     515           14 : gst_gpac_tf_negotiated_src_caps(GstAggregator* agg, GstCaps* caps)
     516              : {
     517           14 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(GST_ELEMENT(agg));
     518           14 :   GstElement* element = GST_ELEMENT(agg);
     519           14 :   GObjectClass* klass = G_OBJECT_CLASS(G_TYPE_INSTANCE_GET_CLASS(
     520              :     G_OBJECT(element), GST_TYPE_GPAC_TF, GstGpacTransformClass));
     521           14 :   GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
     522              : 
     523              :   // Check if this is element is inside our sink bin
     524           14 :   GstObject* sink_bin = gst_element_get_parent(element);
     525           14 :   if (GST_IS_GPAC_SINK(sink_bin)) {
     526              :     // We might already have a destination set
     527            5 :     if ((params->is_single && params->info->destination) ||
     528            4 :         GPAC_PROP_CTX(GPAC_CTX)->destination) {
     529            2 :       return TRUE;
     530              :     }
     531              :   }
     532              : 
     533           12 :   return gpac_memio_set_gst_caps(GPAC_SESS_CTX(GPAC_CTX), caps);
     534              : }
     535              : 
     536              : void
     537         4637 : gst_gpac_request_idr(GstAggregator* agg, GstPad* pad, GstBuffer* buffer)
     538              : {
     539         4637 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(GST_ELEMENT(agg));
     540         4637 :   GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     541              :   GstEvent* gst_event;
     542              : 
     543              :   // Skip if this is not a key frame
     544         4637 :   if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT))
     545         4538 :     return;
     546              : 
     547              :   // Skip if we don't have a valid PTS
     548           99 :   if (!GST_BUFFER_PTS_IS_VALID(buffer))
     549            0 :     return;
     550              : 
     551              :   // Decide on which IDR period to use
     552           99 :   guint64 idr_period = GST_CLOCK_TIME_NONE;
     553           99 :   if (priv->idr_period != GST_CLOCK_TIME_NONE) {
     554           79 :     idr_period = priv->idr_period;
     555              :     // Preserve the IDR period sent by gpac
     556           79 :     gpac_tf->gpac_idr_period = idr_period;
     557              :   }
     558              : 
     559              :   // Use the global IDR period if available
     560           99 :   if (gpac_tf->global_idr_period)
     561            4 :     idr_period = gpac_tf->global_idr_period;
     562              : 
     563              :   // Skip if we don't have an IDR period
     564           99 :   if (idr_period == GST_CLOCK_TIME_NONE)
     565           16 :     return;
     566              : 
     567          166 :   priv->idr_last = gst_segment_to_running_time(
     568           83 :     priv->segment, GST_FORMAT_TIME, GST_BUFFER_PTS(buffer));
     569              : 
     570           83 :   GST_DEBUG_OBJECT(agg,
     571              :                    "Key frame received at %" GST_TIME_FORMAT,
     572              :                    GST_TIME_ARGS(priv->idr_last));
     573              : 
     574              :   // If this is the first IDR, request it immediately
     575           83 :   if (priv->idr_next == GST_CLOCK_TIME_NONE) {
     576            8 :     priv->idr_next = priv->idr_last + idr_period;
     577            8 :     goto request;
     578              :   }
     579              : 
     580              :   // If this IDR arrived before the next scheduled IDR, ignore
     581           75 :   if (priv->idr_last < priv->idr_next) {
     582           56 :     guint64 diff = priv->idr_next - priv->idr_last;
     583           56 :     GST_DEBUG_OBJECT(agg,
     584              :                      "IDR arrived %" GST_TIME_FORMAT
     585              :                      " before the next IDR on pad %s",
     586              :                      GST_TIME_ARGS(diff),
     587              :                      GST_PAD_NAME(pad));
     588           56 :     return;
     589              :   }
     590              : 
     591              :   // Check if this IDR was on time
     592           19 :   guint64 diff = priv->idr_last - priv->idr_next;
     593           19 :   if (diff)
     594            0 :     GST_ELEMENT_WARNING(agg,
     595              :                         STREAM,
     596              :                         FAILED,
     597              :                         ("IDR was late by %" GST_TIME_FORMAT
     598              :                          " on pad %s, reconsider encoding options",
     599              :                          GST_TIME_ARGS(diff),
     600              :                          GST_PAD_NAME(pad)),
     601              :                         (NULL));
     602              : 
     603              :   // Schedule the next IDR at the desired time, regardless of whether current
     604              :   // one was late or not
     605           19 :   priv->idr_next += idr_period;
     606              : 
     607           27 : request:
     608              :   // Send the next IDR request
     609              :   gst_event =
     610           27 :     gst_video_event_new_upstream_force_key_unit(priv->idr_next, TRUE, 1);
     611           27 :   GST_DEBUG_OBJECT(
     612              :     agg, "Requesting IDR at %" GST_TIME_FORMAT, GST_TIME_ARGS(priv->idr_next));
     613           27 :   if (!gst_pad_push_event(pad, gst_event))
     614            0 :     GST_ELEMENT_WARNING(
     615              :       agg, STREAM, FAILED, (NULL), ("Failed to push the force key unit event"));
     616              : }
     617              : 
     618              : static GstFlowReturn
     619         2499 : gst_gpac_tf_aggregate(GstAggregator* agg, gboolean timeout)
     620              : {
     621         2499 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(GST_ELEMENT(agg));
     622              :   GstIterator* pad_iter;
     623         2499 :   GValue item = G_VALUE_INIT;
     624         2499 :   gboolean done = FALSE;
     625         2499 :   gboolean has_buffers = TRUE;
     626              : 
     627              :   // Check and create PIDs if necessary
     628         2499 :   if (!gpac_prepare_pids(GST_ELEMENT(agg))) {
     629            0 :     GST_ELEMENT_ERROR(agg, STREAM, FAILED, (NULL), ("Failed to prepare PIDs"));
     630            0 :     return GST_FLOW_ERROR;
     631              :   }
     632              : 
     633         2499 :   GST_DEBUG_OBJECT(agg, "Aggregating buffers");
     634              : 
     635              :   // Create the temporary queue
     636         2499 :   GQueue* queue = g_queue_new();
     637              : 
     638              :   // Keep consuming buffers until all pads are drained
     639         8115 :   while (has_buffers) {
     640         5616 :     has_buffers = FALSE;
     641         5616 :     done = FALSE;
     642              : 
     643              :     // Iterate over the pads
     644         5616 :     pad_iter = gst_element_iterate_sink_pads(GST_ELEMENT(agg));
     645        27903 :     while (!done) {
     646        22287 :       switch (gst_iterator_next(pad_iter, &item)) {
     647        16671 :         case GST_ITERATOR_OK: {
     648        16671 :           GF_FilterPid* pid = NULL;
     649        16671 :           GstPad* pad = g_value_get_object(&item);
     650        16671 :           GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     651              :           GstBuffer* buffer =
     652        16671 :             gst_aggregator_pad_pop_buffer(GST_AGGREGATOR_PAD(pad));
     653              : 
     654              :           // Continue if no buffer is available
     655        16671 :           if (!buffer) {
     656        10147 :             GST_DEBUG_OBJECT(
     657              :               agg, "No buffer available on pad %s", GST_PAD_NAME(pad));
     658        10147 :             goto next;
     659              :           }
     660              : 
     661              :           // We found at least one buffer, continue the outer loop
     662         6524 :           has_buffers = TRUE;
     663              : 
     664              :           // Skip droppable/gap buffers
     665         6524 :           if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_GAP)) {
     666            0 :             GST_DEBUG_OBJECT(
     667              :               agg, "Gap buffer received on pad %s", GST_PAD_NAME(pad));
     668            0 :             goto next;
     669              :           }
     670              : 
     671              :           // Send the key frame request
     672              :           // Only send IDR request for video pads
     673        13048 :           if (gst_pad_get_pad_template(pad) ==
     674         6524 :               gst_gpac_get_sink_template(GPAC_TEMPLATE_VIDEO)) {
     675         4637 :             gst_gpac_request_idr(agg, pad, buffer);
     676              :           }
     677              : 
     678              :           // Get the PID
     679         6524 :           g_object_get(GST_AGGREGATOR_PAD(pad), "pid", &pid, NULL);
     680         6524 :           g_assert(pid);
     681              : 
     682              :           // Create the packet
     683         6524 :           GF_FilterPacket* packet = gpac_pck_new_from_buffer(buffer, priv, pid);
     684         6524 :           if (!packet) {
     685            0 :             GST_ELEMENT_ERROR(agg,
     686              :                               STREAM,
     687              :                               FAILED,
     688              :                               (NULL),
     689              :                               ("Failed to create packet from buffer"));
     690            0 :             goto next;
     691              :           }
     692              : 
     693              :           // Enqueue the packet
     694         6524 :           g_queue_push_tail(queue, packet);
     695              : 
     696              :           // Select the highest PTS for sync buffer
     697         6524 :           gboolean is_video_pad =
     698         6524 :             gst_pad_get_pad_template(GST_PAD(pad)) ==
     699         6524 :             gst_gpac_get_sink_template(GPAC_TEMPLATE_VIDEO);
     700         6524 :           gboolean is_only_pad = g_list_length(GST_ELEMENT(agg)->sinkpads) == 1;
     701         6524 :           if (is_video_pad || is_only_pad) {
     702         4637 :             if (gpac_tf->sync_buffer) {
     703         3424 :               guint64 current_pts = GST_BUFFER_PTS(buffer);
     704         3424 :               guint64 sync_pts = GST_BUFFER_PTS(gpac_tf->sync_buffer);
     705         3424 :               if (current_pts > sync_pts) {
     706         1374 :                 gst_buffer_replace(&gpac_tf->sync_buffer, buffer);
     707              :               }
     708              :             } else {
     709              :               // If no sync buffer exists, create one
     710         1213 :               gpac_tf->sync_buffer = gst_buffer_ref(buffer);
     711              :             }
     712              :           }
     713              : 
     714         1887 :         next:
     715        16671 :           if (buffer)
     716         6524 :             gst_buffer_unref(buffer);
     717        16671 :           g_value_reset(&item);
     718        16671 :           break;
     719              :         }
     720            0 :         case GST_ITERATOR_RESYNC:
     721            0 :           gst_iterator_resync(pad_iter);
     722            0 :           GST_ELEMENT_WARNING(agg,
     723              :                               STREAM,
     724              :                               FAILED,
     725              :                               (NULL),
     726              :                               ("Data structure changed during pad iteration, "
     727              :                                "discarding all packets"));
     728            0 :           g_queue_clear_full(queue, (GDestroyNotify)gf_filter_pck_unref);
     729            0 :           break;
     730         5616 :         case GST_ITERATOR_ERROR:
     731              :         case GST_ITERATOR_DONE:
     732         5616 :           done = TRUE;
     733         5616 :           break;
     734              :       }
     735              :     }
     736              :   }
     737              : 
     738              :   // Clean up
     739         2499 :   g_value_unset(&item);
     740         2499 :   gst_iterator_free(pad_iter);
     741              : 
     742              :   // Check if we have any packets to send
     743         2499 :   if (g_queue_is_empty(queue)) {
     744           13 :     GST_DEBUG_OBJECT(agg, "No packets to send, returning EOS");
     745           13 :     g_queue_free(queue);
     746           13 :     return GST_FLOW_EOS;
     747              :   }
     748              : 
     749              :   // Merge the queues
     750         9010 :   while (!g_queue_is_empty(queue)) {
     751         6524 :     gpointer pck = g_queue_pop_head(queue);
     752         6524 :     g_queue_push_tail(gpac_tf->queue, pck);
     753              :   }
     754         2486 :   g_queue_free(queue);
     755              : 
     756              :   // Run the filter session
     757         2486 :   if (gpac_session_run(GPAC_SESS_CTX(GPAC_CTX), FALSE) != GF_OK) {
     758            0 :     GST_ELEMENT_ERROR(
     759              :       agg, STREAM, FAILED, (NULL), ("Failed to run the GPAC session"));
     760            0 :     return GST_FLOW_ERROR;
     761              :   }
     762              : 
     763              :   // Consume the output
     764         2486 :   return gst_gpac_tf_consume(agg, FALSE);
     765              : }
     766              : 
     767              : // #MARK: Pad Management
     768              : static GstAggregatorPad*
     769           20 : gst_gpac_tf_create_new_pad(GstAggregator* element,
     770              :                            GstPadTemplate* templ,
     771              :                            const gchar* pad_name,
     772              :                            const GstCaps* caps)
     773              : {
     774           20 :   GstElementClass* klass = GST_ELEMENT_GET_CLASS(element);
     775           20 :   GstGpacTransform* agg = GST_GPAC_TF(element);
     776              :   gchar* name;
     777              :   gint pad_id;
     778              : 
     779              : #define TEMPLATE_CHECK(prefix, count_field)                                    \
     780              :   if (templ == gst_element_class_get_pad_template(klass, prefix "_%u")) {      \
     781              :     agg->count_field++;                                                        \
     782              :     if (pad_name != NULL && sscanf(pad_name, prefix "_%u", &pad_id) == 1) {    \
     783              :       name = g_strdup(pad_name);                                               \
     784              :     } else {                                                                   \
     785              :       pad_id =                                                                 \
     786              :         agg->audio_pad_count + agg->video_pad_count + agg->subtitle_pad_count; \
     787              :       name = g_strdup_printf(prefix "_%u", pad_id);                            \
     788              :     }                                                                          \
     789              :   } else
     790              : 
     791              :   // Check the pad template and decide the pad name
     792           20 :   TEMPLATE_CHECK("video", video_pad_count)
     793            3 :   TEMPLATE_CHECK("audio", audio_pad_count)
     794            0 :   TEMPLATE_CHECK("subtitle", subtitle_pad_count)
     795              :   {
     796            0 :     GST_ELEMENT_WARNING(
     797              :       agg, STREAM, FAILED, (NULL), ("This is not our template!"));
     798            0 :     return NULL;
     799              :   }
     800              : 
     801              : #undef TEMPLATE_CHECK
     802              : 
     803           20 :   GST_DEBUG_OBJECT(agg, "Creating new pad %s", name);
     804              : 
     805              :   // Create the pad
     806           20 :   GstGpacTransformPad* pad = g_object_new(GST_TYPE_GPAC_TF_PAD,
     807              :                                           "name",
     808              :                                           name,
     809              :                                           "direction",
     810           20 :                                           templ->direction,
     811              :                                           "template",
     812              :                                           templ,
     813              :                                           NULL);
     814           20 :   g_free(name);
     815              : 
     816              :   // Initialize the private data
     817           20 :   GpacPadPrivate* priv = gst_pad_get_element_private(GST_PAD(pad));
     818           20 :   priv->id = pad_id;
     819           20 :   if (caps) {
     820            0 :     priv->caps = gst_caps_copy(caps);
     821            0 :     priv->flags |= GPAC_PAD_CAPS_SET;
     822              :   }
     823              : 
     824           20 :   return GST_AGGREGATOR_PAD(pad);
     825              : }
     826              : 
     827              : // #MARK: Lifecycle
     828              : static void
     829           29 : gst_gpac_tf_reset(GstGpacTransform* tf)
     830              : {
     831           29 :   gboolean done = FALSE;
     832           29 :   GValue item = G_VALUE_INIT;
     833           29 :   GstIterator* pad_iter = gst_element_iterate_sink_pads(GST_ELEMENT(tf));
     834           81 :   while (!done) {
     835           52 :     switch (gst_iterator_next(pad_iter, &item)) {
     836           23 :       case GST_ITERATOR_OK: {
     837           23 :         GF_FilterPid* pid = NULL;
     838           23 :         GstPad* pad = g_value_get_object(&item);
     839           23 :         GpacPadPrivate* priv = gst_pad_get_element_private(pad);
     840              : 
     841              :         // Reset the PID
     842           23 :         g_object_set(GST_AGGREGATOR_PAD(pad), "pid", NULL, NULL);
     843           23 :         break;
     844              :       }
     845           29 :       case GST_ITERATOR_RESYNC:
     846              :       case GST_ITERATOR_ERROR:
     847              :       case GST_ITERATOR_DONE:
     848           29 :         done = TRUE;
     849           29 :         break;
     850              :     }
     851              :   }
     852           29 :   g_value_unset(&item);
     853           29 :   gst_iterator_free(pad_iter);
     854              : 
     855              :   // Empty the queue
     856           29 :   if (tf->queue) {
     857           16 :     if (!g_queue_is_empty(tf->queue))
     858            0 :       GST_ERROR_OBJECT(tf,
     859              :                        "GPAC queue not empty during reset, pipeline error?");
     860           16 :     g_queue_clear(tf->queue);
     861              :   }
     862           29 : }
     863              : 
     864              : static gboolean
     865           16 : gst_gpac_tf_start(GstAggregator* aggregator)
     866              : {
     867           16 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(aggregator);
     868           16 :   GstElement* element = GST_ELEMENT(aggregator);
     869           16 :   GObjectClass* klass = G_OBJECT_CLASS(G_TYPE_INSTANCE_GET_CLASS(
     870              :     G_OBJECT(element), GST_TYPE_GPAC_TF, GstGpacTransformClass));
     871           16 :   GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
     872              :   GstSegment segment;
     873              : 
     874              :   // Check if we have the graph property set
     875           16 :   if (!params->is_single && !GPAC_PROP_CTX(GPAC_CTX)->graph) {
     876            0 :     GST_ELEMENT_ERROR(
     877              :       element, STREAM, FAILED, (NULL), ("Graph property must be set"));
     878            0 :     return FALSE;
     879              :   }
     880              : 
     881              :   // Convert the properties to arguments
     882           16 :   if (!gpac_apply_properties(GPAC_PROP_CTX(GPAC_CTX))) {
     883            0 :     GST_ELEMENT_ERROR(
     884              :       element, LIBRARY, INIT, (NULL), ("Failed to apply properties"));
     885            0 :     return FALSE;
     886              :   }
     887              :   // Initialize the GPAC context
     888           16 :   if (!gpac_init(GPAC_CTX, element)) {
     889            0 :     GST_ELEMENT_ERROR(
     890              :       element, LIBRARY, INIT, (NULL), ("Failed to initialize GPAC context"));
     891            0 :     return FALSE;
     892              :   }
     893              : 
     894              :   // Create the session
     895           16 :   if (!gpac_session_init(GPAC_SESS_CTX(GPAC_CTX), element, params)) {
     896            0 :     GST_ELEMENT_ERROR(
     897              :       element, LIBRARY, INIT, (NULL), ("Failed to initialize GPAC session"));
     898            0 :     return FALSE;
     899              :   }
     900              : 
     901              :   // Create the memory input
     902           16 :   gpac_return_val_if_fail(
     903              :     gpac_memio_new(GPAC_SESS_CTX(GPAC_CTX), GPAC_MEMIO_DIR_IN), FALSE);
     904           16 :   gpac_memio_assign_queue(
     905              :     GPAC_SESS_CTX(GPAC_CTX), GPAC_MEMIO_DIR_IN, gpac_tf->queue);
     906              : 
     907              :   // Open the session
     908           16 :   gchar* graph = NULL;
     909           16 :   if (params->is_single) {
     910           12 :     if (params->info->default_options) {
     911           11 :       GList* props = GPAC_PROP_CTX(GPAC_CTX)->properties;
     912           11 :       GString* options = g_string_new(NULL);
     913              : 
     914              :       // Only override if not set already
     915           34 :       for (guint32 i = 0; params->info->default_options[i].name; i++) {
     916           23 :         gboolean found = FALSE;
     917              : 
     918           44 :         for (GList* l = props; l != NULL; l = l->next) {
     919           21 :           gchar* prop = (gchar*)l->data;
     920              : 
     921           21 :           g_autofree gchar* prefix =
     922           21 :             g_strdup_printf("--%s", params->info->default_options[i].name);
     923           21 :           if (g_str_has_prefix(prop, prefix)) {
     924            0 :             found = TRUE;
     925            0 :             break;
     926              :           }
     927              :         }
     928              : 
     929           23 :         if (!found) {
     930           23 :           const gchar* name = params->info->default_options[i].name;
     931           23 :           const gchar* value = params->info->default_options[i].value;
     932           23 :           g_string_append_printf(options, "%s=%s:", name, value);
     933              :         }
     934              :       }
     935              : 
     936              :       // Remove the trailing colon
     937           11 :       if (options->len > 0)
     938           11 :         g_string_truncate(options, options->len - 1);
     939              : 
     940           11 :       if (options->len > 0)
     941              :         graph =
     942           11 :           g_strdup_printf("%s:%s", params->info->filter_name, options->str);
     943              :       else
     944            0 :         graph = g_strdup(params->info->filter_name);
     945           11 :       g_string_free(options, TRUE);
     946              :     } else {
     947            2 :       graph = g_strdup(params->info->filter_name);
     948              :     }
     949              :   } else {
     950            8 :     graph = g_strdup(GPAC_PROP_CTX(GPAC_CTX)->graph);
     951              :   }
     952              : 
     953              :   // Set the destination override on session context
     954           16 :   GPAC_SESS_CTX(GPAC_CTX)->destination = GPAC_PROP_CTX(GPAC_CTX)->destination;
     955              : 
     956           16 :   gpac_return_val_if_fail(gpac_session_open(GPAC_SESS_CTX(GPAC_CTX), graph),
     957              :                           FALSE);
     958           16 :   g_free(graph);
     959              : 
     960              :   // Create the memory output
     961           16 :   gboolean is_inside_sink = GST_IS_GPAC_SINK(gst_element_get_parent(element));
     962           16 :   gboolean requires_memout =
     963           16 :     params->info && GPAC_SE_IS_REQUIRES_MEMOUT(params->info->flags);
     964           20 :   requires_memout = !is_inside_sink || (is_inside_sink && requires_memout) ||
     965            4 :                     GPAC_PROP_CTX(GPAC_CTX)->destination;
     966              : 
     967           16 :   if (requires_memout) {
     968           13 :     gpac_return_val_if_fail(
     969              :       gpac_memio_new(GPAC_SESS_CTX(GPAC_CTX), GPAC_MEMIO_DIR_OUT), FALSE);
     970              :   }
     971              : 
     972              :   // Check if the session has an output
     973           16 :   if (!gpac_session_has_output(GPAC_SESS_CTX(GPAC_CTX))) {
     974            0 :     GST_ELEMENT_ERROR(
     975              :       element, STREAM, FAILED, (NULL), ("Session has no output"));
     976            0 :     return FALSE;
     977              :   }
     978              : 
     979              :   // Initialize the PIDs for all pads
     980           16 :   if (!gpac_prepare_pids(element)) {
     981            0 :     GST_ELEMENT_ERROR(
     982              :       element, LIBRARY, FAILED, (NULL), ("Failed to prepare PIDs"));
     983            0 :     return FALSE;
     984              :   }
     985           16 :   GST_DEBUG_OBJECT(element, "GPAC session started");
     986              : 
     987              :   // Initialize the segment
     988           16 :   gst_segment_init(&segment, GST_FORMAT_TIME);
     989           16 :   gst_aggregator_update_segment(aggregator, &segment);
     990           16 :   return TRUE;
     991              : }
     992              : 
     993              : static gboolean
     994           16 : gst_gpac_tf_stop(GstAggregator* aggregator)
     995              : {
     996           16 :   GstElement* element = GST_ELEMENT(aggregator);
     997           16 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(aggregator);
     998           16 :   GObjectClass* klass = G_OBJECT_CLASS(G_TYPE_INSTANCE_GET_CLASS(
     999              :     G_OBJECT(element), GST_TYPE_GPAC_TF, GstGpacTransformClass));
    1000           16 :   GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
    1001              : 
    1002              :   // Abort the session
    1003           16 :   gpac_memio_set_eos(GPAC_SESS_CTX(GPAC_CTX), NULL);
    1004           16 :   gpac_session_abort(GPAC_SESS_CTX(GPAC_CTX));
    1005              : 
    1006              :   // Close the session
    1007           16 :   if (!gpac_session_close(GPAC_SESS_CTX(GPAC_CTX),
    1008              :                           GPAC_PROP_CTX(GPAC_CTX)->print_stats)) {
    1009            0 :     GST_ELEMENT_ERROR(
    1010              :       element, LIBRARY, SHUTDOWN, (NULL), ("Failed to close GPAC session"));
    1011            0 :     return FALSE;
    1012              :   }
    1013              : 
    1014              :   // Reset the element
    1015           16 :   gst_gpac_tf_reset(gpac_tf);
    1016              : 
    1017              :   // Destroy the GPAC context
    1018           16 :   gpac_destroy(GPAC_CTX);
    1019           16 :   GST_DEBUG_OBJECT(element, "GPAC session stopped");
    1020           16 :   return TRUE;
    1021              : }
    1022              : 
    1023              : static void
    1024            0 : gst_gpac_tf_finalize(GObject* object)
    1025              : {
    1026            0 :   GstGpacTransform* gpac_tf = GST_GPAC_TF(object);
    1027            0 :   GPAC_PropertyContext* ctx = GPAC_PROP_CTX(GPAC_CTX);
    1028              : 
    1029              :   // Free the properties
    1030            0 :   g_list_free(ctx->properties);
    1031            0 :   ctx->properties = NULL;
    1032              : 
    1033            0 :   if (ctx->props_as_argv) {
    1034            0 :     for (u32 i = 0; ctx->props_as_argv[i]; i++)
    1035            0 :       g_free(ctx->props_as_argv[i]);
    1036            0 :     g_free((void*)ctx->props_as_argv);
    1037              :   }
    1038              : 
    1039              :   // Free the queue
    1040            0 :   if (gpac_tf->queue) {
    1041            0 :     g_assert(g_queue_is_empty(gpac_tf->queue));
    1042            0 :     g_queue_free(gpac_tf->queue);
    1043            0 :     gpac_tf->queue = NULL;
    1044              :   }
    1045              : 
    1046            0 :   G_OBJECT_CLASS(parent_class)->finalize(object);
    1047            0 : }
    1048              : 
    1049              : // #MARK: Initialization
    1050              : static void
    1051           13 : gst_gpac_tf_init(GstGpacTransform* tf)
    1052              : {
    1053           13 :   gst_gpac_tf_reset(tf);
    1054           13 :   tf->queue = g_queue_new();
    1055           13 : }
    1056              : 
    1057              : static void
    1058            2 : gst_gpac_tf_class_init(GstGpacTransformClass* klass)
    1059              : {
    1060              :   // Initialize the class
    1061            2 :   GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
    1062            2 :   GstElementClass* gstelement_class = GST_ELEMENT_CLASS(klass);
    1063            2 :   GstAggregatorClass* gstaggregator_class = GST_AGGREGATOR_CLASS(klass);
    1064            2 :   parent_class = g_type_class_peek_parent(klass);
    1065              : 
    1066              :   // Install the pad templates
    1067            2 :   gpac_install_sink_pad_templates(gstelement_class);
    1068              : 
    1069              :   // Set the pad management functions
    1070            2 :   gstaggregator_class->create_new_pad =
    1071            2 :     GST_DEBUG_FUNCPTR(gst_gpac_tf_create_new_pad);
    1072              : 
    1073              :   // Set the aggregator functions
    1074            2 :   gstaggregator_class->sink_event = GST_DEBUG_FUNCPTR(gst_gpac_tf_sink_event);
    1075            2 :   gstaggregator_class->aggregate = GST_DEBUG_FUNCPTR(gst_gpac_tf_aggregate);
    1076            2 :   gstaggregator_class->negotiated_src_caps =
    1077            2 :     GST_DEBUG_FUNCPTR(gst_gpac_tf_negotiated_src_caps);
    1078            2 :   gstaggregator_class->start = GST_DEBUG_FUNCPTR(gst_gpac_tf_start);
    1079            2 :   gstaggregator_class->stop = GST_DEBUG_FUNCPTR(gst_gpac_tf_stop);
    1080              : 
    1081            2 :   gst_type_mark_as_plugin_api(GST_TYPE_GPAC_TF_PAD, 0);
    1082            2 : }
    1083              : 
    1084              : // #MARK: Registration
    1085              : static void
    1086            8 : gst_gpac_tf_subclass_init(GstGpacTransformClass* klass)
    1087              : {
    1088            8 :   GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
    1089            8 :   GstElementClass* gstelement_class = GST_ELEMENT_CLASS(klass);
    1090            8 :   GstGpacParams* params = GST_GPAC_GET_PARAMS(klass);
    1091              : 
    1092              :   // Set the finalizer
    1093            8 :   gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_gpac_tf_finalize);
    1094              : 
    1095              :   // Set the property handlers
    1096            8 :   gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_gpac_tf_set_property);
    1097            8 :   gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_gpac_tf_get_property);
    1098            8 :   gpac_install_global_properties(gobject_class);
    1099            8 :   gpac_install_local_properties(
    1100              :     gobject_class, GPAC_PROP_PRINT_STATS, GPAC_PROP_0);
    1101              : 
    1102              :   // Add the subclass-specific properties and pad templates
    1103            8 :   if (params->is_single) {
    1104            6 :     if (params->is_inside_sink) {
    1105            1 :       gst_element_class_add_static_pad_template(gstelement_class,
    1106              :                                                 &internal_pad_template);
    1107              :     } else {
    1108            5 :       gst_element_class_add_static_pad_template(gstelement_class,
    1109            5 :                                                 &params->info->src_template);
    1110              :     }
    1111              : 
    1112              :     // Set property blacklist
    1113            6 :     GList* blacklist = NULL;
    1114            6 :     if (params->info->default_options) {
    1115            3 :       filter_option* opt = params->info->default_options;
    1116           10 :       for (u32 i = 0; opt[i].name; i++) {
    1117            7 :         if (opt[i].forced)
    1118            4 :           blacklist = g_list_append(blacklist, (gpointer)opt[i].name);
    1119              :       }
    1120              :     }
    1121              : 
    1122              :     // Install the filter properties
    1123            6 :     gpac_install_filter_properties(
    1124            6 :       gobject_class, blacklist, params->info->filter_name);
    1125            6 :     g_list_free(blacklist);
    1126              : 
    1127              :     // Install the signals if not inside the sink bin
    1128              :     // They would be installed by the sink bin itself
    1129            6 :     if (!params->is_inside_sink && params->info->signal_presets) {
    1130            0 :       gpac_install_signals_by_presets(gobject_class,
    1131            0 :                                       params->info->signal_presets);
    1132              :     }
    1133              : 
    1134              :     // Check if we have any filter options to expose
    1135            8 :     for (u32 i = 0; i < G_N_ELEMENTS(filter_options); i++) {
    1136            6 :       filter_option_overrides* opts = &filter_options[i];
    1137            6 :       if (g_strcmp0(opts->filter_name, params->info->filter_name) == 0) {
    1138            8 :         for (u32 j = 0; opts->options[j]; j++) {
    1139            4 :           guint32 prop_id = opts->options[j];
    1140            4 :           gpac_install_local_properties(gobject_class, prop_id, GPAC_PROP_0);
    1141              :         }
    1142            4 :         break;
    1143              :       }
    1144              :     }
    1145              :   } else {
    1146            2 :     gpac_install_src_pad_templates(gstelement_class);
    1147            2 :     gpac_install_local_properties(
    1148              :       gobject_class, GPAC_PROP_GRAPH, GPAC_PROP_DESTINATION, GPAC_PROP_0);
    1149              : 
    1150            2 :     if (!params->is_inside_sink)
    1151            1 :       gpac_install_all_signals(gobject_class);
    1152              : 
    1153              :     // We don't know which filters will be used, so we expose all options
    1154            4 :     for (u32 i = 0; i < G_N_ELEMENTS(filter_options); i++) {
    1155            2 :       filter_option_overrides* opts = &filter_options[i];
    1156            4 :       for (u32 j = 0; opts->options[j]; j++) {
    1157            2 :         guint32 prop_id = opts->options[j];
    1158            2 :         gpac_install_local_properties(gobject_class, prop_id, GPAC_PROP_0);
    1159              :       }
    1160              :     }
    1161              :   }
    1162              : 
    1163              :   // Set the metadata
    1164            8 :   const gchar* longname = "gpac transformer";
    1165            8 :   if (params->is_single) {
    1166            6 :     if (!g_strcmp0(params->info->filter_name, params->info->alias_name))
    1167              :       longname =
    1168            2 :         g_strdup_printf("gpac %s transformer", params->info->filter_name);
    1169              :     else
    1170            4 :       longname = g_strdup_printf("gpac %s (%s) transformer",
    1171            4 :                                  params->info->alias_name,
    1172            4 :                                  params->info->filter_name);
    1173              :   }
    1174            8 :   gst_element_class_set_static_metadata(
    1175              :     gstelement_class,
    1176              :     longname,
    1177              :     "Aggregator/Transform",
    1178              :     "Aggregates and transforms incoming data via GPAC",
    1179              :     "Deniz Ugur <deniz.ugur@motionspell.com>");
    1180            8 : }
    1181              : 
    1182              : gboolean
    1183            2 : gst_gpac_tf_register(GstPlugin* plugin)
    1184              : {
    1185              :   GType type;
    1186              :   GstGpacParams* params;
    1187            2 :   GTypeInfo subclass_typeinfo = {
    1188              :     sizeof(GstGpacTransformClass),
    1189              :     NULL, // base_init
    1190              :     NULL, // base_finalize
    1191              :     (GClassInitFunc)gst_gpac_tf_subclass_init,
    1192              :     NULL, // class_finalize
    1193              :     NULL, // class_data
    1194              :     sizeof(GstGpacTransform),
    1195              :     0,
    1196              :     NULL, // instance_init
    1197              :   };
    1198              : 
    1199            2 :   GST_DEBUG_CATEGORY_INIT(gst_gpac_tf_debug, "gpactf", 0, "GPAC Transform");
    1200              : 
    1201              :   // Register the regular transform element
    1202            2 :   GST_LOG("Registering regular gpac transform element");
    1203            2 :   params = g_new0(GstGpacParams, 1);
    1204            2 :   params->is_single = FALSE;
    1205            2 :   params->is_inside_sink = FALSE;
    1206            2 :   type = g_type_register_static(
    1207              :     GST_TYPE_GPAC_TF, "GstGpacTransformRegular", &subclass_typeinfo, 0);
    1208            2 :   g_type_set_qdata(type, GST_GPAC_PARAMS_QDATA, params);
    1209            2 :   if (!gst_element_register(plugin, "gpactf", GST_RANK_PRIMARY, type)) {
    1210            0 :     GST_ERROR_OBJECT(plugin,
    1211              :                      "Failed to register regular gpac transform element");
    1212            0 :     return FALSE;
    1213              :   }
    1214              : 
    1215              :   // Register subelements
    1216           10 :   for (u32 i = 0; i < G_N_ELEMENTS(subelements); i++) {
    1217            8 :     subelement_info* info = &subelements[i];
    1218              : 
    1219            8 :     if (info->flags & GPAC_SE_SINK_ONLY) {
    1220            2 :       GST_DEBUG_OBJECT(plugin,
    1221              :                        "Subelement %s is a sink only element, skipping",
    1222              :                        info->alias_name);
    1223            2 :       continue;
    1224              :     }
    1225              : 
    1226              :     // Register the sub transform element
    1227            6 :     GST_LOG("Registering %s transform subelement", info->filter_name);
    1228            6 :     params = g_new0(GstGpacParams, 1);
    1229            6 :     params->is_single = TRUE;
    1230            6 :     params->is_inside_sink = FALSE;
    1231            6 :     params->info = info;
    1232            6 :     const gchar* name = g_strdup_printf("gpac%s", info->alias_name);
    1233              :     const gchar* type_name =
    1234            6 :       g_strdup_printf("GstGpacTransform%c%s",
    1235            6 :                       g_ascii_toupper(info->alias_name[0]),
    1236            6 :                       info->alias_name + 1);
    1237              : 
    1238            6 :     type = g_type_register_static(
    1239              :       GST_TYPE_GPAC_TF, type_name, &subclass_typeinfo, 0);
    1240            6 :     g_type_set_qdata(type, GST_GPAC_PARAMS_QDATA, params);
    1241            6 :     if (!gst_element_register(plugin, name, GST_RANK_SECONDARY, type)) {
    1242            0 :       GST_ERROR_OBJECT(
    1243              :         plugin, "Failed to register %s transform subelement", info->alias_name);
    1244            0 :       return FALSE;
    1245              :     }
    1246              :   }
    1247              : 
    1248            2 :   return TRUE;
    1249              : }
    1250              : 
    1251            2 : GST_ELEMENT_REGISTER_DEFINE_CUSTOM(gpac_tf, gst_gpac_tf_register);
    1252              : 
    1253              : // #MARK: Private registration
    1254              : GType
    1255            4 : gst_gpac_tf_register_custom(subelement_info* se_info,
    1256              :                             gboolean is_inside_sink,
    1257              :                             gboolean is_single)
    1258              : {
    1259              :   const gchar* type_name =
    1260            4 :     g_strdup_printf("GstGpacTransformPrivate%c%s",
    1261            4 :                     g_ascii_toupper(se_info->alias_name[0]),
    1262            4 :                     se_info->alias_name + 1);
    1263              : 
    1264              :   // Check if the type is already registered
    1265            4 :   if (g_type_from_name(type_name) != G_TYPE_INVALID) {
    1266            0 :     GST_WARNING("Type %s is already registered, returning existing type",
    1267              :                 type_name);
    1268            0 :     return g_type_from_name(type_name);
    1269              :   }
    1270              : 
    1271              :   GType type;
    1272            4 :   GTypeInfo type_info = {
    1273              :     sizeof(GstGpacTransformClass),
    1274              :     NULL, // base_init
    1275              :     NULL, // base_finalize
    1276              :     (GClassInitFunc)gst_gpac_tf_subclass_init,
    1277              :     NULL, // class_finalize
    1278              :     NULL, // class_data
    1279              :     sizeof(GstGpacTransform),
    1280              :     0,
    1281              :     NULL,
    1282              :   };
    1283              : 
    1284            4 :   GST_DEBUG_CATEGORY_INIT(gst_gpac_tf_debug, "gpactf", 0, "GPAC Transform");
    1285              : 
    1286              :   // Register the custom transform element
    1287            4 :   GST_LOG("Creating a private gpac transform element with subelement info: %p",
    1288              :           se_info);
    1289              : 
    1290            4 :   GstGpacParams* params = g_new0(GstGpacParams, 1);
    1291            4 :   params->is_single = is_single;
    1292            4 :   params->is_inside_sink = is_inside_sink;
    1293            4 :   params->info = se_info;
    1294              : 
    1295            4 :   type = g_type_register_static(GST_TYPE_GPAC_TF, type_name, &type_info, 0);
    1296            4 :   g_type_set_qdata(type, GST_GPAC_PARAMS_QDATA, params);
    1297            4 :   return type;
    1298              : }
        

Generated by: LCOV version 2.0-1