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

Quelle  dvdread_vr.c   Sprache: C

 

/*****************************************************************************
 * dvdread_vr.c : DVD-VideoRecording support for the dvdread input module
 *****************************************************************************
 * Copyright (C) 2024-2025 VLC authors and VideoLAN
 *
 * Authors: Saifelden Mohamed Ismail <saifeldenmi@gmail.com>
 *
 * 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 "dvdread.h"
#include <vlc_arrays.h>
#include <vlc_charset.h>

#ifdef DVDREAD_HAS_DVDVIDEORECORDING
typedef struct demux_sys_t demux_sys_t;
#define DVDVR_FALLBACK_SECTORS_PER_VOBU 512
#define DVDVR_EARLY_SNAP_VOBU_WINDOW 4

/* estimated sector span of a vr program's vobu map
 * dvd-vr has no flat vobu address map like VOBU_ADMAP in dvd-video
 * instead time_info entries store vobu_adr as sector offset and vobu_entn
 * as vobu index so the last entry gives a sectors-per-vobu ratio
 * falls back to a per-program or constant ratio when no usable entries exist
 * returns 0 when the map has no VOBUs */

uint32_t DvdVRGetProgramSectorSpan( const demux_sys_t *p_sys,
                                    const vobu_map_t *map )
{
    if( map->nr_of_time_info > 0 )
    {
        const uint32_t last_adr = map->time_infos[map->nr_of_time_info - 1].vobu_adr;
        const uint32_t last_entn = map->time_infos[map->nr_of_time_info - 1].vobu_entn;
        if( last_entn > 1 )
            return (uint32_t)( (uint64_t)last_adr * map->nr_of_vobu_info /
                               ( last_entn - 1 ) );
    }

    uint32_t sects_per_vobu = DVDVR_FALLBACK_SECTORS_PER_VOBU;
    for( int p = 0; p < p_sys->pgc_gi->nr_of_programs; p++ )
    {
        const vobu_map_t *m = &p_sys->pgc_gi->pgi[p].map;
        if( m->nr_of_time_info > 0 &&
            m->time_infos[m->nr_of_time_info - 1].vobu_entn > 1 )
        {
            sects_per_vobu = m->time_infos[m->nr_of_time_info - 1].vobu_adr /
                             ( m->time_infos[m->nr_of_time_info - 1].vobu_entn - 1 );
            break;
        }
    }

    return map->nr_of_vobu_info * sects_per_vobu;
}

static vlc_tick_t DvdVRProgramDuration( const pgi_t *pgi )
{
    if( pgi->header.vob_v_e_ptm.ptm <= pgi->header.vob_v_s_ptm.ptm )
        return 0;

    const uint32_t delta_ptm =
        pgi->header.vob_v_e_ptm.ptm - pgi->header.vob_v_s_ptm.ptm;
    return FROM_SCALE_NZ( delta_ptm );
}

/* vobu_adr walked in order by callers, must be monotone */
static bool DvdVRTimeInfosSane( const vobu_map_t *map, uint32_t total_sectors )
{
    if( map->nr_of_time_info == 0 || total_sectors == 0 )
        return false;

    uint32_t prev = 0;
    for( int i = 0; i < map->nr_of_time_info; i++ )
    {
        const uint32_t adr = map->time_infos[i].vobu_adr;
        if( adr >= total_sectors )
            return false;
        if( adr < prev )
            return false;
        prev = adr;
    }
    return true;
}

static uint32_t DvdVRReadTimeToVobuOffset( const demux_sys_t *p_sys, vlc_tick_t t )
{
    if( p_sys->type != DVD_VR || !p_sys->pgc_gi || !p_sys->ud_pgcit ||
        p_sys->i_title_start_cell < 0 || p_sys->i_title_end_cell <= p_sys->i_title_start_cell )
        return 0;

    vlc_tick_t acc = 0;
    uint32_t vobu_acc = 0;

    for( int ci = p_sys->i_title_start_cell; ci < p_sys->i_title_end_cell; ci++ )
    {
        uint16_t srpn = p_sys->ud_pgcit->m_c_gi[ci].m_vobi_srpn;
        if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
            return vobu_acc;

        const pgi_t *pgi = &p_sys->pgc_gi->pgi[srpn - 1];
        const uint32_t vobus = pgi->map.nr_of_vobu_info;
        if( vobus == 0 )
            continue;

        const vlc_tick_t dur = DvdVRProgramDuration( pgi );
        if( dur == 0 )
            continue;
        if( acc + dur >= t )
        {
            const vlc_tick_t within = t - acc;
            const uint32_t within_vobu = (uint32_t)(within * vobus / dur);
            return vobu_acc + within_vobu;
        }

        acc += dur;
        vobu_acc += vobus;
    }

    return p_sys->i_title_blocks > 0 ? p_sys->i_title_blocks - 1 : 0;
}

