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

Quelle  demux.c   Sprache: C

 

/*****************************************************************************
 * demux.c: demuxer using libavformat
 *****************************************************************************
 * Copyright (C) 2004-2009 VLC authors and VideoLAN
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *          Gildas Bazin <gbazin@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_demux.h>
#include <vlc_stream.h>
#include <vlc_meta.h>
#include <vlc_input.h>
#include <vlc_charset.h>
#include <vlc_avcodec.h>

#include "../../codec/avcodec/avcodec.h"
#include "../../codec/avcodec/chroma.h"
#include "../../codec/avcodec/avcommon_compat.h"
#include "avformat.h"
#include "../xiph.h"
#include "../vobsub.h"
#include "../av1_unpack.h"

#include <libavformat/avformat.h>
#include <libavutil/display.h>

#if LIBAVUTIL_VERSION_CHECK( 57, 16, 100 )
# include <libavutil/dovi_meta.h>
#endif

//#define AVFORMAT_DEBUG 1

#if LIBAVFORMAT_VERSION_CHECK(59, 0, 100)
# define AVF_MAYBE_CONST const
#else
# define AVF_MAYBE_CONST
#endif


struct avformat_track_s
{
    es_out_id_t *p_es;
    vlc_tick_t i_pcr;
    es_format_t es_format;
};

/*****************************************************************************
 * demux_sys_t: demux descriptor
 *****************************************************************************/

typedef struct
{
    AVF_MAYBE_CONST AVInputFormat *fmt;
    AVFormatContext *ic;

    struct avformat_track_s *tracks;
    unsigned i_tracks;

    vlc_tick_t      i_pcr;

    unsigned    i_ssa_order;

    int                i_attachments;
    input_attachment_t **attachments;

    /* Only one title with seekpoints possible atm. */
    input_title_t *p_title;
    int i_seekpoint;
    unsigned i_update;
} demux_sys_t;

#define AVFORMAT_IOBUFFER_SIZE 32768  /* FIXME */

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

static int Demux  ( demux_t *p_demux );
static int Control( demux_t *p_demux, int i_query, va_list args );

static int IORead( void *opaque, uint8_t *buf, int buf_size );
static int64_t IOSeek( void *opaque, int64_t offset, int whence );

static block_t *BuildSsaFrame( const AVPacket *p_pkt, unsigned i_order );
static void UpdateSeekPoint( demux_t *p_demux, vlc_tick_t i_time );
static void ResetTime( demux_t *p_demux, int64_t i_time );

static vlc_fourcc_t CodecTagToFourcc( uint32_t codec_tag )
{
    // convert from little-endian avcodec codec_tag to VLC native-endian fourcc
#ifdef WORDS_BIGENDIAN
    return vlc_bswap32(codec_tag);
#else
    return codec_tag;
#endif
}

static inline void* GetStreamSideData(const AVStream *s, enum AVPacketSideDataType type)
{
#if LIBAVCODEC_VERSION_CHECK( 60, 29, 100 )
    const AVCodecParameters *cp = s->codecpar;
    const AVPacketSideData *psd =
        av_packet_side_data_get(cp->coded_side_data, cp->nb_coded_side_data, type);
    return psd ? psd->data : NULL;
#else
    return av_stream_get_side_data(s, type, NULL);
#endif
}

/*****************************************************************************
 * Open
 *****************************************************************************/


static void get_rotation(es_format_t *fmt, const AVStream *s)
{
    char const *kRotateKey = "rotate";
    AVDictionaryEntry *rotation = av_dict_get(s->metadata, kRotateKey, NULL, 0);
    long angle = 0;

    int32_t *matrix = GetStreamSideData(s, AV_PKT_DATA_DISPLAYMATRIX);
    if( matrix ) {
        bool flipped = (int64_t)matrix[0] * matrix[4] < (int64_t)matrix[1] * matrix[3];
        if (flipped) {
            /* Flip the matrix to decouple flip and rotation operations.
             * Always assume an horizontal flip for simplicity,
             * it can be changed later if rotation is 180º. */

            av_display_matrix_flip(matrix, 1, 0);
        }
        angle = lround(av_display_rotation_get(matrix));

        if (angle > 45 && angle < 135)
            fmt->video.orientation = ORIENT_ROTATED_270;

        else if (angle > 135 || angle < -135) {
            if (flipped)
                fmt->video.orientation = ORIENT_VFLIPPED;
            else
                fmt->video.orientation = ORIENT_ROTATED_180;
        }
        else if (angle < -45 && angle > -135)
            fmt->video.orientation = ORIENT_ROTATED_90;

        else
            fmt->video.orientation = ORIENT_NORMAL;

        /* Flip is already applied to the 180º case. */
        if (flipped && !(angle > 135 || angle < -135)) {
            video_transform_t transform = (video_transform_t)fmt->video.orientation;
            /* Flip first then rotate */
            fmt->video.orientation = ORIENT_HFLIPPED;
            video_format_TransformBy(&fmt->video, transform);
        }

    } else if( rotation ) {
        angle = strtol(rotation->value, NULL, 10);

        if (angle > 45 && angle < 135)
            fmt->video.orientation = ORIENT_ROTATED_90;

        else if (angle > 135 && angle < 225)
            fmt->video.orientation = ORIENT_ROTATED_180;

        else if (angle > 225 && angle < 315)
            fmt->video.orientation = ORIENT_ROTATED_270;

        else
            fmt->video.orientation = ORIENT_NORMAL;
    }
}

static void get_dovi_config(es_format_t *fmt, const AVStream *s)
{
#if LIBAVUTIL_VERSION_CHECK( 57, 16, 100 )
    const AVDOVIDecoderConfigurationRecord *cfg =
        GetStreamSideData(s, AV_PKT_DATA_DOVI_CONF);
    if (!cfg)
        return;

    fmt->video.dovi.version_major = cfg->dv_version_major;
    fmt->video.dovi.version_minor = cfg->dv_version_minor;
    fmt->video.dovi.profile = cfg->dv_profile;
    fmt->video.dovi.level = cfg->dv_level;
    fmt->video.dovi.rpu_present = cfg->rpu_present_flag;
    fmt->video.dovi.el_present = cfg->el_present_flag;
    fmt->video.dovi.bl_present = cfg->bl_present_flag;
#endif
}

