<commit_before>/* *
 * This file is part of Feng
 *
 * Copyright (C) 2009 by LScube team <<EMAIL>>
 * See AUTHORS for more details
 *
 * feng is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * feng is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with feng; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * */

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#include <fenice/server.h>
#include "demuxer.h"
#include "description.h"
#include <fenice/utils.h>
#include <fenice/fnc_log.h>

// global demuxer modules:
extern Demuxer fnc_demuxer_sd;
extern Demuxer fnc_demuxer_ds;
extern Demuxer fnc_demuxer_avf;

// static array containing all the available demuxers:
static Demuxer *demuxers[] = {
    &fnc_demuxer_sd,
    &fnc_demuxer_ds,
    &fnc_demuxer_avf,
    NULL
};

/*! @brief The resource description cache.
 * This list holds the cache of resource descriptions. This way the mediathread
 * can provide the description buffer without opening the resource every time.
 * This will result in a better performance for RTSP DESCRIBE metod response.
 * Descriptions are ordered for access time, so that the last is the less
 * recently addressed media description and will be chosen for removal if cache
 * reaches the limit.
 * */
static GList *descr_cache=NULL;
//! cache size of descriptions (maybe we need to take it from fenice configuration file?)
#define MAX_DESCR_CACHE_SIZE 10

/*! Private functions for exclusive tracks.
 * */
#define r_descr_find(mrl) g_list_find_custom(descr_cache, mrl, cache_cmp)

static int r_changed(ResourceDescr *descr);

// private functions for specific demuxer

/**
 * Find the correct demuxer for the given resource.
 * First of all, it tries to match the resource extension with one of those
 * served by the demuxers and, if found, probes that demuxer.
 * If no demuxer can be found this way, then it tries every demuxer available.
 * @param i_stream the resource.
 * @return the index of the valid demuxer in the list or -1 if it could not be
 * found.
 * */

static int find_demuxer(InputStream *i_stream)
{
    // this int will contain the index of the demuxer already probed second
    // the extension suggestion, in order to not probe twice the same
    // demuxer that was proved to be not usable.
    int probed = -1;
    char found = 0; // flag for demuxer found.
    int i; // index
    char exts[128], *res_ext, *tkn; /* temp string containing extension
                                     * served by probing demuxer.
                                     */

    // First of all try that with matching extension: we use extension as a
    // hint of resource type.
    if ( (res_ext=strrchr(i_stream->name, '.')) && (res_ext++) ) {
        // extension present
        for (i=0; demuxers[i]; i++) {
            strncpy(exts, demuxers[i]->info->extensions, sizeof(exts));

            for (tkn=strtok(exts, ","); tkn; tkn=strtok(NULL, ",")) {
                if (!strcmp(tkn, res_ext)) {
                    fnc_log(FNC_LOG_DEBUG, "[MT] probing demuxer: \"%s\" "
                                "matches \"%s\" demuxer\n", res_ext,
                                demuxers[i]->info->name);
                    if (demuxers[i]->probe(i_stream) == RESOURCE_OK) {
                        fnc_log(FNC_LOG_DEBUG,
                                "[MT] probing demuxer: \"%s\" demuxer found\n",
                                demuxers[i]->info->name);
                        found = 1;
                        break;
                    }
                }
            }
            if (found) break;
        }
    }
    if (!found) {
        for (i=0; demuxers[i]; i++) {
            if ((i!=probed) && (demuxers[i]->probe(i_stream) == RESOURCE_OK))
            {
                fnc_log(FNC_LOG_DEBUG, "[MT] probing demuxer: \"%s\""
                                       "demuxer found\n",
                                        demuxers[i]->info->name);
                found = 1;
                break;
            }
        }
    }

    return found ? i: -1;
}

// --- Description Cache management --- //
static gint cache_cmp(gconstpointer a, gconstpointer b)
{
    return strcmp( ((ResourceDescr *)a)->info->mrl, (const char *)b );
}

