<commit_before>/*****************************************************************************
 * demux.c
 *****************************************************************************
 * Copyright (C) 1999-2004 VLC authors and VideoLAN
 * $Id$
 *
 * Author: <NAME> <<EMAIL>>
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>

#include "demux.h"
#include <libvlc.h>
#include <vlc_codec.h>
#include <vlc_meta.h>
#include <vlc_url.h>
#include <vlc_modules.h>
#include <vlc_strings.h>

static bool SkipID3Tag( demux_t * );
static bool SkipAPETag( demux_t *p_demux );

struct demux_type
{
    char type[20];
    char demux[8];
};

static int typecmp( const void *k, const void *t )
{
    const char *key = k;
    const struct demux_type *type = t;

    return vlc_ascii_strcasecmp( key, type->type );
}

static const char *demux_FromContentType(const char *mime)
{
    static const struct demux_type types[] =
    {   /* Must be sorted in ascending ASCII order */
        { "audio/aac",           "m4a"     },
        { "audio/aacp",          "m4a"     },
        { "audio/mpeg",          "mp3"     },
        { "application/rss+xml", "podcast" },
        //{ "video/MP1S",          "es,mpgv" }, !b_force
        { "video/dv",            "rawdv"   },
        { "video/MP2P",          "ps"      },
        { "video/MP2T",          "ts"      },
        { "video/nsa",           "nsv"     },
        { "video/nsv",           "nsv"     },
    };
    const struct demux_type *type;

    type = bsearch( mime, types, sizeof (types) / sizeof (types[0]),
                    sizeof (types[0]), typecmp );
    return (type != NULL) ? type->demux : "any";
}

/*****************************************************************************
 * demux_New:
 *  if s is NULL then load a access_demux
 *****************************************************************************/
demux_t *demux_New( vlc_object_t *p_obj, const char *psz_name,
                    const char *psz_location, stream_t *s, es_out_t *out )
{
    return demux_NewAdvanced( p_obj, NULL,
                              (s == NULL) ? psz_name : "",
                              (s != NULL) ? psz_name : "",
                              psz_location, s, out, false );
}

/*****************************************************************************
 * demux_NewAdvanced:
 *  if s is NULL then load a access_demux
 *****************************************************************************/