static void get_palette(es_format_t *fmt, const AVStream *s)
{
    const uint8_t *pal = GetStreamSideData(s, AV_PKT_DATA_PALETTE);
    if (pal) {
        video_palette_t *p_palette = fmt->video.p_palette;
        for (size_t i=0; i<ARRAY_SIZE(p_palette->palette); i++)
        {
            memcpy(p_palette->palette[i], pal, sizeof(p_palette->palette[0]));
            pal += sizeof(p_palette->palette[0]);
        }
    }
}

static AVDictionary * BuildAVOptions( demux_t *p_demux )
{
    char *psz_opts = var_InheritString( p_demux, "avformat-options" );
    AVDictionary *options = NULL;
    if( psz_opts )
    {
        vlc_av_get_options( psz_opts, &options );
        free( psz_opts );
    }
    return options;
}

static void FreeUnclaimedOptions( demux_t *p_demux, AVDictionary **pp_dict )
{
    AVDictionaryEntry *t = NULL;
    while ((t = av_dict_get(*pp_dict, "", t, AV_DICT_IGNORE_SUFFIX))) {
        msg_Err( p_demux, "Unknown option \"%s\"", t->key );
    }
    av_dict_free(pp_dict);
}

static void FindStreamInfo( demux_t *p_demux, AVDictionary *options )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    unsigned nb_streams = p_sys->ic->nb_streams;

    AVDictionary *streamsoptions[nb_streams ? nb_streams : 1];

    streamsoptions[0] = options;
    for ( unsigned i = 1; i < nb_streams; i++ )
    {
        streamsoptions[i] = NULL;
        if( streamsoptions[0] )
            av_dict_copy( &streamsoptions[i], streamsoptions[0], 0 );
    }

    vlc_avcodec_lock(); /* avformat calls avcodec behind our back!!! */
    int error = avformat_find_stream_info( p_sys->ic, streamsoptions );
    vlc_avcodec_unlock();

    FreeUnclaimedOptions( p_demux, &streamsoptions[0] );
    for ( unsigned i = 1; i < nb_streams; i++ ) {
        av_dict_free( &streamsoptions[i] );
    }

    if( error < 0 )
    {
        msg_Warn( p_demux, "Could not find stream info: %s",
                  vlc_strerror_c(AVUNERROR(error)) );
    }
}

static int avformat_ProbeDemux( vlc_object_t *p_this,
                                AVF_MAYBE_CONST AVInputFormat **pp_fmt,
                                const char *psz_url )
{
    demux_t       *p_demux = (demux_t*)p_this;
    AVProbeData   pd = { 0 };
    const uint8_t *peek;

    /* Init Probe data */
    pd.buf_size = vlc_stream_Peek( p_demux->s, &peek, 2048 + 213 );
    if( pd.buf_size <= 0 )
        return VLC_EGENERIC;

    pd.buf = malloc( pd.buf_size + AVPROBE_PADDING_SIZE );
    if( unlikely(pd.buf == NULL) )
        return VLC_ENOMEM;

    memcpy( pd.buf, peek, pd.buf_size );
    memset( pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE );

    if( psz_url != NULL )
        msg_Dbg( p_demux, "trying url: %s", psz_url );

    pd.filename = psz_url;

    vlc_init_avformat(p_this);

    /* Guess format */
    char *psz_format = var_InheritString( p_this, "avformat-format" );
    if( psz_format )
    {
        if( (*pp_fmt = av_find_input_format(psz_format)) )
            msg_Dbg( p_demux, "forcing format: %s", (*pp_fmt)->name );
        free( psz_format );
    }

    if( *pp_fmt == NULL )
        *pp_fmt = av_probe_input_format( &pd, 1 );

    free( pd.buf );

    if( *pp_fmt == NULL )
    {
        msg_Dbg( p_demux, "couldn't guess format" );
        return VLC_EGENERIC;
    }

    if( !p_demux->obj.force )
    {
        static const char ppsz_blacklist[][16] = {
            /* Don't handle MPEG unless forced */
            "mpeg", "vcd", "vob", "mpegts",
            /* libavformat's redirector won't work */
            "redir", "sdp",
            /* Don't handle subtitles format */
            "ass", "srt", "microdvd",
            /* No timestamps at all */
            "hevc", "h264",
            ""
        };

        for( int i = 0; *ppsz_blacklist[i]; i++ )
        {
            if( !strcmp( (*pp_fmt)->name, ppsz_blacklist[i] ) )
                return VLC_EGENERIC;
        }
    }

    /* Don't trigger false alarms on bin files */
    if( !p_demux->obj.force && !strcmp( (*pp_fmt)->name, "psxstr" ) )
    {
        int i_len;

        if( !p_demux->psz_filepath )
            return VLC_EGENERIC;

        i_len = strlen( p_demux->psz_filepath );
        if( i_len < 4 )
            return VLC_EGENERIC;

        if( strcasecmp( &p_demux->psz_filepath[i_len - 4], ".str" ) &&
            strcasecmp( &p_demux->psz_filepath[i_len - 4], ".xai" ) &&
            strcasecmp( &p_demux->psz_filepath[i_len - 3], ".xa" ) )
        {
            return VLC_EGENERIC;
        }
    }

    msg_Dbg( p_demux, "detected format: %s", (*pp_fmt)->name );

    return VLC_SUCCESS;
}

