static inline int get_frame_length
(
    libavsmash_audio_decode_handler_t *adhp,
    uint32_t                           frame_number,
    uint32_t                          *frame_length,
    libavsmash_summary_t             **sp
)
{
    lsmash_sample_t sample;
    if( lsmash_get_sample_info_from_media_timeline( adhp->root, adhp->track_ID, frame_number, &sample ) )
        return -1;
    *sp = &adhp->config.entries[ sample.index - 1 ];
    libavsmash_summary_t *s = *sp;
    if( s->extended.frame_length == 0 )
    {
        /* variable frame length
         * Guess the frame length from sample duration. */
        if( lsmash_get_sample_delta_from_media_timeline( adhp->root, adhp->track_ID, frame_number, frame_length ) )
            return -1;
        *frame_length *= s->extended.upsampling;
    }
    else
        /* constant frame length */
        *frame_length = s->extended.frame_length;
    return 0;
}
static uint32_t libavsmash_vfr2cfr
(
    libavsmash_video_decode_handler_t *vdhp,
    libavsmash_video_output_handler_t *vohp,
    uint32_t                           sample_number
)
{
    /* Convert VFR to CFR. */
    double target_pts  = (double)((uint64_t)(sample_number - 1) * vohp->cfr_den) / vohp->cfr_num;
    double current_pts = DBL_MAX;
    lsmash_sample_t sample;
    if( vdhp->last_sample_number <= vdhp->sample_count )
    {
        uint32_t last_decoding_sample_number = get_decoding_sample_number( vdhp->order_converter, vdhp->last_sample_number );
        if( lsmash_get_sample_info_from_media_timeline( vdhp->root, vdhp->track_id, last_decoding_sample_number, &sample ) < 0 )
            return 0;
        current_pts = (double)(sample.cts - vdhp->min_cts) / vdhp->media_timescale;
        if( target_pts == current_pts )
            return vdhp->last_sample_number;
    }
    if( target_pts < current_pts )
    {
        uint32_t composition_sample_number;
        for( composition_sample_number = vdhp->last_sample_number - 1;
             composition_sample_number;
             composition_sample_number-- )
        {
            uint32_t decoding_sample_number = get_decoding_sample_number( vdhp->order_converter, composition_sample_number );
            if( lsmash_get_sample_info_from_media_timeline( vdhp->root, vdhp->track_id, decoding_sample_number, &sample ) < 0 )
                return 0;
            current_pts = (double)(sample.cts - vdhp->min_cts) / vdhp->media_timescale;
            if( current_pts <= target_pts )
            {
                sample_number = composition_sample_number;
                break;
            }
        }
        if( composition_sample_number == 0 )
            return 0;
    }
    else
    {
        uint32_t composition_sample_number;
        for( composition_sample_number = vdhp->last_sample_number + 1;
             composition_sample_number <= vdhp->sample_count;
             composition_sample_number++ )
        {
            uint32_t decoding_sample_number = get_decoding_sample_number( vdhp->order_converter, composition_sample_number );
            if( lsmash_get_sample_info_from_media_timeline( vdhp->root, vdhp->track_id, decoding_sample_number, &sample ) < 0 )
                return 0;
            current_pts = (double)(sample.cts - vdhp->min_cts) / vdhp->media_timescale;
            if( current_pts > target_pts )
            {
                sample_number = composition_sample_number - 1;
                break;
            }
        }
        if( composition_sample_number > vdhp->sample_count )
            sample_number = vdhp->sample_count;
    }
    return sample_number;
}
static int get_requested_picture
(
    libavsmash_video_decode_handler_t *vdhp,
    AVFrame                           *picture,
    uint32_t                           sample_number
)
{
#define MAX_ERROR_COUNT 3       /* arbitrary */
    codec_configuration_t *config = &vdhp->config;
    uint32_t config_index;
    if( sample_number < vdhp->first_valid_frame_number || vdhp->sample_count == 1 )
    {
        /* Get the index of the decoder configuration. */
        lsmash_sample_t sample;
        uint32_t decoding_sample_number = get_decoding_sample_number( vdhp->order_converter, vdhp->first_valid_frame_number );
        if( lsmash_get_sample_info_from_media_timeline( vdhp->root, vdhp->track_id, decoding_sample_number, &sample ) < 0 )
            goto video_fail;
        config_index = sample.index;
        /* Copy the first valid video frame data. */
        av_frame_unref( picture );
        if( av_frame_ref( picture, vdhp->first_valid_frame ) < 0 )
            goto video_fail;
        /* Force seeking at the next access for valid video frame. */
        vdhp->last_sample_number = vdhp->sample_count + 1;
        goto return_frame;
    }
    uint32_t start_number;  /* number of sample, for normal decoding, where decoding starts excluding decoding delay */
    uint32_t rap_number;    /* number of sample, for seeking, where decoding starts excluding decoding delay */
    int seek_mode = vdhp->seek_mode;
    int roll_recovery = 0;
    if( sample_number > vdhp->last_sample_number
     && sample_number <= vdhp->last_sample_number + vdhp->forward_seek_threshold )
    {
        start_number = vdhp->last_sample_number + 1 + config->delay_count;
        rap_number   = vdhp->last_rap_number;
    }
    else
    {
        roll_recovery = find_random_accessible_point( vdhp, sample_number, 0, &rap_number );
        if( rap_number == vdhp->last_rap_number && sample_number > vdhp->last_sample_number )
        {
            roll_recovery = 0;
            start_number  = vdhp->last_sample_number + 1 + config->delay_count;
        }
        else
        {
            /* Require starting to decode from random accessible sample. */
            vdhp->last_rap_number = rap_number;
            start_number = seek_video( vdhp, picture, sample_number, rap_number, roll_recovery || seek_mode != SEEK_MODE_NORMAL );
        }
    }
    /* Get the desired picture. */
    int error_count = 0;
    while( start_number == 0    /* Failed to seek. */
     || config->update_pending  /* Need to update the decoder configuration to decode pictures. */
     || get_picture( vdhp, picture, start_number, sample_number + config->delay_count ) < 0 )
    {
        if( config->update_pending )
        {
            roll_recovery = find_random_accessible_point( vdhp, sample_number, 0, &rap_number );
            vdhp->last_rap_number = rap_number;
        }
        else
        {
            /* Failed to get the desired picture. */
            if( config->error || seek_mode == SEEK_MODE_AGGRESSIVE )
                goto video_fail;
            if( ++error_count > MAX_ERROR_COUNT || rap_number <= 1 )
            {
                if( seek_mode == SEEK_MODE_UNSAFE )
                    goto video_fail;
                /* Retry to decode from the same random accessible sample with error ignorance. */
                seek_mode = SEEK_MODE_AGGRESSIVE;
            }
            else
            {
                /* Retry to decode from more past random accessible sample. */
                roll_recovery = find_random_accessible_point( vdhp, sample_number, rap_number - 1, &rap_number );
                if( vdhp->last_rap_number == rap_number )
                    goto video_fail;
                vdhp->last_rap_number = rap_number;
            }
        }
        start_number = seek_video( vdhp, picture, sample_number, rap_number, roll_recovery || seek_mode != SEEK_MODE_NORMAL );
    }
    vdhp->last_sample_number = sample_number;
    config_index = config->index;
return_frame:;
    /* Don't exceed the maximum presentation size specified for each sequence. */
    extended_summary_t *extended = &config->entries[ config_index - 1 ].extended;
    if( config->ctx->width > extended->width )
        config->ctx->width = extended->width;
    if( config->ctx->height > extended->height )
        config->ctx->height = extended->height;
    return 0;
video_fail:
    /* fatal error of decoding */
    lw_log_show( &config->lh, LW_LOG_WARNING, "Couldn't read video frame." );
    return -1;
#undef MAX_ERROR_COUNT
}
static int find_random_accessible_point
(
    libavsmash_video_decode_handler_t *vdhp,
    uint32_t                           composition_sample_number,
    uint32_t                           decoding_sample_number,
    uint32_t                          *rap_number
)
{
    if( decoding_sample_number == 0 )
        decoding_sample_number = get_decoding_sample_number( vdhp->order_converter, composition_sample_number );
    lsmash_random_access_flag ra_flags;
    uint32_t distance;  /* distance from the closest random accessible point to the previous. */
    uint32_t number_of_leadings;
    if( lsmash_get_closest_random_accessible_point_detail_from_media_timeline( vdhp->root, vdhp->track_id,
                                                                               decoding_sample_number, rap_number,
                                                                               &ra_flags, &number_of_leadings, &distance ) < 0 )
        *rap_number = 1;
    int roll_recovery = !!(ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR);
    int is_leading    = number_of_leadings && (decoding_sample_number - *rap_number <= number_of_leadings);
    if( (roll_recovery || is_leading) && *rap_number > distance )
        *rap_number -= distance;
    /* Check whether random accessible point has the same decoder configuration or not. */
    decoding_sample_number = get_decoding_sample_number( vdhp->order_converter, composition_sample_number );
    do
    {
        lsmash_sample_t sample;
        lsmash_sample_t rap_sample;
        if( lsmash_get_sample_info_from_media_timeline( vdhp->root, vdhp->track_id, decoding_sample_number, &sample ) < 0
         || lsmash_get_sample_info_from_media_timeline( vdhp->root, vdhp->track_id, *rap_number, &rap_sample ) < 0 )
        {
            /* Fatal error. */
            *rap_number = vdhp->last_rap_number;
            return 0;
        }
        if( sample.index == rap_sample.index )
            break;
        uint32_t sample_index = sample.index;
        for( uint32_t i = decoding_sample_number - 1; i; i-- )
        {
            if( lsmash_get_sample_info_from_media_timeline( vdhp->root, vdhp->track_id, i, &sample ) < 0 )
            {
                /* Fatal error. */
                *rap_number = vdhp->last_rap_number;
                return 0;
            }
            if( sample.index != sample_index )
            {
                if( distance )
                {
                    *rap_number += distance;
                    distance = 0;
                    continue;
                }
                else
                    *rap_number = i + 1;
            }
        }
        break;
    } while( 1 );
    return roll_recovery;
}
uint64_t libavsmash_count_overall_pcm_samples
(
    libavsmash_audio_decode_handler_t *adhp,
    int                                output_sample_rate,
    uint64_t                          *skip_decoded_samples     /* converted to upsampled in this function */
)
{
    codec_configuration_t *config = &adhp->config;
    extended_summary_t    *es     = NULL;
    /* Here, the decoder upsampling is defined only when libavcodec doesn't return upsampled length as AVCodecContext.frame_size. */
    int      current_sample_rate       = 0;     /* after the decoder upsampling */
    uint32_t current_index             = 0;
    uint32_t current_frame_length      = 0;     /* before the decoder upsampling */
    uint64_t sequence_pcm_count        = 0;     /* before the decoder upsampling */
    uint64_t prior_sequences_pcm_count = 0;     /* before the decoder upsampling */
    uint64_t overall_pcm_count         = 0;     /* after the decoder and the resampler upsampling */
    uint64_t orig_skip_decoded_samples = *skip_decoded_samples; /* before the decoder upsampling */
    /* Count the number of output PCM audio samples in each sequence. */
    *skip_decoded_samples = 0;
    for( uint32_t i = 1; i <= adhp->frame_count; i++ )
    {
        /* Get configuration index. */
        lsmash_sample_t sample;
        if( lsmash_get_sample_info_from_media_timeline( adhp->root, adhp->track_ID, i, &sample ) )
            continue;
        if( current_index != sample.index )
        {
            es = &config->entries[ sample.index - 1 ].extended;
            current_index = sample.index;
        }
        else if( !es )
            continue;
        /* Get audio frame length. */
        uint32_t frame_length;
        if( es->frame_length )
            frame_length = es->frame_length;
        else if( lsmash_get_sample_delta_from_media_timeline( adhp->root, adhp->track_ID, i, &frame_length ) )
            continue;
        /* */
        if( (current_sample_rate != es->sample_rate && es->sample_rate > 0)
         || current_frame_length != frame_length )
        {
            /* Encountered a new sequence. */
            if( current_sample_rate > 0 )
            {
                /* Add the number of decoded PCM audio samples which shall be skipped. */
                if( orig_skip_decoded_samples > prior_sequences_pcm_count )
                {
                    if( orig_skip_decoded_samples >= prior_sequences_pcm_count + sequence_pcm_count )
                        /* All decoded PCM audio samples in the previous sequence shall be skipped. */
                        *skip_decoded_samples += sequence_pcm_count * es->upsampling;
                    else
                        /* 0 < orig_skip_decoded_samples - prior_sequences_pcm_count < sequence_pcm_count
                         * Partial decoded PCM audio samples in the previous sequence are not skipped. */
                        *skip_decoded_samples += (orig_skip_decoded_samples - prior_sequences_pcm_count) * es->upsampling;
                }
                prior_sequences_pcm_count += sequence_pcm_count;
                /* Add the number of output PCM audio samples in the previous sequence. */
                overall_pcm_count += count_sequence_output_pcm_samples( sequence_pcm_count * es->upsampling,
                                                                        *skip_decoded_samples,
                                                                        current_sample_rate,
                                                                        output_sample_rate );
                sequence_pcm_count = 0;
            }
            current_sample_rate  = es->sample_rate > 0 ? es->sample_rate : config->ctx->sample_rate;
            current_frame_length = frame_length;
        }
        sequence_pcm_count += frame_length;
    }
    if( !es || (sequence_pcm_count == 0 && overall_pcm_count == 0) )
        return 0;
    /* Count the number of output PCM audio samples in the last sequence. */
    if( orig_skip_decoded_samples > prior_sequences_pcm_count )
        *skip_decoded_samples += (orig_skip_decoded_samples - prior_sequences_pcm_count) * es->upsampling;
    current_sample_rate = es->sample_rate > 0 ? es->sample_rate : config->ctx->sample_rate;
    overall_pcm_count += count_sequence_output_pcm_samples( sequence_pcm_count * es->upsampling,
                                                            *skip_decoded_samples,
                                                            current_sample_rate,
                                                            output_sample_rate );
    /* Return the number of output PCM audio samples. */
    return overall_pcm_count;
}