From acd05e285cf3c2f0b00a58f5d94e98ea7d0e1cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Kita?= Date: Thu, 22 Aug 2024 12:05:40 +0200 Subject: [PATCH 1/5] Add an option to specify FFmpeg encoding parameters --- c_src/membrane_h264_ffmpeg_plugin/encoder.c | 7 ++++- .../encoder.spec.exs | 10 ++++++- lib/membrane_h264_ffmpeg/encoder.ex | 19 +++++++++++++- mix.lock | 26 +++++++++++++++++++ test/encoder/encoder_native_test.exs | 2 +- 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/c_src/membrane_h264_ffmpeg_plugin/encoder.c b/c_src/membrane_h264_ffmpeg_plugin/encoder.c index 6f03a64..aaff8c6 100644 --- a/c_src/membrane_h264_ffmpeg_plugin/encoder.c +++ b/c_src/membrane_h264_ffmpeg_plugin/encoder.c @@ -73,7 +73,7 @@ static void set_x264_defaults(AVDictionary **params, char* preset) { UNIFEX_TERM create(UnifexEnv *env, int width, int height, char *pix_fmt, char *preset, char *tune, char *profile, int max_b_frames, int gop_size, - int timebase_num, int timebase_den, int crf, int sc_threshold) { + int timebase_num, int timebase_den, int crf, int sc_threshold, ffmpeg_option * ffmpeg_options, unsigned int ffmpeg_options_length) { UNIFEX_TERM res; AVDictionary *params = NULL; State *state = unifex_alloc_state(env); @@ -123,6 +123,11 @@ UNIFEX_TERM create(UnifexEnv *env, int width, int height, char *pix_fmt, set_x264_defaults(¶ms, preset); + for(unsigned int i=0; i String.t()}, + description: """ + A map with options that will be passed to the encoder. + """, + default: %{} ] + defmodule FFmpegOption do + @moduledoc false + @enforce_keys [:key, :value] + defstruct @enforce_keys + end + @impl true def handle_init(_ctx, opts) do state = @@ -165,6 +178,9 @@ defmodule Membrane.H264.FFmpeg.Encoder do frames_per_second when is_integer(frames_per_second) -> {1, frames_per_second} end + ffmpeg_options = + Enum.map(state.ffmpeg_options, fn {key, value} -> %FFmpegOption{key: key, value: value} end) + with buffers <- flush_encoder_if_exists(state), {:ok, new_encoder_ref} <- Native.create( @@ -179,7 +195,8 @@ defmodule Membrane.H264.FFmpeg.Encoder do timebase_num, timebase_den, state.crf, - state.sc_threshold + state.sc_threshold, + ffmpeg_options ) do stream_format = create_new_stream_format(stream_format, state) actions = buffers ++ [stream_format: stream_format] diff --git a/mix.lock b/mix.lock index 0954ec8..8621c9c 100644 --- a/mix.lock +++ b/mix.lock @@ -1,9 +1,23 @@ %{ "bunch": {:hex, :bunch, "1.6.1", "5393d827a64d5f846092703441ea50e65bc09f37fd8e320878f13e63d410aec7", [:mix], [], "hexpm", "286cc3add551628b30605efbe2fca4e38cc1bea89bcd0a1a7226920b3364fe4a"}, + "bunch_native": {:hex, :bunch_native, "0.5.0", "8ac1536789a597599c10b652e0b526d8833348c19e4739a0759a2bedfd924e63", [:mix], [{:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "24190c760e32b23b36edeb2dc4852515c7c5b3b8675b1a864e0715bdd1c8f80d"}, "bundlex": {:hex, :bundlex, "1.5.3", "35d01e5bc0679510dd9a327936ffb518f63f47175c26a35e708cc29eaec0890b", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:req, ">= 0.4.0", [hex: :req, repo: "hexpm", optional: false]}, {:zarex, "~> 1.0", [hex: :zarex, repo: "hexpm", optional: false]}], "hexpm", "debd0eac151b404f6216fc60222761dff049bf26f7d24d066c365317650cd118"}, + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, + "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, + "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, + "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, + "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, + "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, + "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "membrane_common_c": {:hex, :membrane_common_c, "0.16.0", "caf3f29d2f5a1d32d8c2c122866110775866db2726e4272be58e66dfdf4bce40", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.5.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "a3c7e91de1ce1f8b23b9823188a5d13654d317235ea0ca781c05353ed3be9b1c"}, "membrane_core": {:hex, :membrane_core, "1.1.0", "c3bbaa5af7c26a7c3748e573efe343c2104801e3463b9e491a607e82860334a4", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 3.0 or ~> 4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3209d7f7e86d736cb7caffbba16b075c571cebb9439ab939ed6119c50fb59a5"}, "membrane_file_plugin": {:hex, :membrane_file_plugin, "0.16.0", "7917f6682c22b9bcfc2ca20ed960eee0f7d03ad31fd5f59ed850f1fe3ddd545a", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "b0727998f75a9b4dab8a2baefdfc13c3eac00a04e061ab1b0e61dc5566927acc"}, @@ -12,5 +26,17 @@ "membrane_precompiled_dependency_provider": {:hex, :membrane_precompiled_dependency_provider, "0.1.2", "8af73b7dc15ba55c9f5fbfc0453d4a8edfb007ade54b56c37d626be0d1189aba", [:mix], [{:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "7fe3e07361510445a29bee95336adde667c4162b76b7f4c8af3aeb3415292023"}, "membrane_raw_video_format": {:hex, :membrane_raw_video_format, "0.3.0", "ba10f475e0814a6fe79602a74536b796047577c7ef5b0e33def27cd344229699", [:mix], [], "hexpm", "2f08760061c8a5386ecf04273480f10e48d25a1a40aa99476302b0bcd34ccb1c"}, "membrane_raw_video_parser_plugin": {:hex, :membrane_raw_video_parser_plugin, "0.12.0", "8edf6aa125ef211282c6379af3223e314ec7ab70269b916bea963af5042faa83", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.16.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}], "hexpm", "9dc2626a738b2698dbc368e77ca4ad2657da8032e7d9e03b57e03353f0e60006"}, + "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, + "numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"}, + "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"}, + "ratio": {:hex, :ratio, "4.0.1", "3044166f2fc6890aa53d3aef0c336f84b2bebb889dc57d5f95cc540daa1912f8", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "c60cbb3ccdff9ffa56e7d6d1654b5c70d9f90f4d753ab3a43a6bf40855b881ce"}, + "req": {:hex, :req, "0.5.6", "8fe1eead4a085510fe3d51ad854ca8f20a622aae46e97b302f499dfb84f726ac", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cfaa8e720945d46654853de39d368f40362c2641c4b2153c886418914b372185"}, + "shmex": {:hex, :shmex, "0.5.1", "81dd209093416bf6608e66882cb7e676089307448a1afd4fc906c1f7e5b94cf4", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "c29f8286891252f64c4e1dac40b217d960f7d58def597c4e606ff8fbe71ceb80"}, + "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "unifex": {:hex, :unifex, "1.2.0", "90d1ec5e6d788350e07e474f7bd8b0ee866d6606beb9ca4e20dbb26328712a84", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}, {:shmex, "~> 0.5.0", [hex: :shmex, repo: "hexpm", optional: false]}], "hexpm", "7a8395aabc3ba6cff04bbe5b995de7f899a38eb57f189e49927d6b8b6ccb6883"}, + "zarex": {:hex, :zarex, "1.0.5", "58239e3ee5d75f343262bb4df5cf466555a1c689f920e5d3651a9333972f7c7e", [:mix], [], "hexpm", "9fb72ef0567c2b2742f5119a1ba8a24a2fabb21b8d09820aefbf3e592fa9a46a"}, } diff --git a/test/encoder/encoder_native_test.exs b/test/encoder/encoder_native_test.exs index cf02cef..4094255 100644 --- a/test/encoder/encoder_native_test.exs +++ b/test/encoder/encoder_native_test.exs @@ -10,7 +10,7 @@ defmodule Encoder.NativeTest do in_path = "../fixtures/reference-100-240p.raw" |> Path.expand(__DIR__) assert {:ok, file} = File.read(in_path) - assert {:ok, ref} = Enc.create(320, 240, :I420, :fast, nil, :high, -1, -1, 1, 1, 23, 40) + assert {:ok, ref} = Enc.create(320, 240, :I420, :fast, nil, :high, -1, -1, 1, 1, 23, 40, []) assert <> = file Enum.each( From badbeb492de9ae70fef2dc06c75a20410e7612d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Kita?= Date: Fri, 23 Aug 2024 13:16:55 +0200 Subject: [PATCH 2/5] Format the code --- c_src/membrane_h264_ffmpeg_plugin/encoder.c | 73 ++++++++------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/c_src/membrane_h264_ffmpeg_plugin/encoder.c b/c_src/membrane_h264_ffmpeg_plugin/encoder.c index aaff8c6..613518e 100644 --- a/c_src/membrane_h264_ffmpeg_plugin/encoder.c +++ b/c_src/membrane_h264_ffmpeg_plugin/encoder.c @@ -8,8 +8,9 @@ void handle_destroy_state(UnifexEnv *env, State *state) { } } -static void set_x264_defaults(AVDictionary **params, char* preset) { - // Override FFmpeg defaults from https://github.com/mirror/x264/blob/eaa68fad9e5d201d42fde51665f2d137ae96baf0/encoder/encoder.c#L674 +static void set_x264_defaults(AVDictionary **params, char *preset) { + // Override FFmpeg defaults from + // https://github.com/mirror/x264/blob/eaa68fad9e5d201d42fde51665f2d137ae96baf0/encoder/encoder.c#L674 av_dict_set(params, "qcomp", "0.6", 0); av_dict_set(params, "me_range", "16", 0); av_dict_set(params, "qdiff", "4", 0); @@ -17,63 +18,45 @@ static void set_x264_defaults(AVDictionary **params, char* preset) { av_dict_set(params, "qmax", "69", 0); av_dict_set(params, "i_qfactor", "1.4", 0); av_dict_set(params, "f_pb_factor", "1.3", 0); - - if (strcmp(preset, "ultrafast") == 0) - { + + if (strcmp(preset, "ultrafast") == 0) { av_dict_set(params, "partitions", "none", 0); av_dict_set(params, "subq", "0", 0); - } - else if (strcmp(preset, "superfast") == 0) - { + } else if (strcmp(preset, "superfast") == 0) { av_dict_set(params, "partitions", "i8x8,i4x4", 0); av_dict_set(params, "subq", "1", 0); - } - else if (strcmp(preset, "veryfast") == 0) - { + } else if (strcmp(preset, "veryfast") == 0) { av_dict_set(params, "partitions", "p8x8,b8x8,i8x8,i4x4", 0); av_dict_set(params, "subq", "2", 0); - } - else if (strcmp(preset, "faster") == 0) - { + } else if (strcmp(preset, "faster") == 0) { av_dict_set(params, "partitions", "p8x8,b8x8,i8x8,i4x4", 0); av_dict_set(params, "subq", "4", 0); - } - else if (strcmp(preset, "fast") == 0) - { + } else if (strcmp(preset, "fast") == 0) { av_dict_set(params, "partitions", "p8x8,b8x8,i8x8,i4x4", 0); av_dict_set(params, "subq", "6", 0); - } - else if (strcmp(preset, "medium") == 0) - { + } else if (strcmp(preset, "medium") == 0) { av_dict_set(params, "partitions", "p8x8,b8x8,i8x8,i4x4", 0); av_dict_set(params, "subq", "7", 0); - } - else if (strcmp(preset, "slow") == 0) - { + } else if (strcmp(preset, "slow") == 0) { av_dict_set(params, "partitions", "all", 0); av_dict_set(params, "subq", "8", 0); - } - else if (strcmp(preset, "slower") == 0) - { + } else if (strcmp(preset, "slower") == 0) { av_dict_set(params, "partitions", "all", 0); av_dict_set(params, "subq", "9", 0); - } - else if (strcmp(preset, "veryslow") == 0) - { + } else if (strcmp(preset, "veryslow") == 0) { av_dict_set(params, "partitions", "all", 0); av_dict_set(params, "subq", "10", 0); - } - else if (strcmp(preset, "placebo") == 0) - { + } else if (strcmp(preset, "placebo") == 0) { av_dict_set(params, "partitions", "all", 0); av_dict_set(params, "subq", "11", 0); - } + } } - UNIFEX_TERM create(UnifexEnv *env, int width, int height, char *pix_fmt, - char *preset, char *tune, char *profile, int max_b_frames, int gop_size, - int timebase_num, int timebase_den, int crf, int sc_threshold, ffmpeg_option * ffmpeg_options, unsigned int ffmpeg_options_length) { + char *preset, char *tune, char *profile, int max_b_frames, + int gop_size, int timebase_num, int timebase_den, int crf, + int sc_threshold, ffmpeg_option *ffmpeg_options, + unsigned int ffmpeg_options_length) { UNIFEX_TERM res; AVDictionary *params = NULL; State *state = unifex_alloc_state(env); @@ -113,7 +96,7 @@ UNIFEX_TERM create(UnifexEnv *env, int width, int height, char *pix_fmt, state->codec_ctx->time_base.num = timebase_num; state->codec_ctx->time_base.den = timebase_den; - + if (max_b_frames > -1) { state->codec_ctx->max_b_frames = max_b_frames; } @@ -123,8 +106,7 @@ UNIFEX_TERM create(UnifexEnv *env, int width, int height, char *pix_fmt, set_x264_defaults(¶ms, preset); - for(unsigned int i=0; i= (*max_frames)) { *max_frames *= 2; frames = unifex_realloc(frames, (*max_frames) * sizeof(*frames)); - decoding_ts = unifex_realloc(decoding_ts, - (*max_frames) * sizeof(*decoding_ts)); - presentation_ts = unifex_realloc(presentation_ts, - (*max_frames) * sizeof(*presentation_ts)); + decoding_ts = + unifex_realloc(decoding_ts, (*max_frames) * sizeof(*decoding_ts)); + presentation_ts = unifex_realloc( + presentation_ts, (*max_frames) * sizeof(*presentation_ts)); } decoding_ts[*frame_cnt] = pkt->dts; @@ -233,7 +214,7 @@ UNIFEX_TERM encode(UnifexEnv *env, UnifexPayload *payload, int64_t pts, frame->format = state->codec_ctx->pix_fmt; frame->width = state->codec_ctx->width; frame->height = state->codec_ctx->height; - if(keyframe_requested) { + if (keyframe_requested) { frame->pict_type = AV_PICTURE_TYPE_I; } av_image_fill_arrays(frame->data, frame->linesize, payload->data, From 6254372e8a2f1305c333cd126d8222538b157e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Kita?= Date: Fri, 23 Aug 2024 13:37:10 +0200 Subject: [PATCH 3/5] Add checks for the overwritten parameters. Rename FFmpegOption to FFmpegParam to distinguish between custom ffmpeg params and module options. --- c_src/membrane_h264_ffmpeg_plugin/encoder.c | 8 ++-- .../encoder.spec.exs | 4 +- lib/membrane_h264_ffmpeg/encoder.ex | 40 ++++++++++++++++--- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/c_src/membrane_h264_ffmpeg_plugin/encoder.c b/c_src/membrane_h264_ffmpeg_plugin/encoder.c index 613518e..3b855f1 100644 --- a/c_src/membrane_h264_ffmpeg_plugin/encoder.c +++ b/c_src/membrane_h264_ffmpeg_plugin/encoder.c @@ -55,8 +55,8 @@ static void set_x264_defaults(AVDictionary **params, char *preset) { UNIFEX_TERM create(UnifexEnv *env, int width, int height, char *pix_fmt, char *preset, char *tune, char *profile, int max_b_frames, int gop_size, int timebase_num, int timebase_den, int crf, - int sc_threshold, ffmpeg_option *ffmpeg_options, - unsigned int ffmpeg_options_length) { + int sc_threshold, ffmpeg_param *ffmpeg_params, + unsigned int ffmpeg_params_length) { UNIFEX_TERM res; AVDictionary *params = NULL; State *state = unifex_alloc_state(env); @@ -106,8 +106,8 @@ UNIFEX_TERM create(UnifexEnv *env, int width, int height, char *pix_fmt, set_x264_defaults(¶ms, preset); - for (unsigned int i = 0; i < ffmpeg_options_length; i++) { - av_dict_set(¶ms, ffmpeg_options[i].key, ffmpeg_options[i].value, 0); + for (unsigned int i = 0; i < ffmpeg_params_length; i++) { + av_dict_set(¶ms, ffmpeg_params[i].key, ffmpeg_params[i].value, 0); } av_dict_set(¶ms, "preset", preset, 0); diff --git a/c_src/membrane_h264_ffmpeg_plugin/encoder.spec.exs b/c_src/membrane_h264_ffmpeg_plugin/encoder.spec.exs index 76da4fb..b1b5453 100644 --- a/c_src/membrane_h264_ffmpeg_plugin/encoder.spec.exs +++ b/c_src/membrane_h264_ffmpeg_plugin/encoder.spec.exs @@ -3,7 +3,7 @@ module Membrane.H264.FFmpeg.Encoder.Native state_type "State" type( - ffmpeg_option :: %Membrane.H264.FFmpeg.Encoder.FFmpegOption{ + ffmpeg_param :: %Membrane.H264.FFmpeg.Encoder.FFmpegParam{ key: string, value: string } @@ -22,7 +22,7 @@ spec create( timebase_den :: int, crf :: int, sc_threshold :: int, - ffmpeg_options :: [ffmpeg_option] + ffmpeg_params :: [ffmpeg_param] ) :: {:ok :: label, state} | {:error :: label, reason :: atom} spec get_frame_size(state) :: {:ok :: label, frame_size :: int} | {:error :: label} diff --git a/lib/membrane_h264_ffmpeg/encoder.ex b/lib/membrane_h264_ffmpeg/encoder.ex index 3cb5020..19297de 100644 --- a/lib/membrane_h264_ffmpeg/encoder.ex +++ b/lib/membrane_h264_ffmpeg/encoder.ex @@ -12,6 +12,7 @@ defmodule Membrane.H264.FFmpeg.Encoder do Please check `t:t/0` for available options. """ use Membrane.Filter + require Membrane.Logger, as: Logger alias __MODULE__.Native alias Membrane.Buffer alias Membrane.H264 @@ -122,15 +123,20 @@ defmodule Membrane.H264.FFmpeg.Encoder do """, default: @default_sc_threshold ], - ffmpeg_options: [ + ffmpeg_params: [ spec: %{String.t() => String.t()}, description: """ - A map with options that will be passed to the encoder. + A map with parameters that are passed to the encoder. + + You can use options from: https://ffmpeg.org/ffmpeg-codecs.html#libx264_002c-libx264rgb + and https://ffmpeg.org/ffmpeg-codecs.html#Codec-Options + Remember not to overwrite options that are available as a separate module options + (like `sc_threshold` or `crf`). """, default: %{} ] - defmodule FFmpegOption do + defmodule FFmpegParam do @moduledoc false @enforce_keys [:key, :value] defstruct @enforce_keys @@ -138,6 +144,8 @@ defmodule Membrane.H264.FFmpeg.Encoder do @impl true def handle_init(_ctx, opts) do + warn_if_ffmpeg_params_overwrite_module_options(opts.ffmpeg_params) + state = opts |> Map.put(:encoder_ref, nil) @@ -178,8 +186,8 @@ defmodule Membrane.H264.FFmpeg.Encoder do frames_per_second when is_integer(frames_per_second) -> {1, frames_per_second} end - ffmpeg_options = - Enum.map(state.ffmpeg_options, fn {key, value} -> %FFmpegOption{key: key, value: value} end) + ffmpeg_params = + Enum.map(state.ffmpeg_params, fn {key, value} -> %FFmpegParam{key: key, value: value} end) with buffers <- flush_encoder_if_exists(state), {:ok, new_encoder_ref} <- @@ -196,7 +204,7 @@ defmodule Membrane.H264.FFmpeg.Encoder do timebase_den, state.crf, state.sc_threshold, - ffmpeg_options + ffmpeg_params ) do stream_format = create_new_stream_format(stream_format, state) actions = buffers ++ [stream_format: stream_format] @@ -274,4 +282,24 @@ defmodule Membrane.H264.FFmpeg.Encoder do other -> other end end + + defp warn_if_ffmpeg_params_overwrite_module_options(ffmpeg_params) do + params_to_options_mapping = %{ + "crf" => "crf", + "preset" => "preset", + "profile" => "profile", + "tune" => "tune", + "max_b_frames" => "max_b_frames", + "g" => "gop_size", + "sc_threshold" => "sc_threshold" + } + + Map.keys(ffmpeg_params) + |> Enum.filter(fn param_name -> params_to_options_mapping[param_name] != nil end) + |> Enum.each(fn param_name -> + Logger.warning( + "The parameter: #{param_name} you provided in the `ffmpeg_params` map overwrites the setting from the modules option: #{params_to_options_mapping[param_name]}." + ) + end) + end end From 9b6941ad29f4bf5bf0a0d7464c42af986f03bc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Kita?= Date: Fri, 23 Aug 2024 15:07:43 +0200 Subject: [PATCH 4/5] Modify the warning message --- lib/membrane_h264_ffmpeg/encoder.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/membrane_h264_ffmpeg/encoder.ex b/lib/membrane_h264_ffmpeg/encoder.ex index 19297de..25c37ea 100644 --- a/lib/membrane_h264_ffmpeg/encoder.ex +++ b/lib/membrane_h264_ffmpeg/encoder.ex @@ -298,7 +298,7 @@ defmodule Membrane.H264.FFmpeg.Encoder do |> Enum.filter(fn param_name -> params_to_options_mapping[param_name] != nil end) |> Enum.each(fn param_name -> Logger.warning( - "The parameter: #{param_name} you provided in the `ffmpeg_params` map overwrites the setting from the modules option: #{params_to_options_mapping[param_name]}." + "The parameter: `#{param_name}` you provided in the `ffmpeg_params` map overwrites the setting from the modules option: `#{params_to_options_mapping[param_name]}`." ) end) end From 68cb873e97c3f5a4be5ed73d09300f14e624a654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Kita?= Date: Fri, 23 Aug 2024 17:01:18 +0200 Subject: [PATCH 5/5] Update lib/membrane_h264_ffmpeg/encoder.ex Co-authored-by: Mateusz Front --- lib/membrane_h264_ffmpeg/encoder.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/membrane_h264_ffmpeg/encoder.ex b/lib/membrane_h264_ffmpeg/encoder.ex index 25c37ea..d62be76 100644 --- a/lib/membrane_h264_ffmpeg/encoder.ex +++ b/lib/membrane_h264_ffmpeg/encoder.ex @@ -130,8 +130,8 @@ defmodule Membrane.H264.FFmpeg.Encoder do You can use options from: https://ffmpeg.org/ffmpeg-codecs.html#libx264_002c-libx264rgb and https://ffmpeg.org/ffmpeg-codecs.html#Codec-Options - Remember not to overwrite options that are available as a separate module options - (like `sc_threshold` or `crf`). + Options available in the element options (`t:#{inspect(__MODULE__)}.t/0`), like `sc_threshold` or `crf`, + must be set there and not through `ffmpeg_params`. """, default: %{} ]