int avformat_OpenDemux( vlc_object_t *p_this )
{
    demux_t       *p_demux = (demux_t*)p_this;
    demux_sys_t   *p_sys;
    AVF_MAYBE_CONST AVInputFormat *fmt = NULL;
    vlc_tick_t    i_start_time = VLC_TICK_INVALID;
    bool          b_can_seek;
    const char    *psz_url;
    int           error;

    if( p_demux->psz_filepath )
        psz_url = p_demux->psz_filepath;
    else
        psz_url = p_demux->psz_url;

    if( avformat_ProbeDemux( p_this, &fmt, psz_url ) != VLC_SUCCESS )
        return VLC_EGENERIC;

    vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek );

    /* Fill p_demux fields */
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
    p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
    if( !p_sys )
        return VLC_ENOMEM;

    p_sys->ic = 0;
    p_sys->fmt = fmt;
    p_sys->tracks = NULL;
    p_sys->i_tracks = 0;
    p_sys->i_ssa_order = 0;
    TAB_INIT( p_sys->i_attachments, p_sys->attachments);
    p_sys->p_title = NULL;
    p_sys->i_seekpoint = 0;
    p_sys->i_update = 0;

    /* Create I/O wrapper */
    unsigned char * p_io_buffer = av_malloc( AVFORMAT_IOBUFFER_SIZE );
    if( !p_io_buffer )
    {
        avformat_CloseDemux( p_this );
        return VLC_ENOMEM;
    }

    p_sys->ic = avformat_alloc_context();
    if( !p_sys->ic )
    {
        av_free( p_io_buffer );
        avformat_CloseDemux( p_this );
        return VLC_ENOMEM;
    }

    AVIOContext *pb = p_sys->ic->pb = avio_alloc_context( p_io_buffer,
        AVFORMAT_IOBUFFER_SIZE, 0, p_demux, IORead, NULL, IOSeek );
    if( !pb )
    {
        av_free( p_io_buffer );
        avformat_CloseDemux( p_this );
        return VLC_ENOMEM;
    }

    /* get all options, open_input will consume its own options from the dict */
    AVDictionary *options = BuildAVOptions( p_demux );

    p_sys->ic->pb->seekable = b_can_seek ? AVIO_SEEKABLE_NORMAL : 0;
    error = avformat_open_input( &p_sys->ic, psz_url, p_sys->fmt, &options );
    if( error < 0 )
    {
        msg_Err( p_demux, "Could not open %s: %s", psz_url,
                 vlc_strerror_c(AVUNERROR(error)) );
        av_dict_free( &options );
        av_free( pb->buffer );
        avio_context_free( &pb );
        p_sys->ic = NULL;
        avformat_CloseDemux( p_this );
        return VLC_EGENERIC;
    }

    /* pass remaining options for as streams options */
    FindStreamInfo( p_demux, options );

    unsigned nb_streams = p_sys->ic->nb_streams; /* it may have changed */
    if( !nb_streams )
    {
        msg_Err( p_demux, "No streams found");
        avformat_CloseDemux( p_this );
        return VLC_EGENERIC;
    }
    p_sys->tracks = calloc( nb_streams, sizeof(*p_sys->tracks) );
    if( !p_sys->tracks )
    {
        avformat_CloseDemux( p_this );
        return VLC_ENOMEM;
    }
    p_sys->i_tracks = nb_streams;

    if( error < 0 )
    {
        msg_Warn( p_demux, "Could not find stream info: %s",
                  vlc_strerror_c(AVUNERROR(error)) );
    }

    for( unsigned i = 0; i < nb_streams; i++ )
    {
        struct avformat_track_s *p_track = &p_sys->tracks[i];
        AVStream *s = p_sys->ic->streams[i];
        const AVCodecParameters *cp = s->codecpar;
        es_format_t es_fmt;
        const char *psz_type = "unknown";

        /* Do not use the cover art as a stream */
        if( s->disposition == AV_DISPOSITION_ATTACHED_PIC )
            continue;

        vlc_fourcc_t fcc = GetVlcFourcc( cp->codec_id );
        switch( cp->codec_type )
        {
        case AVMEDIA_TYPE_AUDIO:
            es_format_Init( &es_fmt, AUDIO_ES, fcc );
            es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag );
            es_fmt.i_bitrate = cp->bit_rate;
#if LIBAVCODEC_VERSION_CHECK(59, 24, 100)
            es_fmt.audio.i_channels = cp->ch_layout.nb_channels;
#else
            es_fmt.audio.i_channels = cp->channels;
#endif
            es_fmt.audio.i_rate = cp->sample_rate;
            es_fmt.audio.i_bitspersample = cp->bits_per_coded_sample;
            es_fmt.audio.i_blockalign = cp->block_align;
            psz_type = "audio";

            if(cp->codec_id == AV_CODEC_ID_AAC_LATM)
            {
                es_fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M');
                es_fmt.b_packetized = false;
            }
            else if(cp->codec_id == AV_CODEC_ID_AAC && p_sys->fmt->long_name &&
                    strstr(p_sys->fmt->long_name, "raw ADTS AAC"))
            {
                es_fmt.i_original_fourcc = VLC_FOURCC('A','D','T','S');
                es_fmt.b_packetized = false;
            }
            break;

        case AVMEDIA_TYPE_VIDEO:
            es_format_Init( &es_fmt, VIDEO_ES, fcc );
            es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag );

            // HACK: keep the bits per sample, will be lost if i_level is used.
            es_fmt.i_level = cp->bits_per_coded_sample;
            /* Special case for raw video data */
            if( cp->codec_id == AV_CODEC_ID_RAWVIDEO )
            {
                msg_Dbg( p_demux, "raw video, pixel format: %i", cp->format );
                if( GetVlcChroma( &es_fmt.video, (enum AVPixelFormat)cp->format ) != VLC_SUCCESS)
                {
                    msg_Err( p_demux, "was unable to find a FourCC match for raw video" );
                }
                else
                    es_fmt.i_codec = es_fmt.video.i_chroma;
            }
            /* We need this for the h264 packetizer */
            else if( cp->codec_id == AV_CODEC_ID_H264 && ( p_sys->fmt == av_find_input_format("flv") ||
                p_sys->fmt == av_find_input_format("matroska") || p_sys->fmt == av_find_input_format("mp4") ) )
                es_fmt.i_original_fourcc = VLC_FOURCC( 'a', 'v', 'c', '1' );

            es_fmt.video.i_width = cp->width;
            es_fmt.video.i_height = cp->height;
            es_fmt.video.i_visible_width = es_fmt.video.i_width;
            es_fmt.video.i_visible_height = es_fmt.video.i_height;

            get_rotation(&es_fmt, s);
            get_dovi_config(&es_fmt, s);
            get_palette(&es_fmt, s);

            psz_type = "video";

            AVRational rate;
            rate = av_guess_frame_rate( p_sys->ic, s, NULL );
            if( rate.den && rate.num )
            {
                es_fmt.video.i_frame_rate = rate.num;
                es_fmt.video.i_frame_rate_base = rate.den;
            }

            AVRational ar;
            ar = av_guess_sample_aspect_ratio( p_sys->ic, s, NULL );
            if( ar.num && ar.den )
            {
                es_fmt.video.i_sar_den = ar.den;
                es_fmt.video.i_sar_num = ar.num;
            }
            break;

        case AVMEDIA_TYPE_SUBTITLE:
            es_format_Init( &es_fmt, SPU_ES, fcc );
            es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag );
            if( strncmp( p_sys->ic->iformat->name, "matroska", 8 ) == 0 &&
                cp->codec_id == AV_CODEC_ID_DVD_SUBTITLE &&
                cp->extradata != NULL &&
                cp->extradata_size > 0 )
            {
                vobsub_extra_parse(p_this, &es_fmt.subs, cp->extradata, cp->extradata_size);
            }
            else if( cp->codec_id == AV_CODEC_ID_DVB_SUBTITLE &&
                     cp->extradata_size > 3 )
            {
                es_fmt.subs.dvb.i_id = GetWBE( cp->extradata ) |
                                      (GetWBE( cp->extradata + 2 ) << 16);
            }
            else if( cp->codec_id == AV_CODEC_ID_MOV_TEXT )
            {
                if( cp->extradata_size && (es_fmt.p_extra = malloc(cp->extradata_size)) )
                {
                    memcpy( es_fmt.p_extra, cp->extradata, cp->extradata_size );
                    es_fmt.i_extra = cp->extradata_size;
                }
            }
            psz_type = "subtitle";
            break;

        default:
            es_format_Init( &es_fmt, UNKNOWN_ES, 0 );
            es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag );
            if( cp->codec_type == AVMEDIA_TYPE_ATTACHMENT )
            {
                input_attachment_t *p_attachment;

                psz_type = "attachment";
                if( cp->codec_id == AV_CODEC_ID_TTF )
                {
                    AVDictionaryEntry *filename = av_dict_get( s->metadata, "filename", NULL, 0 );
                    if( filename && filename->value )
                    {
                        p_attachment = vlc_input_attachment_New(
                                filename->value, "application/x-truetype-font",
                                NULL, cp->extradata, (int)cp->extradata_size );
                        if( p_attachment )
                            TAB_APPEND( p_sys->i_attachments, p_sys->attachments,
                                        p_attachment );
                    }
                }
                else msg_Warn( p_demux, "unsupported attachment type (%u) in avformat demux", cp->codec_id );
            }
            else
            {
                if( cp->codec_type == AVMEDIA_TYPE_DATA )
                    psz_type = "data";

                msg_Warn( p_demux, "unsupported track type (%u:%u) in avformat demux", cp->codec_type, cp->codec_id );
            }
            break;
        }

        AVDictionaryEntry *language = av_dict_get( s->metadata, "language", NULL, 0 );
        if ( language && language->value )
            es_fmt.psz_language = strdup( language->value );

        if( s->disposition & AV_DISPOSITION_DEFAULT )
            es_fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1000;

        if( cp->codec_type != AVMEDIA_TYPE_ATTACHMENT &&
            cp->codec_type != AVMEDIA_TYPE_DATA )
        {
            const bool    b_ogg = !strcmp( p_sys->fmt->name, "ogg" );
            const uint8_t *p_extra = cp->extradata;
            size_t i_extra = cp->extradata_size;

            if( cp->codec_id == AV_CODEC_ID_THEORA && b_ogg )
            {
                size_t pi_size[3];
                const void *pp_data[3];
                size_t i_count;
                for( i_count = 0; i_count < 3; i_count++ )
                {
                    if( i_extra < 2 )
                        break;
                    pi_size[i_count] = GetWBE( p_extra );
                    pp_data[i_count] = &p_extra[2];
                    if( i_extra < pi_size[i_count] + 2 )
                        break;

                    p_extra += 2 + pi_size[i_count];
                    i_extra -= 2 + pi_size[i_count];
                }
                if( i_count > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra,
                                                     pi_size, pp_data, i_count ) )
                {
                    es_fmt.i_extra = 0;
                    es_fmt.p_extra = NULL;
                }
            }
            else if( cp->codec_id == AV_CODEC_ID_SPEEX && b_ogg )
            {
                const uint8_t p_dummy_comment[] = {
                    0, 0, 0, 0,
                    0, 0, 0, 0,
                };
                size_t pi_size[2];
                const void *pp_data[2];

                pi_size[0] = i_extra;
                pp_data[0] = p_extra;

                pi_size[1] = sizeof(p_dummy_comment);
                pp_data[1] = p_dummy_comment;

                if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra,
                                                        pi_size, pp_data, 2 ) )
                {
                    es_fmt.i_extra = 0;
                    es_fmt.p_extra = NULL;
                }
            }
            else if( cp->codec_id == AV_CODEC_ID_OPUS )
            {
                const uint8_t p_dummy_comment[] = {
                    'O', 'p', 'u', 's',
                    'T', 'a', 'g', 's',
                    0, 0, 0, 0, /* Vendor String length */
                                /* Vendor String */
                    0, 0, 0, 0, /* User Comment List Length */

                };
                size_t pi_size[2];
                const void *pp_data[2];

                pi_size[0] = i_extra;
                pp_data[0] = p_extra;

                pi_size[1] = sizeof(p_dummy_comment);
                pp_data[1] = p_dummy_comment;

                if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra,
                                                        pi_size, pp_data, 2 ) )
                {
                    es_fmt.i_extra = 0;
                    es_fmt.p_extra = NULL;
                }
            }
            else if( cp->extradata_size > 0 && !es_fmt.i_extra )
            {
                es_fmt.p_extra = malloc( i_extra );
                if( es_fmt.p_extra )
                {
                    es_fmt.i_extra = i_extra;
                    memcpy( es_fmt.p_extra, p_extra, i_extra );
                }
            }
            else if ( cp->codec_id == AV_CODEC_ID_AV1 )
            {
                // raw AV1, we need a packetizer to detect configuration changes
                es_fmt.b_packetized = false;
            }

            es_fmt.i_id = i;
            p_track->p_es = es_out_Add( p_demux->out, &es_fmt );
            if( p_track->p_es != NULL )
                es_format_Copy( &p_track->es_format, &es_fmt );
            if( p_track->p_es && (s->disposition & AV_DISPOSITION_DEFAULT) )
                es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, p_track->p_es );

            msg_Dbg( p_demux, "adding es: %s codec = %4.4s (0x%x)",
                     psz_type, (char*)&fcc, cp->codec_id  );
        }
        es_format_Clean( &es_fmt );
    }

    if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE )
        i_start_time = FROM_AV_TS_NZ(p_sys->ic->start_time);

    msg_Dbg( p_demux, "AVFormat(%s) supported stream", LIBAVFORMAT_IDENT );
    msg_Dbg( p_demux, "    - format = %s (%s)",
             p_sys->fmt->name, p_sys->fmt->long_name );
    msg_Dbg( p_demux, "    - start time = %"PRId64, i_start_time );
    msg_Dbg( p_demux, "    - duration = %"PRId64,
             ( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE ) ?
             FROM_AV_TS_NZ(p_sys->ic->duration) : -1 );

    if( p_sys->ic->nb_chapters > 0 )
    {
        p_sys->p_title = vlc_input_title_New();
        p_sys->p_title->i_length = FROM_AV_TS_NZ(p_sys->ic->duration);
    }

    for( unsigned i = 0; i < p_sys->ic->nb_chapters; i++ )
    {
        seekpoint_t *s = vlc_seekpoint_New();

        AVDictionaryEntry *title = av_dict_get( p_sys->ic->metadata, "title", NULL, 0);
        if( title && title->value )
        {
            s->psz_name = strdup( title->value );
            EnsureUTF8( s->psz_name );
            msg_Dbg( p_demux, "    - chapter %d: %s", i, s->psz_name );
        }
        s->i_time_offset = FROM_AVSCALE( p_sys->ic->chapters[i]->start,
                                         p_sys->ic->chapters[i]->time_base )
                 - (i_start_time != VLC_TICK_INVALID ? i_start_time : 0 ) + VLC_TICK_0;
        TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s );
    }

    ResetTime( p_demux, 0 );
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close
 *****************************************************************************/

