static int decode_video_sample ( libavsmash_video_decode_handler_t *vdhp, AVFrame *picture, int *got_picture, uint32_t sample_number ) { codec_configuration_t *config = &vdhp->config; AVPacket pkt = { 0 }; int ret = get_sample( vdhp->root, vdhp->track_id, sample_number, config, &pkt ); if( ret ) return ret; if( pkt.flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE ) { pkt.flags = AV_PKT_FLAG_KEY; vdhp->last_rap_number = sample_number; } else pkt.flags = 0; av_frame_unref( picture ); uint64_t cts = pkt.pts; ret = decode_video_packet( config->ctx, picture, got_picture, &pkt ); picture->pts = cts; if( ret < 0 ) { lw_log_show( &config->lh, LW_LOG_WARNING, "Failed to decode a video frame." ); return -1; } return 0; }
int libavsmash_video_initialize_decoder_configuration ( libavsmash_video_decode_handler_t *vdhp, AVFormatContext *format_ctx, int threads ) { char error_string[128] = { 0 }; if( libavsmash_video_get_summaries( vdhp ) < 0 ) return -1; /* libavformat */ uint32_t type = AVMEDIA_TYPE_VIDEO; uint32_t i; for( i = 0; i < format_ctx->nb_streams && format_ctx->streams[i]->codecpar->codec_type != type; i++ ); if( i == format_ctx->nb_streams ) { strcpy( error_string, "Failed to find stream by libavformat.\n" ); goto fail; } /* libavcodec */ AVCodecParameters *codecpar = format_ctx->streams[i]->codecpar; if( libavsmash_find_and_open_decoder( &vdhp->config, codecpar, threads, 1 ) < 0 ) { strcpy( error_string, "Failed to find and open the video decoder.\n" ); goto fail; } return initialize_decoder_configuration( vdhp->root, vdhp->track_id, &vdhp->config ); fail:; lw_log_handler_t *lhp = libavsmash_video_get_log_handler( vdhp ); lw_log_show( lhp, LW_LOG_FATAL, "%s", error_string ); return -1; }
static int get_picture ( libavsmash_video_decode_handler_t *vdhp, AVFrame *picture, uint32_t current, uint32_t goal ) { codec_configuration_t *config = &vdhp->config; int got_picture = (current > goal); while( current <= goal ) { int ret = decode_video_sample( vdhp, picture, &got_picture, current ); if( ret == -1 ) return -1; else if( ret == 1 ) /* Sample doesn't exist. */ break; ++current; if( config->update_pending ) /* A new decoder configuration is needed. Anyway, stop getting picture. */ break; if( !got_picture ) { /* Fundamental seek operations after the decoder initialization is already done, but * more input samples are required to output and the goal become more distant. */ ++ config->delay_count; ++ goal; } } /* Flush the last frames. */ if( current > vdhp->sample_count && get_decoder_delay( config->ctx ) ) while( current <= goal ) { AVPacket pkt = { 0 }; av_init_packet( &pkt ); pkt.data = NULL; pkt.size = 0; av_frame_unref( picture ); if( decode_video_packet( config->ctx, picture, &got_picture, &pkt ) < 0 ) { lw_log_show( &config->lh, LW_LOG_WARNING, "Failed to decode and flush a video frame." ); return -1; } ++current; } return got_picture ? 0 : -1; }
int libavsmash_audio_initialize_decoder_configuration ( libavsmash_audio_decode_handler_t *adhp, AVFormatContext *format_ctx, int threads ) { char error_string[128] = { 0 }; if( libavsmash_audio_get_summaries( adhp ) < 0 ) return -1; /* libavformat */ uint32_t type = AVMEDIA_TYPE_AUDIO; uint32_t i; for( i = 0; i < format_ctx->nb_streams && format_ctx->streams[i]->codec->codec_type != type; i++ ); if( i == format_ctx->nb_streams ) { strcpy( error_string, "Failed to find stream by libavformat.\n" ); goto fail; } /* libavcodec */ AVCodecContext *ctx = format_ctx->streams[i]->codec; AVCodec *codec; libavsmash_audio_set_codec_context( adhp, ctx ); codec = libavsmash_audio_find_decoder( adhp ); if( !codec ) { sprintf( error_string, "Failed to find %s decoder.\n", codec->name ); goto fail; } ctx->thread_count = threads; if( avcodec_open2( ctx, codec, NULL ) < 0 ) { strcpy( error_string, "Failed to avcodec_open2.\n" ); goto fail; } return initialize_decoder_configuration( adhp->root, adhp->track_id, &adhp->config ); fail:; lw_log_handler_t *lhp = libavsmash_audio_get_log_handler( adhp ); lw_log_show( lhp, LW_LOG_FATAL, "%s", error_string ); return -1; }
void lwlibav_flush_buffers ( lwlibav_decode_handler_t *dhp ) { /* Close and reopen the decoder even if the decoder implements avcodec_flush_buffers(). * It seems this brings about more stable composition when seeking. */ AVCodecContext *ctx = dhp->format->streams[ dhp->stream_index ]->codec; const AVCodec *codec = ctx->codec; avcodec_close( ctx ); ctx->codec_id = AV_CODEC_ID_NONE; /* AVCodecContext.codec_id is supposed to be set properly in avcodec_open2(). * This avoids avcodec_open2() failure by the difference of enum AVCodecID. * For instance, when stream is encoded as AC-3, * AVCodecContext.codec_id might have been set to AV_CODEC_ID_EAC3 * while AVCodec.id is set to AV_CODEC_ID_AC3. */ if( open_decoder( ctx, codec ) < 0 ) { dhp->error = 1; lw_log_show( &dhp->lh, LW_LOG_FATAL, "Failed to flush buffers.\n" "It is recommended you reopen the file." ); } dhp->exh.delay_count = 0; }
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 }
/* This function returns the number of the next sample. */ static uint32_t seek_video ( libavsmash_video_decode_handler_t *vdhp, AVFrame *picture, uint32_t composition_sample_number, uint32_t rap_number, int error_ignorance ) { /* Prepare to decode from random accessible sample. */ codec_configuration_t *config = &vdhp->config; if( config->update_pending ) /* Update the decoder configuration. */ update_configuration( vdhp->root, vdhp->track_id, config ); else libavsmash_flush_buffers( config ); if( config->error ) return 0; int got_picture; int output_ready = 0; uint64_t rap_cts = 0; uint32_t i; uint32_t decoder_delay = get_decoder_delay( config->ctx ); uint32_t goal = composition_sample_number + decoder_delay; for( i = rap_number; i < goal; i++ ) { if( config->index == config->queue.index ) config->delay_count = MIN( decoder_delay, i - rap_number ); int ret = decode_video_sample( vdhp, picture, &got_picture, i ); if( got_picture ) { output_ready = 1; if( decoder_delay > config->delay_count ) { /* Shorten the distance to the goal if we got a frame earlier than expected. */ uint32_t new_decoder_delay = config->delay_count; goal -= decoder_delay - new_decoder_delay; decoder_delay = new_decoder_delay; } } else if( output_ready ) { /* More input samples are required to output and the goal become more distant. */ ++decoder_delay; ++goal; } /* 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( i == vdhp->last_rap_number ) rap_cts = picture->pts; if( ret == -1 && (uint64_t)picture->pts >= rap_cts && !error_ignorance ) { lw_log_show( &config->lh, LW_LOG_WARNING, "Failed to decode a video frame." ); return 0; } else if( ret >= 1 ) /* No decoding occurs. */ break; } if( config->index == config->queue.index ) config->delay_count = MIN( decoder_delay, i - rap_number ); return i; }
int libavsmash_video_setup_timestamp_info ( libavsmash_video_decode_handler_t *vdhp, libavsmash_video_output_handler_t *vohp, int64_t *framerate_num, int64_t *framerate_den ) { int err = -1; uint64_t media_timescale = lsmash_get_media_timescale( vdhp->root, vdhp->track_id ); uint64_t media_duration = lsmash_get_media_duration_from_media_timeline( vdhp->root, vdhp->track_id ); if( media_duration == 0 ) media_duration = INT32_MAX; if( vdhp->sample_count == 1 ) { /* Calculate average framerate. */ reduce_fraction( &media_timescale, &media_duration ); *framerate_num = (int64_t)media_timescale; *framerate_den = (int64_t)media_duration; err = 0; goto setup_finish; } lw_log_handler_t *lhp = &vdhp->config.lh; lsmash_media_ts_list_t ts_list; if( lsmash_get_media_timestamps( vdhp->root, vdhp->track_id, &ts_list ) < 0 ) { lw_log_show( lhp, LW_LOG_ERROR, "Failed to get timestamps." ); goto setup_finish; } if( ts_list.sample_count != vdhp->sample_count ) { lw_log_show( lhp, LW_LOG_ERROR, "Failed to count number of video samples." ); goto setup_finish; } uint32_t composition_sample_delay; if( lsmash_get_max_sample_delay( &ts_list, &composition_sample_delay ) < 0 ) { lsmash_delete_media_timestamps( &ts_list ); lw_log_show( lhp, LW_LOG_ERROR, "Failed to get composition delay." ); goto setup_finish; } if( composition_sample_delay ) { /* Consider composition order for keyframe detection. * Note: sample number for L-SMASH is 1-origin. */ vdhp->order_converter = (order_converter_t *)lw_malloc_zero( (ts_list.sample_count + 1) * sizeof(order_converter_t) ); if( !vdhp->order_converter ) { lsmash_delete_media_timestamps( &ts_list ); lw_log_show( lhp, LW_LOG_ERROR, "Failed to allocate memory." ); goto setup_finish; } for( uint32_t i = 0; i < ts_list.sample_count; i++ ) ts_list.timestamp[i].dts = i + 1; lsmash_sort_timestamps_composition_order( &ts_list ); for( uint32_t i = 0; i < ts_list.sample_count; i++ ) vdhp->order_converter[i + 1].composition_to_decoding = (uint32_t)ts_list.timestamp[i].dts; } /* Calculate average framerate. */ uint64_t largest_cts = ts_list.timestamp[0].cts; uint64_t second_largest_cts = 0; uint64_t first_duration = ts_list.timestamp[1].cts - ts_list.timestamp[0].cts; uint64_t composition_timebase = first_duration; int strict_cfr = 1; for( uint32_t i = 1; i < ts_list.sample_count; i++ ) { uint64_t duration = ts_list.timestamp[i].cts - ts_list.timestamp[i - 1].cts; if( duration == 0 ) { lsmash_delete_media_timestamps( &ts_list ); lw_log_show( lhp, LW_LOG_WARNING, "Detected CTS duplication at frame %" PRIu32, i ); err = 0; goto setup_finish; } if( strict_cfr && duration != first_duration ) strict_cfr = 0; composition_timebase = get_gcd( composition_timebase, duration ); second_largest_cts = largest_cts; largest_cts = ts_list.timestamp[i].cts; } uint64_t reduce = reduce_fraction( &media_timescale, &composition_timebase ); uint64_t composition_duration = ((largest_cts - ts_list.timestamp[0].cts) + (largest_cts - second_largest_cts)) / reduce; lsmash_delete_media_timestamps( &ts_list ); double avg_frame_rate = (vdhp->sample_count * ((double)media_timescale / composition_duration)); if( strict_cfr || !lw_try_rational_framerate( avg_frame_rate, framerate_num, framerate_den, composition_timebase ) ) { uint64_t num = (uint64_t)(avg_frame_rate * composition_timebase + 0.5); uint64_t den = composition_timebase; if( num && den ) reduce_fraction( &num, &den ); else { num = 1; den = 1; } *framerate_num = (int64_t)num; *framerate_den = (int64_t)den; } err = 0; setup_finish:; if( vohp->vfr2cfr ) { /* Override average framerate by specified output constant framerate. */ *framerate_num = (int64_t)vohp->cfr_num; *framerate_den = (int64_t)vohp->cfr_den; vohp->frame_count = ((double)vohp->cfr_num / vohp->cfr_den) * ((double)media_duration / media_timescale) + 0.5; } else vohp->frame_count = libavsmash_video_get_sample_count( vdhp ); uint32_t min_cts_sample_number = get_decoding_sample_number( vdhp->order_converter, 1 ); vdhp->config.error = lsmash_get_cts_from_media_timeline( vdhp->root, vdhp->track_id, min_cts_sample_number, &vdhp->min_cts ); return err; }
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; }
uint64_t libavsmash_audio_get_pcm_samples ( libavsmash_audio_decode_handler_t *adhp, libavsmash_audio_output_handler_t *aohp, void *buf, int64_t start, int64_t wanted_length ) { codec_configuration_t *config = &adhp->config; if( config->error ) return 0; uint32_t frame_number; uint64_t output_length = 0; enum audio_output_flag output_flags; aohp->request_length = wanted_length; if( start > 0 && start == adhp->next_pcm_sample_number ) { frame_number = adhp->last_frame_number; output_flags = AUDIO_OUTPUT_NO_FLAGS; 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( adhp->packet.size <= 0 ) ++frame_number; aohp->output_sample_offset = 0; } else { /* Seek audio stream. */ if( flush_resampler_buffers( aohp->avr_ctx ) < 0 ) { config->error = 1; lw_log_show( &config->lh, LW_LOG_FATAL, "Failed to flush resampler buffers.\n" "It is recommended you reopen the file." ); return 0; } libavsmash_flush_buffers( config ); if( config->error ) return 0; adhp->next_pcm_sample_number = 0; adhp->last_frame_number = 0; 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; } start_frame_pos += aohp->skip_decoded_samples; frame_number = find_start_audio_frame( adhp, aohp->output_sample_rate, aohp->skip_decoded_samples, start_frame_pos, &aohp->output_sample_offset ); } do { AVPacket *pkt = &adhp->packet; if( frame_number > adhp->frame_count ) { if( config->delay_count ) { /* Null packet */ av_init_packet( pkt ); pkt->data = NULL; pkt->size = 0; -- config->delay_count; } else goto audio_out; } else if( pkt->size <= 0 ) /* Getting an audio packet must be after flushing all remaining samples in resampler's FIFO buffer. */ while( get_sample( adhp->root, adhp->track_id, frame_number, config, pkt ) == 2 ) if( config->update_pending ) /* Update the decoder configuration. */ update_configuration( adhp->root, adhp->track_id, config ); /* Decode and output from an audio packet. */ output_flags = AUDIO_OUTPUT_NO_FLAGS; output_length += output_pcm_samples_from_packet( aohp, config->ctx, pkt, adhp->frame_buffer, (uint8_t **)&buf, &output_flags ); if( output_flags & AUDIO_DECODER_DELAY ) ++ config->delay_count; if( output_flags & AUDIO_RECONFIG_FAILURE ) { config->error = 1; lw_log_show( &config->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; ++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 ); }