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

Quelle  matroska_segment_seeker.cpp   Sprache: C

 

/*****************************************************************************
 * matroska_segment.hpp : matroska demuxer
 *****************************************************************************
 * Copyright (C) 2016 VLC authors and VideoLAN
 *
 * Authors: Filip Roséen <filip@videolabs.io>
 *
 * 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.
 *****************************************************************************/


#include "matroska_segment_seeker.hpp"
#include "matroska_segment.hpp"
#include "demux.hpp"
#include "Ebml_parser.hpp"
#include "Ebml_dispatcher.hpp"
#include "util.hpp"
#include "stream_io_callback.hpp"

#include <sstream>
#include <limits>

namespace {
    template<class It, class T>
    It greatest_lower_bound( It beg, It end, T const& value )
    {
        It it = std::upper_bound( beg, end, value );
        if( it != beg ) --it;
        return it;
    }

    // std::prev and std::next exists in C++11, in order to avoid ambiguity due
    // to ADL and iterators being defined within namespace std, these two
    // function-names have been postfixed with an underscore.

    template<class It> It prev_( It it ) { return --it; }
    template<class It> It next_( It it ) { return ++it; }
}

namespace mkv {

SegmentSeeker::cluster_positions_t::iterator
SegmentSeeker::add_cluster_position( fptr_t fpos )
{
    cluster_positions_t::iterator insertion_point = std::upper_bound(
      _cluster_positions.begin(),
      _cluster_positions.end(),
      fpos
    );

    return _cluster_positions.insert( insertion_point, fpos );
}

SegmentSeeker::cluster_map_t::iterator
SegmentSeeker::add_cluster( KaxCluster * const p_cluster )
{
    Cluster cinfo = {
        /* fpos     */ p_cluster->GetElementPosition(),
        /* pts      */ vlc_tick_t( VLC_TICK_FROM_NS( p_cluster->GlobalTimestamp() ) ),
        /* duration */ vlc_tick_t( -1 ),
        /* size     */ p_cluster->IsFiniteSize()
            ? p_cluster->GetEndPosition() - p_cluster->GetElementPosition()
            : UINT64_MAX
    };

    add_cluster_position( cinfo.fpos );

    cluster_map_t::iterator it = _clusters.lower_bound( cinfo.pts );

    if( it != _clusters.end() && it->second.pts == cinfo.pts )
    {
        // cluster already known
    }
    else
    {
        it = _clusters.insert( cluster_map_t::value_type( cinfo.pts, cinfo ) ).first;
    }

    // ------------------------------------------------------------------
    // IF we have two adjecent clusters, update duration where applicable
    // ------------------------------------------------------------------

    struct Duration {
        static void fix( Cluster& prev, Cluster& next )
        {
            if( ( prev.fpos + prev.size) == next.fpos )
                prev.duration = next.pts - prev.pts;
        }
    };

    if( it != _clusters.begin() )
    {
        Duration::fix( prev_( it )->second, it->second );
    }

    if( it != _clusters.end() && next_( it ) != _clusters.end() )
    {
        Duration::fix( it->second, next_( it )->second );
    }

    return it;
}

void
SegmentSeeker::add_seekpoint( track_id_t track_id, Seekpoint sp )
{
    seekpoints_t&  seekpoints = _tracks_seekpoints[ track_id ];
    seekpoints_t::iterator it = std::lower_bound( seekpoints.begin(), seekpoints.end(), sp );

    if( it != seekpoints.end() && it->fpos == sp.fpos )
    {
        if (sp.trust_level <= it->trust_level)
            return;

        *it = sp;
    }
    else if( it != seekpoints.end() && it->pts == sp.pts )
    {
        if (sp.trust_level <= it->trust_level)
            return;

        *it = sp;
    }
    else
    {
        seekpoints.insert( it, sp );
    }
}

SegmentSeeker::tracks_seekpoint_t
SegmentSeeker::find_greatest_seekpoints_in_range( fptr_t start_fpos, vlc_tick_t end_pts, track_ids_t const& filter_tracks )
{
    tracks_seekpoint_t tpoints;

    for( tracks_seekpoints_t::const_iterator it = _tracks_seekpoints.begin(); it != _tracks_seekpoints.end(); ++it )
    {
        if ( std::find( filter_tracks.begin(), filter_tracks.end(), it->first ) == filter_tracks.end() )
            continue;

        Seekpoint sp = get_first_seekpoint_around( end_pts, it->second );

        if( sp.fpos < start_fpos )
            continue;

        if( sp.pts > end_pts )
            continue;

        tpoints.insert( tracks_seekpoint_t::value_type( it->first, sp ) );
    }

    if (tpoints.empty())
    {
        // try a further pts
        for( tracks_seekpoints_t::const_iterator it = _tracks_seekpoints.begin(); it != _tracks_seekpoints.end(); ++it )
        {
            if ( std::find( filter_tracks.begin(), filter_tracks.end(), it->first ) == filter_tracks.end() )
                continue;

            Seekpoint sp = get_first_seekpoint_around( end_pts, it->second );

            if( sp.fpos < start_fpos )
                continue;

            tpoints.insert( tracks_seekpoint_t::value_type( it->first, sp ) );
        }
    }

    return tpoints;
}

SegmentSeeker::Seekpoint
SegmentSeeker::get_first_seekpoint_around( vlc_tick_t pts, seekpoints_t const& seekpoints,
                                           Seekpoint::TrustLevel trust_level )
{
    if( seekpoints.empty() )
    {
        return Seekpoint();
    }

    typedef seekpoints_t::const_iterator iterator;

    Seekpoint const needle ( std::numeric_limits<fptr_t>::max(), pts );

    iterator const it_begin  = seekpoints.begin();
    iterator const it_end    = seekpoints.end();
    iterator const it_middle = greatest_lower_bound( it_begin, it_end, needle );

    iterator it_before;

    // rewrind to _previous_ seekpoint with appropriate trust
    for( it_before = it_middle; it_before != it_begin; --it_before )
    {
        if( it_before->trust_level >= trust_level )
            return *it_before;
    }
    return *it_begin;
}

SegmentSeeker::seekpoint_pair_t
SegmentSeeker::get_seekpoints_around( vlc_tick_t pts, seekpoints_t const& seekpoints )
{
    if( seekpoints.empty() )
    {
        return seekpoint_pair_t();
    }

    typedef seekpoints_t::const_iterator iterator;

    Seekpoint const needle ( std::numeric_limits<fptr_t>::max(), pts );

    iterator const it_begin  = seekpoints.begin();
    iterator const it_end    = seekpoints.end();
    iterator const it_middle = greatest_lower_bound( it_begin, it_end, needle );

    if ( it_middle != it_end && (*it_middle).pts > pts)
        // found nothing low enough, use the first one
        return seekpoint_pair_t( *it_begin, Seekpoint() );

    iterator it_before = it_middle;
    iterator it_after = it_middle == it_end ? it_middle : next_( it_middle ) ;

    return seekpoint_pair_t( *it_before,
      it_after == it_end ? Seekpoint() : *it_after
    );
}

SegmentSeeker::seekpoint_pair_t
SegmentSeeker::get_seekpoints_around( vlc_tick_t target_pts, track_ids_t const& priority_tracks )
{
    seekpoint_pair_t points;

    if( _tracks_seekpoints.empty() )
        return points;

    { // locate the max/min seekpoints for priority_tracks //

        typedef track_ids_t::const_iterator track_iterator;

        track_iterator const begin = priority_tracks.begin();
        track_iterator const end   = priority_tracks.end();

        for( track_iterator it = begin; it != end; ++it )
        {
            seekpoint_pair_t track_points = get_seekpoints_around( target_pts, _tracks_seekpoints[ *it ] );

            if( it == begin ) {
                points = track_points;
                continue;
            }

            if( track_points.first.trust_level > Seekpoint::DISABLED &&
                points.first.fpos > track_points.first.fpos )
                points.first = track_points.first;

            if( track_points.second.trust_level > Seekpoint::DISABLED &&
                points.second.fpos < track_points.second.fpos )
                points.second = track_points.second;
        }
    }

    { // check if we got a cluster which is closer to target_pts than the found cues //

        cluster_map_t::iterator it = _clusters.lower_bound( target_pts );

        if( it != _clusters.begin() && --it != _clusters.end() )
        {
            Cluster const& cluster = it->second;

            if( cluster.fpos > points.first.fpos )
            {
                points.first = Seekpoint( cluster.fpos, cluster.pts );

                // do we need to update the max point? //

                if( points.second.fpos < points.first.fpos )
                    points.second = Seekpoint( cluster.fpos + cluster.size, cluster.pts + cluster.duration );
            }
        }
    }

    return points;
}

SegmentSeeker::tracks_seekpoint_t
SegmentSeeker::get_seekpoints( matroska_segment_c& ms, vlc_tick_t target_pts,
                               track_ids_t const& priority_tracks, track_ids_t const& filter_tracks )
{
    struct contains_all_of_t {
        bool operator()( tracks_seekpoint_t const& haystack, track_ids_t const& track_ids )
        {
            for( track_ids_t::const_iterator it = track_ids.begin(); it != track_ids.end(); ++it ) {
                if( haystack.find( *it ) == haystack.end() )
                    return false;
            }

            return true;
        }
    };

    for( vlc_tick_t needle_pts = target_pts; ; )
    {
        seekpoint_pair_t seekpoints = get_seekpoints_around( needle_pts, priority_tracks );

        Seekpoint const& start = seekpoints.first;
        Seekpoint const& end   = seekpoints.second;

        if ( start.fpos == std::numeric_limits<fptr_t>::max() )
            return tracks_seekpoint_t();

        if ( (end.fpos != std::numeric_limits<fptr_t>::max() || !ms.b_cues) &&
             (needle_pts != start.pts || start.trust_level < Seekpoint::TRUSTED))
            // do not read the whole (infinite?) file to get seek indexes
            // do not generate an index if we already have the correct seekpoint
            index_range( ms, Range( start.fpos, end.fpos ), needle_pts );

        tracks_seekpoint_t tpoints = find_greatest_seekpoints_in_range( start.fpos, target_pts, filter_tracks );

        if( contains_all_of_t() ( tpoints, priority_tracks ) )
            return tpoints;

        // Avoid busyloop, don't iterate on the same seekpoint
        if( needle_pts == start.pts - 1 )
            // we found the same needle twice, stop looking
            return tpoints;

        needle_pts = start.pts - 1;
    }

    vlc_assert_unreachable();
}

void
SegmentSeeker::index_range( matroska_segment_c& ms, Range search_area, vlc_tick_t max_pts )
{
    ranges_t areas_to_search = get_search_areas( search_area.start, search_area.end );

    for( ranges_t::const_iterator range_it = areas_to_search.begin(); range_it != areas_to_search.end(); ++range_it )
        index_unsearched_range( ms, *range_it, max_pts );
}

void
SegmentSeeker::index_unsearched_range( matroska_segment_c& ms, Range search_area, vlc_tick_t max_pts )
{
    mkv_jump_to( ms, search_area.start );

    search_area.start = ms.es.I_O().getFilePointer();

    fptr_t  block_pos = search_area.start;
    vlc_tick_t block_pts;

    while( block_pos < search_area.end )
    {
        KaxBlock * block;
        KaxSimpleBlock * simpleblock;
        KaxBlockAdditions *additions;

        bool     b_key_picture;
        bool     b_discardable_picture;
        uint64_t  i_block_duration;
        track_id_t track_id;

        if( ms.BlockGet( block, simpleblock, additions,
                         &b_key_picture, &b_discardable_picture, &i_block_duration ) )
        {
            delete additions;
            break;
        }
        delete additions;

        KaxInternalBlock& internal_block = simpleblock
            ? static_cast<KaxInternalBlock&>( *simpleblock )
            : static_cast<KaxInternalBlock&>( *block );

        block_pos = internal_block.GetElementPosition();
        block_pts = VLC_TICK_FROM_NS(internal_block.GlobalTimestamp());
        track_id  = internal_block.TrackNum();

        bool const b_valid_track = ms.FindTrackByBlock( block, simpleblock ) != NULL;

        delete block;

        if( b_valid_track )
        {
            if( b_key_picture )
                add_seekpoint( track_id, Seekpoint( block_pos, block_pts ) );

            if( max_pts < block_pts )
                break;
        }
    }

    search_area.end = ms.es.I_O().getFilePointer();

    mark_range_as_searched( search_area );
}

void
SegmentSeeker::mark_range_as_searched( Range data )
{
    /* TODO: this is utterly ugly, we should do the insertion in-place */

    _ranges_searched.insert( std::upper_bound( _ranges_searched.begin(), _ranges_searched.end(), data ), data );

    {
        ranges_t merged;

        for( ranges_t::iterator it = _ranges_searched.begin(); it != _ranges_searched.end(); ++it )
        {
            if( merged.size() )
            {
                Range& last_entry = *merged.rbegin();

                if( last_entry.end+1 >= it->start && last_entry.end < it->end )
                {
                    last_entry.end = it->end;
                    continue;
                }

                if( it->start >= last_entry.start && it->end <= last_entry.end )
                {
                    last_entry.end = std::max( last_entry.end, it->end );
                    continue;
                }
            }

            merged.push_back( *it );
        }

        _ranges_searched = std::move(merged);
    }
}


SegmentSeeker::ranges_t
SegmentSeeker::get_search_areas( fptr_t start, fptr_t end ) const
{
    ranges_t areas_to_search;
    Range needle ( start, end );

    ranges_t::const_iterator it = greatest_lower_bound( _ranges_searched.begin(), _ranges_searched.end(), needle );

    for( ; it != _ranges_searched.end() && needle.start < needle.end; ++it )
    {
        if( needle.start < it->start )
        {
            areas_to_search.push_back( Range( needle.start, it->start ) );
        }

        if( needle.start <= it->end )
            needle.start = it->end + 1;
    }

    needle.start = std::max( needle.start, start );
    if( it == _ranges_searched.end() && needle.start < needle.end )
    {
        areas_to_search.push_back( needle );
    }

    return areas_to_search;
}

void
SegmentSeeker::mkv_jump_to( matroska_segment_c& ms, fptr_t fpos )
{
    fptr_t i_cluster_pos = -1;

    if ( fpos != std::numeric_limits<SegmentSeeker::fptr_t>::max() )
    {
        if ( !_cluster_positions.empty() )
        {
            cluster_positions_t::iterator cluster_it = greatest_lower_bound(
              _cluster_positions.begin(), _cluster_positions.end(), fpos
            );

            ms.es.I_O().setFilePointer( *cluster_it );
            ms.ep.reconstruct( &ms.es, ms.segment, &ms.sys.demuxer );

            for(;;)
            {
                ms.cluster = NULL;
                EbmlElement *el = ms.ep.Get();
                if( el == nullptr )
                {
                    msg_Err( &ms.sys.demuxer, "unable to read KaxCluster during seek, giving up" );
                    return;
                }
                if (!MKV_IS_ID( el, KaxCluster ))
                {
                    ms.cluster = nullptr; // the previous cluster found is not valid anymore
                    continue; // look for the next element
                }

                ms.cluster = static_cast<KaxCluster*>( el );

                i_cluster_pos = ms.cluster->GetElementPosition();

                add_cluster_position( i_cluster_pos );

                mark_range_as_searched( Range( i_cluster_pos, ms.es.I_O().getFilePointer() ) );

                if ( !ms.cluster->IsFiniteSize() || ms.cluster->GetEndPosition() >= fpos)
                    break;
            }
        }
    }
    else if (ms.cluster != NULL)
    {
        // make sure we start reading after the Cluster start
        ms.es.I_O().setFilePointer(ms.cluster->GetDataStart());
    }

    assert(ms.ep.GetLevel() == 1);
    ms.ep.Down();

    /* read until cluster/timecode to initialize cluster */

    while( EbmlElement * el = ms.ep.Get() )
    {
        try {
            if( MKV_CHECKED_PTR_DECL( p_tc, KaxClusterTimestamp, el ) )
            {
                p_tc->ReadData( ms.es.I_O(), SCOPE_ALL_DATA );
                ms.cluster->InitTimestamp( static_cast<uint64_t>( *p_tc ), ms.i_timescale );
                add_cluster(ms.cluster);
                break;
            }
            else if( MKV_CHECKED_PTR_DECL( crc, EbmlCrc32, el ) )
            {
                crc->ReadData( ms.es.I_O(), SCOPE_ALL_DATA ); /* avoid a skip that may fail */
            }
        }
        catch(...)
        {
            msg_Err( &ms.sys.demuxer,"Error while reading %s",  EBML_NAME(el) );
        }
    }

    /* TODO: add error handling; what if we never get a KaxCluster and/or KaxClusterTimestamp? */

    mark_range_as_searched( Range( i_cluster_pos, ms.es.I_O().getFilePointer() ) );

    /* jump to desired position */

    if ( fpos != std::numeric_limits<SegmentSeeker::fptr_t>::max() )
        ms.es.I_O().setFilePointer( fpos );
}

} // namespace

Messung
CHZG
84948988

¤ Dauer der Verarbeitung: 0.5 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© 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.