void avformat_CloseDemux( vlc_object_t *p_this )
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;

    for( unsigned i = 0; i < p_sys->i_tracks; i++ )
        es_format_Clean( &p_sys->tracks[i].es_format );
    free( p_sys->tracks );

    if( p_sys->ic )
    {
        if( p_sys->ic->pb )
        {
            av_free( p_sys->ic->pb->buffer );
            avio_context_free( &p_sys->ic->pb );
        }
        avformat_close_input( &p_sys->ic );
    }

    for( int i = 0; i < p_sys->i_attachments; i++ )
        vlc_input_attachment_Release( p_sys->attachments[i] );
    TAB_CLEAN( p_sys->i_attachments, p_sys->attachments);

    if( p_sys->p_title )
        vlc_input_title_Delete( p_sys->p_title );

    free( p_sys );
}

/*****************************************************************************
 * Demux:
 *****************************************************************************/

static int Demux( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    AVPacket    pkt;
    block_t     *p_frame;
    vlc_tick_t  i_start_time;

    /* Read a frame */
    int i_av_ret = av_read_frame( p_sys->ic, &pkt );
    if( i_av_ret )
    {
        /* Avoid EOF if av_read_frame returns AVERROR(EAGAIN) */
        if( i_av_ret == AVERROR(EAGAIN) )
            return VLC_DEMUXER_SUCCESS;

        return VLC_DEMUXER_EOF;
    }
    if( pkt.stream_index < 0 || (unsigned) pkt.stream_index >= p_sys->i_tracks )
    {
        av_packet_unref( &pkt );
        return VLC_DEMUXER_SUCCESS;
    }
    struct avformat_track_s *p_track = &p_sys->tracks[pkt.stream_index];
    const AVStream *p_stream = p_sys->ic->streams[pkt.stream_index];
    if( p_stream->time_base.den <= 0 )
    {
        msg_Warn( p_demux, "Invalid time base for the stream %d", pkt.stream_index );
        av_packet_unref( &pkt );
        return VLC_DEMUXER_SUCCESS;
    }

    // handle extra data change, this can happen for FLV
#if LIBAVUTIL_VERSION_MAJOR < 57
# define EXTRA_SIZE_TYPE int
#else
# define EXTRA_SIZE_TYPE size_t
#endif
    EXTRA_SIZE_TYPE side_data_size_query;
    uint8_t *side_data = av_packet_get_side_data( &pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size_query );
    if( side_data_size_query > 0 ) {
        // ignore new extradata which is the same as previous version
        size_t side_data_size = (size_t)side_data_size_query;
        if (side_data_size != p_track->es_format.i_extra ||
            memcmp( side_data, p_track->es_format.p_extra, side_data_size ) != 0 )
        {
            msg_Warn( p_demux, "New extra data found, seek may not work as expected" );
            void *p_extra = realloc( p_track->es_format.p_extra, side_data_size );
            bool success = p_extra != NULL;
            if( likely(success) )
            {
                p_track->es_format.p_extra = p_extra;
                memcpy( p_track->es_format.p_extra, side_data, side_data_size );
                p_track->es_format.i_extra = side_data_size;
                success = es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT,
                        p_track->p_es, &p_track->es_format ) == VLC_SUCCESS;
            }

            if( !success )
            {
                FREENULL( p_track->es_format.p_extra );
                p_track->es_format.i_extra = 0;
                av_packet_unref( &pkt );
                return VLC_DEMUXER_EOF;
            }
        }
    }

    if( p_stream->codecpar->codec_id == AV_CODEC_ID_SSA )
    {
        p_frame = BuildSsaFrame( &pkt, p_sys->i_ssa_order++ );
        if( !p_frame )
        {
            av_packet_unref( &pkt );
            return VLC_DEMUXER_SUCCESS;
        }
    }
    else if( p_stream->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE )
    {
        if( ( p_frame = block_Alloc( pkt.size + 3 ) ) == NULL )
        {
            av_packet_unref( &pkt );
            return VLC_DEMUXER_EOF;
        }
        p_frame->p_buffer[0] = 0x20;
        p_frame->p_buffer[1] = 0x00;
        memcpy( &p_frame->p_buffer[2], pkt.data, pkt.size );
        p_frame->p_buffer[p_frame->i_buffer - 1] = 0x3f;
    }
    else
    {
        if( ( p_frame = block_Alloc( pkt.size ) ) == NULL )
        {
            av_packet_unref( &pkt );
            return VLC_DEMUXER_EOF;
        }
        memcpy( p_frame->p_buffer, pkt.data, pkt.size );
    }

    if( pkt.flags & AV_PKT_FLAG_KEY )
        p_frame->i_flags |= BLOCK_FLAG_TYPE_I;

    if( p_stream->codecpar->codec_id == AV_CODEC_ID_AV1 )
    {
        /* Prepare the packet like the mkv/mp4 demuxers do. */
        p_frame = AV1_Unpack_Sample( p_frame );
        if( unlikely( p_frame == NULL ) )
        {
            av_packet_unref( &pkt );
            return VLC_DEMUXER_EOF;
        }
    }

    /* Used to avoid timestamps overflow */
    if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE )
    {
        i_start_time = FROM_AV_TS_NZ(p_sys->ic->start_time);
    }
    else
        i_start_time = 0;

    if( pkt.dts == (int64_t)AV_NOPTS_VALUE )
        p_frame->i_dts = VLC_TICK_INVALID;
    else
    {
        p_frame->i_dts = FROM_AVSCALE( pkt.dts, p_stream->time_base )
                - i_start_time + VLC_TICK_0;
    }

    if( pkt.pts == (int64_t)AV_NOPTS_VALUE )
        p_frame->i_pts = VLC_TICK_INVALID;
    else
    {
        p_frame->i_pts = FROM_AVSCALE( pkt.pts, p_stream->time_base )
                - i_start_time + VLC_TICK_0;
    }
    if( pkt.duration > 0 && p_frame->i_length <= 0 )
        p_frame->i_length = FROM_AVSCALE( pkt.duration, p_stream->time_base );

    /* Add here notoriously bugged file formats/samples */
    if( !strcmp( p_sys->fmt->name, "flv" ) )
    {
        /* FLV and video PTS */
        if( p_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
            pkt.dts != (int64_t)AV_NOPTS_VALUE && pkt.dts == pkt.pts )
                p_frame->i_pts = VLC_TICK_INVALID;

        /* Handle broken dts/pts increase with AAC. Duration is correct.
         * sky_the80s_aacplus.flv #8195 */

        if( p_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
            p_stream->codecpar->codec_id == AV_CODEC_ID_AAC )
        {
            if( p_track->i_pcr != VLC_TICK_INVALID &&
                p_track->i_pcr + p_frame->i_length > p_frame->i_dts )
            {
                p_frame->i_dts = p_frame->i_pts = p_track->i_pcr + p_frame->i_length;
            }
        }
    }