static int DvdVRReadSetArea( demux_t *p_demux, int i_title, int i_chapter,
                      int i_angle )
{
    VLC_UNUSED( i_angle );
    demux_sys_t *p_sys = p_demux->p_sys;

    if( i_title >= 0 && (unsigned)i_title >= p_sys->ud_pgcit->nr_of_pgci )
        return VLC_EGENERIC;

    /* user made title selection */
    if( i_title >= 0 && i_title < p_sys->i_titles &&
        i_title != p_sys->i_title )
    {
        int i_start_cell, i_end_cell;

        if( p_sys->p_title != NULL )
            DVDCloseFile( p_sys->p_title );
        if( !( p_sys->p_title = DVDOpenFile( p_sys->p_dvdread, 0, DVD_READ_TITLE_VOBS ) ) )
        {
            msg_Err( p_demux, "cannot open VR_MOVIE.VRO");
            return VLC_EGENERIC;
        }

        uint16_t fpid = p_sys->ud_pgcit->ud_pgci_items[i_title].first_prog_id;
        if( fpid == 0 || fpid == 0xFFFF
            || fpid > p_sys->pgc_gi->nr_of_programs )
        {
            msg_Warn( p_demux, "invalid first_prog_id %u for title %d",
                      fpid, i_title );
            return VLC_EGENERIC;
        }
        i_start_cell = p_sys->i_title_start_cell = fpid - 1;

        p_sys->i_cur_cell = i_start_cell;
        i_end_cell = p_sys->i_title_end_cell = p_sys->ud_pgcit->ud_pgci_items[i_title].first_prog_id - 1 +
           p_sys->ud_pgcit->ud_pgci_items[i_title].nr_of_programs;

        if( i_end_cell > p_sys->pgc_gi->nr_of_programs )
        {
            msg_Warn( p_demux, "invalid end cell %d for title %d",
                      i_end_cell, i_title );
            return VLC_EGENERIC;
        }

        p_sys->i_chapters = 0;
        for (int c = i_start_cell; c < i_end_cell; c++)
        {
            m_c_gi_t *cell = &p_sys->ud_pgcit->m_c_gi[c];
            p_sys->i_chapters += cell->c_epi_n ? cell->c_epi_n : 1;
        }

        /* which program we should search for our timestamp */
        uint16_t m_vobi_srpn =  p_sys->ud_pgcit->m_c_gi[i_start_cell].m_vobi_srpn;
        if( m_vobi_srpn == 0 || m_vobi_srpn > p_sys->pgc_gi->nr_of_programs )
        {
            msg_Err( p_demux, "invalid m_vobi_srpn %u for cell %d",
                     m_vobi_srpn, i_start_cell );
            return VLC_EGENERIC;
        }

        /* find the corresponding address */
        p_sys->p_cur_pgi = &p_sys->pgc_gi->pgi[m_vobi_srpn - 1];

        uint32_t vob_offset = p_sys->p_cur_pgi->map.vob_offset;

        p_sys->i_cur_block = vob_offset;

        p_sys->i_title_blocks = 0;
        for( int c = i_start_cell; c < i_end_cell; c++ )
        {
            uint16_t srpn = p_sys->ud_pgcit->m_c_gi[c].m_vobi_srpn;
            if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
            {
                msg_Warn( p_demux, "invalid m_vobi_srpn %" PRIu16 " in title %d",
                          srpn, i_title );
                return VLC_EGENERIC;
            }
            p_sys->i_title_blocks += p_sys->pgc_gi->pgi[srpn - 1].map.nr_of_vobu_info;
        }
        p_sys->i_title_offset = 0;
        p_sys->i_pack_len = 0;
        DvdReadResetCellTs( p_sys );

        p_sys->i_title = i_title;

        /*
         * Destroy obsolete ES by reinitializing program 0
         * and find all ES in title with ifo data
         */


        for( int i = 0; i < PS_TK_COUNT; i++ )
        {
            ps_track_t *tk = &p_sys->tk[i];
            if( tk->b_configured )
            {
                es_format_Clean( &tk->fmt );
                if( tk->es ) es_out_Del( p_demux->out, tk->es );
            }
            tk->b_configured = false;
        }

        es_out_Control( p_demux->out, ES_OUT_RESET_PCR );

        if( p_sys->cur_title != i_title)
        {
            p_sys->updates |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
            p_sys->cur_title = i_title;
            p_sys->cur_chapter = 0;
        }

    }
    else if( i_title != -1 && i_title != p_sys->i_title )
    {
        return VLC_EGENERIC; /* Couldn't set title */
    }

    /* Search for chapter */
    if( i_chapter >= 0 && i_chapter < p_sys->i_chapters )
    {
        int i, j;
        int accum_chapter = 0;
        uint32_t chapter_ptm;
        uint32_t chapter_program;
        int found = 0;

        /* find the correct entry point */
        for (i = p_sys->i_title_start_cell;
            i < p_sys->i_title_end_cell && !found; i++)
        {
            int n_ep = p_sys->ud_pgcit->m_c_gi[i].c_epi_n;
            if( n_ep == 0 )
            {
                /* cell has no explicit entry points — treat as one chapter */
                if( accum_chapter == i_chapter )
                {
                    chapter_ptm = p_sys->ud_pgcit->m_c_gi[i].c_v_s_ptm.ptm;
                    chapter_program = p_sys->ud_pgcit->m_c_gi[i].m_vobi_srpn;
                    found = 1;
                    break;
                }
                accum_chapter++;
            }
            else
            {
                for (j = 0; j < n_ep; j++)
                {
                    if (accum_chapter == i_chapter)
                    {
                        chapter_ptm = p_sys->ud_pgcit->m_c_gi[i].m_c_epi[j].ep_ptm.ptm;
                        chapter_program = p_sys->ud_pgcit->m_c_gi[i].m_vobi_srpn;
                        found = 1;
                        break;
                    }
                    accum_chapter++;
                }
            }
        }
        if (!found)
            return VLC_EGENERIC;

        if( chapter_program == 0 || chapter_program > p_sys->pgc_gi->nr_of_programs )
            return VLC_EGENERIC;

        const pgi_t *chapter_pgi = &p_sys->pgc_gi->pgi[chapter_program - 1];
        const vobu_map_t *map = &chapter_pgi->map;

        const int64_t relative_ptm = (int64_t)chapter_ptm -
            (int64_t)chapter_pgi->header.vob_v_s_ptm.ptm;
        const int64_t total_pts = (int64_t)chapter_pgi->header.vob_v_e_ptm.ptm -
            (int64_t)chapter_pgi->header.vob_v_s_ptm.ptm;
        const uint32_t total_sectors = DvdVRGetProgramSectorSpan( p_sys, map );

        uint32_t within_vobu = 0;
        if( total_pts > 0 && relative_ptm >= 0 && total_sectors > 0 )
        {
            if( map->nr_of_vobu_info > 0 )
                within_vobu = (uint32_t)( relative_ptm *
                              map->nr_of_vobu_info / total_pts );
        }
        if( map->nr_of_vobu_info > 0 && within_vobu >= map->nr_of_vobu_info )
            within_vobu = map->nr_of_vobu_info - 1;

        /* time_infos sparse, snap to entry only near program start */
        uint32_t snap_sector = 0;
        if( within_vobu < DVDVR_EARLY_SNAP_VOBU_WINDOW &&
            DvdVRTimeInfosSane( map, total_sectors ) )
        {
            int idx = 0;
            while( idx + 1 < map->nr_of_time_info &&
                   map->time_infos[idx + 1].vobu_entn <= within_vobu )
                idx++;
            snap_sector = map->time_infos[idx].vobu_adr;
        }

        const uint32_t chapter_offset = map->vob_offset + snap_sector;
        p_sys->i_cur_block = chapter_offset;
        p_sys->i_pack_len = total_sectors > snap_sector ? total_sectors - snap_sector : 1;
        p_sys->i_chapter = i_chapter;
        p_sys->cur_chapter = i_chapter;
        DvdReadResetCellTs( p_sys );
        es_out_Control( p_demux->out, ES_OUT_RESET_PCR );

        p_sys->i_cur_cell = -1;
        for( int ci = p_sys->i_title_start_cell;
             ci < p_sys->i_title_end_cell; ci++ )
        {
            if( p_sys->ud_pgcit->m_c_gi[ci].m_vobi_srpn == chapter_program )
            {
                p_sys->i_cur_cell = ci;
                break;
            }
        }
        if( p_sys->i_cur_cell < p_sys->i_title_start_cell )
            return VLC_EGENERIC;

        p_sys->i_title_offset = 0;
        for( int ci = p_sys->i_title_start_cell;
             ci < p_sys->i_cur_cell; ci++ )
        {
            uint16_t s = p_sys->ud_pgcit->m_c_gi[ci].m_vobi_srpn;
            if( s == 0 || s > p_sys->pgc_gi->nr_of_programs )
                return VLC_EGENERIC;
            p_sys->i_title_offset += p_sys->pgc_gi->pgi[s - 1].map.nr_of_vobu_info;
        }
        /* add within-program vobu offset for position accuracy */
        p_sys->i_title_offset += within_vobu;

        return VLC_SUCCESS;
    }
    else if( i_chapter != -1 )
    {
        return VLC_EGENERIC; /* Couln't set chapter */
    }

    return VLC_SUCCESS;
}