static ResourceDescr *r_descr_new(Resource *r)
{
    ResourceDescr *new_descr;
    MediaDescr *new_mdescr;
    GList *tracks;

    new_descr=g_new(ResourceDescr, 1);
    new_descr->media = NULL;

    new_descr->info=r->info;
    MObject_ref(r->info);
    new_descr->last_change=mrl_mtime(r->info->mrl);

    for (tracks=g_list_first(r->tracks); tracks; tracks=g_list_next(tracks)) {
        new_mdescr = g_new(MediaDescr, 1);
        new_mdescr->info = TRACK(tracks)->info;
        MObject_ref(TRACK(tracks)->info);
        new_mdescr->properties = TRACK(tracks)->properties;
        MObject_ref(TRACK(tracks)->properties);
        new_mdescr->last_change = mrl_mtime(TRACK(tracks)->info->mrl);
        new_descr->media = g_list_prepend(new_descr->media, new_mdescr);
    }
    // put the Media description in the same order of tracks.
    new_descr->media=g_list_reverse(new_descr->media);

    return new_descr;
}

static void r_descr_free(ResourceDescr *descr)
{
    GList *m_descr;

    if (!descr)
        return;

    for (m_descr=g_list_first(descr->media);
         m_descr;
         m_descr=g_list_next(m_descr) )
    {
        MObject_unref( MOBJECT(MEDIA_DESCR(m_descr)->info) );
        MObject_unref( MOBJECT(MEDIA_DESCR(m_descr)->properties) );
    }
    g_list_free(descr->media);

    MObject_unref( MOBJECT(descr->info) );
    g_free(descr);
}

static void r_descr_cache_update(Resource *r)
{
    GList *cache_el;
    ResourceDescr *r_descr=NULL;

    if ( ( cache_el = r_descr_find(r->info->mrl) ) ) {
        r_descr = RESOURCE_DESCR(cache_el);
        // TODO free ResourceDescr
        descr_cache = g_list_remove_link(descr_cache, cache_el);
        if (r_changed(r_descr)) {
            r_descr_free(r_descr);
            r_descr=NULL;
        }
    }
    if (!r_descr)
        r_descr = r_descr_new(r);

    descr_cache=g_list_prepend(descr_cache, r_descr);

    if (g_list_length(descr_cache)>MAX_DESCR_CACHE_SIZE) {
        cache_el = g_list_last(descr_cache);
        r_descr_free(RESOURCE_DESCR(cache_el));
        descr_cache = g_list_delete_link(descr_cache, cache_el);
    }
}

static void resinfo_free(void *resinfo)
{
    if (!resinfo)
        return;

    g_free(((ResourceInfo *)resinfo)->mrl);

    g_free(resinfo);
}

static void trackinfo_free(void *trackinfo)
{
    if (!trackinfo)
        return;

    g_free(((TrackInfo *)trackinfo)->mrl);

    g_free(trackinfo);
}

static void free_sdp_field(sdp_field *sdp, void *unused)
{
    if (!sdp)
        return;

    g_free(sdp->field);
    g_free(sdp);
}

static void properties_free(void *properties)
{
    MediaProperties *props = properties;

    if (!props)
        return;
    if (props->sdp_private)
        g_list_foreach(props->sdp_private, (GFunc)free_sdp_field, NULL);

    g_free(props);
}