#undef demux_NewAdvanced
demux_t *demux_NewAdvanced( vlc_object_t *p_obj, input_thread_t *p_parent_input,
                            const char *psz_access, const char *psz_demux,
                            const char *psz_location,
                            stream_t *s, es_out_t *out, bool b_preparsing )
{
    demux_t *p_demux = vlc_custom_create( p_obj, sizeof( *p_demux ), "demux" );
    if( unlikely(p_demux == NULL) )
        return NULL;

    if( s != NULL && (!strcasecmp( psz_demux, "any" ) || !psz_demux[0]) )
    {   /* Look up demux by Content-Type for hard to detect formats */
        char *type = stream_ContentType( s );
        if( type != NULL )
        {
            psz_demux = demux_FromContentType( type );
            free( type );
        }
    }

    p_demux->p_input = p_parent_input;
    p_demux->psz_access = strdup( psz_access );
    p_demux->psz_demux = strdup( psz_demux );
    p_demux->psz_location = strdup( psz_location );
    p_demux->psz_file = get_path( psz_location ); /* parse URL */

    if( unlikely(p_demux->psz_access == NULL
              || p_demux->psz_demux == NULL
              || p_demux->psz_location == NULL) )
        goto error;

    if( !b_preparsing )
        msg_Dbg( p_obj, "creating demux: access='%s' demux='%s' "
                 "location='%s' file='%s'",
                 p_demux->psz_access, p_demux->psz_demux,
                 p_demux->psz_location, p_demux->psz_file );

    p_demux->s              = s;
    p_demux->out            = out;
    p_demux->b_preparsing   = b_preparsing;

    p_demux->pf_demux   = NULL;
    p_demux->pf_control = NULL;
    p_demux->p_next     = NULL;
    p_demux->p_sys      = NULL;
    p_demux->info.i_update = 0;
    p_demux->info.i_title  = 0;
    p_demux->info.i_seekpoint = 0;

    /* NOTE: Add only file without any problems here and with strong detection:
     * - no .mp3, .a52, ...
     *  - wav can't be added 'cause of a52 and dts in them as raw audio
     */
    static const struct { char ext[5]; char demux[9]; } exttodemux[] =
    {
        { "aiff", "aiff" },
        { "asf",  "asf" }, { "wmv",  "asf" }, { "wma",  "asf" },
        { "avi",  "avi" },
        { "au",   "au" },
        { "flac", "flac" },
        { "dv",   "dv" },
        { "drc",  "dirac" },
        { "m3u",  "m3u" },
        { "mkv",  "mkv" }, { "mka",  "mkv" }, { "mks",  "mkv" },
        { "mp4",  "mp4" }, { "m4a",  "mp4" }, { "mov",  "mp4" }, { "moov", "mp4" },
        { "nsv",  "nsv" },
        { "ogg",  "ogg" }, { "ogm",  "ogg" }, /* legacy Ogg */
        { "oga",  "ogg" }, { "spx",  "ogg" }, { "ogv", "ogg" },
        { "ogx",  "ogg" }, /*RFC5334*/
        { "opus", "ogg" }, /*draft-terriberry-oggopus-01*/
        { "pva",  "pva" },
        { "rm",   "avformat" },
        { "m4v",  "m4v" },
        { "h264", "h264" },
        { "voc",  "voc" },
        { "mid",  "smf" }, { "rmi",  "smf" }, { "kar", "smf" },
        { "",  "" },
    };
    /* Here, we don't mind if it does not work, it must be quick */
    static const struct { char ext[4]; char demux[5]; } exttodemux_quick[] =
    {
        { "mp3", "mpga" },
        { "ogg", "ogg" },
        { "wma", "asf" },
        { "", "" }
    };

    if( s != NULL )
    {
        const char *psz_ext;
        const char *psz_module = p_demux->psz_demux;

        if( !strcmp(psz_module, "any") && p_demux->psz_file != NULL
         && (psz_ext = strrchr( p_demux->psz_file, '.' )) != NULL )
        {
            psz_ext++; // skip '.'

            if( !b_preparsing )
            {
                for( unsigned i = 0; exttodemux[i].ext[0]; i++ )
                {
                    if( !strcasecmp( psz_ext, exttodemux[i].ext ) )
                    {
                        psz_module = exttodemux[i].demux;
                        break;
                    }
                }
            }
            else
            {
                for( unsigned i = 0; exttodemux_quick[i].ext[0]; i++ )
                {
                    if( !strcasecmp( psz_ext, exttodemux_quick[i].ext ) )
                    {
                        psz_module = exttodemux_quick[i].demux;
                        break;
                    }
                }
            }
        }

        /* ID3/APE tags will mess-up demuxer probing so we skip it here.
         * ID3/APE parsers will called later on in the demuxer to access the
         * skipped info. */
        while (SkipID3Tag( p_demux ))
          ;
        SkipAPETag( p_demux );

        p_demux->p_module =
            module_need( p_demux, "demux", psz_module,
                         !strcmp( psz_module, p_demux->psz_demux ) );
    }
    else
    {
        p_demux->p_module =
            module_need( p_demux, "access_demux", p_demux->psz_access, true );
    }

    if( p_demux->p_module == NULL )
        goto error;

    return p_demux;
error:
    free( p_demux->psz_file );
    free( p_demux->psz_location );
    free( p_demux->psz_demux );
    free( p_demux->psz_access );
    vlc_object_release( p_demux );
    return NULL;
}

demux_t *input_DemuxNew( vlc_object_t *obj, const char *access_name,
                         const char *demux_name, const char *path,
                         es_out_t *out, bool preparsing, input_thread_t *input )
{
    char *demux_var = NULL;

    assert( access_name != NULL );
    assert( demux_name != NULL );
    assert( path != NULL );

    if( demux_name[0] == '\0' )
    {
        /* special hack for forcing a demuxer with --demux=module
         * (and do nothing with a list) */
        demux_var = var_InheritString( obj, "demux" );
        if( demux_var != NULL )
        {
            demux_name = demux_var;
            msg_Dbg( obj, "specified demux: %s", demux_name );
        }
        else
            demux_name = "any";
    }

    demux_t *demux = NULL;

    if( preparsing )
    {
        if( strcasecmp( demux_name, "any" ) )
            goto out;

        msg_Dbg( obj, "preparsing %s://%s", access_name, path );
    }
    else /* Try access_demux first */
        demux = demux_NewAdvanced( obj, input, access_name, demux_name, path,
                                   NULL, out, false );

    if( demux == NULL )
    {   /* Then try a real access,stream,demux chain */
        /* Create the stream_t */
        stream_t *stream = NULL;
        char *url;

        if( likely(asprintf( &url, "%s://%s", access_name, path) >= 0) )
        {
            stream = stream_AccessNew( obj, input, preparsing, url );
            free( url );
        }

        if( stream == NULL )
        {
            msg_Err( obj, "cannot access %s://%s", access_name, path );
            goto out;
        }

        /* Add stream filters */
        stream = stream_FilterAutoNew( stream );

        char *filters = var_InheritString( obj, "stream-filter" );
        if( filters != NULL )
        {
            stream = stream_FilterChainNew( stream, filters );
            free( filters );
        }

        if( var_InheritBool( obj, "input-record-native" ) )
            stream = stream_FilterChainNew( stream, "record" );

        /* FIXME: Hysterical raisins. Access is not updated according to any
         * redirect but path is. This does not make much sense. Probably the
         * URL should be passed as a whole and demux_t.psz_access removed. */
        if( stream->psz_url != NULL )
        {
            path = strstr( stream->psz_url, "://" );
            if( path == NULL )
            {
                stream_Delete( stream );
                goto out;
            }
            path += 3;
        }

        demux = demux_NewAdvanced( obj, input, access_name, demux_name, path,
                                   stream, out, preparsing );
        if( demux == NULL )
        {
            msg_Err( obj, "cannot parse %s://%s", access_name, path );
            stream_Delete( stream );
        }
    }
out:
    free( demux_var );
    return demux;
}