static void DvdVRFindCell( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    p_sys->i_next_cell = p_sys->i_cur_cell + 1;

    if( p_sys->i_cur_cell >= p_sys->i_title_end_cell )
        return;

    /* labelled as entry point in dvd-vr */
    int chapter = 0;

    /* see if we've reached the next entry point */
    ud_pgci_t *psi = &p_sys->ud_pgcit->ud_pgci_items[p_sys->i_title];
    int cell_base = psi->first_prog_id - 1;

    for( int i = 0; i < psi->nr_of_programs; i++ )
    {
        int cell_idx = cell_base + i;
        m_c_gi_t *cell = &p_sys->ud_pgcit->m_c_gi[cell_idx];

        if ( cell_idx == p_sys->i_cur_cell )
            break;

        /* entry points are chapters, count cell as 1 if no entry points */
        chapter += cell->c_epi_n ? cell->c_epi_n : 1;

    }

    if( chapter >= p_sys->i_chapters )
        chapter = p_sys->i_chapters > 0 ? p_sys->i_chapters - 1 : 0;

    if( chapter != p_sys->i_chapter )
    {
        p_sys->i_chapter = chapter;
        if( p_sys->cur_chapter != chapter )
        {
            p_sys->updates |= INPUT_UPDATE_SEEKPOINT;
            p_sys->cur_chapter = chapter;
        }
    }

}