#ifdef AVFORMAT_DEBUG
    msg_Dbg( p_demux, "tk[%d] dts=%"PRId64" pts=%"PRId64,
             pkt.stream_index, p_frame->i_dts, p_frame->i_pts );
#endif
    if( p_frame->i_dts != VLC_TICK_INVALID && p_track->p_es != NULL )
        p_track->i_pcr = p_frame->i_dts;

    vlc_tick_t i_ts_max = VLC_TICK_MIN;
    for( unsigned i = 0; i < p_sys->i_tracks; i++ )
    {
        if( p_sys->tracks[i].p_es != NULL )
            i_ts_max = __MAX( i_ts_max, p_sys->tracks[i].i_pcr );
    }

    vlc_tick_t i_ts_min = VLC_TICK_MAX;
    for( unsigned i = 0; i < p_sys->i_tracks; i++ )
    {
        if( p_sys->tracks[i].p_es != NULL &&
                p_sys->tracks[i].i_pcr != VLC_TICK_INVALID &&
                p_sys->tracks[i].i_pcr + VLC_TICK_FROM_SEC(10)>= i_ts_max )
            i_ts_min = __MIN( i_ts_min, p_sys->tracks[i].i_pcr );
    }
    if( i_ts_min >= p_sys->i_pcr && likely(i_ts_min != VLC_TICK_MAX) )
    {
        p_sys->i_pcr = i_ts_min;
        es_out_SetPCR( p_demux->out, p_sys->i_pcr );
        UpdateSeekPoint( p_demux, p_sys->i_pcr );
    }

    if( p_track->p_es != NULL )
        es_out_Send( p_demux->out, p_track->p_es, p_frame );
    else
        block_Release( p_frame );

    av_packet_unref( &pkt );
    return VLC_DEMUXER_SUCCESS;
}