/*****************************************************************************
 * demux_Delete:
 *****************************************************************************/
void demux_Delete( demux_t *p_demux )
{
    stream_t *s;

    demux_t *p_next = p_demux->p_next;
    if ( p_next != NULL )
        demux_Delete( p_next );

    module_unneed( p_demux, p_demux->p_module );
    free( p_demux->psz_file );
    free( p_demux->psz_location );
    free( p_demux->psz_demux );
    free( p_demux->psz_access );

    s = p_demux->s;
    vlc_object_release( p_demux );
    if( s != NULL )
        stream_Delete( s );
}

#define static_control_match(foo) \<commit_msg>demux: add private destruction callback<commit_after>/*****************************************************************************
 * demux.c
 *****************************************************************************
 * Copyright (C) 1999-2004 VLC authors and VideoLAN
 * $Id$
 *
 * Author: <NAME> <<EMAIL>>
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>

#include "demux.h"
#include <libvlc.h>
#include <vlc_codec.h>
#include <vlc_meta.h>
#include <vlc_url.h>
#include <vlc_modules.h>
#include <vlc_strings.h>

static bool SkipID3Tag( demux_t * );
static bool SkipAPETag( demux_t *p_demux );

struct demux_type
{
    char type[20];
    char demux[8];
};

static int typecmp( const void *k, const void *t )
{
    const char *key = k;
    const struct demux_type *type = t;

    return vlc_ascii_strcasecmp( key, type->type );
}

static const char *demux_FromContentType(const char *mime)
{
    static const struct demux_type types[] =
    {   /* Must be sorted in ascending ASCII order */
        { "audio/aac",           "m4a"     },
        { "audio/aacp",          "m4a"     },
        { "audio/mpeg",          "mp3"     },
        { "application/rss+xml", "podcast" },
        //{ "video/MP1S",          "es,mpgv" }, !b_force
        { "video/dv",            "rawdv"   },
        { "video/MP2P",          "ps"      },
        { "video/MP2T",          "ts"      },
        { "video/nsa",           "nsv"     },
        { "video/nsv",           "nsv"     },
    };
    const struct demux_type *type;

    type = bsearch( mime, types, sizeof (types) / sizeof (types[0]),
                    sizeof (types[0]), typecmp );
    return (type != NULL) ? type->demux : "any";
}

/*****************************************************************************
 * demux_New:
 *  if s is NULL then load a access_demux
 *****************************************************************************/
demux_t *demux_New( vlc_object_t *p_obj, const char *psz_name,
                    const char *psz_location, stream_t *s, es_out_t *out )
{
    return demux_NewAdvanced( p_obj, NULL,
                              (s == NULL) ? psz_name : "",
                              (s != NULL) ? psz_name : "",
                              psz_location, s, out, false );
}

typedef struct demux_priv_t
{
    demux_t demux;
    void (*destroy)(demux_t *);
} demux_priv_t;

static void demux_DestroyDemux(demux_t *demux)
{
    assert(demux->s != NULL);
    stream_Delete(demux->s);
}

static void demux_DestroyAccessDemux(demux_t *demux)
{
    assert(demux->s == NULL);
    (void) demux;
}

/*****************************************************************************
 * demux_NewAdvanced:
 *  if s is NULL then load a access_demux
 *****************************************************************************/