static vlc_tick_t DVDVRGetTitleLength( pgc_gi_t *pgc_gi, ud_pgcit_t *ud_pgcit, int program)
{
    if( program < 0 || (unsigned)program >= ud_pgcit->nr_of_pgci )
        return 0;

    uint64_t length_ptm = 0;
    uint16_t first_prog_id = ud_pgcit->ud_pgci_items[program].first_prog_id;
    uint16_t nr_of_programs = ud_pgcit->ud_pgci_items[program].nr_of_programs;

    if( first_prog_id == 0 || first_prog_id > pgc_gi->nr_of_programs ||
        nr_of_programs == 0 ||
        first_prog_id - 1 + nr_of_programs > pgc_gi->nr_of_programs )
        return 0;

    for ( int i = 0; i < nr_of_programs; i++ )
        length_ptm += pgc_gi->pgi[first_prog_id -1 + i].header.vob_v_e_ptm.ptm
            - pgc_gi->pgi[first_prog_id -1 + i].header.vob_v_s_ptm.ptm;

    return FROM_SCALE_NZ( length_ptm );
}

static int DvdVRReadSeek( demux_t *p_demux, uint32_t i_block_offset )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    int i_chapter = 0;
    uint32_t vobu_accum = 0;
    bool found_cell = false;

    int cell_base = p_sys->ud_pgcit->ud_pgci_items[p_sys->i_title].first_prog_id - 1;
    int nr = p_sys->ud_pgcit->ud_pgci_items[p_sys->i_title].nr_of_programs;

    for( int c = 0; c < nr; c++ )
    {
        int cell_idx = cell_base + c;
        uint16_t srpn = p_sys->ud_pgcit->m_c_gi[cell_idx].m_vobi_srpn;
        if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
            return VLC_EGENERIC;
        uint16_t cell_vobus = p_sys->pgc_gi->pgi[srpn - 1].map.nr_of_vobu_info;

        if( i_block_offset < vobu_accum + cell_vobus )
        {
            p_sys->i_cur_cell = cell_idx;
            found_cell = true;
            break;
        }
        vobu_accum += cell_vobus;

        i_chapter += p_sys->ud_pgcit->m_c_gi[cell_idx].c_epi_n
                   ? p_sys->ud_pgcit->m_c_gi[cell_idx].c_epi_n : 1;
    }

    /* seek past title end: flag end so demux advances to the next title */
    if( !found_cell )
    {
        p_sys->i_cur_cell = p_sys->i_title_end_cell;
        p_sys->i_pack_len = 0;
        p_sys->i_title_offset = p_sys->i_title_blocks;
        DvdReadResetCellTs( p_sys );
        es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
        return VLC_SUCCESS;
    }

    if( i_chapter < p_sys->i_chapters &&
        p_sys->cur_chapter != i_chapter )
    {
        p_sys->updates |= INPUT_UPDATE_SEEKPOINT;
        p_sys->cur_chapter = i_chapter;
    }

    if( p_sys->i_cur_cell < cell_base || p_sys->i_cur_cell >= cell_base + nr )
        return VLC_EGENERIC;

    uint16_t cur_srpn = p_sys->ud_pgcit->m_c_gi[p_sys->i_cur_cell].m_vobi_srpn;
    if( unlikely( cur_srpn == 0 || cur_srpn > p_sys->pgc_gi->nr_of_programs ) )
        return VLC_EGENERIC;

    const vobu_map_t *map = &p_sys->pgc_gi->pgi[cur_srpn - 1].map;
    uint32_t within_program = i_block_offset - vobu_accum;
    const uint32_t total_sectors = DvdVRGetProgramSectorSpan( p_sys, map );
    if( map->nr_of_vobu_info > 0 && within_program >= map->nr_of_vobu_info )
        within_program = map->nr_of_vobu_info - 1;

    uint32_t raw_sector = 0;
    if( total_sectors > 0 && map->nr_of_vobu_info > 0 )
    {
        uint64_t scaled_sector = 0;
        if( !ckd_mul( &scaled_sector, (uint64_t)within_program,
                      (uint64_t)total_sectors ) )
            raw_sector = (uint32_t)( scaled_sector / map->nr_of_vobu_info );
    }

    /* time_infos sparse, snap to entry only near program start */
    uint32_t sector = raw_sector;
    if( within_program < DVDVR_EARLY_SNAP_VOBU_WINDOW &&
        DvdVRTimeInfosSane( map, total_sectors ) )
    {
        int idx = 0;
        while( idx + 1 < map->nr_of_time_info &&
               map->time_infos[idx + 1].vobu_adr <= raw_sector )
            idx++;
        sector = map->time_infos[idx].vobu_adr;
    }

    if( sector >= total_sectors )
        sector = total_sectors > 0 ? total_sectors - 1 : 0;

    p_sys->i_cur_block = map->vob_offset + sector;
    p_sys->i_pack_len = total_sectors > sector ? total_sectors - sector : 1;
    p_sys->i_title_offset = i_block_offset;
    p_sys->i_chapter = i_chapter;
    DvdReadResetCellTs( p_sys );
    es_out_Control( p_demux->out, ES_OUT_RESET_PCR );

    return VLC_SUCCESS;
}