static void UpdateSeekPoint( demux_t *p_demux, vlc_tick_t i_time )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int i;

    if( !p_sys->p_title )
        return;

    for( i = 0; i < p_sys->p_title->i_seekpoint; i++ )
    {
        if( i_time < p_sys->p_title->seekpoint[i]->i_time_offset )
            break;
    }
    i--;

    if( i != p_sys->i_seekpoint && i >= 0 )
    {
        p_sys->i_seekpoint = i;
        p_sys->i_update |= INPUT_UPDATE_SEEKPOINT;
    }
}

static void ResetTime( demux_t *p_demux, int64_t i_time )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    vlc_tick_t t;

    if( p_sys->ic->start_time == (int64_t)AV_NOPTS_VALUE || i_time < 0 )
        t = VLC_TICK_INVALID;
    else
        t = FROM_AV_TS( i_time );

    p_sys->i_pcr = t;
    for( unsigned i = 0; i < p_sys->i_tracks; i++ )
        p_sys->tracks[i].i_pcr = VLC_TICK_INVALID;

    if( t != VLC_TICK_INVALID )
    {
        es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, t );
        UpdateSeekPoint( p_demux, t );
    }
}

static block_t *BuildSsaFrame( const AVPacket *p_pkt, unsigned i_order )
{
    if( p_pkt->size <= 0 )
        return NULL;

    char buffer[256];
    const size_t i_buffer_size = __MIN( (int)sizeof(buffer) - 1, p_pkt->size );
    memcpy( buffer, p_pkt->data, i_buffer_size );
    buffer[i_buffer_size] = '\0';

    /* */
    int i_layer;
    int h0, m0, s0, c0;
    int h1, m1, s1, c1;
    int i_position = 0;
    if( sscanf( buffer, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,%n", &i_layer,
                &h0, &m0, &s0, &c0, &h1, &m1, &s1, &c1, &i_position ) < 9 )
        return NULL;
    if( i_position <= 0 || (unsigned)i_position >= i_buffer_size )
        return NULL;

    char *p;
    if( asprintf( &p, "%u,%d,%.*s", i_order, i_layer, p_pkt->size - i_position, p_pkt->data + i_position ) < 0 )
        return NULL;

    block_t *p_frame = block_heap_Alloc( p, strlen(p) + 1 );
    if( p_frame )
        p_frame->i_length = vlc_tick_from_sec((h1-h0) * 3600 +
                                         (m1-m0) * 60 +
                                         (s1-s0) * 1) +
                             VLC_TICK_FROM_MS (c1-c0) / 100;
    return p_frame;
}

