diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 78bcfd1ab16..4c774f386c5 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -40,20 +40,21 @@ #include "imbuf.h" #include "BKE_global.h" #ifdef WITH_AVI # include "AVI_avi.h" #endif #ifdef WITH_FFMPEG # include "ffmpeg_compat.h" +# include #endif static const char magic[] = "BlenMIdx"; static const char temp_ext[] = "_part"; static const int proxy_sizes[] = {IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75, IMB_PROXY_100}; static const float proxy_fac[] = {0.25, 0.50, 0.75, 1.00}; #ifdef WITH_FFMPEG static int tc_types[] = { @@ -471,20 +472,23 @@ struct proxy_output_ctx { struct transcode_output_ctx { AVCodecContext *codec_context; AVDictionary *codec_context_opts; struct SwsContext *sws_ctx; int orig_height; } transcode_output_ctx; struct proxy_transcode_ctx { AVCodecContext *input_codec_context; + AVFormatContext *input_format_ctx; + int input_stream_index; + struct transcode_output_ctx *output_context[IMB_PROXY_MAX_SLOT]; }; typedef struct FFmpegIndexBuilderContext { /* Common data for building process. */ int anim_type; struct anim *anim; int quality; int num_proxy_sizes; int num_indexers; @@ -492,34 +496,30 @@ typedef struct FFmpegIndexBuilderContext { IMB_Timecode_Type tcs_in_use; IMB_Proxy_Size proxy_sizes_in_use; /* Builder contexts. */ struct input_ctx *input_ctx; struct proxy_output_ctx *proxy_ctx[IMB_PROXY_MAX_SLOT]; struct proxy_transcode_ctx **transcode_context_array; anim_index_builder *indexer[IMB_TC_MAX_SLOT]; /* Common data for transcoding. */ - GHash *source_packets; GHash *transcoded_packets; /* Job coordination. */ ThreadMutex packet_access_mutex; - ThreadCondition reader_suspend_cond; - ThreadMutex reader_suspend_mutex; ThreadCondition **transcode_suspend_cond; ThreadMutex **transcode_suspend_mutex; ThreadCondition writer_suspend_cond; ThreadMutex writer_suspend_mutex; ThreadCondition builder_suspend_cond; ThreadMutex builder_suspend_mutex; - bool all_packets_read; int transcode_jobs_done; int last_gop_chunk_written; bool all_packets_written; short *stop; short *do_update; float *progress; /* TC index building */ uint64_t seek_pos; uint64_t last_seek_pos; @@ -559,21 +559,21 @@ static struct SwsContext *alloc_proxy_output_sws_context(AVCodecContext *input_c static AVFormatContext *alloc_proxy_output_output_format_context(struct anim *anim, int proxy_size) { char fname[FILE_MAX]; get_proxy_filename(anim, proxy_size, fname, true); BLI_make_existing_file(fname); AVFormatContext *format_context = avformat_alloc_context(); format_context->oformat = av_guess_format("avi", NULL, NULL); - BLI_strncpy(format_context->filename, fname, sizeof(format_context->filename)); + format_context->url = av_strdup(fname); /* Codec stuff must be initialized properly here. */ if (avio_open(&format_context->pb, fname, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Couldn't open outputfile! " "Proxy not built!\n"); av_free(format_context); return NULL; } @@ -587,78 +587,83 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( "alloc_proxy_output"); proxy_out_ctx->proxy_size = proxy_size; proxy_out_ctx->anim = anim; proxy_out_ctx->output_format = alloc_proxy_output_output_format_context(anim, proxy_size); proxy_out_ctx->stream = avformat_new_stream(proxy_out_ctx->output_format, NULL); proxy_out_ctx->stream->id = 0; - proxy_out_ctx->codec_context = proxy_out_ctx->stream->codec; - proxy_out_ctx->codec_context->thread_count = BLI_system_thread_count(); + proxy_out_ctx->codec_context = avcodec_alloc_context3(NULL); + proxy_out_ctx->codec_context->thread_count = 3; /* XXX */ proxy_out_ctx->codec_context->thread_type = FF_THREAD_SLICE; proxy_out_ctx->codec_context->codec_type = AVMEDIA_TYPE_VIDEO; proxy_out_ctx->codec_context->codec_id = AV_CODEC_ID_H264; proxy_out_ctx->codec_context->width = width; proxy_out_ctx->codec_context->height = height; proxy_out_ctx->codec_context->gop_size = 2; proxy_out_ctx->codec_context->max_b_frames = 0; /* Correct wrong default ffmpeg param which crash x264. */ proxy_out_ctx->codec_context->qmin = 10; proxy_out_ctx->codec_context->qmax = 51; proxy_out_ctx->output_format->oformat->video_codec = proxy_out_ctx->codec_context->codec_id; proxy_out_ctx->codec = avcodec_find_encoder(proxy_out_ctx->codec_context->codec_id); if (!proxy_out_ctx->codec) { fprintf(stderr, - "No ffmpeg MJPEG encoder available? " + "No ffmpeg encoder available? " "Proxy not built!\n"); + avcodec_free_context(&proxy_out_ctx->codec_context); av_free(proxy_out_ctx->output_format); return NULL; } if (proxy_out_ctx->codec->pix_fmts) { proxy_out_ctx->codec_context->pix_fmt = proxy_out_ctx->codec->pix_fmts[0]; } else { proxy_out_ctx->codec_context->pix_fmt = AV_PIX_FMT_YUVJ420P; } proxy_out_ctx->codec_context->sample_aspect_ratio = proxy_out_ctx->stream->sample_aspect_ratio = - inpuf_stream->codec->sample_aspect_ratio; + inpuf_stream->sample_aspect_ratio; proxy_out_ctx->codec_context->time_base.den = 25; proxy_out_ctx->codec_context->time_base.num = 1; proxy_out_ctx->stream->time_base = proxy_out_ctx->codec_context->time_base; if (proxy_out_ctx->output_format->flags & AVFMT_GLOBALHEADER) { proxy_out_ctx->codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; } + avcodec_parameters_from_context(proxy_out_ctx->stream->codecpar, proxy_out_ctx->codec_context); + /* there's no way to set JPEG quality in the same way as in AVI JPEG and image sequence, * but this seems to be giving expected quality result */ /*int ffmpeg_quality = (int)(1.0f + 30.0f * (1.0f - (float)quality / 100.0f) + 0.5f); av_opt_set_int(proxy_out_ctx->codec_context, "qmin", ffmpeg_quality, 0); av_opt_set_int(proxy_out_ctx->codec_context, "qmax", ffmpeg_quality, 0);*/ - fprintf(stderr, "Starting work on proxy: %s\n", proxy_out_ctx->output_format->filename); if (avformat_write_header(proxy_out_ctx->output_format, NULL) < 0) { fprintf(stderr, "Couldn't set output parameters? " "Proxy not built!\n"); + avcodec_free_context(&proxy_out_ctx->codec_context); av_free(proxy_out_ctx->output_format); return 0; } + fprintf(stderr, "Starting work on proxy: %s\n", proxy_out_ctx->output_format->url); + return proxy_out_ctx; } static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) { char fname[FILE_MAX]; char fname_tmp[FILE_MAX]; if (!ctx) { return; @@ -703,21 +708,21 @@ static AVFormatContext *index_ffmpeg_context_open_input_format(FFmpegIndexBuilde return format_context; } static int index_ffmpeg_context_find_video_stream(struct anim *anim, AVFormatContext *format_context) { int streamcount = anim->streamindex; int video_stream = -1; for (int i = 0; i < format_context->nb_streams; i++) { - if (format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (streamcount > 0) { streamcount--; continue; } video_stream = i; break; } } return video_stream; @@ -735,33 +740,35 @@ static struct input_ctx *index_ffmpeg_create_input_context(FFmpegIndexBuilderCon input_context->video_stream = index_ffmpeg_context_find_video_stream( context->anim, input_context->format_context); if (input_context->video_stream == -1) { avformat_close_input(&input_context->format_context); MEM_freeN(input_context); return NULL; } input_context->stream = input_context->format_context->streams[input_context->video_stream]; - input_context->codec_context = input_context->stream->codec; - input_context->codec = avcodec_find_decoder(input_context->codec_context->codec_id); + input_context->codec = avcodec_find_decoder(input_context->stream->codecpar->codec_id); if (input_context->codec == NULL) { avformat_close_input(&input_context->format_context); MEM_freeN(input_context); return NULL; } - input_context->codec_context->workaround_bugs = 1; + input_context->codec_context = avcodec_alloc_context3(NULL); + avcodec_parameters_to_context(input_context->codec_context, input_context->stream->codecpar); + input_context->codec_context->workaround_bugs = FF_BUG_AUTODETECT; if (avcodec_open2(input_context->codec_context, input_context->codec, NULL) < 0) { avformat_close_input(&input_context->format_context); + avcodec_free_context(&input_context->codec_context); MEM_freeN(input_context); return NULL; } return input_context; } static void index_ffmpeg_free_input_context(struct input_ctx *input_context) { avcodec_flush_buffers(input_context->codec_context); @@ -781,20 +788,21 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, context->anim = anim; context->quality = quality; context->tcs_in_use = tcs_in_use; context->proxy_sizes_in_use = proxy_sizes_in_use; context->num_proxy_sizes = IMB_PROXY_MAX_SLOT; context->num_indexers = IMB_TC_MAX_SLOT; /* Use only half of thread count, because transcoding contexts use a lot of RAM. Ideally this * would use CPU count directly, because hyper-threading shouldn't have great effect here. But * using only half of threads may be slightly slower in practice in some cases. */ context->num_transcode_threads = BLI_system_thread_count() / 2; + context->num_transcode_threads = 2; /* Setup input file context. */ context->input_ctx = index_ffmpeg_create_input_context(context); if (context->input_ctx == NULL) { MEM_freeN(context); return NULL; } /* Setup proxy file writing context. */ struct input_ctx *input_context = context->input_ctx; @@ -852,43 +860,43 @@ static void index_ffmpeg_free_context(FFmpegIndexBuilderContext *context, int st MEM_freeN(context); } static void index_ffmpeg_create_transcode_output_opts(struct transcode_output_ctx *output_ctx) { output_ctx->codec_context_opts = NULL; /* High quality preset value. */ av_dict_set_int(&output_ctx->codec_context_opts, "crf", 20, 0); /* Prefer smaller filesize. */ - av_dict_set(&output_ctx->codec_context_opts, "preset", "slower", 0); + av_dict_set(&output_ctx->codec_context_opts, "preset", "slow", 0); } static struct transcode_output_ctx *index_ffmpeg_create_transcode_output_context( AVStream *input_stream, AVStream *output_stream, AVCodecContext *proxy_codec_context, AVCodec *proxy_codec, int width, int height) { struct transcode_output_ctx *output_ctx = MEM_callocN(sizeof(struct transcode_output_ctx), "alloc_proxy_output"); index_ffmpeg_create_transcode_output_opts(output_ctx); output_ctx->codec_context = avcodec_alloc_context3(NULL); avcodec_copy_context(output_ctx->codec_context, proxy_codec_context); avcodec_open2(output_ctx->codec_context, proxy_codec, &output_ctx->codec_context_opts); - output_ctx->orig_height = av_get_cropped_height_from_codec(input_stream->codec); + output_ctx->orig_height = input_stream->codecpar->height; - if (input_stream->codec->width != width || input_stream->codec->height != height || - input_stream->codec->pix_fmt != output_ctx->codec_context->pix_fmt) { + if (input_stream->codecpar->width != width || input_stream->codecpar->height != height || + input_stream->codecpar->format != output_ctx->codec_context->pix_fmt) { output_ctx->sws_ctx = alloc_proxy_output_sws_context(input_stream->codec, output_ctx->codec_context); } return output_ctx; } static void index_ffmpeg_free_transcode_output_context(struct transcode_output_ctx *output_ctx) { @@ -911,20 +919,21 @@ static void index_ffmpeg_free_transcode_contexts(FFmpegIndexBuilderContext *cont /* Free mutex and thread condition */ BLI_condition_end(context->transcode_suspend_cond[i]); MEM_freeN(context->transcode_suspend_cond[i]); BLI_mutex_free(context->transcode_suspend_mutex[i]); for (int size = 0; size < context->num_proxy_sizes; size++) { if (transcode_context->output_context[size] == NULL) { continue; } + avcodec_flush_buffers(transcode_context->output_context[size]->codec_context); index_ffmpeg_free_transcode_output_context(transcode_context->output_context[size]); } MEM_freeN(transcode_context); } MEM_freeN(context->transcode_context_array); MEM_freeN(context->transcode_suspend_mutex); MEM_freeN(context->transcode_suspend_cond); } static void index_ffmpeg_create_transcode_context(FFmpegIndexBuilderContext *context, @@ -942,38 +951,37 @@ static void index_ffmpeg_create_transcode_context(FFmpegIndexBuilderContext *con context->transcode_suspend_mutex = MEM_callocN(sizeof(context->transcode_suspend_mutex) * context->num_transcode_threads, "transcode suspend mutex array"); /* Job coordination */ context->stop = stop; context->do_update = do_update; context->progress = progress; context->last_gop_chunk_written = 0; context->all_packets_written = false; - BLI_condition_init(&context->reader_suspend_cond); BLI_condition_init(&context->writer_suspend_cond); BLI_condition_init(&context->builder_suspend_cond); - BLI_mutex_init(&context->reader_suspend_mutex); BLI_mutex_init(&context->writer_suspend_mutex); BLI_mutex_init(&context->builder_suspend_mutex); BLI_mutex_init(&context->packet_access_mutex); - context->source_packets = BLI_ghash_new( - BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "source packets ghash"); context->transcoded_packets = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "transcoded packets ghash"); for (int i = 0; i < context->num_transcode_threads; i++) { context->transcode_context_array[i] = MEM_mallocN(sizeof(struct proxy_transcode_ctx), "transcode context"); struct proxy_transcode_ctx *transcode_context = context->transcode_context_array[i]; /* Copy input codec context. */ + transcode_context->input_format_ctx = index_ffmpeg_context_open_input_format(context, + context->anim); + transcode_context->input_stream_index = context->input_ctx->video_stream; transcode_context->input_codec_context = avcodec_alloc_context3(NULL); avcodec_copy_context(transcode_context->input_codec_context, context->input_ctx->codec_context); avcodec_open2(transcode_context->input_codec_context, context->input_ctx->codec, NULL); /* Setup mutex and therad condition. */ context->transcode_suspend_mutex[i] = BLI_mutex_alloc(); context->transcode_suspend_cond[i] = MEM_callocN(sizeof(ThreadCondition), "transcode suspend cond"); BLI_condition_init(context->transcode_suspend_cond[i]); @@ -995,195 +1003,132 @@ static void index_ffmpeg_create_transcode_context(FFmpegIndexBuilderContext *con proxy_context->codec, context->input_ctx->codec_context->width * proxy_fac[size], av_get_cropped_height_from_codec(context->input_ctx->codec_context) * proxy_fac[size]); } } } static void index_ffmpeg_free_transcode_context(FFmpegIndexBuilderContext *context) { - BLI_condition_end(&context->reader_suspend_cond); BLI_condition_end(&context->writer_suspend_cond); BLI_condition_end(&context->builder_suspend_cond); - BLI_mutex_end(&context->reader_suspend_mutex); BLI_mutex_end(&context->writer_suspend_mutex); BLI_mutex_end(&context->builder_suspend_mutex); BLI_mutex_end(&context->packet_access_mutex); - BLI_ghash_free(context->source_packets, NULL, MEM_freeN); BLI_ghash_free(context->transcoded_packets, NULL, MEM_freeN); index_ffmpeg_free_transcode_contexts(context); } typedef struct output_packet_wrap { AVPacket *output_packet[IMB_PROXY_MAX_SLOT]; int frame_index; int thread_number; int gop_chunk_index; int64_t pos; bool is_transcoded; /* Needed for TC building.*/ uint64_t pts_from_frame; -} output_packet_wrap; - -typedef struct source_packet_wrap { - AVPacket *input_packet; - int frame_index; - int thread_number; - int gop_chunk_index; - uint64_t pts_from_frame; -} source_packet_wrap; +} PacketWrap; typedef struct TranscodeJob { FFmpegIndexBuilderContext *context; int thread_number; - int output_packet_frame_index; - output_packet_wrap *output_packet; + PacketWrap *output_packet; /* Temporary storage for pocessed data. */ + AVPacket *source_packet; + int source_gop_index; + int source_gop_start_frame_index; + int source_frame_index; + + /* PacketWrap corresponding to frame that has been decoded. Needed to transfer pts from frame to + * output_packet. This must be done, otherwise */ + PacketWrap *decoded_packet; + AVPacket *flushing_packet; AVFrame *decoded_frame; AVFrame *scaled_frame[IMB_PROXY_MAX_SLOT]; } TranscodeJob; -static source_packet_wrap *create_source_packet_wrap(FFmpegIndexBuilderContext *context, - AVPacket *packet, - int gop_chunk_index, - uint64_t frame_index) -{ - source_packet_wrap *packet_wrap = MEM_callocN(sizeof(source_packet_wrap), ""); - packet_wrap->input_packet = packet; - packet_wrap->frame_index = frame_index; - packet_wrap->thread_number = gop_chunk_index % context->num_transcode_threads; - packet_wrap->gop_chunk_index = gop_chunk_index; - BLI_mutex_lock(&context->packet_access_mutex); - BLI_ghash_insert(context->source_packets, (void *)frame_index, packet_wrap); - BLI_mutex_unlock(&context->packet_access_mutex); - return packet_wrap; -} - -static output_packet_wrap *create_output_packet_wrap(FFmpegIndexBuilderContext *context, - AVPacket *packet, - int gop_chunk_index, - uint64_t frame_index) -{ - - output_packet_wrap *packet_wrap = MEM_callocN(sizeof(output_packet_wrap), ""); - BLI_mutex_lock(&context->packet_access_mutex); - BLI_ghash_insert(context->transcoded_packets, (void *)frame_index, packet_wrap); - packet_wrap->pos = packet->pos; - packet_wrap->frame_index = frame_index; - packet_wrap->thread_number = gop_chunk_index % context->num_transcode_threads; - packet_wrap->gop_chunk_index = gop_chunk_index; - BLI_mutex_unlock(&context->packet_access_mutex); - return packet_wrap; -} - -static source_packet_wrap *get_source_packet_wrap(FFmpegIndexBuilderContext *context, - uint64_t index) -{ - BLI_mutex_lock(&context->packet_access_mutex); - source_packet_wrap *packet_wrap = BLI_ghash_lookup(context->source_packets, (void *)index); - BLI_mutex_unlock(&context->packet_access_mutex); - return packet_wrap; -} - -static output_packet_wrap *get_output_packet_wrap(FFmpegIndexBuilderContext *context, - uint64_t index) -{ - BLI_mutex_lock(&context->packet_access_mutex); - output_packet_wrap *packet_wrap = BLI_ghash_lookup(context->transcoded_packets, (void *)index); - BLI_mutex_unlock(&context->packet_access_mutex); - return packet_wrap; -} - -static int index_ffmpeg_transcode_source_packet_count_get(FFmpegIndexBuilderContext *context) -{ - GHash *av_packet_base = context->source_packets; - BLI_mutex_lock(&context->packet_access_mutex); - int packets_read = BLI_ghash_len(av_packet_base); - BLI_mutex_unlock(&context->packet_access_mutex); - return packets_read; -} - -static void index_ffmpeg_read_resume(FFmpegIndexBuilderContext *context) -{ - ThreadCondition *suspend_cond = &context->reader_suspend_cond; - BLI_condition_notify_one(suspend_cond); -} +# define FFMPEG_DEBUG_READ 0 +# define FFMPEG_DEBUG_DECODE 0 +# define FFMPEG_DEBUG_ENCODE 1 +# define FFMPEG_DEBUG_WRITE 0 -static void index_ffmpeg_read_suspend(FFmpegIndexBuilderContext *context, int gop_chunk_index) +static struct proxy_transcode_ctx *proxy_transcode_ctx_get(TranscodeJob *transcode_job) { - /* All transcode threads must have at least 1 GOP chunk available. Greater lookahead will be - * probably better for files with small GOP size. */ - int gop_lookahead_margin = context->num_transcode_threads * 10; - ThreadMutex *suspend_mutex = &context->reader_suspend_mutex; - ThreadCondition *suspend_cond = &context->reader_suspend_cond; - BLI_mutex_lock(suspend_mutex); - while (gop_chunk_index > (context->last_gop_chunk_written + gop_lookahead_margin) && - !context->all_packets_written && !*context->stop) { - BLI_condition_wait(suspend_cond, suspend_mutex); - } - BLI_mutex_unlock(suspend_mutex); + return transcode_job->context->transcode_context_array[transcode_job->thread_number]; } static void index_ffmpeg_transcode_resume(FFmpegIndexBuilderContext *context) { for (int i = 0; i < context->num_transcode_threads; i++) { ThreadCondition *suspend_cond = context->transcode_suspend_cond[i]; BLI_condition_notify_one(suspend_cond); } } -static source_packet_wrap *index_ffmpeg_transcode_get_read_packet(TranscodeJob *transcode_job, - int frame_index) -{ - FFmpegIndexBuilderContext *context = transcode_job->context; - ThreadMutex *suspend_mutex = context->transcode_suspend_mutex[transcode_job->thread_number]; - ThreadCondition *suspend_cond = context->transcode_suspend_cond[transcode_job->thread_number]; - BLI_mutex_lock(suspend_mutex); - while (index_ffmpeg_transcode_source_packet_count_get(context) <= frame_index && - !context->all_packets_read && !*context->stop) { - BLI_condition_wait(suspend_cond, suspend_mutex); - } - BLI_mutex_unlock(suspend_mutex); - - return get_source_packet_wrap(context, frame_index); -} - static void index_ffmpeg_write_resume(FFmpegIndexBuilderContext *context) { ThreadCondition *suspend_cond = &context->writer_suspend_cond; BLI_condition_notify_one(suspend_cond); } static void index_ffmpeg_resume_all(FFmpegIndexBuilderContext *context) { index_ffmpeg_write_resume(context); index_ffmpeg_transcode_resume(context); - index_ffmpeg_read_resume(context); BLI_condition_notify_one(&context->builder_suspend_cond); } -static output_packet_wrap *get_decoded_output_packet_wrap(FFmpegIndexBuilderContext *context, - uint64_t index) +static PacketWrap *create_output_packet_wrap(FFmpegIndexBuilderContext *context, + AVPacket *packet, + int gop_chunk_index, + uint64_t frame_index) +{ + + PacketWrap *packet_wrap = MEM_callocN(sizeof(PacketWrap), ""); + BLI_mutex_lock(&context->packet_access_mutex); + BLI_ghash_insert(context->transcoded_packets, (void *)frame_index, packet_wrap); + packet_wrap->pos = packet->pos; + packet_wrap->frame_index = frame_index; + packet_wrap->thread_number = gop_chunk_index % context->num_transcode_threads; + packet_wrap->gop_chunk_index = gop_chunk_index; + packet_wrap->is_transcoded = false; + memset(packet_wrap->output_packet, 0, sizeof(packet_wrap->output_packet)); + BLI_mutex_unlock(&context->packet_access_mutex); + return packet_wrap; +} + +static PacketWrap *get_output_packet_wrap(FFmpegIndexBuilderContext *context, uint64_t index) +{ + BLI_mutex_lock(&context->packet_access_mutex); + PacketWrap *packet_wrap = BLI_ghash_lookup(context->transcoded_packets, (void *)index); + BLI_mutex_unlock(&context->packet_access_mutex); + return packet_wrap; +} + +static PacketWrap *get_decoded_output_packet_wrap(FFmpegIndexBuilderContext *context, + uint64_t index) { ThreadMutex *suspend_mutex = &context->writer_suspend_mutex; ThreadCondition *suspend_cond = &context->writer_suspend_cond; BLI_mutex_lock(suspend_mutex); /* Try to get packet. */ - output_packet_wrap *packet_wrap = get_output_packet_wrap(context, index); - while (!context->all_packets_read && packet_wrap == NULL && !*context->stop) { + PacketWrap *packet_wrap = get_output_packet_wrap(context, index); + while (context->transcode_jobs_done < context->num_transcode_threads && packet_wrap == NULL && + !*context->stop) { BLI_condition_wait(suspend_cond, suspend_mutex); packet_wrap = get_output_packet_wrap(context, index); } if (packet_wrap == NULL) { BLI_mutex_unlock(suspend_mutex); return NULL; } /* Wait until packet is transcoded. */ @@ -1191,23 +1136,22 @@ static output_packet_wrap *get_decoded_output_packet_wrap(FFmpegIndexBuilderCont context->transcode_jobs_done < context->num_transcode_threads && !*context->stop) { BLI_condition_wait(suspend_cond, suspend_mutex); } BLI_mutex_unlock(suspend_mutex); return packet_wrap; } static void build_timecode_index(FFmpegIndexBuilderContext *context, int frame_index) { - source_packet_wrap *source_wrap = get_source_packet_wrap(context, frame_index); - output_packet_wrap *output_wrap = get_output_packet_wrap(context, frame_index); - AVPacket *source_packet = source_wrap->input_packet; + PacketWrap *output_wrap = get_output_packet_wrap(context, frame_index); + AVPacket *source_packet; //= source_wrap->input_packet; if (source_packet->flags & AV_PKT_FLAG_KEY) { context->last_seek_pos = context->seek_pos; context->last_seek_pos_dts = context->seek_pos_dts; context->seek_pos = source_packet->pos; context->seek_pos_dts = source_packet->dts; context->seek_pos_pts = source_packet->pts; } uint64_t s_pos = context->seek_pos; @@ -1245,81 +1189,60 @@ static void build_timecode_index(FFmpegIndexBuilderContext *context, int frame_i IMB_index_builder_proc_frame(context->indexer[i], source_packet->data, source_packet->size, tc_frameno, s_pos, s_dts, pts); } } context->frameno_gapless++; - av_packet_free(&source_wrap->input_packet); } -static void *index_ffmpeg_read_packets(void *job_data) +static void copy_pts_from_frame_to_packet(TranscodeJob *transcode_job) { - FFmpegIndexBuilderContext *context = job_data; - struct input_ctx *input_ctx = context->input_ctx; - - /* Needed for TC building. */ - context->frame_rate = av_q2d( - av_guess_frame_rate(context->input_ctx->format_context, context->input_ctx->stream, NULL)); - context->pts_time_base = av_q2d(context->input_ctx->stream->time_base); - - int gop_chunk_index = -1; /* First I-frame will increase this to 0. */ - int ret = 0; - int frame_index = 0; - - while (ret >= 0) { - index_ffmpeg_read_suspend(context, gop_chunk_index); - AVPacket *av_packet = av_packet_alloc(); - ret = av_read_frame(input_ctx->format_context, av_packet); - - if (*context->stop || ret < 0) { - break; - } - - /* Only process packets from chosen video stream. */ - if (av_packet->stream_index != input_ctx->video_stream) { - continue; - } - - /* Packets are separated into segments separated by I-frames, because decoding must start on - * I-frame. */ - if (av_packet->flags & AV_PKT_FLAG_KEY) { - gop_chunk_index++; - } - - create_source_packet_wrap(context, av_packet, gop_chunk_index, frame_index); - create_output_packet_wrap(context, av_packet, gop_chunk_index, frame_index); - - frame_index++; - index_ffmpeg_transcode_resume(context); - } - - context->all_packets_read = true; - index_ffmpeg_resume_all(context); - return 0; + struct proxy_transcode_ctx *transcode_ctx = proxy_transcode_ctx_get(transcode_job); + PacketWrap *output_packet = transcode_job->decoded_packet; + output_packet->pts_from_frame = av_get_pts_from_frame(transcode_ctx->input_format_ctx, + transcode_job->decoded_frame); } -static struct proxy_transcode_ctx *proxy_transcode_ctx_get(TranscodeJob *transcode_job) +static int index_ffmpeg_decode_send_packet(TranscodeJob *transcode_job, AVPacket *packet) { - return transcode_job->context->transcode_context_array[transcode_job->thread_number]; + struct proxy_transcode_ctx *transcode_ctx = proxy_transcode_ctx_get(transcode_job); + int ret = avcodec_send_packet(transcode_ctx->input_codec_context, packet); + if (ret < 0) { + printf("Thread %d encoder didn't accept frame. %s\n", + transcode_job->thread_number, + av_err2str(ret)); + } + return ret; } -static bool index_ffmpeg_decode_packet(TranscodeJob *transcode_job, AVPacket *packet) +static bool index_ffmpeg_decode_receive_frame(TranscodeJob *transcode_job) { struct proxy_transcode_ctx *transcode_ctx = proxy_transcode_ctx_get(transcode_job); - int frame_finished = 0; - avcodec_decode_video2( - transcode_ctx->input_codec_context, transcode_job->decoded_frame, &frame_finished, packet); - return frame_finished != 0; + int ret = avcodec_receive_frame(transcode_ctx->input_codec_context, + transcode_job->decoded_frame); + + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + return false; + } + if (ret < 0) { + printf("Can't read decoded frame. %s\n", av_err2str(ret)); + } + + copy_pts_from_frame_to_packet(transcode_job); + transcode_job->decoded_packet = get_output_packet_wrap( + transcode_job->context, transcode_job->decoded_packet->frame_index + 1); + + return true; } static void index_ffmpeg_scale_frame(TranscodeJob *transcode_job) { FFmpegIndexBuilderContext *context = transcode_job->context; struct proxy_transcode_ctx *transcode_ctx = proxy_transcode_ctx_get(transcode_job); for (int size = 0; size < context->num_proxy_sizes; size++) { struct transcode_output_ctx *output_ctx = transcode_ctx->output_context[size]; if (output_ctx == NULL) { @@ -1341,274 +1264,335 @@ static void index_ffmpeg_scale_frame(TranscodeJob *transcode_job) scaled_frame[size]->data, scaled_frame[size]->linesize); } else { scaled_frame[size] = decoded_frame; } } } } -static void copy_pts_from_decoded_frame(FFmpegIndexBuilderContext *context, - output_packet_wrap *output_packet) -{ - source_packet_wrap *source_packet = get_source_packet_wrap(context, output_packet->frame_index); - output_packet->pts_from_frame = source_packet->pts_from_frame; -} - static void index_ffmpeg_transcode_reset_codec(TranscodeJob *transcode_job) { FFmpegIndexBuilderContext *context = transcode_job->context; struct proxy_transcode_ctx *transcode_ctx = proxy_transcode_ctx_get(transcode_job); avcodec_flush_buffers(transcode_ctx->input_codec_context); avcodec_close(transcode_ctx->input_codec_context); + avcodec_free_context(&transcode_ctx->input_codec_context); + + transcode_ctx->input_codec_context = avcodec_alloc_context3(NULL); + avcodec_copy_context(transcode_ctx->input_codec_context, context->input_ctx->codec_context); avcodec_open2(transcode_ctx->input_codec_context, context->input_ctx->codec, NULL); for (int i = 0; i < 4; i++) { if (transcode_ctx->output_context[i]) { avcodec_flush_buffers(transcode_ctx->output_context[i]->codec_context); avcodec_close(transcode_ctx->output_context[i]->codec_context); + avcodec_free_context(&transcode_ctx->output_context[i]->codec_context); + + transcode_ctx->output_context[i]->codec_context = avcodec_alloc_context3(NULL); + avcodec_copy_context(transcode_ctx->output_context[i]->codec_context, + context->proxy_ctx[i]->codec_context); index_ffmpeg_create_transcode_output_opts(transcode_ctx->output_context[i]); avcodec_open2(transcode_ctx->output_context[i]->codec_context, context->proxy_ctx[i]->codec, &transcode_ctx->output_context[i]->codec_context_opts); } } } -static int index_ffmpeg_transcode_jump_to_next_gop(TranscodeJob *transcode_job, - int current_frame_index) +static void index_ffmpeg_encode_send_frame(TranscodeJob *transcode_job, bool do_flush) { - int frame_index = current_frame_index; + struct proxy_transcode_ctx *transcode_ctx = proxy_transcode_ctx_get(transcode_job); + for (int size = 0; size < IMB_PROXY_MAX_SLOT; size++) { + struct transcode_output_ctx *output_ctx = transcode_ctx->output_context[size]; - int gop_jump_length = 0; - source_packet_wrap *source_packet; - while (source_packet = index_ffmpeg_transcode_get_read_packet(transcode_job, frame_index)) { - if (source_packet == NULL) { - return gop_jump_length; + if (output_ctx == NULL) { + continue; } - if ((source_packet->thread_number) == transcode_job->thread_number) { - break; + AVFrame *frame; + if (do_flush) { + frame = NULL; + } + else { + frame = transcode_job->scaled_frame[size]; } - gop_jump_length++; - frame_index++; - } - - return gop_jump_length; -} - -static output_packet_wrap *index_ffmpeg_encode_get_output_packet(TranscodeJob *transcode_job) -{ - FFmpegIndexBuilderContext *context = transcode_job->context; - - /* Init output_packet if it is not already. */ - if (transcode_job->output_packet == NULL) { - transcode_job->output_packet = get_output_packet_wrap(context, 0); - } - - transcode_job->output_packet = get_output_packet_wrap(context, - transcode_job->output_packet_frame_index); - - if (transcode_job->output_packet == NULL) { - return NULL; - } - - /* Skip to next GOP if necessary. */ - if ((transcode_job->output_packet->thread_number) != transcode_job->thread_number) { - transcode_job->output_packet_frame_index += index_ffmpeg_transcode_jump_to_next_gop( - transcode_job, transcode_job->output_packet_frame_index); - transcode_job->output_packet = get_output_packet_wrap( - context, transcode_job->output_packet_frame_index); + avcodec_send_frame(output_ctx->codec_context, frame); } - - return transcode_job->output_packet; } -static bool index_ffmpeg_encode_frame(TranscodeJob *transcode_job, bool do_flush) +static bool index_ffmpeg_encode_receive_packet(TranscodeJob *transcode_job) { FFmpegIndexBuilderContext *context = transcode_job->context; struct proxy_transcode_ctx *transcode_ctx = proxy_transcode_ctx_get(transcode_job); - output_packet_wrap *packet_wrap = index_ffmpeg_encode_get_output_packet(transcode_job); - if (packet_wrap == NULL) { + PacketWrap *output_packet = transcode_job->output_packet; + + if (output_packet == NULL) { + printf("No output packet, shouldn't happen\n"); return false; } - int got_output; + bool got_output = false; + for (int size = 0; size < IMB_PROXY_MAX_SLOT; size++) { struct transcode_output_ctx *output_ctx = transcode_ctx->output_context[size]; if (output_ctx == NULL) { continue; } AVPacket *packet = av_packet_alloc(); - AVFrame *frame; - if (do_flush) { - frame = NULL; - } - else { - frame = transcode_job->scaled_frame[size]; + int ret = avcodec_receive_packet(output_ctx->codec_context, packet); + + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + continue; } - int ret = avcodec_encode_video2(output_ctx->codec_context, packet, frame, &got_output); if (ret < 0) { fprintf(stderr, "Thread %0d: Error encoding proxy frame %d for '%s. %s'\n", transcode_job->thread_number, - packet_wrap->frame_index, + output_packet->frame_index, context->proxy_ctx[size]->output_format->filename, av_err2str(ret)); *context->stop = 1; return false; } - if (got_output) { - if (packet->pts != AV_NOPTS_VALUE) { - packet->pts = av_rescale_q(packet->pts, - output_ctx->codec_context->time_base, - context->proxy_ctx[size]->stream->time_base); - } - if (packet->dts != AV_NOPTS_VALUE) { - packet->dts = av_rescale_q(packet->dts, - output_ctx->codec_context->time_base, - context->proxy_ctx[size]->stream->time_base); - } - packet->stream_index = context->proxy_ctx[size]->stream->index; + got_output = true; + + if (packet->pts != AV_NOPTS_VALUE) { + packet->pts = av_rescale_q(packet->pts, + output_ctx->codec_context->time_base, + context->proxy_ctx[size]->stream->time_base); + } + if (packet->dts != AV_NOPTS_VALUE) { + packet->dts = av_rescale_q(packet->dts, + output_ctx->codec_context->time_base, + context->proxy_ctx[size]->stream->time_base); + } + packet->stream_index = context->proxy_ctx[size]->stream->index; - packet_wrap->output_packet[size] = packet; - copy_pts_from_decoded_frame(context, packet_wrap); - transcode_job->output_packet_frame_index++; + output_packet->output_packet[size] = packet; + + if (FFMPEG_DEBUG_ENCODE) { + printf( + "thread %0d encoded %0000d\n", transcode_job->thread_number, output_packet->frame_index); } } - packet_wrap->is_transcoded = got_output > 0; - return got_output > 0; + if (got_output) { + transcode_job->output_packet = get_output_packet_wrap( + transcode_job->context, transcode_job->output_packet->frame_index + 1); + output_packet->is_transcoded = true; + } + + return got_output; } static void index_ffmpeg_transcode_flush(TranscodeJob *transcode_job) { if (transcode_job->output_packet == NULL) { return; } - FFmpegIndexBuilderContext *context = transcode_job->context; - /* Flush decoder. */ - while (index_ffmpeg_decode_packet(transcode_job, transcode_job->flushing_packet)) { + index_ffmpeg_decode_send_packet(transcode_job, transcode_job->flushing_packet); + while (index_ffmpeg_decode_receive_frame(transcode_job)) { index_ffmpeg_scale_frame(transcode_job); - index_ffmpeg_encode_frame(transcode_job, false); - index_ffmpeg_write_resume(context); + index_ffmpeg_encode_send_frame(transcode_job, false); + while (index_ffmpeg_encode_receive_packet(transcode_job)) { + index_ffmpeg_write_resume(transcode_job->context); + } } /* Flush encoder. */ - while (index_ffmpeg_encode_frame(transcode_job, true)) { - /* Do nothing. */ + index_ffmpeg_encode_send_frame(transcode_job, true); + while (index_ffmpeg_encode_receive_packet(transcode_job)) { + index_ffmpeg_write_resume(transcode_job->context); } index_ffmpeg_transcode_reset_codec(transcode_job); } -static void *index_ffmpeg_transcode_packets(void *job_data) +static bool index_ffmpeg_transcode_read_packet(TranscodeJob *transcode_job) { - TranscodeJob *transcode_job = job_data; - FFmpegIndexBuilderContext *context = transcode_job->context; + struct proxy_transcode_ctx *transcode_ctx = proxy_transcode_ctx_get(transcode_job); + bool success = true; - int frame_index = 0; - source_packet_wrap *source_packet; + while (success) { + success = av_read_frame(transcode_ctx->input_format_ctx, transcode_job->source_packet) >= 0; - while (source_packet = index_ffmpeg_transcode_get_read_packet(transcode_job, frame_index)) { - if (*context->stop || source_packet == NULL) { + /* Only process packets from video stream. */ + if (transcode_job->source_packet->stream_index == transcode_ctx->input_stream_index) { break; } + } + + transcode_job->source_frame_index++; + + /* Packets are separated into segments separated by I-frames, because decoding must start on + * I-frame. */ + if (transcode_job->source_packet->flags & AV_PKT_FLAG_KEY) { + transcode_job->source_gop_index++; + transcode_job->source_gop_start_frame_index = transcode_job->source_frame_index; + } + + return success; +} + +static bool index_ffmpeg_transcode_seek_next_gop(TranscodeJob *transcode_job) +{ + FFmpegIndexBuilderContext *context = transcode_job->context; + bool success = true; + int thread = -1; + + while (success && thread != transcode_job->thread_number) { + success = index_ffmpeg_transcode_read_packet(transcode_job); + thread = transcode_job->source_gop_index % context->num_transcode_threads; + } + + return success; +} + +static bool index_ffmpeg_transcode_get_packet(TranscodeJob *transcode_job) +{ + FFmpegIndexBuilderContext *context = transcode_job->context; + bool success = index_ffmpeg_transcode_read_packet(transcode_job); + bool do_flush = false; + int thread = transcode_job->source_gop_index % context->num_transcode_threads; + + if (thread != transcode_job->thread_number) { + if (transcode_job->source_frame_index > 0) { + do_flush = true; + } + success = index_ffmpeg_transcode_seek_next_gop(transcode_job); + } + + if (success) { + av_packet_ref(transcode_job->source_packet, transcode_job->source_packet); + if (FFMPEG_DEBUG_READ) { + printf("thread %d, read packet %d\n", + transcode_job->thread_number, + transcode_job->source_frame_index); + } - /* Each thread works on own segment of packets. Jump GOP's to next one. Codecs must be flushed. - */ - if ((source_packet->thread_number) != transcode_job->thread_number) { + PacketWrap *output_packet = create_output_packet_wrap(context, + transcode_job->source_packet, + transcode_job->source_gop_index, + transcode_job->source_frame_index); + + if (do_flush) { + /* Each thread works on own segment of packets. Jump GOP's to next one. + * Codecs must be flushed. */ /* MJPEG doesn't need flushing so it can be skipped in order to get better performance. * check flag context->input_ctx->codec->capabilities & AV_CODEC_CAP_DELAY */ index_ffmpeg_transcode_flush(transcode_job); - frame_index += index_ffmpeg_transcode_jump_to_next_gop(transcode_job, frame_index); - continue; + transcode_job->output_packet = output_packet; + transcode_job->decoded_packet = output_packet; + } + + if (transcode_job->output_packet == NULL) { + transcode_job->output_packet = output_packet; + transcode_job->decoded_packet = output_packet; + } + } + + return success; +} + +static void *index_ffmpeg_transcode_packets(void *job_data) +{ + TranscodeJob *transcode_job = job_data; + FFmpegIndexBuilderContext *context = transcode_job->context; + + while (index_ffmpeg_transcode_get_packet(transcode_job)) { + if (*context->stop || transcode_job->source_packet == NULL) { + break; } /* Transcode. */ - if (index_ffmpeg_decode_packet(transcode_job, source_packet->input_packet)) { + index_ffmpeg_decode_send_packet(transcode_job, transcode_job->source_packet); + while (index_ffmpeg_decode_receive_frame(transcode_job)) { index_ffmpeg_scale_frame(transcode_job); - index_ffmpeg_encode_frame(transcode_job, false); - index_ffmpeg_write_resume(context); + index_ffmpeg_encode_send_frame(transcode_job, false); + while (index_ffmpeg_encode_receive_packet(transcode_job)) { + index_ffmpeg_write_resume(context); + } } - frame_index++; } /* Finish process. */ index_ffmpeg_transcode_flush(transcode_job); context->transcode_jobs_done++; index_ffmpeg_resume_all(context); return 0; } static void *index_ffmpeg_write_frames(void *job_data) { FFmpegIndexBuilderContext *context = job_data; int frame_index = 0; - output_packet_wrap *output_packet; + PacketWrap *output_packet; while (output_packet = get_decoded_output_packet_wrap(context, frame_index)) { if (*context->stop) { break; } for (int size = 0; size < context->num_proxy_sizes; size++) { if (output_packet->output_packet[size]) { AVPacket *packet = output_packet->output_packet[size]; if (av_interleaved_write_frame(context->proxy_ctx[size]->output_format, packet) != 0) { fprintf(stderr, "Error writing proxy frame %d " "into '%s'\n", context->proxy_ctx[size]->cfra - 1, context->proxy_ctx[size]->output_format->filename); return 0; } + else if (FFMPEG_DEBUG_WRITE) { + printf("Writing frame %0000d\n", output_packet->frame_index); + } av_packet_free(&packet); } } uint64_t stream_size = avio_size(context->input_ctx->format_context->pb); float next_progress = (float)((int)floor( ((double)output_packet->pos) * 100 / ((double)stream_size) + 0.5)) / 100; if (*context->progress != next_progress) { *context->progress = next_progress; *context->do_update = true; } context->last_gop_chunk_written = output_packet->gop_chunk_index; - build_timecode_index(context, frame_index); - + // build_timecode_index(context, frame_index); frame_index++; - index_ffmpeg_read_resume(context); } context->all_packets_written = true; index_ffmpeg_resume_all(context); return 0; } static void index_ffmpeg_transcode_init_temporary_data(TranscodeJob *transcode_job) { + transcode_job->source_packet = av_packet_alloc(); transcode_job->flushing_packet = av_packet_alloc(); transcode_job->decoded_frame = av_frame_alloc(); for (int size = 0; size < IMB_PROXY_MAX_SLOT; size++) { struct transcode_output_ctx *output_ctx = proxy_transcode_ctx_get(transcode_job)->output_context[size]; if (output_ctx == NULL || output_ctx->sws_ctx == NULL) { continue; } @@ -1619,119 +1603,111 @@ static void index_ffmpeg_transcode_init_temporary_data(TranscodeJob *transcode_j output_ctx->codec_context->height), "alloc proxy output frame"), output_ctx->codec_context->pix_fmt, round_up(output_ctx->codec_context->width, 16), output_ctx->codec_context->height); } } static void index_ffmpeg_transcode_free_temporary_data(TranscodeJob *transcode_job) { + av_packet_free(&transcode_job->source_packet); av_packet_free(&transcode_job->flushing_packet); av_frame_free(&transcode_job->decoded_frame); /* Is freed by encoder? */ for (int size = 0; size < IMB_PROXY_MAX_SLOT; size++) { struct transcode_output_ctx *output_ctx = proxy_transcode_ctx_get(transcode_job)->output_context[size]; if (output_ctx == NULL || output_ctx->sws_ctx == NULL) { continue; } if (transcode_job->scaled_frame[size] && transcode_job->scaled_frame[size]->data[0]) { MEM_freeN(transcode_job->scaled_frame[size]->data[0]); } if (transcode_job->scaled_frame[size]) { av_frame_free(&transcode_job->scaled_frame[size]); } } } static TranscodeJob **index_rebuild_ffmpeg_init_jobs(FFmpegIndexBuilderContext *context, - ListBase *reader_thread, ListBase *transcoder_thread, ListBase *encoder_thread) { - BLI_threadpool_init(reader_thread, index_ffmpeg_read_packets, 1); BLI_threadpool_init( transcoder_thread, index_ffmpeg_transcode_packets, context->num_transcode_threads); BLI_threadpool_init(encoder_thread, index_ffmpeg_write_frames, 1); TranscodeJob **transcode_job_array = MEM_callocN( sizeof(TranscodeJob *) * context->num_transcode_threads, "transcode job array"); for (int i = 0; i < context->num_transcode_threads; i++) { transcode_job_array[i] = MEM_callocN(sizeof(TranscodeJob), "transcode job"); transcode_job_array[i]->context = context; transcode_job_array[i]->thread_number = i; transcode_job_array[i]->output_packet = NULL; - transcode_job_array[i]->output_packet_frame_index = 0; + /* First frame is always keyframe which will increment this to 0. */ + transcode_job_array[i]->source_gop_index = -1; + transcode_job_array[i]->source_frame_index = -1; index_ffmpeg_transcode_init_temporary_data(transcode_job_array[i]); } return transcode_job_array; } static void index_rebuild_ffmpeg_free_jobs(FFmpegIndexBuilderContext *context, - ListBase *reader_thread, ListBase *transcoder_thread, ListBase *writer_thread, TranscodeJob **transcode_job_array) { for (int i = 0; i < context->num_transcode_threads; i++) { index_ffmpeg_transcode_free_temporary_data(transcode_job_array[i]); MEM_freeN(transcode_job_array[i]); } MEM_freeN(transcode_job_array); - BLI_threadpool_remove(reader_thread, context); - BLI_threadpool_end(reader_thread); BLI_threadpool_remove(transcoder_thread, context); BLI_threadpool_end(transcoder_thread); BLI_threadpool_remove(writer_thread, context); BLI_threadpool_end(writer_thread); } static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, short *stop, short *do_update, float *progress) { index_ffmpeg_create_transcode_context(context, stop, do_update, progress); - ListBase reader_thread; ListBase transcoder_thread; ListBase writer_thread; TranscodeJob **transcode_job_array = index_rebuild_ffmpeg_init_jobs( - context, &reader_thread, &transcoder_thread, &writer_thread); - - /* Read packets. */ - BLI_threadpool_insert(&reader_thread, context); + context, &transcoder_thread, &writer_thread); /* Transcode. Job runs in multiple threads working on chunks separated by I-frames. */ for (int i = 0; i < context->num_transcode_threads; i++) { BLI_threadpool_insert(&transcoder_thread, transcode_job_array[i]); } /* Write frames. */ BLI_threadpool_insert(&writer_thread, context); /* Wait until all jobs are done. */ BLI_mutex_lock(&context->builder_suspend_mutex); - while (!context->all_packets_read || - context->transcode_jobs_done < context->num_transcode_threads || + while (context->transcode_jobs_done < context->num_transcode_threads || !context->all_packets_written) { BLI_condition_wait(&context->builder_suspend_cond, &context->builder_suspend_mutex); } BLI_mutex_unlock(&context->builder_suspend_mutex); /* Free jobs. */ - index_rebuild_ffmpeg_free_jobs( - context, &reader_thread, &transcoder_thread, &writer_thread, transcode_job_array); + index_rebuild_ffmpeg_free_jobs(context, &transcoder_thread, &writer_thread, transcode_job_array); index_ffmpeg_free_transcode_context(context); return 1; } #endif /* ---------------------------------------------------------------------- * - internal AVI (fallback) rebuilder * ---------------------------------------------------------------------- */