static const char* ParseTxtEncoding( uint8_t txt_encoding )
{
    const char* charset = "Unknown";

    switch (txt_encoding) {
    case 0x00: charset = "ASCII"; break;
    case 0x01: charset = "ISO646-JP"; break;
    case 0x10: charset = "JIS_C6220-1969-RO"; break;
    case 0x11: charset = "ISO_8859-1"; break;
    case 0x12: charset = "SHIFT_JIS"; break;
    default:
        charset = "ISO_8859-15";
        break;
    }

    return charset;
}

static vlc_tick_t DvdVRReadTitleLength( const demux_sys_t *p_sys )
{
    if( p_sys->cur_title < 0 || p_sys->cur_title >= p_sys->i_titles )
        return 0;
    return p_sys->titles[p_sys->cur_title]->i_length;
}

static bool DvdVRReadTimelineBase( const demux_sys_t *p_sys, vlc_tick_t *base )
{
    if( p_sys->i_title_start_cell < 0 || p_sys->i_cur_cell < 0 ||
        p_sys->i_title_start_cell > p_sys->i_cur_cell ||
        p_sys->i_cur_cell >= p_sys->i_title_end_cell )
        return false;

    vlc_tick_t cell_dvd_time = 0;
    for( int ci = p_sys->i_title_start_cell;
         ci < p_sys->i_cur_cell && ci < p_sys->i_title_end_cell; ci++ )
    {
        uint16_t srpn = p_sys->ud_pgcit->m_c_gi[ci].m_vobi_srpn;
        if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
            return false;
        const pgi_t *scan_pgi = &p_sys->pgc_gi->pgi[srpn - 1];
        const vlc_tick_t scan_dur = DvdVRProgramDuration( scan_pgi );
        if( scan_dur <= 0 )
            return false;
        cell_dvd_time += scan_dur;
    }

    uint16_t srpn = p_sys->ud_pgcit->m_c_gi[p_sys->i_cur_cell].m_vobi_srpn;
    if( srpn == 0 || srpn > p_sys->pgc_gi->nr_of_programs )
        return false;

    const pgi_t *pgi = &p_sys->pgc_gi->pgi[srpn - 1];
    const vlc_tick_t program_duration = DvdVRProgramDuration( pgi );
    if( program_duration <= 0 )
        return false;
    const uint32_t total_sectors = DvdVRGetProgramSectorSpan( p_sys, &pgi->map );
    if( total_sectors > 0 )
    {
        int64_t within_sectors = (int64_t)p_sys->i_cur_block - pgi->map.vob_offset;
        if( within_sectors < 0 )
            within_sectors = 0;
        else if( within_sectors >= (int64_t)total_sectors )
            within_sectors = (int64_t)total_sectors - 1;

        vlc_tick_t scaled;
        if( !ckd_mul( &scaled, within_sectors, program_duration ) )
            cell_dvd_time += scaled / total_sectors;
    }

    *base = cell_dvd_time;
    return true;
}