/*****************************************************************************
 * Control:
 *****************************************************************************/

static int Control( demux_t *p_demux, int i_query, va_list args )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    const int64_t i_start_time = p_sys->ic->start_time != AV_NOPTS_VALUE ? p_sys->ic->start_time : 0;
    double f, *pf;
    int64_t i64;
    uint64_t u64;

    switch( i_query )
    {
        case DEMUX_CAN_SEEK:
            *va_arg( args, bool * ) = true;
            return VLC_SUCCESS;

        case DEMUX_GET_POSITION:
            pf = va_arg( args, double * );
            if( vlc_stream_GetSize( p_demux->s, &u64 ) != VLC_SUCCESS )
            {
                *pf = 0.0;
            }
            else
            {
                double current = vlc_stream_Tell( p_demux->s );
                *pf = current / (double)u64;
            }

            if( (p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE) && (p_sys->i_pcr > 0) )
            {
                *pf = (double)p_sys->i_pcr / (double)p_sys->ic->duration;
            }

            return VLC_SUCCESS;

        case DEMUX_SET_POSITION:
        {
            f = va_arg( args, double );
            bool precise = va_arg( args, int );
            i64 = p_sys->ic->duration * f + i_start_time;

            msg_Warn( p_demux, "DEMUX_SET_POSITION: %"PRId64, i64 );

            /* If we have a duration, we prefer to seek by time
               but if we don't, or if the seek fails, try BYTE seeking */

            if( p_sys->ic->duration == (int64_t)AV_NOPTS_VALUE ||
                (av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BACKWARD ) < 0) )
            {
                uint64_t i_size;
                if( vlc_stream_GetSize( p_demux->s, &i_size ) != VLC_SUCCESS ||
                    i_size > INT64_MAX )
                {
                    msg_Err( p_demux, "Can not use stream size for SET_POSITION" );
                    return VLC_EGENERIC;
                }

                i64 = (i_size * f);

                msg_Warn( p_demux, "DEMUX_SET_BYTE_POSITION: %"PRId64, i64 );
                if( av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BYTE ) < 0 )
                    return VLC_EGENERIC;

                ResetTime( p_demux, -1 );
            }
            else
            {
                if( precise )
                    ResetTime( p_demux, i64 - i_start_time );
                else
                    ResetTime( p_demux, -1 );
            }
            return VLC_SUCCESS;
        }

        case DEMUX_GET_LENGTH:
            if( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE )
                *va_arg( args, vlc_tick_t * ) = FROM_AV_TS_NZ(p_sys->ic->duration);
            else
                *va_arg( args, vlc_tick_t * ) = 0;
            return VLC_SUCCESS;

        case DEMUX_GET_TIME:
            *va_arg( args, vlc_tick_t * ) = p_sys->i_pcr;
            return VLC_SUCCESS;

        case DEMUX_SET_TIME:
        {
            i64 = TO_AV_TS(va_arg( args, vlc_tick_t ));
            bool precise = va_arg( args, int );

            msg_Warn( p_demux, "DEMUX_SET_TIME: %"PRId64, i64 );

            if( av_seek_frame( p_sys->ic, -1, i64 + i_start_time, AVSEEK_FLAG_BACKWARD ) < 0 )
            {
                return VLC_EGENERIC;
            }
            if( precise )
                ResetTime( p_demux, i64 - i_start_time );
            else
                ResetTime( p_demux, -1 );
            return VLC_SUCCESS;
        }

        case DEMUX_HAS_UNSUPPORTED_META:
        {
            bool *pb_bool = va_arg( args, bool* );
            *pb_bool = true;
            return VLC_SUCCESS;
        }


        case DEMUX_GET_META:
        {
            static const char names[][11] = {
                [vlc_meta_Title] = "title",
                [vlc_meta_Artist] = "artist",
                [vlc_meta_Genre] = "genre",
                [vlc_meta_Copyright] = "copyright",
                [vlc_meta_Album] = "album",
                //[vlc_meta_TrackNumber] -- TODO: parse number/total value
                [vlc_meta_Description] = "comment",
                //[vlc_meta_Rating]
                [vlc_meta_Date] = "date",
                [vlc_meta_Setting] = "encoder",
                //[vlc_meta_URL]
                [vlc_meta_Language] = "language",
                //[vlc_meta_NowPlaying]
                [vlc_meta_Publisher] = "publisher",
                [vlc_meta_EncodedBy] = "encoded_by",
                //[vlc_meta_ArtworkURL]
                //[vlc_meta_TrackID]
                //[vlc_meta_TrackTotal]
            };
            vlc_meta_t *p_meta = va_arg( args, vlc_meta_t * );
            AVDictionary *dict = p_sys->ic->metadata;

            for( unsigned i = 0; i < ARRAY_SIZE(names); i++)
            {
                AVDictionaryEntry *e = av_dict_get( dict, names[i], NULL, 0 );
                if( e != NULL && e->value != NULL && IsUTF8(e->value) )
                    vlc_meta_Set( p_meta, i, e->value );
            }
            return VLC_SUCCESS;
        }

        case DEMUX_GET_ATTACHMENTS:
        {
            input_attachment_t ***ppp_attach =
                va_arg( args, input_attachment_t*** );
            int *pi_int = va_arg( args, int * );
            int i;

            if( p_sys->i_attachments <= 0 )
                return VLC_EGENERIC;

            *ppp_attach = vlc_alloc( p_sys->i_attachments, sizeof(input_attachment_t*) );
            if( *ppp_attach == NULL )
                return VLC_EGENERIC;

            for( i = 0; i < p_sys->i_attachments; i++ )
            {
                (*ppp_attach)[i] = vlc_input_attachment_Hold( p_sys->attachments[i] );
            }
            *pi_int = i;
            return VLC_SUCCESS;
        }

        case DEMUX_GET_TITLE_INFO:
        {
            input_title_t ***ppp_title = va_arg( args, input_title_t *** );
            int *pi_int = va_arg( args, int * );
            int *pi_title_offset = va_arg( args, int * );
            int *pi_seekpoint_offset = va_arg( args, int * );

            if( !p_sys->p_title )
                return VLC_EGENERIC;

            *ppp_title = malloc( sizeof( input_title_t*) );
            if( *ppp_title == NULL )
                return VLC_EGENERIC;
            (*ppp_title)[0] = vlc_input_title_Duplicate( p_sys->p_title );
            *pi_int = (*ppp_title)[0] ? 1 : 0;
            *pi_title_offset = 0;
            *pi_seekpoint_offset = 0;
            return VLC_SUCCESS;
        }
        case DEMUX_SET_TITLE:
        {
            const int i_title = va_arg( args, int );
            if( !p_sys->p_title || i_title != 0 )
                return VLC_EGENERIC;
            return VLC_SUCCESS;
        }
        case DEMUX_SET_SEEKPOINT:
        {
            const int i_seekpoint = va_arg( args, int );
            if( !p_sys->p_title )
                return VLC_EGENERIC;

            i64 = TO_AV_TS(p_sys->p_title->seekpoint[i_seekpoint]->i_time_offset) +
                  i_start_time;

            msg_Warn( p_demux, "DEMUX_SET_SEEKPOINT: %"PRId64, i64 );

            if( av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BACKWARD ) < 0 )
            {
                return VLC_EGENERIC;
            }
            ResetTime( p_demux, i64 - i_start_time );
            return VLC_SUCCESS;
        }
        case DEMUX_TEST_AND_CLEAR_FLAGS:
        {
            unsigned *restrict flags = va_arg(args, unsigned *);
            *flags &= p_sys->i_update;
            p_sys->i_update &= ~*flags;
            return VLC_SUCCESS;
        }
        case DEMUX_GET_TITLE:
            if( p_sys->p_title == NULL )
                return VLC_EGENERIC;
            *va_arg( args, int * ) = 0;
            return VLC_SUCCESS;
        case DEMUX_GET_SEEKPOINT:
            if( p_sys->p_title == NULL )
                return VLC_EGENERIC;
            *va_arg( args, int * ) = p_sys->i_seekpoint;
            return VLC_SUCCESS;
        case DEMUX_CAN_PAUSE:
        case DEMUX_SET_PAUSE_STATE:
        case DEMUX_CAN_CONTROL_PACE:
        case DEMUX_GET_PTS_DELAY:
            return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
        default:
            return VLC_EGENERIC;
    }
}

