Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/demux/playlist/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 18 kB image not shown  

Quelle  m3u.c   Sprache: C

 

/*****************************************************************************
 * m3u.c : M3U playlist format import
 *****************************************************************************
 * Copyright (C) 2004 VLC authors and VideoLAN
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *          Sigmund Augdal Helberg <dnumgis@videolan.org>
 *
 * 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.
 *****************************************************************************/


/*****************************************************************************
 * Preamble
 *****************************************************************************/


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

#include <vlc_common.h>
#include <vlc_arrays.h>
#include <vlc_access.h>
#include <vlc_charset.h>
#include <vlc_strings.h>

#include "playlist.h"

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

static int ReadDir( stream_t *, input_item_node_t * );
static bool ContainsURL(const uint8_t *, size_t);

static char *GuessEncoding (const char *str)
{
    return IsUTF8 (str) ? strdup (str) : FromLatin1 (str);
}

static char *CheckUnicode (const char *str)
{
    return IsUTF8 (str) ? strdup (str): NULL;
}

/*****************************************************************************
 * Import_M3U: main import function
 *****************************************************************************/

int Import_M3U( vlc_object_t *p_this )
{
    stream_t *p_stream = (stream_t *)p_this;
    const uint8_t *p_peek;
    ssize_t i_peek;
    unsigned int offset = 0;

    i_peek = vlc_stream_Peek( p_stream->s, &p_peek, 1024 );
    if( i_peek < 8 )
        return VLC_EGENERIC;

    /* Encoding: UTF-8 or unspecified */
    char *(*pf_dup) (const char *) = GuessEncoding;

    if (memcmp( p_peek, "\xef\xbb\xbf", 3) == 0) /* UTF-8 Byte Order Mark */
    {
        if( i_peek < 12 )
            return VLC_EGENERIC;
        pf_dup = CheckUnicode;
        offset = 3;
        p_peek += offset;
        i_peek -= offset;
    }

    if (stream_HasExtension(p_stream, ".m3u8")
     || strncasecmp((const char *)p_peek, "RTSPtext", 8) == 0) /* QuickTime */
        pf_dup = CheckUnicode;

    /* File type: playlist, or not (HLS manifest or whatever else) */
    char *type = stream_MimeType(p_stream->s);
    bool match;

    if (!p_stream->obj.force
     && memcmp(p_peek, "#EXTM3U", 7 ) != 0
     && (type == NULL
      || (vlc_ascii_strcasecmp(type, "application/mpegurl") != 0
       && vlc_ascii_strcasecmp(type, "application/x-mpegurl") != 0
       && vlc_ascii_strcasecmp(type, "audio/mpegurl") != 0
       && vlc_ascii_strcasecmp(type, "vnd.apple.mpegURL") != 0
       && vlc_ascii_strcasecmp(type, "audio/x-mpegurl") != 0))
     && !stream_HasExtension(p_stream, ".m3u8")
     && !stream_HasExtension(p_stream, ".m3u")
     && !stream_HasExtension(p_stream, ".vlc")
     && strncasecmp((const char *)p_peek, "RTSPtext", 8) != 0
     && !ContainsURL(p_peek, i_peek))
        match = false;
    else
        match = true;

    free(type);

    if (!match)
        return VLC_EGENERIC;

    if (offset && vlc_stream_Read(p_stream->s, NULL, offset) != offset)
        return VLC_EGENERIC;

    msg_Dbg( p_stream, "found valid M3U playlist" );
    p_stream->p_sys = pf_dup;
    p_stream->pf_readdir = ReadDir;
    p_stream->pf_control = PlaylistControl;

    return VLC_SUCCESS;
}

static bool ContainsURL(const uint8_t *p_peek, size_t i_peek)
{
    const char *ps = (const char *)p_peek;
    const char *ps_end = (const char *) p_peek + i_peek;
    const size_t i_max = sizeof( "https://" );
    if( i_peek < i_max + 1 )
        return false;

    bool b_newline = true;
    while( ps + i_max + 1 < ps_end )
    {
        if( *ps <= 0 )
            return false;

        /* Goto next line */
        if( *ps == '\n' )
        {
            ps++;
            b_newline = true;
            continue;
        }

        /* One line starting with a URL is enough */
        if( b_newline )
        {
            const char *ps_match = strnstr( ps, "://", i_max );
            if(ps_match)
            {
                switch(ps_match - ps)
                {
                    case 3:
                        if( !strncasecmp( ps, "mms", 3 ) ||
                            !strncasecmp( ps, "ftp", 3 ) )
                            return true;
                        break;
                    case 4:
                        if( !strncasecmp( ps, "http", 4 ) ||
                            !strncasecmp( ps, "rtsp", 4 ) ||
                            !strncasecmp( ps, "ftps", 4 ) )
                            return true;
                        break;
                    case 5:
                        if( !strncasecmp( ps, "https", 5 ) ||
                            !strncasecmp( ps, "ftpes", 5 ) )
                            return true;
                    default:
                        break;
                }
            }

            /* Comments and blank lines are ignored */
            if( *ps != '#' && *ps != '\n' && *ps != '\r')
                return false;

            b_newline = false;
        }

        ps++;
    }
    return false;
}

