static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, int stop) { struct anim *anim = context->anim; char fname[FILE_MAX]; char fname_tmp[FILE_MAX]; int i; for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { if (context->proxy_sizes_in_use & proxy_sizes[i]) { AVI_close_compress(context->proxy_ctx[i]); MEM_freeN(context->proxy_ctx[i]); get_proxy_filename(anim, proxy_sizes[i], fname_tmp, TRUE); get_proxy_filename(anim, proxy_sizes[i], fname, FALSE); if (stop) { unlink(fname_tmp); } else { unlink(fname); rename(fname_tmp, fname); } } } }
static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) { int i; char fname[FILE_MAX]; char fname_tmp[FILE_MAX]; if (!ctx) { return; } if (!rollback) { while (add_to_proxy_output_ffmpeg(ctx, NULL)) {} } avcodec_flush_buffers(ctx->c); av_write_trailer(ctx->of); avcodec_close(ctx->c); for (i = 0; i < ctx->of->nb_streams; i++) { if (&ctx->of->streams[i]) { av_freep(&ctx->of->streams[i]); } } if (ctx->of->oformat) { if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) { avio_close(ctx->of->pb); } } av_free(ctx->of); MEM_freeN(ctx->video_buffer); if (ctx->sws_ctx) { sws_freeContext(ctx->sws_ctx); MEM_freeN(ctx->frame->data[0]); av_free(ctx->frame); } get_proxy_filename(ctx->anim, ctx->proxy_size, fname_tmp, TRUE); if (rollback) { unlink(fname_tmp); } else { get_proxy_filename(ctx->anim, ctx->proxy_size, fname, FALSE); unlink(fname); BLI_rename(fname_tmp, fname); } MEM_freeN(ctx); }
IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim) { const int num_proxy_sizes = IMB_PROXY_MAX_SLOT; IMB_Proxy_Size existing = 0; int i; for (i = 0; i < num_proxy_sizes; ++i) { IMB_Proxy_Size proxy_size = proxy_sizes[i]; char filename[FILE_MAX]; get_proxy_filename(anim, proxy_size, filename, false); if (BLI_exists(filename)) { existing |= proxy_size; } } return existing; }
struct anim *IMB_anim_open_proxy( struct anim *anim, IMB_Proxy_Size preview_size) { char fname[FILE_MAX]; int i = IMB_proxy_size_to_array_index(preview_size); if (anim->proxy_anim[i]) { return anim->proxy_anim[i]; } if (anim->proxies_tried & preview_size) { return NULL; } get_proxy_filename(anim, preview_size, fname, FALSE); anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0); anim->proxies_tried |= preview_size; return anim->proxy_anim[i]; }
struct anim *IMB_anim_open_proxy( struct anim *anim, IMB_Proxy_Size preview_size) { char fname[FILE_MAX]; int i = IMB_proxy_size_to_array_index(preview_size); if (anim->proxy_anim[i]) { return anim->proxy_anim[i]; } if (anim->proxies_tried & preview_size) { return NULL; } get_proxy_filename(anim, preview_size, fname, false); /* proxies are generated in the same color space as animation itself */ anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, anim->colorspace); anim->proxies_tried |= preview_size; return anim->proxy_anim[i]; }
static IndexBuildContext *index_fallback_create_context(struct anim *anim, IMB_Timecode_Type UNUSED(tcs_in_use), IMB_Proxy_Size proxy_sizes_in_use, int quality) { FallbackIndexBuilderContext *context; int i; /* since timecode indices only work with ffmpeg right now, * don't know a sensible fallback here... * * so no proxies, no game to play... */ if (proxy_sizes_in_use == IMB_PROXY_NONE) { return NULL; } context = MEM_callocN(sizeof(FallbackIndexBuilderContext), "fallback index builder context"); context->anim = anim; context->proxy_sizes_in_use = proxy_sizes_in_use; memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx)); for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { if (context->proxy_sizes_in_use & proxy_sizes[i]) { char fname[FILE_MAX]; get_proxy_filename(anim, proxy_sizes[i], fname, TRUE); BLI_make_existing_file(fname); context->proxy_ctx[i] = alloc_proxy_output_avi(anim, fname, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality); } } return (IndexBuildContext *)context; }
static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality) { struct proxy_output_ctx *rv = MEM_callocN( sizeof(struct proxy_output_ctx), "alloc_proxy_output"); char fname[FILE_MAX]; int ffmpeg_quality; /* JPEG requires this */ width = round_up(width, 8); height = round_up(height, 8); rv->proxy_size = proxy_size; rv->anim = anim; get_proxy_filename(rv->anim, rv->proxy_size, fname, TRUE); BLI_make_existing_file(fname); rv->of = avformat_alloc_context(); rv->of->oformat = av_guess_format("avi", NULL, NULL); BLI_strncpy(rv->of->filename, fname, sizeof(rv->of->filename)); fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename); rv->st = avformat_new_stream(rv->of, NULL); rv->st->id = 0; rv->c = rv->st->codec; rv->c->codec_type = AVMEDIA_TYPE_VIDEO; rv->c->codec_id = AV_CODEC_ID_MJPEG; rv->c->width = width; rv->c->height = height; rv->of->oformat->video_codec = rv->c->codec_id; rv->codec = avcodec_find_encoder(rv->c->codec_id); if (!rv->codec) { fprintf(stderr, "No ffmpeg MJPEG encoder available? " "Proxy not built!\n"); av_free(rv->of); return NULL; } if (rv->codec->pix_fmts) { rv->c->pix_fmt = rv->codec->pix_fmts[0]; } else { rv->c->pix_fmt = PIX_FMT_YUVJ420P; } rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio; rv->c->time_base.den = 25; rv->c->time_base.num = 1; rv->st->time_base = rv->c->time_base; /* 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 */ ffmpeg_quality = (int)(1.0f + 30.0f * (1.0f - (float)quality / 100.0f) + 0.5f); av_opt_set_int(rv->c, "qmin", ffmpeg_quality, 0); av_opt_set_int(rv->c, "qmax", ffmpeg_quality, 0); if (rv->of->flags & AVFMT_GLOBALHEADER) { rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER; } if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Couldn't open outputfile! " "Proxy not built!\n"); av_free(rv->of); return 0; } avcodec_open2(rv->c, rv->codec, NULL); rv->orig_height = av_get_cropped_height_from_codec(st->codec); if (st->codec->width != width || st->codec->height != height || st->codec->pix_fmt != rv->c->pix_fmt) { rv->frame = avcodec_alloc_frame(); avpicture_fill((AVPicture *) rv->frame, MEM_mallocN(avpicture_get_size( rv->c->pix_fmt, round_up(width, 16), height), "alloc proxy output frame"), rv->c->pix_fmt, round_up(width, 16), height); rv->sws_ctx = sws_getContext( st->codec->width, rv->orig_height, st->codec->pix_fmt, width, height, rv->c->pix_fmt, SWS_FAST_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL); } if (avformat_write_header(rv->of, NULL) < 0) { fprintf(stderr, "Couldn't set output parameters? " "Proxy not built!\n"); av_free(rv->of); return 0; } return rv; }
static void index_rebuild_fallback(struct anim * anim, IMB_Timecode_Type UNUSED(tcs_in_use), IMB_Proxy_Size proxy_sizes_in_use, int quality, short *stop, short *do_update, float *progress) { int cnt = IMB_anim_get_duration(anim, IMB_TC_NONE); int i, pos; AviMovie * proxy_ctx[IMB_PROXY_MAX_SLOT]; char fname[FILE_MAXDIR+FILE_MAXFILE]; char fname_tmp[FILE_MAXDIR+FILE_MAXFILE]; memset(proxy_ctx, 0, sizeof(proxy_ctx)); /* since timecode indices only work with ffmpeg right now, don't know a sensible fallback here... so no proxies, no game to play... */ if (proxy_sizes_in_use == IMB_PROXY_NONE) { return; } for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { if (proxy_sizes_in_use & proxy_sizes[i]) { char fname[FILE_MAXDIR+FILE_MAXFILE]; get_proxy_filename(anim, proxy_sizes[i], fname, TRUE); BLI_make_existing_file(fname); proxy_ctx[i] = alloc_proxy_output_avi( anim, fname, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality); } } for (pos = 0; pos < cnt; pos++) { struct ImBuf * ibuf = IMB_anim_absolute( anim, pos, IMB_TC_NONE, IMB_PROXY_NONE); int next_progress = (int) ((double) pos / (double) cnt); if (*progress != next_progress) { *progress = next_progress; *do_update = 1; } if (*stop) { break; } IMB_flipy(ibuf); for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { if (proxy_sizes_in_use & proxy_sizes[i]) { int x = anim->x * proxy_fac[i]; int y = anim->y * proxy_fac[i]; struct ImBuf * s_ibuf = IMB_scalefastImBuf( ibuf, x, y); IMB_convert_rgba_to_abgr(s_ibuf); AVI_write_frame (proxy_ctx[i], pos, AVI_FORMAT_RGB32, s_ibuf->rect, x * y * 4); /* note that libavi free's the buffer... */ s_ibuf->rect = 0; IMB_freeImBuf(s_ibuf); } } } for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { if (proxy_sizes_in_use & proxy_sizes[i]) { AVI_close_compress (proxy_ctx[i]); MEM_freeN (proxy_ctx[i]); get_proxy_filename(anim, proxy_sizes[i], fname_tmp, TRUE); get_proxy_filename(anim, proxy_sizes[i], fname, FALSE); if (*stop) { unlink(fname_tmp); } else { rename(fname_tmp, fname); } } } }
static struct proxy_output_ctx * alloc_proxy_output_ffmpeg( struct anim * anim, AVStream * st, int proxy_size, int width, int height, int quality) { struct proxy_output_ctx * rv = MEM_callocN( sizeof(struct proxy_output_ctx), "alloc_proxy_output"); char fname[FILE_MAXDIR+FILE_MAXFILE]; // JPEG requires this width = round_up(width, 8); height = round_up(height, 8); rv->proxy_size = proxy_size; rv->anim = anim; get_proxy_filename(rv->anim, rv->proxy_size, fname, TRUE); BLI_make_existing_file(fname); rv->of = avformat_alloc_context(); rv->of->oformat = av_guess_format("avi", NULL, NULL); BLI_snprintf(rv->of->filename, sizeof(rv->of->filename), "%s", fname); fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename); rv->st = av_new_stream(rv->of, 0); rv->c = rv->st->codec; rv->c->codec_type = AVMEDIA_TYPE_VIDEO; rv->c->codec_id = CODEC_ID_MJPEG; rv->c->width = width; rv->c->height = height; rv->of->oformat->video_codec = rv->c->codec_id; rv->codec = avcodec_find_encoder(rv->c->codec_id); if (!rv->codec) { fprintf(stderr, "No ffmpeg MJPEG encoder available? " "Proxy not built!\n"); av_free(rv->of); return NULL; } if (rv->codec->pix_fmts) { rv->c->pix_fmt = rv->codec->pix_fmts[0]; } else { rv->c->pix_fmt = PIX_FMT_YUVJ420P; } rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio; rv->c->time_base.den = 25; rv->c->time_base.num = 1; rv->st->time_base = rv->c->time_base; if (rv->of->flags & AVFMT_GLOBALHEADER) { rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER; } if (av_set_parameters(rv->of, NULL) < 0) { fprintf(stderr, "Couldn't set output parameters? " "Proxy not built!\n"); av_free(rv->of); return 0; } if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Couldn't open outputfile! " "Proxy not built!\n"); av_free(rv->of); return 0; } avcodec_open(rv->c, rv->codec); rv->video_buffersize = 2000000; rv->video_buffer = (uint8_t*)MEM_mallocN( rv->video_buffersize, "FFMPEG video buffer"); rv->orig_height = st->codec->height; if (st->codec->width != width || st->codec->height != height || st->codec->pix_fmt != rv->c->pix_fmt) { rv->frame = avcodec_alloc_frame(); avpicture_fill((AVPicture*) rv->frame, MEM_mallocN(avpicture_get_size( rv->c->pix_fmt, round_up(width, 16), height), "alloc proxy output frame"), rv->c->pix_fmt, round_up(width, 16), height); rv->sws_ctx = sws_getContext( st->codec->width, st->codec->height, st->codec->pix_fmt, width, height, rv->c->pix_fmt, SWS_FAST_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL); } av_write_header(rv->of); return rv; }
IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, int quality, const bool overwrite, GSet *file_list) { IndexBuildContext *context = NULL; IMB_Proxy_Size proxy_sizes_to_build = proxy_sizes_in_use; int i; /* Don't generate the same file twice! */ if (file_list) { for (i = 0; i < IMB_PROXY_MAX_SLOT; ++i) { IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & proxy_sizes_to_build) { char filename[FILE_MAX]; get_proxy_filename(anim, proxy_size, filename, false); void **filename_key_p; if (!BLI_gset_ensure_p_ex(file_list, filename, &filename_key_p)) { *filename_key_p = BLI_strdup(filename); } else { proxy_sizes_to_build &= ~proxy_size; printf("Proxy: %s already registered for generation, skipping\n", filename); } } } } if (!overwrite) { IMB_Proxy_Size built_proxies = IMB_anim_proxy_get_existing(anim); if (built_proxies != 0) { for (i = 0; i < IMB_PROXY_MAX_SLOT; ++i) { IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & built_proxies) { char filename[FILE_MAX]; get_proxy_filename(anim, proxy_size, filename, false); printf("Skipping proxy: %s\n", filename); } } } proxy_sizes_to_build &= ~built_proxies; } fflush(stdout); if (proxy_sizes_to_build == 0) { return NULL; } switch (anim->curtype) { #ifdef WITH_FFMPEG case ANIM_FFMPEG: context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); break; #endif #ifdef WITH_AVI default: context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); break; #endif } if (context) context->anim_type = anim->curtype; return context; UNUSED_VARS(tcs_in_use, proxy_sizes_in_use, quality); }