/*****************************************************************************
 * I/O wrappers for libavformat
 *****************************************************************************/

static int IORead( void *opaque, uint8_t *buf, int buf_size )
{
    demux_t *p_demux = opaque;
    if( buf_size < 0 ) return AVERROR_EOF;
    int i_ret = vlc_stream_Read( p_demux->s, buf, buf_size );
    return i_ret > 0 ? i_ret : AVERROR_EOF;
}

static int64_t IOSeek( void *opaque, int64_t offset, int whence )
{
    demux_t *p_demux = opaque;
    int64_t i_absolute;
    int64_t i_size = stream_Size( p_demux->s );

#ifdef AVFORMAT_DEBUG
    msg_Warn( p_demux, "IOSeek offset: %"PRId64", whence: %i", offset, whence );
#endif

    switch( whence )
    {
#ifdef AVSEEK_SIZE
        case AVSEEK_SIZE:
            return i_size;
#endif
        case SEEK_SET:
            i_absolute = (int64_t)offset;
            break;
        case SEEK_CUR:
            i_absolute = vlc_stream_Tell( p_demux->s ) + (int64_t)offset;
            break;
        case SEEK_END:
            i_absolute = i_size + (int64_t)offset;
            break;
        default:
            return -1;

    }

    if( i_absolute < 0 )
    {
        msg_Dbg( p_demux, "Trying to seek before the beginning" );
        return -1;
    }

    if( i_size > 0 && i_absolute >= i_size )
    {
        msg_Dbg( p_demux, "Trying to seek too far : EOF?" );
        return -1;
    }

    if( vlc_stream_Seek( p_demux->s, i_absolute ) )
    {
        msg_Warn( p_demux, "we were not allowed to seek, or EOF " );
        return -1;
    }

    return vlc_stream_Tell( p_demux->s );
}

Messung
CHZG
83978588

¤ Dauer der Verarbeitung: 0.18 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.