struct entry_meta_s
{
    char *psz_name;
    char *psz_artist;
    char *psz_album_art;
    char *psz_mrl;
    char *psz_language;
    char *psz_tvgid;
    char *psz_grouptitle;
    vlc_tick_t i_duration;
    const char**ppsz_options;
    int   i_options;
    vlc_meta_priority_t priority;
};

static void entry_meta_Init( struct entry_meta_s *e )
{
    memset(e, 0, sizeof(*e));
    e->i_duration = INPUT_DURATION_INDEFINITE;
    e->priority = VLC_META_PRIORITY_BASIC;
}

static void entry_meta_Clean( struct entry_meta_s *e )
{
    free( e->psz_name );
    free( e->psz_artist );
    free( e->psz_album_art );
    free( e->psz_mrl );
    free( e->psz_language );
    free( e->psz_tvgid );
    free( e->psz_grouptitle );
    while( e->i_options-- ) free( (char*)e->ppsz_options[e->i_options] );
    TAB_CLEAN( e->i_options, e->ppsz_options );
}

static void parseEXTINF( char *, char *(*)(const char *), struct entry_meta_s * );

static int CreateEntry( input_item_node_t *p_node, const struct entry_meta_s *meta )
{
    if( !meta->psz_mrl )
        return VLC_EGENERIC;

    input_item_t *p_input =
        input_item_NewExt( meta->psz_mrl, meta->psz_name, meta->i_duration,
                           ITEM_TYPE_UNKNOWN, ITEM_NET_UNKNOWN );
    if( !p_input )
        return VLC_EGENERIC;

    input_item_AddOptions( p_input, meta->i_options, meta->ppsz_options, 0 );

    vlc_mutex_lock( &p_input->lock );
    if( meta->psz_artist )
        vlc_meta_SetWithPriority( p_input->p_meta, vlc_meta_Artist, meta->psz_artist, meta->priority );
    if( meta->psz_name )
        vlc_meta_SetWithPriority( p_input->p_meta, vlc_meta_Title, meta->psz_name, meta->priority );
    if( meta->psz_album_art )
        vlc_meta_SetWithPriority( p_input->p_meta, vlc_meta_ArtworkURL, meta->psz_album_art, meta->priority );
    if( meta->psz_language )
        vlc_meta_SetWithPriority( p_input->p_meta, vlc_meta_Language, meta->psz_language, meta->priority );
    if( meta->psz_grouptitle )
        vlc_meta_SetWithPriority( p_input->p_meta, vlc_meta_Publisher, meta->psz_grouptitle, meta->priority );
    vlc_mutex_unlock( &p_input->lock );
    if( meta->psz_tvgid )
        input_item_AddInfo( p_input, "XMLTV", "tvg-id", "%s", meta->psz_tvgid );

    input_item_node_AppendItem( p_node, p_input );
    input_item_Release( p_input );

    return VLC_SUCCESS;
}

