static uint32_t seek_video ( lwlibav_video_decode_handler_t *vdhp, AVFrame *picture, uint32_t presentation_sample_number, uint32_t rap_number, int64_t rap_pos, int error_ignorance ) { /* Prepare to decode from random accessible sample. */ lwlibav_extradata_handler_t *exhp = &vdhp->exh; int extradata_index = vdhp->frame_list[rap_number].extradata_index; if( extradata_index != exhp->current_index ) /* Update the decoder configuration. */ lwlibav_update_configuration( (lwlibav_decode_handler_t *)vdhp, rap_number, extradata_index, rap_pos ); else lwlibav_flush_buffers( (lwlibav_decode_handler_t *)vdhp ); if( vdhp->error ) return 0; if( av_seek_frame( vdhp->format, vdhp->stream_index, rap_pos, vdhp->av_seek_flags ) < 0 ) av_seek_frame( vdhp->format, vdhp->stream_index, rap_pos, vdhp->av_seek_flags | AVSEEK_FLAG_ANY ); int got_picture = 0; int64_t rap_pts = AV_NOPTS_VALUE; uint32_t current; uint32_t decoder_delay = get_decoder_delay( vdhp->ctx ); uint32_t thread_delay = decoder_delay - vdhp->ctx->has_b_frames; uint32_t goal = presentation_sample_number + decoder_delay; exhp->delay_count = 0; vdhp->last_half_frame = 0; for( current = rap_number; current <= goal; current++ ) { int ret = decode_video_picture( vdhp, picture, &got_picture, ¤t, goal, rap_number ); if( ret == -2 ) return 0; else if( ret >= 1 ) { /* No decoding occurs. */ got_picture = 0; break; } /* Handle decoder delay derived from PAFF field coded pictures. */ if( current <= vdhp->frame_count && current >= rap_number + decoder_delay && !got_picture && vdhp->frame_list[current].repeat_pict == 0 ) { /* No output picture since the second field coded picture of the next frame is not decoded yet. */ if( decoder_delay - thread_delay < 2 * vdhp->ctx->has_b_frames + 1UL ) { uint32_t new_decoder_delay = thread_delay + 2 * vdhp->ctx->has_b_frames + 1UL; goal += new_decoder_delay - decoder_delay; decoder_delay = new_decoder_delay; } } if( got_picture ) { exhp->delay_count = MIN( decoder_delay, current - rap_number ); uint32_t frame_number = current - exhp->delay_count; vdhp->last_half_frame = (frame_number <= vdhp->frame_count && vdhp->frame_list[frame_number].repeat_pict == 0); } /* Some decoders return -1 when feeding a leading sample. * We don't consider as an error if the return value -1 is caused by a leading sample since it's not fatal at all. */ if( current == vdhp->last_rap_number && picture->pts != AV_NOPTS_VALUE ) rap_pts = picture->pts; if( ret == -1 && (picture->pts == AV_NOPTS_VALUE || picture->pts >= rap_pts) && !error_ignorance ) { if( vdhp->lh.show_log ) vdhp->lh.show_log( &vdhp->lh, LW_LOG_ERROR, "Failed to decode a video frame." ); return 0; } } exhp->delay_count = MIN( decoder_delay, current - rap_number ); if( current > rap_number && vdhp->last_half_frame ) { if( got_picture ) /* first field of PAFF field coded picture */ vdhp->last_half_offset = 0; else { /* second field of PAFF field coded picture */ vdhp->last_half_frame = UINT32_MAX; vdhp->last_half_offset = 1; /* A picture of the second field is already decoded. */ } } else { vdhp->last_half_frame = 0; vdhp->last_half_offset = 0; } return current; }
uint64_t lwlibav_audio_get_pcm_samples ( lwlibav_audio_decode_handler_t *adhp, lwlibav_audio_output_handler_t *aohp, void *buf, int64_t start, int64_t wanted_length ) { if( adhp->error ) return 0; uint32_t frame_number; uint32_t rap_number = 0; uint32_t past_rap_number = 0; uint64_t output_length = 0; enum audio_output_flag output_flags = AUDIO_OUTPUT_NO_FLAGS; AVPacket *pkt = &adhp->packet; AVPacket *alter_pkt = &adhp->alter_packet; int already_gotten; aohp->request_length = wanted_length; if( start > 0 && start == adhp->next_pcm_sample_number ) { frame_number = adhp->last_frame_number; output_length += output_pcm_samples_from_buffer( aohp, adhp->frame_buffer, (uint8_t **)&buf, &output_flags ); if( output_flags & AUDIO_OUTPUT_ENOUGH ) goto audio_out; if( alter_pkt->size <= 0 ) ++frame_number; aohp->output_sample_offset = 0; already_gotten = 0; } else { /* Seek audio stream. */ adhp->next_pcm_sample_number = 0; adhp->last_frame_number = 0; /* Get frame_number. */ uint64_t start_frame_pos; if( start >= 0 ) start_frame_pos = start; else { uint64_t silence_length = -start; put_silence_audio_samples( (int)(silence_length * aohp->output_block_align), aohp->output_bits_per_sample == 8, (uint8_t **)&buf ); output_length += silence_length; aohp->request_length -= silence_length; start_frame_pos = 0; } frame_number = find_start_audio_frame( adhp, aohp->output_sample_rate, start_frame_pos, &aohp->output_sample_offset ); retry_seek: av_packet_unref( pkt ); /* Flush audio resampler buffers. */ if( flush_resampler_buffers( aohp->avr_ctx ) < 0 ) { adhp->error = 1; lw_log_show( &adhp->lh, LW_LOG_FATAL, "Failed to flush resampler buffers.\n" "It is recommended you reopen the file." ); return 0; } /* Flush audio decoder buffers. */ lwlibav_extradata_handler_t *exhp = &adhp->exh; int extradata_index = adhp->frame_list[frame_number].extradata_index; if( extradata_index != exhp->current_index ) { /* Update the extradata. */ rap_number = get_audio_rap( adhp, frame_number ); assert( rap_number != 0 ); lwlibav_update_configuration( (lwlibav_decode_handler_t *)adhp, rap_number, extradata_index, 0 ); } else lwlibav_flush_buffers( (lwlibav_decode_handler_t *)adhp ); if( adhp->error ) return 0; /* Seek and get a audio packet. */ rap_number = seek_audio( adhp, frame_number, past_rap_number, pkt, output_flags != AUDIO_OUTPUT_NO_FLAGS ? adhp->frame_buffer : NULL ); already_gotten = 1; } do { if( already_gotten ) { already_gotten = 0; make_decodable_packet( alter_pkt, pkt ); } else if( frame_number > adhp->frame_count ) { av_packet_unref( pkt ); if( adhp->exh.delay_count || !(output_flags & AUDIO_OUTPUT_ENOUGH) ) { /* Null packet */ av_init_packet( pkt ); make_null_packet( pkt ); *alter_pkt = *pkt; if( adhp->exh.delay_count ) adhp->exh.delay_count -= 1; } else goto audio_out; } else if( alter_pkt->size <= 0 ) { /* Getting an audio packet must be after flushing all remaining samples in resampler's FIFO buffer. */ lwlibav_get_av_frame( adhp->format, adhp->stream_index, frame_number, pkt ); make_decodable_packet( alter_pkt, pkt ); } /* Decode and output from an audio packet. */ output_flags = AUDIO_OUTPUT_NO_FLAGS; output_length += output_pcm_samples_from_packet( aohp, adhp->ctx, alter_pkt, adhp->frame_buffer, (uint8_t **)&buf, &output_flags ); if( output_flags & AUDIO_DECODER_DELAY ) { if( rap_number > 1 && (output_flags & AUDIO_DECODER_ERROR) ) { /* Retry to seek from more past audio keyframe because libavformat might have failed seek. * This operation occurs only at the first decoding time after seek. */ past_rap_number = get_audio_rap( adhp, rap_number - 1 ); if( past_rap_number && past_rap_number < rap_number ) goto retry_seek; } ++ adhp->exh.delay_count; } else /* Disable seek retry. */ rap_number = 0; if( output_flags & AUDIO_RECONFIG_FAILURE ) { adhp->error = 1; lw_log_show( &adhp->lh, LW_LOG_FATAL, "Failed to reconfigure resampler.\n" "It is recommended you reopen the file." ); goto audio_out; } if( output_flags & AUDIO_OUTPUT_ENOUGH ) goto audio_out; if( output_flags & (AUDIO_DECODER_ERROR | AUDIO_DECODER_RECEIVED_PACKET) ) ++frame_number; } while( 1 ); audio_out: adhp->next_pcm_sample_number = start + output_length; adhp->last_frame_number = frame_number; return output_length; }
void lwlibav_update_configuration ( lwlibav_decode_handler_t *dhp, uint32_t frame_number, int extradata_index, int64_t rap_pos ) { lwlibav_extradata_handler_t *exhp = &dhp->exh; if( exhp->entry_count == 0 || extradata_index < 0 ) { /* No need to update the extradata. */ exhp->current_index = extradata_index; lwlibav_flush_buffers( dhp ); return; } AVCodecContext *ctx = dhp->format->streams[ dhp->stream_index ]->codec; void *app_specific = ctx->opaque; avcodec_close( ctx ); if( ctx->extradata ) { av_freep( &ctx->extradata ); ctx->extradata_size = 0; } /* Find an appropriate decoder. */ char error_string[96] = { 0 }; lwlibav_extradata_t *entry = &exhp->entries[extradata_index]; const AVCodec *codec = find_decoder( entry->codec_id, dhp->preferred_decoder_names ); if( !codec ) { strcpy( error_string, "Failed to find the decoder.\n" ); goto fail; } /* Get decoder default settings. */ int thread_count = ctx->thread_count; if( avcodec_get_context_defaults3( ctx, codec ) < 0 ) { strcpy( error_string, "Failed to get CODEC default.\n" ); goto fail; } /* Set up decoder basic settings. */ if( ctx->codec_type == AVMEDIA_TYPE_VIDEO ) set_video_basic_settings( dhp, frame_number ); else set_audio_basic_settings( dhp, frame_number ); /* Update extradata. */ if( entry->extradata_size > 0 ) { ctx->extradata = (uint8_t *)av_malloc( entry->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE ); if( !ctx->extradata ) { strcpy( error_string, "Failed to allocate extradata.\n" ); goto fail; } ctx->extradata_size = entry->extradata_size; memcpy( ctx->extradata, entry->extradata, ctx->extradata_size ); memset( ctx->extradata + ctx->extradata_size, 0, FF_INPUT_BUFFER_PADDING_SIZE ); } /* AVCodecContext.codec_id is supposed to be set properly in avcodec_open2(). * See lwlibav_flush_buffers(), why this is needed. */ ctx->codec_id = AV_CODEC_ID_NONE; /* This is needed by some CODECs such as UtVideo and raw video. */ ctx->codec_tag = entry->codec_tag; /* Open an appropriate decoder. * Here, we force single threaded decoding since some decoder doesn't do its proper initialization with multi-threaded decoding. */ ctx->thread_count = 1; if( open_decoder( ctx, codec ) < 0 ) { strcpy( error_string, "Failed to open decoder.\n" ); goto fail; } exhp->current_index = extradata_index; exhp->delay_count = 0; /* Set up decoder basic settings by actual decoding. */ if( ctx->codec_type == AVMEDIA_TYPE_VIDEO ? try_decode_video_frame( dhp, frame_number, rap_pos, error_string ) < 0 : try_decode_audio_frame( dhp, frame_number, error_string ) < 0 ) goto fail; /* Reopen/flush with the requested number of threads. */ ctx->thread_count = thread_count; int width = ctx->width; int height = ctx->height; lwlibav_flush_buffers( dhp ); ctx->get_buffer2 = exhp->get_buffer ? exhp->get_buffer : avcodec_default_get_buffer2; ctx->opaque = app_specific; /* avcodec_open2() may have changed resolution unexpectedly. */ ctx->width = width; ctx->height = height; return; fail: exhp->delay_count = 0; dhp->error = 1; lw_log_show( &dhp->lh, LW_LOG_FATAL, "%sIt is recommended you reopen the file.", error_string ); }