#undef demux_NewAdvanced
demux_t *demux_NewAdvanced( vlc_object_t *p_obj, input_thread_t *p_parent_input,
                            const char *psz_access, const char *psz_demux,
                            const char *psz_location,
                            stream_t *s, es_out_t *out, bool b_preparsing )
{
    demux_priv_t *priv = vlc_custom_create(p_obj, sizeof (*priv), "demux");
    if (unlikely(priv == NULL))
        return NULL;

    demux_t *p_demux = &priv->demux;

    if( s != NULL && (!strcasecmp( psz_demux, "any" ) || !psz_demux[0]) )
    {   /* Look up demux by Content-Type for hard to detect formats */
        char *type = stream_ContentType( s );
        if( type != NULL )
        {
            psz_demux = demux_FromContentType( type );
            free( type );
        }
    }

    p_demux->p_input = p_parent_input;
    p_demux->psz_access = strdup( psz_access );
    p_demux->psz_demux = strdup( psz_demux );
    p_demux->psz_location = strdup( psz_location );
    p_demux->psz_file = get_path( psz_location ); /* parse URL */

    if( unlikely(p_demux->psz_access == NULL
              || p_demux->psz_demux == NULL
              || p_demux->psz_location == NULL) )
        goto error;

    if( !b_preparsing )
        msg_Dbg( p_obj, "creating demux: access='%s' demux='%s' "
                 "location='%s' file='%s'",
                 p_demux->psz_access, p_demux->psz_demux,
                 p_demux->psz_location, p_demux->psz_file );

    p_demux->s              = s;
    p_demux->out            = out;
    p_demux->b_preparsing   = b_preparsing;

    p_demux->pf_demux   = NULL;
    p_demux->pf_control = NULL;
    p_demux->p_next     = NULL;
    p_demux->p_sys      = NULL;
    p_demux->info.i_update = 0;
    p_demux->info.i_title  = 0;
    p_demux->info.i_seekpoint = 0;
    priv->destroy = s ? demux_DestroyDemux : demux_DestroyAccessDemux;

    /* NOTE: Add only file without any problems here and with strong detection:
     * - no .mp3, .a52, ...
     *  - wav can't be added 'cause of a52 and dts in them as raw audio
     */
    static const struct { char ext[5]; char demux[9]; } exttodemux[] =
    {
        { "aiff", "aiff" },
        { "asf",  "asf" }, { "wmv",  "asf" }, { "wma",  "asf" },
        { "avi",  "avi" },
        { "au",   "au" },
        { "flac", "flac" },
        { "dv",   "dv" },
        { "drc",  "dirac" },
        { "m3u",  "m3u" },
        { "mkv",  "mkv" }, { "mka",  "mkv" }, { "mks",  "mkv" },
        { "mp4",  "mp4" }, { "m4a",  "mp4" }, { "mov",  "mp4" }, { "moov", "mp4" },
        { "nsv",  "nsv" },
        { "ogg",  "ogg" }, { "ogm",  "ogg" }, /* legacy Ogg */
        { "oga",  "ogg" }, { "spx",  "ogg" }, { "ogv", "ogg" },
        { "ogx",  "ogg" }, /*RFC5334*/
        { "opus", "ogg" }, /*draft-terriberry-oggopus-01*/
        { "pva",  "pva" },
        { "rm",   "avformat" },
        { "m4v",  "m4v" },
        { "h264", "h264" },
        { "voc",  "voc" },
        { "mid",  "smf" }, { "rmi",  "smf" }, { "kar", "smf" },
        { "",  "" },
    };
    /* Here, we don't mind if it does not work, it must be quick */
    static const struct { char ext[4]; char demux[5]; } exttodemux_quick[] =
    {
        { "mp3", "mpga" },
        { "ogg", "ogg" },
        { "wma", "asf" },
        { "", "" }
    };

    if( s != NULL )
    {
        const char *psz_ext;
        const char *psz_module = p_demux->psz_demux;

        if( !strcmp(psz_module, "any") && p_demux->psz_file != NULL
         && (psz_ext = strrchr( p_demux->psz_file, '.' )) != NULL )
        {
            psz_ext++; // skip '.'

            if( !b_preparsing )
            {
                for( unsigned i = 0; exttodemux[i].ext[0]; i++ )
                {
                    if( !strcasecmp( psz_ext, exttodemux[i].ext ) )
                    {
                        psz_module = exttodemux[i].demux;
                        break;
                    }
                }
            }
            else
            {
                for( unsigned i = 0; exttodemux_quick[i].ext[0]; i++ )
                {
                    if( !strcasecmp( psz_ext, exttodemux_quick[i].ext ) )
                    {
                        psz_module = exttodemux_quick[i].demux;
                        break;
                    }
                }
            }
        }

        /* ID3/APE tags will mess-up demuxer probing so we skip it here.
         * ID3/APE parsers will called later on in the demuxer to access the
         * skipped info. */
        while (SkipID3Tag( p_demux ))
          ;
        SkipAPETag( p_demux );

        p_demux->p_module =
            module_need( p_demux, "demux", psz_module,
                         !strcmp( psz_module, p_demux->psz_demux ) );
    }
    else
    {
        p_demux->p_module =
            module_need( p_demux, "access_demux", p_demux->psz_access, true );
    }

    if( p_demux->p_module == NULL )
        goto error;

    return p_demux;
error:
    free( p_demux->psz_file );
    free( p_demux->psz_location );
    free( p_demux->psz_demux );
    free( p_demux->psz_access );
    vlc_object_release( p_demux );
    return NULL;
}

