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; }