static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
{
    char       *psz_line;
    char       *psz_group = NULL; /* group is toggling tag */
    struct entry_meta_s meta;
    entry_meta_Init( &meta );
    char *    (*pf_dup) (const char *) = p_demux->p_sys;

    psz_line = vlc_stream_ReadLine( p_demux->s );
    while( psz_line )
    {
        char *psz_parse = psz_line;

        /* Skip leading tabs and spaces */
        while( *psz_parse == ' ' || *psz_parse == '\t' ||
               *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++;

        if( *psz_parse == '#' )
        {
            /* Parse extra info */

            /* Skip leading tabs and spaces */
            while( *psz_parse == ' ' || *psz_parse == '\t' ||
                   *psz_parse == '\n' || *psz_parse == '\r' ||
                   *psz_parse == '#' ) psz_parse++;

            if( !*psz_parse ) goto nextline;

            if( !strncasecmp( psz_parse, "EXTINF:", sizeof("EXTINF:") -1 ) )
            {
                /* Extended info */
                psz_parse += sizeof("EXTINF:") - 1;
                meta.i_duration = INPUT_DURATION_INDEFINITE;
                parseEXTINF( psz_parse, pf_dup, &meta );
                meta.priority = VLC_META_PRIORITY_PLAYLIST;
            }
            else if( !strncasecmp( psz_parse, "EXTGRP:", sizeof("EXTGRP:") -1 ) )
            {
                psz_parse += sizeof("EXTGRP:") - 1;
                if( *psz_parse )
                {
                    free( psz_group );
                    psz_group = pf_dup( psz_parse );
                }
            }
            else if( !strncasecmp( psz_parse, "EXTVLCOPT:",
                                   sizeof("EXTVLCOPT:") -1 ) )
            {
                /* VLC Option */
                char *psz_option;
                psz_parse += sizeof("EXTVLCOPT:") -1;
                if( !*psz_parse ) goto nextline;

                psz_option = pf_dup( psz_parse );
                if( psz_option )
                    TAB_APPEND( meta.i_options, meta.ppsz_options, psz_option );
            }
            /* Special case for jamendo which provide the albumart */
            else if( !strncasecmp( psz_parse, "EXTALBUMARTURL:",
                     sizeof( "EXTALBUMARTURL:" ) -1 ) )
            {
                psz_parse += sizeof( "EXTALBUMARTURL:" ) - 1;
                if( *psz_parse )
                {
                    free( meta.psz_album_art );
                    meta.psz_album_art = pf_dup( psz_parse );
                    meta.priority = VLC_META_PRIORITY_PLAYLIST;
                }
            }
            else if ( !strncasecmp( psz_parse, "PLAYLIST:",
                      sizeof( "PLAYLIST:" ) - 1 ) )
            {
                psz_parse += sizeof( "PLAYLIST:" ) - 1;
                input_item_SetTitle( p_subitems->p_item, psz_parse );
            }
        }
        else if( !strncasecmp( psz_parse, "RTSPtext", sizeof("RTSPtext") -1 ) )
        {
            ;/* special case to handle QuickTime RTSPtext redirect files */
        }
        else if( *psz_parse )
        {
            psz_parse = pf_dup( psz_parse );
            if( !meta.psz_name && psz_parse )
                /* Use filename as name for relative entries */
                meta.psz_name = strdup( psz_parse );
            if( psz_group && !meta.psz_grouptitle )
                meta.psz_grouptitle = strdup( psz_group );

            meta.psz_mrl = ProcessMRL( psz_parse, p_demux->psz_url );
            free( psz_parse );

            CreateEntry( p_subitems, &meta );

            /* Cleanup state after entry */
            entry_meta_Clean( &meta );
            entry_meta_Init( &meta );
        }

 nextline:
        /* Fetch another line */
        free( psz_line );
        psz_line = vlc_stream_ReadLine( p_demux->s );
        if( !psz_line )
        {
            /* Cleanup state */
            entry_meta_Clean( &meta );
            entry_meta_Init( &meta );
            free( psz_group );
        }
    }
    return VLC_SUCCESS; /* Needed for correct operation of go back */
}

static void parseEXTINFTitle( char *psz_string,
                              char *(*pf_dup) (const char *),
                              struct entry_meta_s *meta )
{
    char *psz_item = strstr( psz_string, " - " );

    /* here we have the 0.8.2+ format with artist */
    if ( psz_item )
    {
        /* *** "EXTINF:time,artist - name" */
        *psz_item = '\0';
        if( *psz_string )
            meta->psz_artist = pf_dup( psz_string );
        if( psz_item[3] )
            meta->psz_name = pf_dup( &psz_item[3] ); /* points directly after ' - ' */
        return;
    }

    /* reaching this point means: 0.8.1- with artist or something without artist */
    if ( *psz_string == ',' )
    {
        /* *** "EXTINF:time,,name" */
        psz_string++;
        if( *psz_string )
            meta->psz_name = pf_dup( psz_string );
        return;
    }

    psz_item = psz_string;
    psz_string = strchr( psz_string, ',' );
    if ( psz_string )
    {
        /* *** "EXTINF:time,artist,name" */
        *psz_string = '\0';
        if( *psz_item )
            meta->psz_artist = pf_dup( psz_item );
        if( psz_string[1] )
            meta->psz_name = pf_dup( &psz_string[1] );
    }
    else
    {
        /* *** "EXTINF:time,name" */
        if( *psz_item )
            meta->psz_name = pf_dup( psz_item );
    }
}

static void parseEXTINFIptvDiots( char *psz_string,
                                  char *(*pf_dup)(const char *),
                                  struct entry_meta_s *meta )
{
    char *psz = strchr( psz_string, '=' );
    if( unlikely(!psz) )
        return;

    char **ppsz_meta = NULL;
    *psz = 0;
    if( !strncasecmp( psz_string, "tvg-", 4 ) )
    {
        if( !strcasecmp( psz_string + 4, "logo" ) )
            ppsz_meta = &meta->psz_album_art;
        else if( !strcasecmp( psz_string + 4, "name" ) )
            ppsz_meta = &meta->psz_name;
        else if( !strcasecmp( psz_string + 4, "language" ) )
            ppsz_meta = &meta->psz_language;
        else if( !strcasecmp( psz_string + 4, "id" ) )
            ppsz_meta = &meta->psz_tvgid;
    }
    else if( !strcasecmp( psz_string, "group-title" ) )
    {
        ppsz_meta = &meta->psz_grouptitle;
    }
    *psz = '=';

    if( !ppsz_meta || *ppsz_meta /* no overwrite */ )
        return;

    size_t i_valuelen = strlen( ++psz );
    if( unlikely(i_valuelen == 0) )
        return;

    bool b_escaped = (*psz == '"');
    if( i_valuelen > 2 && b_escaped )
    {
        psz[ i_valuelen - 1 ] = 0;
        *ppsz_meta = pf_dup( psz + 1 );
    }
    else
        *ppsz_meta = pf_dup( psz );
}

static void parseEXTINFIptvDiotsInDuration( char *psz_string,
                                            char *(*pf_dup)(const char *),
                                            struct entry_meta_s *meta,
                                            char **ppsz_end )
{
    for( ;; )
    {
        while( *psz_string == '\t' || *psz_string == ' ' )
            psz_string++;

        char *psz_start = psz_string;
        bool b_escaped = false;
        bool b_key = false;
        bool b_value = false;
        bool b_next = false;

        for( ;!b_next; ++psz_string )
        {
            switch( *psz_string )
            {
                case ',': /* Last comma for title */
                    if(!b_escaped)
                    {
                        if(b_value)
                        {
                            *psz_string = '\0';
                            parseEXTINFIptvDiots( psz_start, pf_dup, meta );
                        }
                        *ppsz_end = psz_string + 1;
                        return;
                    }
                    break;
                case '"':
                    if(!b_escaped && b_value)
                        return;
                    if(!b_key)
                        return;
                    b_value = true;
                    b_escaped = !b_escaped;
                    break;
                case '\0':
                    if(!b_escaped && b_value)
                        parseEXTINFIptvDiots( psz_start, pf_dup, meta );
                    return;
                case ' ':
                    if(!b_escaped)
                    {
                        *psz_string = '\0';
                        if(b_value)
                            parseEXTINFIptvDiots( psz_start, pf_dup, meta );
                        b_next = true;
                    }
                    break;
                case '=':
                    b_key = true;
                    break;
                default:
                    if(b_key)
                        b_value = true;
                    break;
            }
        }
    }
}

static void parseEXTINF( char *psz_string,
                         char *(*pf_dup)(const char *),
                         struct entry_meta_s *meta )
{
    char *end = psz_string + strlen( psz_string );

    FREENULL( meta->psz_name );
    FREENULL( meta->psz_artist );

    /* strip leading whitespaces */
    while( psz_string < end && ( *psz_string == '\t' || *psz_string == ' ' ) )
        psz_string++;

    /* Parse duration */
    char *psz_end = NULL;
    float i_parsed_duration = vlc_strtof_c( psz_string, &psz_end );
    if( i_parsed_duration > 0 )
        meta->i_duration = vlc_tick_from_sec( (double)i_parsed_duration );

    /* skip to first unmatched */
    if( psz_end )
        psz_string = psz_end;

    /* skip whitespaces */
    while( psz_string < end && ( *psz_string == '\t' || *psz_string == ' ' ) )
        psz_string++;

    if( psz_string == end )
        return;

    /* EXTINF:1,title*/
    /* EXTINF: -123.12  ,title*/
    if( *psz_string == ',' )
    {
        if( ++psz_string < end )
            parseEXTINFTitle( psz_string, pf_dup, meta );
    }
    /* EXTINF: -1  tvg-foo="val" tvg-foo2="val",title */
    /* EXTINF: -1  tvg-foo="val,val2" ,title */
    else if( *psz_string >= 'A' && *psz_string <= 'z' )
    {
        psz_end = NULL;
        parseEXTINFIptvDiotsInDuration( psz_string, pf_dup, meta, &psz_end );
        if( psz_end && *psz_end ) /* returns past last comma */
        {
            free(meta->psz_name);
            meta->psz_name = pf_dup( psz_end );
        }
    }
}


Messung
CHZG
84979993

¤ Dauer der Verarbeitung: 0.11 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.