Changeset View
Changeset View
Standalone View
Standalone View
source/blender/imbuf/intern/indexer.c
| Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #ifdef WITH_AVI | #ifdef WITH_AVI | ||||
| # include "AVI_avi.h" | # include "AVI_avi.h" | ||||
| #endif | #endif | ||||
| #ifdef WITH_FFMPEG | #ifdef WITH_FFMPEG | ||||
| # include "ffmpeg_compat.h" | # include "ffmpeg_compat.h" | ||||
| # include <libavutil/imgutils.h> | |||||
| #endif | #endif | ||||
| static const char magic[] = "BlenMIdx"; | static const char binary_header_str[] = "BlenMIdx"; | ||||
| static const char temp_ext[] = "_part"; | 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 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}; | static const float proxy_fac[] = {0.25, 0.50, 0.75, 1.00}; | ||||
| #ifdef WITH_FFMPEG | #ifdef WITH_FFMPEG | ||||
| static int tc_types[] = { | static int tc_types[] = { | ||||
| IMB_TC_RECORD_RUN, | IMB_TC_RECORD_RUN, | ||||
| IMB_TC_FREE_RUN, | IMB_TC_FREE_RUN, | ||||
| IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN, | IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN, | ||||
| IMB_TC_RECORD_RUN_NO_GAPS, | IMB_TC_RECORD_RUN_NO_GAPS, | ||||
| }; | }; | ||||
| #endif | #endif | ||||
| #define INDEX_FILE_VERSION 1 | #define INDEX_FILE_VERSION 2 | ||||
| /* ---------------------------------------------------------------------- | /* ---------------------------------------------------------------------- | ||||
| * - time code index functions | * - time code index functions | ||||
| * ---------------------------------------------------------------------- */ | * ---------------------------------------------------------------------- */ | ||||
| anim_index_builder *IMB_index_builder_create(const char *name) | anim_index_builder *IMB_index_builder_create(const char *name) | ||||
| { | { | ||||
| Show All 14 Lines | if (!rv->fp) { | ||||
| fprintf(stderr, | fprintf(stderr, | ||||
| "Couldn't open index target: %s! " | "Couldn't open index target: %s! " | ||||
| "Index build broken!\n", | "Index build broken!\n", | ||||
| rv->temp_name); | rv->temp_name); | ||||
| MEM_freeN(rv); | MEM_freeN(rv); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| fprintf(rv->fp, "%s%c%.3d", magic, (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v', INDEX_FILE_VERSION); | fprintf(rv->fp, | ||||
| "%s%c%.3d", | |||||
| binary_header_str, | |||||
| (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v', | |||||
| INDEX_FILE_VERSION); | |||||
| return rv; | return rv; | ||||
| } | } | ||||
| void IMB_index_builder_add_entry( | void IMB_index_builder_add_entry(anim_index_builder *fp, | ||||
| anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts) | int frameno, | ||||
| uint64_t seek_pos, | |||||
| uint64_t seek_pos_pts, | |||||
| uint64_t seek_pos_dts, | |||||
| uint64_t pts) | |||||
| { | { | ||||
| fwrite(&frameno, sizeof(int), 1, fp->fp); | fwrite(&frameno, sizeof(int), 1, fp->fp); | ||||
| fwrite(&seek_pos, sizeof(uint64_t), 1, fp->fp); | fwrite(&seek_pos, sizeof(uint64_t), 1, fp->fp); | ||||
| fwrite(&seek_pos_pts, sizeof(uint64_t), 1, fp->fp); | |||||
| fwrite(&seek_pos_dts, sizeof(uint64_t), 1, fp->fp); | fwrite(&seek_pos_dts, sizeof(uint64_t), 1, fp->fp); | ||||
| fwrite(&pts, sizeof(uint64_t), 1, fp->fp); | fwrite(&pts, sizeof(uint64_t), 1, fp->fp); | ||||
| } | } | ||||
| void IMB_index_builder_proc_frame(anim_index_builder *fp, | void IMB_index_builder_proc_frame(anim_index_builder *fp, | ||||
| uchar *buffer, | uchar *buffer, | ||||
| int data_size, | int data_size, | ||||
| int frameno, | int frameno, | ||||
| uint64_t seek_pos, | uint64_t seek_pos, | ||||
| uint64_t seek_pos_pts, | |||||
| uint64_t seek_pos_dts, | uint64_t seek_pos_dts, | ||||
| uint64_t pts) | uint64_t pts) | ||||
| { | { | ||||
| if (fp->proc_frame) { | if (fp->proc_frame) { | ||||
| anim_index_entry e; | anim_index_entry e; | ||||
| e.frameno = frameno; | e.frameno = frameno; | ||||
| e.seek_pos = seek_pos; | e.seek_pos = seek_pos; | ||||
| e.seek_pos_pts = seek_pos_pts; | |||||
| e.seek_pos_dts = seek_pos_dts; | e.seek_pos_dts = seek_pos_dts; | ||||
| e.pts = pts; | e.pts = pts; | ||||
| fp->proc_frame(fp, buffer, data_size, &e); | fp->proc_frame(fp, buffer, data_size, &e); | ||||
| } | } | ||||
| else { | else { | ||||
| IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_dts, pts); | IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_pts, seek_pos_dts, pts); | ||||
| } | } | ||||
| } | } | ||||
| void IMB_index_builder_finish(anim_index_builder *fp, int rollback) | void IMB_index_builder_finish(anim_index_builder *fp, int rollback) | ||||
| { | { | ||||
| if (fp->delete_priv_data) { | if (fp->delete_priv_data) { | ||||
| fp->delete_priv_data(fp); | fp->delete_priv_data(fp); | ||||
| } | } | ||||
| Show All 14 Lines | |||||
| struct anim_index *IMB_indexer_open(const char *name) | struct anim_index *IMB_indexer_open(const char *name) | ||||
| { | { | ||||
| char header[13]; | char header[13]; | ||||
| struct anim_index *idx; | struct anim_index *idx; | ||||
| FILE *fp = BLI_fopen(name, "rb"); | FILE *fp = BLI_fopen(name, "rb"); | ||||
| int i; | int i; | ||||
| if (!fp) { | if (!fp) { | ||||
| fprintf(stderr, "Couldn't open indexer file: %s\n", name); | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| if (fread(header, 12, 1, fp) != 1) { | if (fread(header, 12, 1, fp) != 1) { | ||||
| fprintf(stderr, "Couldn't read indexer file: %s\n", name); | |||||
| fclose(fp); | fclose(fp); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| header[12] = 0; | header[12] = 0; | ||||
| if (memcmp(header, magic, 8) != 0) { | if (memcmp(header, binary_header_str, 8) != 0) { | ||||
| fprintf(stderr, "Error reading %s: Binary file type string missmatch\n", name); | |||||
| fclose(fp); | fclose(fp); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| if (atoi(header + 9) != INDEX_FILE_VERSION) { | if (atoi(header + 9) != INDEX_FILE_VERSION) { | ||||
| fprintf(stderr, "Error reading %s: File version missmatch\n", name); | |||||
| fclose(fp); | fclose(fp); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| idx = MEM_callocN(sizeof(struct anim_index), "anim_index"); | idx = MEM_callocN(sizeof(struct anim_index), "anim_index"); | ||||
| BLI_strncpy(idx->name, name, sizeof(idx->name)); | BLI_strncpy(idx->name, name, sizeof(idx->name)); | ||||
| fseek(fp, 0, SEEK_END); | fseek(fp, 0, SEEK_END); | ||||
| idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */ | idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */ | ||||
| sizeof(uint64_t) + /* seek_pos */ | sizeof(uint64_t) + /* seek_pos */ | ||||
| sizeof(uint64_t) + /* seek_pos_pts */ | |||||
| sizeof(uint64_t) + /* seek_pos_dts */ | sizeof(uint64_t) + /* seek_pos_dts */ | ||||
| sizeof(uint64_t) /* pts */ | sizeof(uint64_t) /* pts */ | ||||
| ); | ); | ||||
| fseek(fp, 12, SEEK_SET); | fseek(fp, 12, SEEK_SET); | ||||
| idx->entries = MEM_callocN(sizeof(struct anim_index_entry) * idx->num_entries, | idx->entries = MEM_callocN(sizeof(struct anim_index_entry) * idx->num_entries, | ||||
| "anim_index_entries"); | "anim_index_entries"); | ||||
| size_t items_read = 0; | size_t items_read = 0; | ||||
| for (i = 0; i < idx->num_entries; i++) { | for (i = 0; i < idx->num_entries; i++) { | ||||
| items_read += fread(&idx->entries[i].frameno, sizeof(int), 1, fp); | items_read += fread(&idx->entries[i].frameno, sizeof(int), 1, fp); | ||||
| items_read += fread(&idx->entries[i].seek_pos, sizeof(uint64_t), 1, fp); | items_read += fread(&idx->entries[i].seek_pos, sizeof(uint64_t), 1, fp); | ||||
| items_read += fread(&idx->entries[i].seek_pos_pts, sizeof(uint64_t), 1, fp); | |||||
| items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(uint64_t), 1, fp); | items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(uint64_t), 1, fp); | ||||
| items_read += fread(&idx->entries[i].pts, sizeof(uint64_t), 1, fp); | items_read += fread(&idx->entries[i].pts, sizeof(uint64_t), 1, fp); | ||||
| } | } | ||||
| if (UNLIKELY(items_read != idx->num_entries * 4)) { | if (UNLIKELY(items_read != idx->num_entries * 5)) { | ||||
| perror("error reading animation index file"); | fprintf(stderr, "Error: Element data size missmatch in: %s\n", name); | ||||
| MEM_freeN(idx->entries); | MEM_freeN(idx->entries); | ||||
| MEM_freeN(idx); | MEM_freeN(idx); | ||||
| fclose(fp); | fclose(fp); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| if (((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V'))) { | if (((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V'))) { | ||||
| for (i = 0; i < idx->num_entries; i++) { | for (i = 0; i < idx->num_entries; i++) { | ||||
| BLI_endian_switch_int32(&idx->entries[i].frameno); | BLI_endian_switch_int32(&idx->entries[i].frameno); | ||||
| BLI_endian_switch_uint64(&idx->entries[i].seek_pos); | BLI_endian_switch_uint64(&idx->entries[i].seek_pos); | ||||
| BLI_endian_switch_uint64(&idx->entries[i].seek_pos_pts); | |||||
| BLI_endian_switch_uint64(&idx->entries[i].seek_pos_dts); | BLI_endian_switch_uint64(&idx->entries[i].seek_pos_dts); | ||||
| BLI_endian_switch_uint64(&idx->entries[i].pts); | BLI_endian_switch_uint64(&idx->entries[i].pts); | ||||
| } | } | ||||
| } | } | ||||
| fclose(fp); | fclose(fp); | ||||
| return idx; | return idx; | ||||
| } | } | ||||
| uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index) | uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index) | ||||
| { | { | ||||
| if (frame_index < 0) { | if (frame_index < 0) { | ||||
| frame_index = 0; | frame_index = 0; | ||||
| } | } | ||||
| if (frame_index >= idx->num_entries) { | if (frame_index >= idx->num_entries) { | ||||
| frame_index = idx->num_entries - 1; | frame_index = idx->num_entries - 1; | ||||
| } | } | ||||
| return idx->entries[frame_index].seek_pos; | return idx->entries[frame_index].seek_pos; | ||||
| } | } | ||||
| uint64_t IMB_indexer_get_seek_pos_pts(struct anim_index *idx, int frame_index) | |||||
| { | |||||
| if (frame_index < 0) { | |||||
| frame_index = 0; | |||||
| } | |||||
| if (frame_index >= idx->num_entries) { | |||||
| frame_index = idx->num_entries - 1; | |||||
| } | |||||
| return idx->entries[frame_index].seek_pos_pts; | |||||
| } | |||||
| uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index) | uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index) | ||||
| { | { | ||||
| if (frame_index < 0) { | if (frame_index < 0) { | ||||
| frame_index = 0; | frame_index = 0; | ||||
| } | } | ||||
| if (frame_index >= idx->num_entries) { | if (frame_index >= idx->num_entries) { | ||||
| frame_index = idx->num_entries - 1; | frame_index = idx->num_entries - 1; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | void IMB_indexer_close(struct anim_index *idx) | ||||
| MEM_freeN(idx->entries); | MEM_freeN(idx->entries); | ||||
| MEM_freeN(idx); | MEM_freeN(idx); | ||||
| } | } | ||||
| int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size) | int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size) | ||||
| { | { | ||||
| switch (pr_size) { | switch (pr_size) { | ||||
| case IMB_PROXY_NONE: | case IMB_PROXY_NONE: | ||||
| /* if we got here, something is broken anyways, so sane defaults... */ | return -1; | ||||
| return 0; | |||||
| case IMB_PROXY_25: | case IMB_PROXY_25: | ||||
| return 0; | return 0; | ||||
| case IMB_PROXY_50: | case IMB_PROXY_50: | ||||
| return 1; | return 1; | ||||
| case IMB_PROXY_75: | case IMB_PROXY_75: | ||||
| return 2; | return 2; | ||||
| case IMB_PROXY_100: | case IMB_PROXY_100: | ||||
| return 3; | return 3; | ||||
| default: | default: | ||||
| return 0; | BLI_assert(!"Unhandled proxy size enum!"); | ||||
| return -1; | |||||
| } | } | ||||
| } | } | ||||
| int IMB_timecode_to_array_index(IMB_Timecode_Type tc) | int IMB_timecode_to_array_index(IMB_Timecode_Type tc) | ||||
| { | { | ||||
| switch (tc) { | switch (tc) { | ||||
| case IMB_TC_NONE: /* if we got here, something is broken anyways, | case IMB_TC_NONE: | ||||
| * so sane defaults... */ | return -1; | ||||
| return 0; | |||||
| case IMB_TC_RECORD_RUN: | case IMB_TC_RECORD_RUN: | ||||
| return 0; | return 0; | ||||
| case IMB_TC_FREE_RUN: | case IMB_TC_FREE_RUN: | ||||
| return 1; | return 1; | ||||
| case IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN: | case IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN: | ||||
| return 2; | return 2; | ||||
| case IMB_TC_RECORD_RUN_NO_GAPS: | case IMB_TC_RECORD_RUN_NO_GAPS: | ||||
| return 3; | return 3; | ||||
| default: | default: | ||||
| return 0; | BLI_assert(!"Unhandled timecode type enum!"); | ||||
| return -1; | |||||
| } | } | ||||
| } | } | ||||
| /* ---------------------------------------------------------------------- | /* ---------------------------------------------------------------------- | ||||
| * - rebuild helper functions | * - rebuild helper functions | ||||
| * ---------------------------------------------------------------------- */ | * ---------------------------------------------------------------------- */ | ||||
| static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_len) | static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_len) | ||||
| Show All 19 Lines | |||||
| static bool get_proxy_filename(struct anim *anim, | static bool get_proxy_filename(struct anim *anim, | ||||
| IMB_Proxy_Size preview_size, | IMB_Proxy_Size preview_size, | ||||
| char *fname, | char *fname, | ||||
| bool temp) | bool temp) | ||||
| { | { | ||||
| char index_dir[FILE_MAXDIR]; | char index_dir[FILE_MAXDIR]; | ||||
| int i = IMB_proxy_size_to_array_index(preview_size); | int i = IMB_proxy_size_to_array_index(preview_size); | ||||
| BLI_assert(i >= 0); | |||||
| char proxy_name[256]; | char proxy_name[256]; | ||||
| char stream_suffix[20]; | char stream_suffix[20]; | ||||
| const char *name = (temp) ? "proxy_%d%s_part.avi" : "proxy_%d%s.avi"; | const char *name = (temp) ? "proxy_%d%s_part.avi" : "proxy_%d%s.avi"; | ||||
| stream_suffix[0] = 0; | stream_suffix[0] = 0; | ||||
| if (anim->streamindex > 0) { | if (anim->streamindex > 0) { | ||||
| BLI_snprintf(stream_suffix, sizeof(stream_suffix), "_st%d", anim->streamindex); | BLI_snprintf(stream_suffix, sizeof(stream_suffix), "_st%d", anim->streamindex); | ||||
| Show All 15 Lines | static bool get_proxy_filename(struct anim *anim, | ||||
| BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); | BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); | ||||
| return true; | return true; | ||||
| } | } | ||||
| static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname) | static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname) | ||||
| { | { | ||||
| char index_dir[FILE_MAXDIR]; | char index_dir[FILE_MAXDIR]; | ||||
| int i = IMB_timecode_to_array_index(tc); | int i = IMB_timecode_to_array_index(tc); | ||||
| BLI_assert(i >= 0); | |||||
| const char *index_names[] = { | const char *index_names[] = { | ||||
| "record_run%s%s.blen_tc", | "record_run%s%s.blen_tc", | ||||
| "free_run%s%s.blen_tc", | "free_run%s%s.blen_tc", | ||||
| "interp_free_run%s%s.blen_tc", | "interp_free_run%s%s.blen_tc", | ||||
| "record_run_no_gaps%s%s.blen_tc", | "record_run_no_gaps%s%s.blen_tc", | ||||
| }; | }; | ||||
| char stream_suffix[20]; | char stream_suffix[20]; | ||||
| Show All 34 Lines | struct proxy_output_ctx { | ||||
| struct SwsContext *sws_ctx; | struct SwsContext *sws_ctx; | ||||
| AVFrame *frame; | AVFrame *frame; | ||||
| int cfra; | int cfra; | ||||
| int proxy_size; | int proxy_size; | ||||
| int orig_height; | int orig_height; | ||||
| struct anim *anim; | struct anim *anim; | ||||
| }; | }; | ||||
| // work around stupid swscaler 16 bytes alignment bug... | |||||
| static int round_up(int x, int mod) | |||||
| { | |||||
| return x + ((mod - (x % mod)) % mod); | |||||
| } | |||||
| static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( | static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( | ||||
| struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality) | 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"); | struct proxy_output_ctx *rv = MEM_callocN(sizeof(struct proxy_output_ctx), "alloc_proxy_output"); | ||||
| char fname[FILE_MAX]; | char fname[FILE_MAX]; | ||||
| rv->proxy_size = proxy_size; | rv->proxy_size = proxy_size; | ||||
| rv->anim = anim; | rv->anim = anim; | ||||
| get_proxy_filename(rv->anim, rv->proxy_size, fname, true); | get_proxy_filename(rv->anim, rv->proxy_size, fname, true); | ||||
| BLI_make_existing_file(fname); | BLI_make_existing_file(fname); | ||||
| rv->of = avformat_alloc_context(); | rv->of = avformat_alloc_context(); | ||||
| rv->of->oformat = av_guess_format("avi", NULL, NULL); | rv->of->oformat = av_guess_format("avi", NULL, NULL); | ||||
| BLI_strncpy(rv->of->filename, fname, sizeof(rv->of->filename)); | rv->of->url = av_strdup(fname); | ||||
| fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename); | fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url); | ||||
| rv->st = avformat_new_stream(rv->of, NULL); | rv->st = avformat_new_stream(rv->of, NULL); | ||||
| rv->st->id = 0; | rv->st->id = 0; | ||||
| rv->c = rv->st->codec; | rv->c = avcodec_alloc_context3(NULL); | ||||
| rv->c->codec_type = AVMEDIA_TYPE_VIDEO; | rv->c->codec_type = AVMEDIA_TYPE_VIDEO; | ||||
| rv->c->codec_id = AV_CODEC_ID_H264; | rv->c->codec_id = AV_CODEC_ID_H264; | ||||
| rv->c->width = width; | |||||
| rv->c->height = height; | |||||
| rv->c->gop_size = 10; | |||||
| rv->c->max_b_frames = 0; | |||||
| /* Correct wrong default ffmpeg param which crash x264. */ | |||||
| rv->c->qmin = 10; | |||||
| rv->c->qmax = 51; | |||||
| rv->of->oformat->video_codec = rv->c->codec_id; | rv->of->oformat->video_codec = rv->c->codec_id; | ||||
| rv->codec = avcodec_find_encoder(rv->c->codec_id); | rv->codec = avcodec_find_encoder(rv->c->codec_id); | ||||
| if (!rv->codec) { | if (!rv->codec) { | ||||
| fprintf(stderr, | fprintf(stderr, | ||||
| "No ffmpeg encoder available? " | "No ffmpeg encoder available? " | ||||
| "Proxy not built!\n"); | "Proxy not built!\n"); | ||||
| av_free(rv->of); | avcodec_free_context(&rv->c); | ||||
| avformat_free_context(rv->of); | |||||
| MEM_freeN(rv); | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| avcodec_get_context_defaults3(rv->c, rv->codec); | |||||
| rv->c->width = width; | |||||
| rv->c->height = height; | |||||
| rv->c->gop_size = 10; | |||||
| rv->c->max_b_frames = 0; | |||||
| if (rv->codec->pix_fmts) { | if (rv->codec->pix_fmts) { | ||||
| rv->c->pix_fmt = rv->codec->pix_fmts[0]; | rv->c->pix_fmt = rv->codec->pix_fmts[0]; | ||||
| } | } | ||||
| else { | else { | ||||
| rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P; | rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P; | ||||
| } | } | ||||
| rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio; | rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->sample_aspect_ratio; | ||||
| rv->c->time_base.den = 25; | rv->c->time_base.den = 25; | ||||
| rv->c->time_base.num = 1; | rv->c->time_base.num = 1; | ||||
| rv->st->time_base = rv->c->time_base; | rv->st->time_base = rv->c->time_base; | ||||
| /* This range matches eFFMpegCrf. Crf_range_min corresponds to lowest quality, crf_range_max to | /* This range matches eFFMpegCrf. Crf_range_min corresponds to lowest quality, crf_range_max to | ||||
| * highest quality. */ | * highest quality. */ | ||||
| const int crf_range_min = 32; | const int crf_range_min = 32; | ||||
| const int crf_range_max = 17; | const int crf_range_max = 17; | ||||
| int crf = round_fl_to_int((quality / 100.0f) * (crf_range_max - crf_range_min) + crf_range_min); | int crf = round_fl_to_int((quality / 100.0f) * (crf_range_max - crf_range_min) + crf_range_min); | ||||
| AVDictionary *codec_opts = NULL; | AVDictionary *codec_opts = NULL; | ||||
| /* High quality preset value. */ | /* High quality preset value. */ | ||||
| av_dict_set_int(&codec_opts, "crf", crf, 0); | av_dict_set_int(&codec_opts, "crf", crf, 0); | ||||
| /* Prefer smaller file-size. */ | /* Prefer smaller file-size. Presets from veryslow to veryfast produce output with very similar | ||||
| av_dict_set(&codec_opts, "preset", "slow", 0); | * file-size, but there is big difference in performance. In some cases veryfast preset will | ||||
| * produce smallest file-size. */ | |||||
| av_dict_set(&codec_opts, "preset", "veryfast", 0); | |||||
| av_dict_set(&codec_opts, "tune", "fastdecode", 0); | |||||
| if (rv->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { | if (rv->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { | ||||
| rv->c->thread_count = 0; | rv->c->thread_count = 0; | ||||
| } | } | ||||
| else { | else { | ||||
| rv->c->thread_count = BLI_system_thread_count(); | rv->c->thread_count = BLI_system_thread_count(); | ||||
| } | } | ||||
| if (rv->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) { | if (rv->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) { | ||||
| rv->c->thread_type = FF_THREAD_FRAME; | rv->c->thread_type = FF_THREAD_FRAME; | ||||
| } | } | ||||
| else if (rv->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) { | else if (rv->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) { | ||||
| rv->c->thread_type = FF_THREAD_SLICE; | rv->c->thread_type = FF_THREAD_SLICE; | ||||
| } | } | ||||
| if (rv->of->flags & AVFMT_GLOBALHEADER) { | if (rv->of->flags & AVFMT_GLOBALHEADER) { | ||||
| rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER; | rv->c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; | ||||
| } | } | ||||
| if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) { | avcodec_parameters_from_context(rv->st->codecpar, rv->c); | ||||
| int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE); | |||||
| if (ret < 0) { | |||||
| fprintf(stderr, | fprintf(stderr, | ||||
| "Couldn't open outputfile! " | "Couldn't open IO: %s\n" | ||||
| "Proxy not built!\n"); | "Proxy not built!\n", | ||||
| av_free(rv->of); | av_err2str(ret)); | ||||
| return 0; | avcodec_free_context(&rv->c); | ||||
| avformat_free_context(rv->of); | |||||
| MEM_freeN(rv); | |||||
| return NULL; | |||||
| } | } | ||||
| avcodec_open2(rv->c, rv->codec, &codec_opts); | ret = avcodec_open2(rv->c, rv->codec, &codec_opts); | ||||
| if (ret < 0) { | |||||
| fprintf(stderr, | |||||
| "Couldn't open codec: %s\n" | |||||
| "Proxy not built!\n", | |||||
| av_err2str(ret)); | |||||
| avcodec_free_context(&rv->c); | |||||
| avformat_free_context(rv->of); | |||||
| MEM_freeN(rv); | |||||
| return NULL; | |||||
| } | |||||
| rv->orig_height = av_get_cropped_height_from_codec(st->codec); | rv->orig_height = st->codecpar->height; | ||||
| if (st->codec->width != width || st->codec->height != height || | if (st->codecpar->width != width || st->codecpar->height != height || | ||||
| st->codec->pix_fmt != rv->c->pix_fmt) { | st->codecpar->format != rv->c->pix_fmt) { | ||||
| rv->frame = av_frame_alloc(); | rv->frame = av_frame_alloc(); | ||||
| avpicture_fill((AVPicture *)rv->frame, | |||||
| MEM_mallocN(avpicture_get_size(rv->c->pix_fmt, round_up(width, 16), height), | av_image_fill_arrays(rv->frame->data, | ||||
| rv->frame->linesize, | |||||
| MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, width, height, 1), | |||||
| "alloc proxy output frame"), | "alloc proxy output frame"), | ||||
| rv->c->pix_fmt, | rv->c->pix_fmt, | ||||
| round_up(width, 16), | width, | ||||
| height); | height, | ||||
| 1); | |||||
| rv->sws_ctx = sws_getContext(st->codec->width, | rv->frame->format = rv->c->pix_fmt; | ||||
| rv->frame->width = width; | |||||
| rv->frame->height = height; | |||||
| rv->sws_ctx = sws_getContext(st->codecpar->width, | |||||
| rv->orig_height, | rv->orig_height, | ||||
| st->codec->pix_fmt, | st->codecpar->format, | ||||
| width, | width, | ||||
| height, | height, | ||||
| rv->c->pix_fmt, | rv->c->pix_fmt, | ||||
| SWS_FAST_BILINEAR | SWS_PRINT_INFO, | SWS_FAST_BILINEAR | SWS_PRINT_INFO, | ||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| NULL); | NULL); | ||||
| } | } | ||||
| if (avformat_write_header(rv->of, NULL) < 0) { | ret = avformat_write_header(rv->of, NULL); | ||||
| if (ret < 0) { | |||||
| fprintf(stderr, | fprintf(stderr, | ||||
| "Couldn't set output parameters? " | "Couldn't write header: %s\n" | ||||
| "Proxy not built!\n"); | "Proxy not built!\n", | ||||
| av_free(rv->of); | av_err2str(ret)); | ||||
| return 0; | |||||
| if (rv->frame) { | |||||
| av_frame_free(&rv->frame); | |||||
| } | |||||
| avcodec_free_context(&rv->c); | |||||
| avformat_free_context(rv->of); | |||||
| MEM_freeN(rv); | |||||
| return NULL; | |||||
| } | } | ||||
| return rv; | return rv; | ||||
| } | } | ||||
| static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame) | static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame) | ||||
| { | { | ||||
| AVPacket packet = {0}; | |||||
| int ret, got_output; | |||||
| av_init_packet(&packet); | |||||
| if (!ctx) { | if (!ctx) { | ||||
| return 0; | return; | ||||
| } | } | ||||
| if (ctx->sws_ctx && frame && | if (ctx->sws_ctx && frame && | ||||
| (frame->data[0] || frame->data[1] || frame->data[2] || frame->data[3])) { | (frame->data[0] || frame->data[1] || frame->data[2] || frame->data[3])) { | ||||
| sws_scale(ctx->sws_ctx, | sws_scale(ctx->sws_ctx, | ||||
| (const uint8_t *const *)frame->data, | (const uint8_t *const *)frame->data, | ||||
| frame->linesize, | frame->linesize, | ||||
| 0, | 0, | ||||
| ctx->orig_height, | ctx->orig_height, | ||||
| ctx->frame->data, | ctx->frame->data, | ||||
| ctx->frame->linesize); | ctx->frame->linesize); | ||||
| } | } | ||||
| frame = ctx->sws_ctx ? (frame ? ctx->frame : 0) : frame; | frame = ctx->sws_ctx ? (frame ? ctx->frame : 0) : frame; | ||||
| if (frame) { | if (frame) { | ||||
| frame->pts = ctx->cfra++; | frame->pts = ctx->cfra++; | ||||
| } | } | ||||
| ret = avcodec_encode_video2(ctx->c, &packet, frame, &got_output); | int ret = avcodec_send_frame(ctx->c, frame); | ||||
| if (ret < 0) { | if (ret < 0) { | ||||
| fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", ctx->cfra - 1, ctx->of->filename); | /* Can't send frame to encoder. This shouldn't happen. */ | ||||
| return 0; | fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret)); | ||||
| return; | |||||
| } | } | ||||
| AVPacket *packet = av_packet_alloc(); | |||||
| while (ret >= 0) { | |||||
| ret = avcodec_receive_packet(ctx->c, packet); | |||||
| if (got_output) { | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { | ||||
| if (packet.pts != AV_NOPTS_VALUE) { | /* No more packets to flush. */ | ||||
| packet.pts = av_rescale_q(packet.pts, ctx->c->time_base, ctx->st->time_base); | break; | ||||
| } | } | ||||
| if (packet.dts != AV_NOPTS_VALUE) { | if (ret < 0) { | ||||
| packet.dts = av_rescale_q(packet.dts, ctx->c->time_base, ctx->st->time_base); | fprintf(stderr, | ||||
| "Error encoding proxy frame %d for '%s': %s\n", | |||||
| ctx->cfra - 1, | |||||
| ctx->of->url, | |||||
| av_err2str(ret)); | |||||
| break; | |||||
| } | } | ||||
| packet.stream_index = ctx->st->index; | packet->stream_index = ctx->st->index; | ||||
| av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base); | |||||
| # ifdef FFMPEG_USE_DURATION_WORKAROUND | |||||
| my_guess_pkt_duration(ctx->of, ctx->st, packet); | |||||
| # endif | |||||
| if (av_interleaved_write_frame(ctx->of, &packet) != 0) { | int write_ret = av_interleaved_write_frame(ctx->of, packet); | ||||
| if (write_ret != 0) { | |||||
| fprintf(stderr, | fprintf(stderr, | ||||
| "Error writing proxy frame %d " | "Error writing proxy frame %d " | ||||
| "into '%s'\n", | "into '%s': %s\n", | ||||
| ctx->cfra - 1, | ctx->cfra - 1, | ||||
| ctx->of->filename); | ctx->of->url, | ||||
| return 0; | av_err2str(write_ret)); | ||||
| break; | |||||
| } | } | ||||
| return 1; | |||||
| } | } | ||||
| return 0; | av_packet_free(&packet); | ||||
| } | } | ||||
| static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) | static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) | ||||
| { | { | ||||
| char fname[FILE_MAX]; | char fname[FILE_MAX]; | ||||
| char fname_tmp[FILE_MAX]; | char fname_tmp[FILE_MAX]; | ||||
| if (!ctx) { | if (!ctx) { | ||||
| return; | return; | ||||
| } | } | ||||
| if (!rollback) { | if (!rollback) { | ||||
| while (add_to_proxy_output_ffmpeg(ctx, NULL)) { | /* Flush the remaining packets. */ | ||||
| } | add_to_proxy_output_ffmpeg(ctx, NULL); | ||||
| } | } | ||||
| avcodec_flush_buffers(ctx->c); | avcodec_flush_buffers(ctx->c); | ||||
| av_write_trailer(ctx->of); | av_write_trailer(ctx->of); | ||||
| avcodec_close(ctx->c); | avcodec_free_context(&ctx->c); | ||||
| if (ctx->of->oformat) { | if (ctx->of->oformat) { | ||||
| if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) { | if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) { | ||||
| avio_close(ctx->of->pb); | avio_close(ctx->of->pb); | ||||
| } | } | ||||
| } | } | ||||
| avformat_free_context(ctx->of); | avformat_free_context(ctx->of); | ||||
| Show All 32 Lines | typedef struct FFmpegIndexBuilderContext { | ||||
| struct proxy_output_ctx *proxy_ctx[IMB_PROXY_MAX_SLOT]; | struct proxy_output_ctx *proxy_ctx[IMB_PROXY_MAX_SLOT]; | ||||
| anim_index_builder *indexer[IMB_TC_MAX_SLOT]; | anim_index_builder *indexer[IMB_TC_MAX_SLOT]; | ||||
| IMB_Timecode_Type tcs_in_use; | IMB_Timecode_Type tcs_in_use; | ||||
| IMB_Proxy_Size proxy_sizes_in_use; | IMB_Proxy_Size proxy_sizes_in_use; | ||||
| uint64_t seek_pos; | uint64_t seek_pos; | ||||
| uint64_t last_seek_pos; | |||||
| uint64_t seek_pos_dts; | |||||
| uint64_t seek_pos_pts; | uint64_t seek_pos_pts; | ||||
| uint64_t seek_pos_dts; | |||||
| uint64_t last_seek_pos; | |||||
| uint64_t last_seek_pos_pts; | |||||
| uint64_t last_seek_pos_dts; | uint64_t last_seek_pos_dts; | ||||
| uint64_t start_pts; | uint64_t start_pts; | ||||
| double frame_rate; | double frame_rate; | ||||
| double pts_time_base; | double pts_time_base; | ||||
| int frameno, frameno_gapless; | int frameno, frameno_gapless; | ||||
| int start_pts_set; | int start_pts_set; | ||||
| } FFmpegIndexBuilderContext; | } FFmpegIndexBuilderContext; | ||||
| Show All 27 Lines | if (avformat_find_stream_info(context->iFormatCtx, NULL) < 0) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| streamcount = anim->streamindex; | streamcount = anim->streamindex; | ||||
| /* Find the video stream */ | /* Find the video stream */ | ||||
| context->videoStream = -1; | context->videoStream = -1; | ||||
| for (i = 0; i < context->iFormatCtx->nb_streams; i++) { | for (i = 0; i < context->iFormatCtx->nb_streams; i++) { | ||||
| if (context->iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { | if (context->iFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { | ||||
| if (streamcount > 0) { | if (streamcount > 0) { | ||||
| streamcount--; | streamcount--; | ||||
| continue; | continue; | ||||
| } | } | ||||
| context->videoStream = i; | context->videoStream = i; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (context->videoStream == -1) { | if (context->videoStream == -1) { | ||||
| avformat_close_input(&context->iFormatCtx); | avformat_close_input(&context->iFormatCtx); | ||||
| MEM_freeN(context); | MEM_freeN(context); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| context->iStream = context->iFormatCtx->streams[context->videoStream]; | context->iStream = context->iFormatCtx->streams[context->videoStream]; | ||||
| context->iCodecCtx = context->iStream->codec; | |||||
| context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id); | context->iCodec = avcodec_find_decoder(context->iStream->codecpar->codec_id); | ||||
| if (context->iCodec == NULL) { | if (context->iCodec == NULL) { | ||||
| avformat_close_input(&context->iFormatCtx); | avformat_close_input(&context->iFormatCtx); | ||||
| MEM_freeN(context); | MEM_freeN(context); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| context->iCodecCtx->workaround_bugs = 1; | context->iCodecCtx = avcodec_alloc_context3(NULL); | ||||
| avcodec_parameters_to_context(context->iCodecCtx, context->iStream->codecpar); | |||||
| context->iCodecCtx->workaround_bugs = FF_BUG_AUTODETECT; | |||||
| if (context->iCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { | if (context->iCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { | ||||
| context->iCodecCtx->thread_count = 0; | context->iCodecCtx->thread_count = 0; | ||||
| } | } | ||||
| else { | else { | ||||
| context->iCodecCtx->thread_count = BLI_system_thread_count(); | context->iCodecCtx->thread_count = BLI_system_thread_count(); | ||||
| } | } | ||||
| if (context->iCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) { | if (context->iCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) { | ||||
| context->iCodecCtx->thread_type = FF_THREAD_FRAME; | context->iCodecCtx->thread_type = FF_THREAD_FRAME; | ||||
| } | } | ||||
| else if (context->iCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) { | else if (context->iCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) { | ||||
| context->iCodecCtx->thread_type = FF_THREAD_SLICE; | context->iCodecCtx->thread_type = FF_THREAD_SLICE; | ||||
| } | } | ||||
| if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) { | if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) { | ||||
| avformat_close_input(&context->iFormatCtx); | avformat_close_input(&context->iFormatCtx); | ||||
| avcodec_free_context(&context->iCodecCtx); | |||||
| MEM_freeN(context); | MEM_freeN(context); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| for (i = 0; i < num_proxy_sizes; i++) { | for (i = 0; i < num_proxy_sizes; i++) { | ||||
| if (proxy_sizes_in_use & proxy_sizes[i]) { | if (proxy_sizes_in_use & proxy_sizes[i]) { | ||||
| context->proxy_ctx[i] = alloc_proxy_output_ffmpeg( | context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(anim, | ||||
| anim, | |||||
| context->iStream, | context->iStream, | ||||
| proxy_sizes[i], | proxy_sizes[i], | ||||
| context->iCodecCtx->width * proxy_fac[i], | context->iCodecCtx->width * proxy_fac[i], | ||||
| av_get_cropped_height_from_codec(context->iCodecCtx) * proxy_fac[i], | context->iCodecCtx->height * proxy_fac[i], | ||||
| quality); | quality); | ||||
| if (!context->proxy_ctx[i]) { | if (!context->proxy_ctx[i]) { | ||||
| proxy_sizes_in_use &= ~proxy_sizes[i]; | proxy_sizes_in_use &= ~proxy_sizes[i]; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| for (i = 0; i < num_indexers; i++) { | for (i = 0; i < num_indexers; i++) { | ||||
| if (tcs_in_use & tc_types[i]) { | if (tcs_in_use & tc_types[i]) { | ||||
| Show All 22 Lines | static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int stop) | ||||
| } | } | ||||
| for (i = 0; i < context->num_proxy_sizes; i++) { | for (i = 0; i < context->num_proxy_sizes; i++) { | ||||
| if (context->proxy_sizes_in_use & proxy_sizes[i]) { | if (context->proxy_sizes_in_use & proxy_sizes[i]) { | ||||
| free_proxy_output_ffmpeg(context->proxy_ctx[i], stop); | free_proxy_output_ffmpeg(context->proxy_ctx[i], stop); | ||||
| } | } | ||||
| } | } | ||||
| avcodec_close(context->iCodecCtx); | avcodec_free_context(&context->iCodecCtx); | ||||
| avformat_close_input(&context->iFormatCtx); | avformat_close_input(&context->iFormatCtx); | ||||
| MEM_freeN(context); | MEM_freeN(context); | ||||
| } | } | ||||
| static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *context, | static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *context, | ||||
| AVPacket *curr_packet, | AVPacket *curr_packet, | ||||
| AVFrame *in_frame) | AVFrame *in_frame) | ||||
| { | { | ||||
| int i; | int i; | ||||
| uint64_t s_pos = context->seek_pos; | uint64_t s_pos = context->seek_pos; | ||||
| uint64_t s_pts = context->seek_pos_pts; | |||||
| uint64_t s_dts = context->seek_pos_dts; | uint64_t s_dts = context->seek_pos_dts; | ||||
| uint64_t pts = av_get_pts_from_frame(context->iFormatCtx, in_frame); | uint64_t pts = av_get_pts_from_frame(in_frame); | ||||
| for (i = 0; i < context->num_proxy_sizes; i++) { | for (i = 0; i < context->num_proxy_sizes; i++) { | ||||
| add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame); | add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame); | ||||
| } | } | ||||
| if (!context->start_pts_set) { | if (!context->start_pts_set) { | ||||
| context->start_pts = pts; | context->start_pts = pts; | ||||
| context->start_pts_set = true; | context->start_pts_set = true; | ||||
| } | } | ||||
| context->frameno = floor( | context->frameno = floor( | ||||
| (pts - context->start_pts) * context->pts_time_base * context->frame_rate + 0.5); | (pts - context->start_pts) * context->pts_time_base * context->frame_rate + 0.5); | ||||
| /* decoding starts *always* on I-Frames, | int64_t seek_pos_pts = timestamp_from_pts_or_dts(context->seek_pos_pts, context->seek_pos_dts); | ||||
| * so: P-Frames won't work, even if all the | |||||
| * information is in place, when we seek | |||||
| * to the I-Frame presented *after* the P-Frame, | |||||
| * but located before the P-Frame within | |||||
| * the stream */ | |||||
| if (pts < context->seek_pos_pts) { | if (pts < seek_pos_pts) { | ||||
| /* Decoding starts *always* on I-Frames. In this case our position is | |||||
| * before our seek I-Frame. So we need to pick the previous available | |||||
| * I-Frame to be able to decode this one properly. | |||||
| */ | |||||
| s_pos = context->last_seek_pos; | s_pos = context->last_seek_pos; | ||||
| s_pts = context->last_seek_pos_pts; | |||||
| s_dts = context->last_seek_pos_dts; | s_dts = context->last_seek_pos_dts; | ||||
| } | } | ||||
| for (i = 0; i < context->num_indexers; i++) { | for (i = 0; i < context->num_indexers; i++) { | ||||
| if (context->tcs_in_use & tc_types[i]) { | if (context->tcs_in_use & tc_types[i]) { | ||||
| int tc_frameno = context->frameno; | int tc_frameno = context->frameno; | ||||
| if (tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS) { | if (tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS) { | ||||
| tc_frameno = context->frameno_gapless; | tc_frameno = context->frameno_gapless; | ||||
| } | } | ||||
| IMB_index_builder_proc_frame(context->indexer[i], | IMB_index_builder_proc_frame(context->indexer[i], | ||||
| curr_packet->data, | curr_packet->data, | ||||
| curr_packet->size, | curr_packet->size, | ||||
| tc_frameno, | tc_frameno, | ||||
| s_pos, | s_pos, | ||||
| s_pts, | |||||
| s_dts, | s_dts, | ||||
| pts); | pts); | ||||
| } | } | ||||
| } | } | ||||
| context->frameno_gapless++; | context->frameno_gapless++; | ||||
| } | } | ||||
| static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, | static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, | ||||
| const short *stop, | const short *stop, | ||||
| short *do_update, | short *do_update, | ||||
| float *progress) | float *progress) | ||||
| { | { | ||||
| AVFrame *in_frame = 0; | AVFrame *in_frame = av_frame_alloc(); | ||||
| AVPacket next_packet; | AVPacket *next_packet = av_packet_alloc(); | ||||
| uint64_t stream_size; | uint64_t stream_size; | ||||
| memset(&next_packet, 0, sizeof(AVPacket)); | |||||
| in_frame = av_frame_alloc(); | |||||
| stream_size = avio_size(context->iFormatCtx->pb); | stream_size = avio_size(context->iFormatCtx->pb); | ||||
| context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL)); | context->frame_rate = av_q2d(context->iStream->r_frame_rate); | ||||
| context->pts_time_base = av_q2d(context->iStream->time_base); | context->pts_time_base = av_q2d(context->iStream->time_base); | ||||
| while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) { | while (av_read_frame(context->iFormatCtx, next_packet) >= 0) { | ||||
| int frame_finished = 0; | |||||
| float next_progress = | float next_progress = | ||||
| (float)((int)floor(((double)next_packet.pos) * 100 / ((double)stream_size) + 0.5)) / 100; | (float)((int)floor(((double)next_packet->pos) * 100 / ((double)stream_size) + 0.5)) / 100; | ||||
| if (*progress != next_progress) { | if (*progress != next_progress) { | ||||
| *progress = next_progress; | *progress = next_progress; | ||||
| *do_update = true; | *do_update = true; | ||||
| } | } | ||||
| if (*stop) { | if (*stop) { | ||||
| av_free_packet(&next_packet); | |||||
| break; | break; | ||||
| } | } | ||||
| if (next_packet.stream_index == context->videoStream) { | if (next_packet->stream_index == context->videoStream) { | ||||
| if (next_packet.flags & AV_PKT_FLAG_KEY) { | if (next_packet->flags & AV_PKT_FLAG_KEY) { | ||||
| context->last_seek_pos = context->seek_pos; | context->last_seek_pos = context->seek_pos; | ||||
| context->last_seek_pos_pts = context->seek_pos_pts; | |||||
| context->last_seek_pos_dts = context->seek_pos_dts; | context->last_seek_pos_dts = context->seek_pos_dts; | ||||
| context->seek_pos = next_packet.pos; | |||||
| context->seek_pos_dts = next_packet.dts; | |||||
| context->seek_pos_pts = next_packet.pts; | |||||
| } | |||||
| avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet); | context->seek_pos = next_packet->pos; | ||||
| context->seek_pos_pts = next_packet->pts; | |||||
| context->seek_pos_dts = next_packet->dts; | |||||
| } | } | ||||
| if (frame_finished) { | int ret = avcodec_send_packet(context->iCodecCtx, next_packet); | ||||
| index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame); | while (ret >= 0) { | ||||
| ret = avcodec_receive_frame(context->iCodecCtx, in_frame); | |||||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { | |||||
| /* No more frames to flush. */ | |||||
| break; | |||||
| } | |||||
| if (ret < 0) { | |||||
| fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret)); | |||||
| break; | |||||
| } | |||||
| index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame); | |||||
| } | |||||
| } | } | ||||
| av_free_packet(&next_packet); | av_packet_unref(next_packet); | ||||
| } | } | ||||
| /* process pictures still stuck in decoder engine after EOF | /* process pictures still stuck in decoder engine after EOF | ||||
| * according to ffmpeg docs using 0-size packets. | * according to ffmpeg docs using NULL packets. | ||||
| * | * | ||||
| * At least, if we haven't already stopped... */ | * At least, if we haven't already stopped... */ | ||||
| /* this creates the 0-size packet and prevents a memory leak. */ | |||||
| av_free_packet(&next_packet); | |||||
| if (!*stop) { | if (!*stop) { | ||||
| int frame_finished; | int ret = avcodec_send_packet(context->iCodecCtx, NULL); | ||||
| do { | |||||
| frame_finished = 0; | |||||
| avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet); | while (ret >= 0) { | ||||
| ret = avcodec_receive_frame(context->iCodecCtx, in_frame); | |||||
| if (frame_finished) { | if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { | ||||
| index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame); | /* No more frames to flush. */ | ||||
| break; | |||||
| } | |||||
| if (ret < 0) { | |||||
| fprintf(stderr, "Error flushing proxy frame: %s\n", av_err2str(ret)); | |||||
| break; | |||||
| } | |||||
| index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame); | |||||
| } | } | ||||
| } while (frame_finished); | |||||
| } | } | ||||
| av_packet_free(&next_packet); | |||||
| av_free(in_frame); | av_free(in_frame); | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| #endif | #endif | ||||
| /* ---------------------------------------------------------------------- | /* ---------------------------------------------------------------------- | ||||
| ▲ Show 20 Lines • Show All 321 Lines • ▼ Show 20 Lines | void IMB_anim_set_index_dir(struct anim *anim, const char *dir) | ||||
| IMB_free_indices(anim); | IMB_free_indices(anim); | ||||
| } | } | ||||
| struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) | struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) | ||||
| { | { | ||||
| char fname[FILE_MAX]; | char fname[FILE_MAX]; | ||||
| int i = IMB_proxy_size_to_array_index(preview_size); | int i = IMB_proxy_size_to_array_index(preview_size); | ||||
| if (i < 0) { | |||||
| return NULL; | |||||
| } | |||||
| if (anim->proxy_anim[i]) { | if (anim->proxy_anim[i]) { | ||||
| return anim->proxy_anim[i]; | return anim->proxy_anim[i]; | ||||
| } | } | ||||
| if (anim->proxies_tried & preview_size) { | if (anim->proxies_tried & preview_size) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| get_proxy_filename(anim, preview_size, fname, false); | get_proxy_filename(anim, preview_size, fname, false); | ||||
| /* proxies are generated in the same color space as animation itself */ | /* proxies are generated in the same color space as animation itself */ | ||||
| anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, anim->colorspace); | anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, anim->colorspace); | ||||
| anim->proxies_tried |= preview_size; | anim->proxies_tried |= preview_size; | ||||
| return anim->proxy_anim[i]; | return anim->proxy_anim[i]; | ||||
| } | } | ||||
| struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) | struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) | ||||
| { | { | ||||
| char fname[FILE_MAX]; | char fname[FILE_MAX]; | ||||
| int i = IMB_timecode_to_array_index(tc); | int i = IMB_timecode_to_array_index(tc); | ||||
| if (i < 0) { | |||||
| return NULL; | |||||
| } | |||||
| if (anim->curr_idx[i]) { | if (anim->curr_idx[i]) { | ||||
| return anim->curr_idx[i]; | return anim->curr_idx[i]; | ||||
| } | } | ||||
| if (anim->indices_tried & tc) { | if (anim->indices_tried & tc) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| Show All 35 Lines | |||||