static uint32_t DvdVRReadTimeToSeekOffset( const demux_sys_t *p_sys, vlc_tick_t time,
                                           uint32_t i_block_offset )
{
    VLC_UNUSED( i_block_offset );
    return DvdVRReadTimeToVobuOffset( p_sys, time );
}

static int DvdVRReadPsSource( void )
{
    return PS_SOURCE_VOB;
}

static void DvdVRReadAdjustAnchor( demux_sys_t *p_sys, block_t *pkt )
{
    if( p_sys->cell_ts.dvd == VLC_TICK_INVALID || p_sys->cell_ts.ps == VLC_TICK_INVALID )
        return;

    vlc_tick_t anchor = p_sys->cell_ts.ps;
    if( pkt->i_dts != VLC_TICK_INVALID && pkt->i_dts < anchor )
        anchor = pkt->i_dts;
    if( pkt->i_pts != VLC_TICK_INVALID && pkt->i_pts < anchor )
        anchor = pkt->i_pts;
    if( anchor < p_sys->cell_ts.ps )
        p_sys->cell_ts.ps = anchor;
}

static void DvdVRReadDemuxTitles( demux_t *p_demux, int *pi_angle )
{
    VLC_UNUSED( pi_angle );

    demux_sys_t *p_sys = p_demux->p_sys;
    input_title_t *t;
    seekpoint_t *s;

    const char *disc_charset = ParseTxtEncoding( p_sys->p_vmg_file->rtav_vmgi->txt_encoding );
    const int32_t i_titles = p_sys->ud_pgcit->nr_of_pgci;
    msg_Dbg( p_demux, "number of titles: %d", i_titles );

    for( int i = 0; i < i_titles; i++ )
    {
        int32_t i_chapters = 0;
        uint16_t fpid = p_sys->ud_pgcit->ud_pgci_items[i].first_prog_id;
        if( fpid == 0 || fpid == 0xFFFF || fpid > p_sys->pgc_gi->nr_of_programs )
            continue;

        /* chapters are entry points across all cells */
        int cell_base = fpid - 1;
        int nr = p_sys->ud_pgcit->ud_pgci_items[i].nr_of_programs;
        for( int c = 0; c < nr; c++ )
        {
            m_c_gi_t *cell = &p_sys->ud_pgcit->m_c_gi[cell_base + c];
            i_chapters += cell->c_epi_n ? cell->c_epi_n : 1;
        }

        msg_Dbg( p_demux, "title %d has %d chapters", i, i_chapters );

        t = vlc_input_title_New();
        if( unlikely( !t ) )
            return;

        /* ep_ptm disc-global, subtract title start for relative offset */
        vlc_tick_t title_base = 0;
        if( nr > 0 )
            title_base = FROM_SCALE_NZ( p_sys->ud_pgcit->m_c_gi[cell_base].c_v_s_ptm.ptm );

        char *converted_title = FromCharset(
            disc_charset,
            p_sys->ud_pgcit->ud_pgci_items[i].title,
            strnlen( p_sys->ud_pgcit->ud_pgci_items[i].title,
                     sizeof( p_sys->ud_pgcit->ud_pgci_items[i].title ) )
        );
        if( converted_title && *converted_title )
            t->psz_name = converted_title;
        else
        {
            free( converted_title );
            t->psz_name = strndup( p_sys->ud_pgcit->ud_pgci_items[i].label,
                                   sizeof( p_sys->ud_pgcit->ud_pgci_items[i].label ) );
        }
        t->i_length = DVDVRGetTitleLength( p_sys->pgc_gi, p_sys->ud_pgcit, i );

        /* create one seekpoint per entry point */
        for( int c = 0; c < nr; c++ )
        {
            m_c_gi_t *cell = &p_sys->ud_pgcit->m_c_gi[cell_base + c];
            if( cell->c_epi_n > 0 )
            {
                for( int ep = 0; ep < cell->c_epi_n; ep++ )
                {
                    s = vlc_seekpoint_New();
                    if( unlikely( !s ) )
                        goto fail;

                    vlc_tick_t ep_time =
                        FROM_SCALE_NZ( cell->m_c_epi[ep].ep_ptm.ptm ) - title_base;
                    s->i_time_offset = ep_time > 0 ? ep_time : 0;
                    TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
                }
            }
            else
            {
                s = vlc_seekpoint_New();
                if( unlikely( !s ) )
                    goto fail;

                vlc_tick_t cell_time = FROM_SCALE_NZ( cell->c_v_s_ptm.ptm ) - title_base;
                s->i_time_offset = cell_time > 0 ? cell_time : 0;
                TAB_APPEND( t->i_seekpoint, t->seekpoint, s );
            }
        }

        TAB_APPEND( p_sys->i_titles, p_sys->titles, t );
    }
    return;

fail:
    vlc_input_title_Delete( t );
}

const dvdread_ops_t DvdVRReadOps = {
    .set_area = DvdVRReadSetArea,
    .seek = DvdVRReadSeek,
    .find_cell = DvdVRFindCell,
    .title_length = DvdVRReadTitleLength,
    .demux_titles = DvdVRReadDemuxTitles,
    .timeline_base = DvdVRReadTimelineBase,
    .time_to_seek_offset = DvdVRReadTimeToSeekOffset,
    .ps_source = DvdVRReadPsSource,
    .adjust_anchor = DvdVRReadAdjustAnchor,
    .requires_vts = false,
};

int OpenVideoRecording( vlc_object_t *p_this )
{
    return OpenCommonDvdread( p_this, DVD_VR, NULL );
}
#else
int OpenVideoRecording( vlc_object_t *p_this )
{
    VLC_UNUSED( p_this );
    return VLC_ENOTSUP;
}
#endif

Messung
CHZG
84969993

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