Resource *r_open(struct feng *srv, const char *inner_path)
{
    Resource *r;
    int dmx_idx;
    InputStream *i_stream;
    gchar *mrl = g_strjoin ("/",
                            srv->config_storage[0]->document_root->ptr,
                            inner_path,
                            NULL);

    if( !(i_stream = istream_open(mrl)) )
        return NULL;

    if ( (dmx_idx = find_demuxer(i_stream))<0 ) {
        fnc_log(FNC_LOG_DEBUG, "[MT] Could not find a valid demuxer"<commit_msg>Use the foreach interface of GList instead of doing so manually.<commit_after>/* *
 * This file is part of Feng
 *
 * Copyright (C) 2009 by LScube team <<EMAIL>>
 * See AUTHORS for more details
 *
 * feng is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * feng is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with feng; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * */

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#include <fenice/server.h>
#include "demuxer.h"
#include "description.h"
#include <fenice/utils.h>
#include <fenice/fnc_log.h>

// global demuxer modules:
extern Demuxer fnc_demuxer_sd;
extern Demuxer fnc_demuxer_ds;
extern Demuxer fnc_demuxer_avf;

// static array containing all the available demuxers:
static Demuxer *demuxers[] = {
    &fnc_demuxer_sd,
    &fnc_demuxer_ds,
    &fnc_demuxer_avf,
    NULL
};

/*! @brief The resource description cache.
 * This list holds the cache of resource descriptions. This way the mediathread
 * can provide the description buffer without opening the resource every time.
 * This will result in a better performance for RTSP DESCRIBE metod response.
 * Descriptions are ordered for access time, so that the last is the less
 * recently addressed media description and will be chosen for removal if cache
 * reaches the limit.
 * */
static GList *descr_cache=NULL;
//! cache size of descriptions (maybe we need to take it from fenice configuration file?)
#define MAX_DESCR_CACHE_SIZE 10

/*! Private functions for exclusive tracks.
 * */
#define r_descr_find(mrl) g_list_find_custom(descr_cache, mrl, cache_cmp)

static int r_changed(ResourceDescr *descr);

// private functions for specific demuxer

/**
 * Find the correct demuxer for the given resource.
 * First of all, it tries to match the resource extension with one of those
 * served by the demuxers and, if found, probes that demuxer.
 * If no demuxer can be found this way, then it tries every demuxer available.
 * @param i_stream the resource.
 * @return the index of the valid demuxer in the list or -1 if it could not be
 * found.
 * */

static int find_demuxer(InputStream *i_stream)
{
    // this int will contain the index of the demuxer already probed second
    // the extension suggestion, in order to not probe twice the same
    // demuxer that was proved to be not usable.
    int probed = -1;
    char found = 0; // flag for demuxer found.
    int i; // index
    char exts[128], *res_ext, *tkn; /* temp string containing extension
                                     * served by probing demuxer.
                                     */

    // First of all try that with matching extension: we use extension as a
    // hint of resource type.
    if ( (res_ext=strrchr(i_stream->name, '.')) && (res_ext++) ) {
        // extension present
        for (i=0; demuxers[i]; i++) {
            strncpy(exts, demuxers[i]->info->extensions, sizeof(exts));

            for (tkn=strtok(exts, ","); tkn; tkn=strtok(NULL, ",")) {
                if (!strcmp(tkn, res_ext)) {
                    fnc_log(FNC_LOG_DEBUG, "[MT] probing demuxer: \"%s\" "
                                "matches \"%s\" demuxer\n", res_ext,
                                demuxers[i]->info->name);
                    if (demuxers[i]->probe(i_stream) == RESOURCE_OK) {
                        fnc_log(FNC_LOG_DEBUG,
                                "[MT] probing demuxer: \"%s\" demuxer found\n",
                                demuxers[i]->info->name);
                        found = 1;
                        break;
                    }
                }
            }
            if (found) break;
        }
    }
    if (!found) {
        for (i=0; demuxers[i]; i++) {
            if ((i!=probed) && (demuxers[i]->probe(i_stream) == RESOURCE_OK))
            {
                fnc_log(FNC_LOG_DEBUG, "[MT] probing demuxer: \"%s\""
                                       "demuxer found\n",
                                        demuxers[i]->info->name);
                found = 1;
                break;
            }
        }
    }

    return found ? i: -1;
}

// --- Description Cache management --- //
static gint cache_cmp(gconstpointer a, gconstpointer b)
{
    return strcmp( ((ResourceDescr *)a)->info->mrl, (const char *)b );
}

/**
 * @brief Create new media descriptions
 *
 * @param element The track to create a media description for
 * @param user_data The new resource descriptions to add the
 *                  descriptions to
 *
 * @internal This function should only be called through
 *           g_list_foreach().
 */
static void r_descr_new_mdescr(gpointer element, gpointer user_data)
{
    ResourceDescr *new_descr = (ResourceDescr *)user_data;
    Track *track = (Track*)element;

    MediaDescr *new_mdescr = g_new(MediaDescr, 1);
    new_mdescr->info = track->info;
    MObject_ref(track->info);
    new_mdescr->properties = track->properties;
    MObject_ref(track->properties);
    new_mdescr->last_change = mrl_mtime(track->info->mrl);
    new_descr->media = g_list_append(new_descr->media, new_mdescr);
}

static ResourceDescr *r_descr_new(Resource *r)
{
    ResourceDescr *new_descr = g_new(ResourceDescr, 1);
    new_descr->media = NULL;

    new_descr->info=r->info;
    MObject_ref(r->info);
    new_descr->last_change=mrl_mtime(r->info->mrl);

    g_list_foreach(r->tracks, r_descr_new_mdescr, new_descr);

    return new_descr;
}

/**
 * @brief Unreference a list of media descriptions
 *
 * @param element The media description to unreference
 * @param user_data Unused
 *
 * @internal This function should only be called through
 *           g_list_foreach().
 */
static void r_descr_free_media(gpointer element, gpointer user_data)
{
    MediaDescr *m_descr = (MediaDescr *)element;

    MObject_unref( MOBJECT(m_descr->info) );
    MObject_unref( MOBJECT(m_descr->properties) );
}

static void r_descr_free(ResourceDescr *descr)
{
    GList *m_descr;

    if (!descr)
        return;

    g_list_foreach(descr->media, r_descr_free_media, NULL);
    g_list_free(descr->media);

    MObject_unref( MOBJECT(descr->info) );
    g_free(descr);
}

static void r_descr_cache_update(Resource *r)
{
    GList *cache_el;
    ResourceDescr *r_descr=NULL;

    if ( ( cache_el = r_descr_find(r->info->mrl) ) ) {
        r_descr = RESOURCE_DESCR(cache_el);
        // TODO free ResourceDescr
        descr_cache = g_list_remove_link(descr_cache, cache_el);
        if (r_changed(r_descr)) {
            r_descr_free(r_descr);
            r_descr=NULL;
        }
    }
    if (!r_descr)
        r_descr = r_descr_new(r);

    descr_cache=g_list_prepend(descr_cache, r_descr);

    if (g_list_length(descr_cache)>MAX_DESCR_CACHE_SIZE) {
        cache_el = g_list_last(descr_cache);
        r_descr_free(RESOURCE_DESCR(cache_el));
        descr_cache = g_list_delete_link(descr_cache, cache_el);
    }
}

static void resinfo_free(void *resinfo)
{
    if (!resinfo)
        return;

    g_free(((ResourceInfo *)resinfo)->mrl);

    g_free(resinfo);
}

static void trackinfo_free(void *trackinfo)
{
    if (!trackinfo)
        return;

    g_free(((TrackInfo *)trackinfo)->mrl);

    g_free(trackinfo);
}

static void free_sdp_field(sdp_field *sdp, void *unused)
{
    if (!sdp)
        return;

    g_free(sdp->field);
    g_free(sdp);
}

static void properties_free(void *properties)
{
    MediaProperties *props = properties;

    if (!props)
        return;

    g_list_foreach(props->sdp_private, (GFunc)free_sdp_field, NULL);

    g_free(props);
}

Resource *r_open(struct feng *srv, const char *inner_path)
{
    Resource *r;
    int dmx_idx;
    InputStream *i_stream;
    gchar *mrl = g_strjoin ("/",
                            srv->config_storage[0]->document_root->ptr,
                            inner_path,
                            NULL);

    if( !(i_stream = istream_open(mrl)) )
        return NULL;

    if ( (dmx_idx = find_demuxer(i_stream))<0 ) {
        fnc_log(FNC_LOG_DEBUG, "[MT] Could not find a valid demuxer"
{'fraction_non_alphanumeric': 0.10715793282277031, 'fraction_numerical': 0.004100584936380631, 'mean_word_length': 2.544347082709981, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 116, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6115849', 'n_tokens_mistral': 5510, 'n_tokens_neox': 5341, 'n_words': 1477}