[Bf-blender-cvs] [6813420d1ec] temp-vse-h264-proxy: [WIP] Use H264 codec for proxies

Richard Antalik noreply at git.blender.org
Mon Mar 8 11:18:43 CET 2021


Commit: 6813420d1eced11ed794fe992c205619ce3915d3
Author: Richard Antalik
Date:   Mon Mar 8 11:18:14 2021 +0100
Branches: temp-vse-h264-proxy
https://developer.blender.org/rB6813420d1eced11ed794fe992c205619ce3915d3

[WIP] Use H264 codec for proxies

Same patch as D10394, but using H264 codec with small GOP size (2 frames in this case).

Main reason to use H264 is smaller filesize, current patch produces about 3x smaller files.
Playback is also about 1.5x faster than MJPEG

Complexity of this patch is much higher though, because encoder must work in own threadpool and encode frames with own gop size.
This means that scaled frames from encoder must be allocated per packet, which leads to worse performance overall. Patch is still about 3x faster than original. Interesting thing is that there is no performance improvement when running transcoding in more than 4 threads.

Possible ways to improve performance are to reuse memory for packets and frames, which would require redesign, where threads would wait for data to be written.
Another possibly much cleaner method would be to decode portions of input into multiple streams and remux these into proxy file.
In both cases managing complexity would be fairly important as it can get out of hand quite quickly.

Maniphest Tasks: T85469

Differential Revision: https://developer.blender.org/D10630

===================================================================

M	source/blender/imbuf/intern/indexer.c

===================================================================

diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index c9581c108c0..73559f65389 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -451,19 +451,90 @@ typedef struct IndexBuildContext {
 
 #ifdef WITH_FFMPEG
 
+struct input_ctx {
+  AVFormatContext *format_context;
+  AVCodecContext *codec_context;
+  AVCodec *codec;
+  AVStream *stream;
+  int video_stream;
+};
+
 struct proxy_output_ctx {
-  AVFormatContext *of;
-  AVStream *st;
-  AVCodecContext *c;
+  AVFormatContext *output_format;
+  AVStream *stream;
   AVCodec *codec;
-  struct SwsContext *sws_ctx;
-  AVFrame *frame;
+  AVCodecContext *codec_context;
   int cfra;
   int proxy_size;
-  int orig_height;
   struct anim *anim;
 };
 
+struct transcode_output_ctx {
+  AVCodecContext *codec_context;
+  struct SwsContext *sws_ctx;
+  int orig_height;
+} transcode_output_ctx;
+
+struct proxy_transcode_ctx {
+  AVCodecContext *input_codec_context;
+  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;
+  int num_transcode_threads;
+  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 encoder_suspend_cond;
+  ThreadMutex encoder_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;
+  bool all_frames_encoded;
+  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;
+  uint64_t seek_pos_dts;
+  uint64_t seek_pos_pts;
+  uint64_t last_seek_pos_dts;
+  uint64_t start_pts;
+  double frame_rate;
+  double pts_time_base;
+  int frameno, frameno_gapless;
+  int start_pts_set;
+} FFmpegIndexBuilderContext;
+
 // work around stupid swscaler 16 bytes alignment bug...
 
 static int round_up(int x, int mod)
@@ -471,174 +542,117 @@ static int round_up(int x, int mod)
   return x + ((mod - (x % mod)) % mod);
 }
 
-static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
-    struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality)
+static struct SwsContext *alloc_proxy_output_sws_context(AVCodecContext *input_codec_ctx,
+                                                         AVCodecContext *proxy_codec_ctx)
 {
-  struct proxy_output_ctx *rv = MEM_callocN(sizeof(struct proxy_output_ctx), "alloc_proxy_output");
+  struct SwsContext *sws_ctx = sws_getContext(input_codec_ctx->width,
+                                              av_get_cropped_height_from_codec(input_codec_ctx),
+                                              input_codec_ctx->pix_fmt,
+                                              proxy_codec_ctx->width,
+                                              proxy_codec_ctx->height,
+                                              proxy_codec_ctx->pix_fmt,
+                                              SWS_FAST_BILINEAR | SWS_PRINT_INFO,
+                                              NULL,
+                                              NULL,
+                                              NULL);
+  return sws_ctx;
+}
 
+static AVFormatContext *alloc_proxy_output_output_format_context(struct anim *anim, int proxy_size)
+{
   char fname[FILE_MAX];
-  int ffmpeg_quality;
 
-  rv->proxy_size = proxy_size;
-  rv->anim = anim;
-
-  get_proxy_filename(rv->anim, rv->proxy_size, fname, true);
+  get_proxy_filename(anim, 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);
+  AVFormatContext *format_context = avformat_alloc_context();
+  format_context->oformat = av_guess_format("avi", NULL, NULL);
 
-  rv->st = avformat_new_stream(rv->of, NULL);
-  rv->st->id = 0;
+  BLI_strncpy(format_context->filename, fname, sizeof(format_context->filename));
 
-  rv->c = rv->st->codec;
-  rv->c->thread_count = BLI_system_thread_count();
-  rv->c->thread_type = FF_THREAD_SLICE;
-  rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
-  rv->c->codec_id = AV_CODEC_ID_MJPEG;
-  rv->c->width = width;
-  rv->c->height = height;
+  /* 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;
+  }
 
-  rv->of->oformat->video_codec = rv->c->codec_id;
-  rv->codec = avcodec_find_encoder(rv->c->codec_id);
+  return format_context;
+}
 
-  if (!rv->codec) {
+static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
+    struct anim *anim, AVStream *inpuf_stream, int proxy_size, int width, int height, int quality)
+{
+  struct proxy_output_ctx *proxy_out_ctx = MEM_callocN(sizeof(struct proxy_output_ctx),
+                                                       "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->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? "
             "Proxy not built!\n");
-    av_free(rv->of);
+    av_free(proxy_out_ctx->output_format);
     return NULL;
   }
 
-  if (rv->codec->pix_fmts) {
-    rv->c->pix_fmt = rv->codec->pix_fmts[0];
+  if (proxy_out_ctx->codec->pix_fmts) {
+    proxy_out_ctx->codec_context->pix_fmt = proxy_out_ctx->codec->pix_fmts[0];
   }
   else {
-    rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P;
+    proxy_out_ctx->codec_context->pix_fmt = AV_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;
+  proxy_out_ctx->codec_context->sample_aspect_ratio = proxy_out_ctx->stream->sample_aspect_ratio =
+      inpuf_stream->codec->sample_aspect_ratio;
 
-  /* 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);
+  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 (rv->of->flags & AVFMT_GLOBALHEADER) {
-    rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+  if (proxy_out_ctx->output_format->flags & AVFMT_GLOBALHEADER) {
+    proxy_out_ctx->codec_context->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);
+  /* 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);*/
 
-  if (st->codec->width != width || st->codec->height != height ||
-      st->codec->pix_fmt != rv->c->pix_fmt) {
-    rv->frame = av_frame_alloc();
-    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,
-                         

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list