demux_t *input_DemuxNew( vlc_object_t *obj, const char *access_name,
                         const char *demux_name, const char *path,
                         es_out_t *out, bool preparsing, input_thread_t *input )
{
    char *demux_var = NULL;

    assert( access_name != NULL );
    assert( demux_name != NULL );
    assert( path != NULL );

    if( demux_name[0] == '\0' )
    {
        /* special hack for forcing a demuxer with --demux=module
         * (and do nothing with a list) */
        demux_var = var_InheritString( obj, "demux" );
        if( demux_var != NULL )
        {
            demux_name = demux_var;
            msg_Dbg( obj, "specified demux: %s", demux_name );
        }
        else
            demux_name = "any";
    }

    demux_t *demux = NULL;

    if( preparsing )
    {
        if( strcasecmp( demux_name, "any" ) )
            goto out;

        msg_Dbg( obj, "preparsing %s://%s", access_name, path );
    }
    else /* Try access_demux first */
        demux = demux_NewAdvanced( obj, input, access_name, demux_name, path,
                                   NULL, out, false );

    if( demux == NULL )
    {   /* Then try a real access,stream,demux chain */
        /* Create the stream_t */
        stream_t *stream = NULL;
        char *url;

        if( likely(asprintf( &url, "%s://%s", access_name, path) >= 0) )
        {
            stream = stream_AccessNew( obj, input, preparsing, url );
            free( url );
        }

        if( stream == NULL )
        {
            msg_Err( obj, "cannot access %s://%s", access_name, path );
            goto out;
        }

        /* Add stream filters */
        stream = stream_FilterAutoNew( stream );

        char *filters = var_InheritString( obj, "stream-filter" );
        if( filters != NULL )
        {
            stream = stream_FilterChainNew( stream, filters );
            free( filters );
        }

        if( var_InheritBool( obj, "input-record-native" ) )
            stream = stream_FilterChainNew( stream, "record" );

        /* FIXME: Hysterical raisins. Access is not updated according to any
         * redirect but path is. This does not make much sense. Probably the
         * URL should be passed as a whole and demux_t.psz_access removed. */
        if( stream->psz_url != NULL )
        {
            path = strstr( stream->psz_url, "://" );
            if( path == NULL )
            {
                stream_Delete( stream );
                goto out;
            }
            path += 3;
        }

        demux = demux_NewAdvanced( obj, input, access_name, demux_name, path,
                                   stream, out, preparsing );
        if( demux == NULL )
        {
            msg_Err( obj, "cannot parse %s://%s", access_name, path );
            stream_Delete( stream );
        }
    }
out:
    free( demux_var );
    return demux;
}

/*****************************************************************************
 * demux_Delete:
 *****************************************************************************/
void demux_Delete( demux_t *p_demux )
{
    demux_priv_t *priv = (demux_priv_t *)p_demux;

    module_unneed( p_demux, p_demux->p_module );

    priv->destroy(p_demux);
    free( p_demux->psz_file );
    free( p_demux->psz_location );
    free( p_demux->psz_demux );
    free( p_demux->psz_access );
    vlc_object_release( p_demux );
}

#define static_control_match(foo) \
{'fraction_non_alphanumeric': 0.18526665283253788, 'fraction_numerical': 0.006640381821954763, 'mean_word_length': 1.9540272158881942, 'pattern_counts': {'":': 2, '<': 23, '<?xml version=': 0, '>': 119, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1500969', 'n_tokens_mistral': 8065, 'n_tokens_neox': 7829, 'n_words': 1913}