diff --git a/.circleci/config.yml b/.circleci/config.yml index b3f50192..826ae26c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,12 +2,102 @@ version: 2.1 orbs: elixir: membraneframework/elixir@1 -jobs: +executors: + linux_x86_executor: + docker: + - image: membraneframeworklabs/docker_membrane + resource_class: medium + + linux_arm_executor: + docker: + - image: membraneframeworklabs/docker_membrane + resource_class: arm.medium + + manylinux_x86_executor: + docker: + - image: quay.io/pypa/manylinux2014_x86_64 + resource_class: medium + + manylinux_arm_executor: + docker: + - image: quay.io/pypa/manylinux2014_aarch64 + resource_class: arm.medium + + macos_arm_executor: + macos: + xcode: 16.4.0 + resource_class: macos.m1.medium.gen1 + +commands: + mix_release_server: + parameters: + platform: + type: enum + enum: [macos-arm, linux-x86, linux-arm] + cache-version: + description: "Cache version number, can be used to force cache recreation" + type: integer + default: 1 + + steps: + - checkout + - when: + condition: + equal: [<< parameters.platform >>, macos-arm] + steps: + - run: + name: Install erlang and elixir + command: | + brew install asdf openssl + asdf plugin add elixir || true + asdf plugin add erlang || true + asdf install + echo 'export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"' >> $BASH_ENV + - run: + name: Install hex and rebar + command: | + mix local.hex --force + mix local.rebar --force + - elixir/get_mix_deps: + cache-version: << parameters.cache-version >> + - elixir/use_build_cache: + cache-version: << parameters.cache-version >> + - run: + name: Mix release + command: mix release server + - run: + name: Tar the release + command: | + mkdir -p artifacts + tar -czvf artifacts/boombox-server-<< parameters.platform >>.tar.gz -C _build/prod/rel/server . + - persist_to_workspace: + root: ~/project/ + paths: + - artifacts + + publish_python_package_linux: + steps: + - checkout: + path: ~/project + - run: + name: Build wheel and repair with auditwheel + command: | + /opt/python/cp311-cp311/bin/pip install build twine + /opt/python/cp311-cp311/bin/python -m build --wheel + auditwheel repair dist/*.whl --wheel-dir ../wheelhouse + - run: + name: Publish to PyPI + working_directory: ~/project/wheelhouse + command: | + /opt/python/cp311-cp311/bin/pip install twine + /opt/python/cp311-cp311/bin/twine upload --username __token__ --password $PYPI_API_KEY * + +jobs: test_exclude_browser: docker: - image: membraneframeworklabs/docker_membrane - environment: - MIX_ENV: test + environment: + MIX_ENV: test working_directory: ~/app steps: @@ -18,29 +108,174 @@ jobs: - run: mix compile - run: mix test --exclude browser + + compare_versions: + docker: + - image: ubuntu + + steps: + - checkout + - run: + name: Compare the versions from mix.exs and python/pyproject.toml + command: | + python_package_version=$(grep "^version =" python/pyproject.toml | awk '{print $3}' | tr -d ' "') + elixir_package_version=$(grep "^ @version" mix.exs | awk '{print $2}' | tr -d ' "') + if [ $python_package_version != $elixir_package_version ]; then + echo "package versions don't match - python: $python_package_version, elixir: $elixir_package_version" + exit 1 + fi + + mix_release_server_macos_arm: + executor: macos_arm_executor + + environment: + MIX_ENV: prod + + steps: + - mix_release_server: + platform: macos-arm + + mix_release_server_linux_x86: + executor: linux_x86_executor + + environment: + MIX_ENV: prod + + steps: + - mix_release_server: + platform: linux-x86 + + mix_release_server_linux_arm: + executor: linux_arm_executor + + environment: + MIX_ENV: prod + + steps: + - mix_release_server: + platform: linux-arm + + publish_github_release: + parameters: + version: + description: version of boombox + type: string + + docker: + - image: ubuntu + + steps: + - attach_workspace: + at: ~/ + - run: + name: Install ghr tool + command: | + apt update + apt install -y wget + wget https://github.com/tcnksm/ghr/releases/download/v0.16.0/ghr_v0.16.0_linux_amd64.tar.gz + tar -xf ghr_v0.16.0_linux_amd64.tar.gz + - run: + name: Publish release + command: + ./ghr_v0.16.0_linux_amd64/ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete << parameters.version >> ~/artifacts/ + + publish_python_package_macos_arm: + executor: macos_arm_executor + working_directory: ~/project/python + steps: + - checkout: + path: ~/project + - run: + name: Install build and publish tools + command: pip install build twine + - run: + name: Build wheel (no sdist) + command: python -m build --wheel + - run: + name: Publish to PyPI + command: twine upload --verbose --username __token__ --password $PYPI_API_KEY dist/* + + publish_python_package_linux_x86: + executor: manylinux_x86_executor + working_directory: ~/project/python + steps: + - publish_python_package_linux + + publish_python_package_linux_arm: + executor: manylinux_arm_executor + working_directory: ~/project/python + steps: + - publish_python_package_linux + + build_python_docs: + docker: + - image: curlimages/curl:8.16.0 + + steps: + - run: + name: Trigger docs build + command: + curl -X POST -d token=$READTHEDOCS_WEBHOOK_TOKEN $READTHEDOCS_WEBHOOK_URL + workflows: - version: 2 build: jobs: - elixir/build_test: - filters: &filters + filters: &push_filters tags: only: /v.*/ - elixir/lint: - filters: - <<: *filters + filters: *push_filters - test_exclude_browser: - filters: - <<: *filters + filters: *push_filters + - compare_versions: + filters: *push_filters - elixir/hex_publish: requires: - elixir/build_test - test_exclude_browser - elixir/lint - context: - - Deployment - filters: + - compare_versions + filters: &publish_filters branches: ignore: /.*/ tags: only: /v.*/ + context: + - Deployment + - mix_release_server_macos_arm: &mix_release_job_config + requires: + - elixir/build_test + - test_exclude_browser + - elixir/lint + - compare_versions + filters: *publish_filters + context: + - Deployment + - mix_release_server_linux_x86: *mix_release_job_config + - mix_release_server_linux_arm: *mix_release_job_config + - publish_github_release: + requires: + - mix_release_server_macos_arm + - mix_release_server_linux_x86 + - mix_release_server_linux_arm + filters: *publish_filters + context: + - Deployment + version: << pipeline.git.tag >> + - publish_python_package_macos_arm: &publish_python_package_job_config + requires: + - publish_github_release + filters: *publish_filters + context: + - Deployment + - publish_python_package_linux_x86: *publish_python_package_job_config + - publish_python_package_linux_arm: *publish_python_package_job_config + - build_python_docs: + requires: + - publish_python_package_macos_arm + - publish_python_package_linux_x86 + - publish_python_package_linux_arm + filters: *publish_filters + context: + - Deployment diff --git a/mix.exs b/mix.exs index 2676c3dc..250542f7 100644 --- a/mix.exs +++ b/mix.exs @@ -52,7 +52,7 @@ defmodule Boombox.Mixfile do {:membrane_mp4_plugin, "~> 0.36.0"}, {:membrane_realtimer_plugin, "~> 0.9.0"}, {:membrane_http_adaptive_stream_plugin, "~> 0.20.1"}, - {:membrane_rtmp_plugin, "~> 0.27.2"}, + {:membrane_rtmp_plugin, "~> 0.29.1"}, {:membrane_rtsp_plugin, "~> 0.6.1"}, {:membrane_rtp_plugin, "~> 0.30.0"}, {:membrane_rtp_format, "~> 0.10.0"}, @@ -60,6 +60,7 @@ defmodule Boombox.Mixfile do {:membrane_rtp_h264_plugin, "~> 0.20.0"}, {:membrane_rtp_opus_plugin, "~> 0.10.0"}, {:membrane_rtp_h265_plugin, "~> 0.5.2"}, + {:membrane_vpx_plugin, "~> 0.4.2"}, {:membrane_ffmpeg_swresample_plugin, "~> 0.20.0"}, {:membrane_hackney_plugin, "~> 0.11.0"}, {:membrane_ffmpeg_swscale_plugin, "~> 0.16.2"}, @@ -211,7 +212,10 @@ defmodule Boombox.Mixfile do |> Enum.map(&Path.relative_to(&1, base_dir)) |> Map.new(&{Path.basename(&1), &1}) - Path.wildcard("#{base_dir}/*/priv/bundlex/*/*") + # Restore symlinks + # /priv/shared/precompiled/ -> ../../../../bundlex-/priv/shared//lib + Path.join(base_dir, "*/priv/bundlex/*/*") + |> Path.wildcard() |> Enum.each(fn path -> name = Path.basename(path) @@ -225,6 +229,9 @@ defmodule Boombox.Mixfile do end end) + # Restore symlinks + # bundlex-/priv/shared/precompiled//lib/.dylib -> ..dylib + # e.g. libvpx.dylib -> libvpx.11.dylib Path.join(base_dir, "bundlex*/priv/shared/precompiled/*/lib") |> Path.wildcard() |> Enum.map(fn lib_dir -> diff --git a/mix.lock b/mix.lock index 6a4e7c10..cdb93181 100644 --- a/mix.lock +++ b/mix.lock @@ -25,8 +25,8 @@ "ex_dtls": {:hex, :ex_dtls, "0.18.0", "0815e3384bb0c1e6c06559012479cf9a94a501ddf46c3df54dc2d1b169e29d5c", [:mix], [{:bundlex, "~> 1.5.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "562eda1815eeaed8360b2b5c34d4db5b453794bc096404a4c64f193fa7b18bf2"}, "ex_hls": {:hex, :ex_hls, "0.1.2", "fca2c2a4ddf8459b9a47bf1fd6552c5d74cccf5dc72f56cf87129111c3e2f8ee", [:mix], [{:ex_m3u8, "~> 0.15.3", [hex: :ex_m3u8, repo: "hexpm", optional: false]}, {:membrane_h26x_plugin, "~> 0.10.2", [hex: :membrane_h26x_plugin, repo: "hexpm", optional: false]}, {:membrane_mp4_plugin, "~> 0.36.0", [hex: :membrane_mp4_plugin, repo: "hexpm", optional: false]}, {:mpeg_ts, "~> 2.0.0", [hex: :mpeg_ts, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}, {:req, "~> 0.5.10", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "8129921a863918999cda4032cf66e5e4de9fa24faa33cee28a09b34f13438a49"}, "ex_ice": {:hex, :ex_ice, "0.13.0", "13a6ae106b26bb5f2957a586bf20d4031299e5b968533828e637bb4ac7645d31", [:mix], [{:elixir_uuid, "~> 1.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:ex_stun, "~> 0.2.0", [hex: :ex_stun, repo: "hexpm", optional: false]}, {:ex_turn, "~> 0.2.0", [hex: :ex_turn, repo: "hexpm", optional: false]}], "hexpm", "0d65afa15e36b5610d0f51e72e4c25b22346caa9a6d7d2f6f1cfd8db94bd494e"}, - "ex_libsrt": {:hex, :ex_libsrt, "0.1.2", "a2e5e212382a7f004c6f0d0910fd9588c9da3d87a0dc4b60ab1b39321652d851", [:mix], [{:unifex, "~> 1.2.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "d599f7f1c0fd0e246be083557ae2001bf1f8edd457ad3af3ed037696c2573af0"}, - "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"}, + "ex_libsrt": {:hex, :ex_libsrt, "0.1.3", "e4a07c96cb88f392187fcd2622a15251e71e524a59f291066cd72629075a3461", [:mix], [{:membrane_precompiled_dependency_provider, "~> 0.2.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.2.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "c4a7774db47e3f7066a10db73fa1b909a4e36f0feec1d12c44696f8207d3f1c4"}, + "ex_libsrtp": {:hex, :ex_libsrtp, "0.7.3", "f0a0dcb6c6518986c61a01ff47e99d71ff6eeef8108a207d92e3ab8a3687b435", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "0964a9ad35f4aa871a472fa827cfef8dcd3cbf22c912a32bc7b19a8769fbc744"}, "ex_m3u8": {:hex, :ex_m3u8, "0.15.3", "c10427f450b2ed7bfd85808d8dce21214f1fe9fa18927591cbbf96fea0a6a8aa", [:mix], [{:nimble_parsec, "~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "99f20c0b44bab130dc6aca71fefe0d1a174413ae9ac2763220994b29bd310939"}, "ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"}, "ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"}, @@ -47,27 +47,27 @@ "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [: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", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, - "membrane_aac_fdk_plugin": {:hex, :membrane_aac_fdk_plugin, "0.18.12", "f5f71e4c94ee4fdfd04412f57526ae85e8946905466ba0cfd70e4deb73d7e2c8", [:mix], [{:bunch, "~> 1.4", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "fae60e3a53de35bde1b1ffb917a12247fa5ea211c025d50bb2ff88b3333d9f0b"}, + "membrane_aac_fdk_plugin": {:hex, :membrane_aac_fdk_plugin, "0.18.13", "5baed1190e1231e23bd8f99bb8a84e282199beed649341c0f9de818890178293", [:mix], [{:bunch, "~> 1.4", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "4bc789c34a432099fe9c61c917c95ff66154d06ee7efd1785bc317a75b413247"}, "membrane_aac_format": {:hex, :membrane_aac_format, "0.8.0", "515631eabd6e584e0e9af2cea80471fee6246484dbbefc4726c1d93ece8e0838", [:mix], [{:bimap, "~> 1.1", [hex: :bimap, repo: "hexpm", optional: false]}], "hexpm", "a30176a94491033ed32be45e51d509fc70a5ee6e751f12fd6c0d60bd637013f6"}, "membrane_aac_plugin": {:hex, :membrane_aac_plugin, "0.19.1", "29b9eecf75e3d60b16e7aac0861a501caa16f6d34f88e6d1d4140fdd4926292b", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "21158745f4d748eb15dd63e872d21a7deacb055294c0efb24b31960ad0400171"}, "membrane_cmaf_format": {:hex, :membrane_cmaf_format, "0.7.1", "9ea858faefdcb181cdfa8001be827c35c5f854e9809ad57d7062cff1f0f703fd", [:mix], [], "hexpm", "3c7b4ed2a986e27f6f336d2f19e9442cb31d93b3142fc024c019572faca54a73"}, "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.2.4", "3f9fc78cef29b69acadd4f959c8ec23cbb1544c26c8e8474589b143ada9a0da2", [: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", "ec7a77b7ab457267c0243338383365f6ef5ace2686ddc129939e502a58eba546"}, - "membrane_ffmpeg_swresample_plugin": {:hex, :membrane_ffmpeg_swresample_plugin, "0.20.2", "2e669f0b25418d10b51a73bc52d2e12e4a3a26b416c5c1199d852c3f781a18b3", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:mockery, "~> 2.1", [hex: :mockery, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "6c8d3bcd61d568dd94cabb9b45f29e8926e0076e4432d8f419378e004e02147c"}, - "membrane_ffmpeg_swscale_plugin": {:hex, :membrane_ffmpeg_swscale_plugin, "0.16.2", "581909312d6d12ed560ee99caa1b1674a339760ab2ad6835d243326806c23da1", [:mix], [{:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.4.1", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}], "hexpm", "46c185dacff1e1b404d0ceb74d0d5224f0931fe1e8b951cc3776ebd099e39afc"}, + "membrane_ffmpeg_swresample_plugin": {:hex, :membrane_ffmpeg_swresample_plugin, "0.20.3", "6ff3c71413b0172f409482b441d7f31011d7f9823ce8a810141c7834b3553578", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:mockery, "~> 2.1", [hex: :mockery, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "a1c1b14fbb5c3ebd26907942beaaeb4b7e6185c9cb668ef59d0819315fa3375a"}, + "membrane_ffmpeg_swscale_plugin": {:hex, :membrane_ffmpeg_swscale_plugin, "0.16.3", "82e127221cb72eedd65ab08127abfaa1e67cfadc763e309a539b8cc8c57ebe22", [:mix], [{:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.4.1", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}], "hexpm", "45384da60bb58ab2236bdd19119baf3df09e70906acf5801ac5dd1bd22921c4a"}, "membrane_file_plugin": {:hex, :membrane_file_plugin, "0.17.2", "650e134c2345d946f930082fac8bac9f5aba785a7817d38a9a9da41ffc56fa92", [:mix], [{:logger_backends, "~> 1.0", [hex: :logger_backends, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "df50c6040004cd7b901cf057bd7e99c875bbbd6ae574efc93b2c753c96f43b9d"}, "membrane_flv_plugin": {:hex, :membrane_flv_plugin, "0.12.0", "d715ad405af86dcaf4b2f479e34088e1f6738c7280366828e1066b39d2aa493a", [:mix], [{:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.1", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}], "hexpm", "a317872d6d394e550c7bfd8979f12a3a1cc1e89b547d75360321025b403d3279"}, "membrane_funnel_plugin": {:hex, :membrane_funnel_plugin, "0.9.2", "2b2e840dbb232ce29aaff2d55bd329d9978766518dbeb6e8dba7aba7115fadcc", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "865ac9d84f86698e2cfeb7904d3b12ab74855a38ca651a880db1505965fa77cc"}, - "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.32.5", "30542fb5d6d36961a51906549b4338f4fc66a304bf92e7c7123e2b9971e3502d", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.1", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.4.1", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "8c80e11b9ec9ca23d44304ed7bb3daf665e98b91b2488608ee5718a88182e363"}, + "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.32.6", "7da7575e908ec982a14bdbfd5fb069ea121021b322739815e3fe194444539bfc", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.1", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.4.1", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "1e333b0343f90d4ec58acc44a7edf7f6a8c5a73eb70407e42b201d664357b290"}, "membrane_h264_format": {:hex, :membrane_h264_format, "0.6.1", "44836cd9de0abe989b146df1e114507787efc0cf0da2368f17a10c47b4e0738c", [:mix], [], "hexpm", "4b79be56465a876d2eac2c3af99e115374bbdc03eb1dea4f696ee9a8033cd4b0"}, - "membrane_h265_ffmpeg_plugin": {:hex, :membrane_h265_ffmpeg_plugin, "0.4.2", "6dcd932fc2c65a851ab7a44f3996cc9613163bdf23b00568c87c9c0754a64edf", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h265_format, "~> 0.2.0", [hex: :membrane_h265_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.4.1", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "d7eb02110a47a9c11f1432f95b775ba7ce5e962dbe8fc07b421bdd2ce5283b2d"}, + "membrane_h265_ffmpeg_plugin": {:hex, :membrane_h265_ffmpeg_plugin, "0.4.3", "7decdf3e97052946b2a972da68628f7e1b309f027221553d21b7fbec84f2784b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h265_format, "~> 0.2.0", [hex: :membrane_h265_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.4.1", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "24dc587731e7b5bc4b35da1b85e849476eff6a9a7b934ff0b2b5efae037b8589"}, "membrane_h265_format": {:hex, :membrane_h265_format, "0.2.0", "1903c072cf7b0980c4d0c117ab61a2cd33e88782b696290de29570a7fab34819", [:mix], [], "hexpm", "6df418bdf242c0d9f7dbf2e5aea4c2d182e34ac9ad5a8b8cef2610c290002e83"}, "membrane_h26x_plugin": {:hex, :membrane_h26x_plugin, "0.10.5", "e9fa1ee9cda944259c4d2728c8b279bfe0152a3a6c1af187b07fa8411ca4e25e", [:mix], [{:bunch, "~> 1.4", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_h265_format, "~> 0.2.0", [hex: :membrane_h265_format, repo: "hexpm", optional: false]}], "hexpm", "dd0287a6b6223e47bba30a8952d6ec53db35f6a3e33203b7ad786e995711f098"}, "membrane_hackney_plugin": {:hex, :membrane_hackney_plugin, "0.11.0", "54b368333a23394e7cac2f4d6b701bf8c5ee6614670a31f4ebe009b5e691a5c1", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "2b28fd1be3c889d5824d7d985598386c7673828c88f49a91221df3626af8a998"}, "membrane_http_adaptive_stream_plugin": {:hex, :membrane_http_adaptive_stream_plugin, "0.20.1", "dc42d4b64f54bb8e658ee75096805ef110f4eb79644095bf9c4abd8118b827e1", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_hls, "~> 0.1.2", [hex: :ex_hls, repo: "hexpm", optional: false]}, {:membrane_aac_plugin, "~> 0.19.0", [hex: :membrane_aac_plugin, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.2", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h26x_plugin, "~> 0.10.0", [hex: :membrane_h26x_plugin, repo: "hexpm", optional: false]}, {:membrane_mp4_plugin, "~> 0.36.0", [hex: :membrane_mp4_plugin, repo: "hexpm", optional: false]}, {:membrane_tee_plugin, "~> 0.12.0", [hex: :membrane_tee_plugin, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:stream_split, "~> 0.1.7", [hex: :stream_split, repo: "hexpm", optional: false]}], "hexpm", "66e7fcc1dcd1c51ebe09a7b6f9f4474a0bde19bacd5d632731500a4c105da0b8"}, "membrane_ivf_plugin": {:hex, :membrane_ivf_plugin, "0.8.0", "0495a4fd34a1b9841d288b3a078d2b09c48020294a6452ce08e8954711648ac8", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.17.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_vp8_format, "~> 0.5.0", [hex: :membrane_vp8_format, repo: "hexpm", optional: false]}, {:membrane_vp9_format, "~> 0.5.0", [hex: :membrane_vp9_format, repo: "hexpm", optional: false]}], "hexpm", "b17805a451f1066dab68c19577efad7819200c20970e3ba5f7d6fbbc751240e9"}, - "membrane_mp3_lame_plugin": {:hex, :membrane_mp3_lame_plugin, "0.18.3", "441af1b5115c0d9cfc56023a25de42ee88d42ed9c4f4093bee344fe07d950e10", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_mpegaudio_format, "~> 0.3.0", [hex: :membrane_mpegaudio_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}], "hexpm", "f25b985d7f80a7c721d1f127dc62c57ea4797d5f617e334766ed29f391187a6b"}, - "membrane_mp3_mad_plugin": {:hex, :membrane_mp3_mad_plugin, "0.18.4", "30003dfbacfa92b51d5124107f9c502ee3f48d8dbc310d304d0164a819f8b8a8", [:mix], [{:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_mpegaudio_format, "~> 0.3.0", [hex: :membrane_mpegaudio_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "dc78f5ca1bc52380add89a2d0cce9706e97d00abcfbdd46ad82afe44b745a43b"}, + "membrane_mp3_lame_plugin": {:hex, :membrane_mp3_lame_plugin, "0.18.4", "340444758d741f6e4e7025a1f4e5393a5c39b6504e3740ad95edc3ff36ca2e21", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_mpegaudio_format, "~> 0.3.0", [hex: :membrane_mpegaudio_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}], "hexpm", "7d31f241fc2f6402aa7135d926b1086f098ce74ff63700564e5d48bba6fa5a0f"}, + "membrane_mp3_mad_plugin": {:hex, :membrane_mp3_mad_plugin, "0.18.5", "2e6adcdd250641a2485d4f1fd597036e632e877409043a6a3bb92a428fa69160", [:mix], [{:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_mpegaudio_format, "~> 0.3.0", [hex: :membrane_mpegaudio_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "84119929e6ca93044b5fa1c84a5bfcd5370dfa111d9c44d4b255a25f2d25e795"}, "membrane_mp4_format": {:hex, :membrane_mp4_format, "0.8.0", "8c6e7d68829228117d333b4fbb030e7be829aab49dd8cb047fdc664db1812e6a", [:mix], [], "hexpm", "148dea678a1f82ccfd44dbde6f936d2f21255f496cb45a22cc6eec427f025522"}, "membrane_mp4_plugin": {:hex, :membrane_mp4_plugin, "0.36.0", "ef4fc6fc72cc439974f2ae0ce579f3dbf8674f02ab00f7f524a6600b786a9ca8", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_cmaf_format, "~> 0.7.0", [hex: :membrane_cmaf_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.17.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.1", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_h265_format, "~> 0.2.0", [hex: :membrane_h265_format, repo: "hexpm", optional: false]}, {:membrane_mp4_format, "~> 0.8.0", [hex: :membrane_mp4_format, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_timestamp_queue, "~> 0.2.1", [hex: :membrane_timestamp_queue, repo: "hexpm", optional: false]}], "hexpm", "84f55a42c69cb557b73d6272f958812f607abaaa6a3473f301d22393f2a62808"}, "membrane_mpeg_ts_plugin": {:hex, :membrane_mpeg_ts_plugin, "1.3.4", "bb10d15a8caa8f17e9d34b7a7ef1702856abd4589c66f12467b3a2b43e95d517", [:mix], [{:crc, "~> 0.10", [hex: :crc, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mpeg_ts, "~> 2.0", [hex: :mpeg_ts, repo: "hexpm", optional: false]}], "hexpm", "d3aa72bfcdaac9b915f37fe3e8b42c7b1bf350c64311328a2cde4b3395c8a80e"}, @@ -75,12 +75,12 @@ "membrane_mpegts_plugin": {:hex, :membrane_mpegts_plugin, "0.5.0", "b9c72e01f7d0cb060468d2a61b6eeac7eda41a402c2ae5cd9e995ee1a8d35d4a", [:mix], [{:crc, "~> 0.10", [hex: :crc, repo: "hexpm", optional: false]}, {:membrane_aac_plugin, "~> 0.19.0", [hex: :membrane_aac_plugin, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.2.3", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h26x_plugin, "~> 0.10.4", [hex: :membrane_h26x_plugin, repo: "hexpm", optional: false]}, {:membrane_timestamp_queue, "~> 0.2.2", [hex: :membrane_timestamp_queue, repo: "hexpm", optional: false]}], "hexpm", "6a43ce1285aeb1ba5b6f359b0a479eb00f979839538d77b1a65f70cd357c9bd5"}, "membrane_ogg_plugin": {:hex, :membrane_ogg_plugin, "0.5.0", "1e910cee8f311e186f8ee26cd4ab06bde7e9d0072eb04eb1abb890372463308f", [:mix], [{:crc, "~> 0.10", [hex: :crc, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}], "hexpm", "dd009453c1e01c4da0e6afc23f0037d5cf6b1d3fcdf79959a61768588e725186"}, "membrane_opus_format": {:hex, :membrane_opus_format, "0.3.0", "3804d9916058b7cfa2baa0131a644d8186198d64f52d592ae09e0942513cb4c2", [:mix], [], "hexpm", "8fc89c97be50de23ded15f2050fe603dcce732566fe6fdd15a2de01cb6b81afe"}, - "membrane_opus_plugin": {:hex, :membrane_opus_plugin, "0.20.5", "aa344bb9931c8e22b2286778cce0658e0d4aa071a503c18c55e1b161e17ab337", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "94fd4447b6576780afc6144dbb0520b43bd399c86a10bf5df1fa878a91798cf6"}, - "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_opus_plugin": {:hex, :membrane_opus_plugin, "0.20.6", "cbdd4cb4e37ed5bf874d8bce8204576ccc897e44a9ef18fada7bc8f89a989eba", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "d852c5a9aa40d321f8ed3080f7c166d7d5614b927799c0a0821f5ccab164cdc4"}, + "membrane_precompiled_dependency_provider": {:hex, :membrane_precompiled_dependency_provider, "0.2.1", "d385afa61f9e30318d672960acdb951669bb911cd5ee98062d06c3b739a44a76", [:mix], [{:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "5470400b720581871efe688c9e446aef17085042ff80c7b8855f10de55d73c93"}, "membrane_raw_audio_format": {:hex, :membrane_raw_audio_format, "0.12.0", "b574cd90f69ce2a8b6201b0ccf0826ca28b0fbc8245b8078d9f11cef65f7d5d5", [:mix], [{:bimap, "~> 1.1", [hex: :bimap, repo: "hexpm", optional: false]}, {:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "6e6c98e3622a2b9df19eab50ba65d7eb45949b1ba306fa8423df6cdb12fd0b44"}, "membrane_raw_video_format": {:hex, :membrane_raw_video_format, "0.4.1", "d7344499c2d80f236a7ef962b5490c651341a501052ee43dec56cf0319fa3936", [:mix], [], "hexpm", "9920b7d445b5357608a364fec5685acdfce85334c647f745045237a0d296c442"}, "membrane_realtimer_plugin": {:hex, :membrane_realtimer_plugin, "0.9.0", "27210d5e32a5e8bfd101c41e4d8c1876e873a52cc129ebfbee4d0ccbea1cbd21", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "b2e96d62135ee57ef9a5fdea94b3a9ab1198e5ea8ee248391b89c671125d1b51"}, - "membrane_rtmp_plugin": {:hex, :membrane_rtmp_plugin, "0.27.3", "2ca6705668e207a2af1dfd459ec94cebf43bd40338b1b176af1b7ecfb7f5017e", [:mix], [{:membrane_aac_plugin, "~> 0.19.0", [hex: :membrane_aac_plugin, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.17.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_flv_plugin, "~> 0.12.0", [hex: :membrane_flv_plugin, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.9.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.1", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_h26x_plugin, "~> 0.10.0", [hex: :membrane_h26x_plugin, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.2", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "e3b3a858cee514aae1cd78b5c85a24b68edb15183e7aa909180c0ce7fb92ab10"}, + "membrane_rtmp_plugin": {:hex, :membrane_rtmp_plugin, "0.29.1", "ad3f9c937aa28eaa6b837a250367cb1ad4d600aa0c38ddcc7f2f2f2721cdaeaf", [:mix], [{:membrane_aac_plugin, "~> 0.19.0", [hex: :membrane_aac_plugin, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.17.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_flv_plugin, "~> 0.12.0", [hex: :membrane_flv_plugin, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.9.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.1", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_h26x_plugin, "~> 0.10.0", [hex: :membrane_h26x_plugin, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.2", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "8d0c70899627ba00e3d524945715971d8626a636e80eb76d4d63bbd1a88a3895"}, "membrane_rtp_aac_plugin": {:hex, :membrane_rtp_aac_plugin, "0.9.4", "355efe237151b304a479a8f0db12043aea2528718d045cb596cbfb85f64ff20a", [:mix], [{:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.10.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "f72d12b88b57a3c93eeea19c02c95c878c4b09883dbec703ae3d1557d3af44c0"}, "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.10.0", "e7f62b1f522647a64d88b60f635476dcb71db61dccda32ffa21ecdb965314c91", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "493245bb45faf3a08343e29281e2aadac72b795d48d64bb805ea8daa31cfd414"}, "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.20.2", "ab84db505d3102a9cdc300f137c78245ef3982a7ec545838f9544b6b0a2ca1ba", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.10.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "27f38c49544d1acf6f7c3f8770a7893f90813a31e8a26461e112a3d3142aff46"}, @@ -101,7 +101,7 @@ "membrane_udp_plugin": {:hex, :membrane_udp_plugin, "0.14.1", "18b807e85b01b8466ed2c7e23f35cd7f4db13ce6c8f1b3eaa23b7a6155de757f", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "ba7168c1436813c4c56f9aa1904997b9254b82db72a6573c8cd192e30bd452d5"}, "membrane_vp8_format": {:hex, :membrane_vp8_format, "0.5.0", "a589c20bb9d97ddc9b717684d00cefc84e2500ce63a0c33c4b9618d9b2f9b2ea", [:mix], [], "hexpm", "d29e0dae4bebc6838e82e031c181fe626d168c687e4bc617c1d0772bdeed19d5"}, "membrane_vp9_format": {:hex, :membrane_vp9_format, "0.5.0", "c6a4f2cbfc39dba5d80ad8287162c52b5cf6488676bd64435c1ac957bd16e66f", [:mix], [], "hexpm", "68752d8cbe7270ec222fc84a7d1553499f0d8ff86ef9d9e89f8955d49e20278e"}, - "membrane_vpx_plugin": {:hex, :membrane_vpx_plugin, "0.4.1", "e1cacee1c0ba0f95f4b22c62c4a0dda506480d168167c389100c57ba2031359e", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.4.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:membrane_vp8_format, "~> 0.5.0", [hex: :membrane_vp8_format, repo: "hexpm", optional: false]}, {:membrane_vp9_format, "~> 0.5.0", [hex: :membrane_vp9_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.2", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "f5d8306ea6e8e284f630b2db91071065031c476a4782c282ae9a2a7aa383b4b6"}, + "membrane_vpx_plugin": {:hex, :membrane_vpx_plugin, "0.4.2", "781944d370845c0259eb2d5c2eb2209bece17dc49ac3e85c11ef1c4161c0afd1", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.2.1", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.4.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:membrane_vp8_format, "~> 0.5.0", [hex: :membrane_vp8_format, repo: "hexpm", optional: false]}, {:membrane_vp9_format, "~> 0.5.0", [hex: :membrane_vp9_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.2", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "5621a389fe4fcd1c964b14d665025ec91127d1c9d848f38110000c25f8299c9d"}, "membrane_wav_plugin": {:hex, :membrane_wav_plugin, "0.10.1", "f2827079070463f881f37844d5eaba8c7caaa5a9c44efc2dd7c3699d314c406d", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.17.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: true]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}], "hexpm", "0c2dad1e263728fc859fd0bb8165f85f0518f7bff3f218641ffe3cc9ae64cc7e"}, "membrane_webrtc_plugin": {:hex, :membrane_webrtc_plugin, "0.26.0", "8605a8c35fe6e08e20eef927f0e730c8dff2b27ea015a4b4708c6c8f17c9e7d1", [:mix], [{:bandit, "~> 1.2", [hex: :bandit, repo: "hexpm", optional: false]}, {:corsica, "~> 2.0", [hex: :corsica, repo: "hexpm", optional: false]}, {:ex_webrtc, "~> 0.15.0", [hex: :ex_webrtc, repo: "hexpm", optional: false]}, {:membrane_core, ">= 1.2.2 and < 2.0.0-0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_h264_plugin, "~> 0.20.1", [hex: :membrane_rtp_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_opus_plugin, "~> 0.10.0", [hex: :membrane_rtp_opus_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_plugin, "~> 0.30.0", [hex: :membrane_rtp_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_vp8_plugin, "~> 0.9.4", [hex: :membrane_rtp_vp8_plugin, repo: "hexpm", optional: false]}, {:phoenix, ">= 0.0.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.0", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "fc84b7b0f7440b61abf9461139c768f136d30068bf42036644037d52169e0743"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, @@ -115,7 +115,7 @@ "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"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, - "phoenix_html": {:hex, :phoenix_html, "4.2.1", "35279e2a39140068fc03f8874408d58eef734e488fc142153f055c5454fd1c08", [:mix], [], "hexpm", "cff108100ae2715dd959ae8f2a8cef8e20b593f8dfd031c9cba92702cf23e053"}, + "phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"}, "playwright": {:hex, :playwright, "1.49.1-alpha.2", "911e7771f51874cca6d075b2c73e7a32e3d7e9b29dc38732f5121ac916cae35d", [:mix], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:esbuild, "~> 0.8.1", [hex: :esbuild, repo: "hexpm", optional: false]}, {:gun, "~> 1.3.3", [hex: :gun, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:recase, "~> 0.7", [hex: :recase, repo: "hexpm", optional: false]}], "hexpm", "492386163d6fd65967c39e4822478639e198bcf45e527253825f57d72867af52"}, "plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"}, "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, diff --git a/python/README.md b/python/README.md index 21b95a47..e2404969 100644 --- a/python/README.md +++ b/python/README.md @@ -1,7 +1,23 @@ ![boombox_transparent](https://github.com/user-attachments/assets/1c5f25a2-cc27-4349-ae72-91315d43d6a1) This package is a Python API of Boombox - a high-level tool for audio & video streaming tool based on the [Membrane Framework](https://membrane.stream). -For more comprehensive overview, check the [documentation](https://boombox.readthedocs.io). + +- **Documentation:** https://boombox.readthedocs.io +- **Source code:** https://github.com/membraneframework/boombox/tree/master/python + +## Installation + +The package is available on PyPI and can be downloaded with pip: + +```bash +pip install boomboxlib +``` + +Because of some reliance on native libraries for encoding and decoding, the package is available for +the following platforms: + - MacOS on ARM architecture + - Linux on x86 architecture + - Linux on ARM architecture ## Example usage The following example will read the MP4 file from the provided URL, flip the video and save it to diff --git a/python/pyproject.toml b/python/pyproject.toml index da312a68..66a8ec74 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -1,6 +1,6 @@ [project] -name = "boombox" -version = "0.1.0" +name = "boomboxlib" +version = "0.2.5" description = "Boombox SDK for Python" readme = "README.md" authors = [ @@ -11,7 +11,9 @@ license-files = [ "LICENSE" ] requires-python = ">=3.11" dependencies = [ "numpy >= 2.2.5", - "typing-extensions >= 4.13.2, < 5" + "typing-extensions >= 4.13.2, < 5", + "tqdm >= 4.67.1, < 5", + "platformdirs >= 4.4, < 5" ] [build-system] @@ -26,7 +28,3 @@ where = ["src"] [tool.setuptools.package-data] "*" = ["**"] - -#[tool.distutils.bdist_wheel] -#plat-name = "macosx_14_4_arm64" - diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 00000000..2f57c307 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,9 @@ +from setuptools import Extension, setup + +# This empty C file is only so that when building wheels they are built for the +# specific platform +setup( + ext_modules=[ + Extension("guard", ["src/boombox/guard.c"]), + ], +) diff --git a/python/src/boombox/boombox.py b/python/src/boombox/boombox.py index 02a0a4e1..0310e64e 100644 --- a/python/src/boombox/boombox.py +++ b/python/src/boombox/boombox.py @@ -12,6 +12,13 @@ import warnings import dataclasses import threading +import tqdm +import urllib.request +import platformdirs +import platform +import importlib.metadata +import tarfile +import logging from ._vendor.pyrlang import process, node from ._vendor.term import Atom, Pid @@ -21,6 +28,10 @@ from typing_extensions import override +RELEASES_URL = "https://github.com/membraneframework/boombox/releases" +PACKAGE_NAME = "boomboxlib" + + class Boombox(process.Process): """ Boombox is a tool that allows to transform a media stream from one @@ -79,6 +90,9 @@ class Boombox(process.Process): _python_node_name: ClassVar[str] _cookie: ClassVar[str] + _data_dir: str + _server_release_path: str + _version: str _process_name: Atom _erlang_node_name: Atom _receiver: tuple[Atom, Atom] | Pid @@ -103,10 +117,11 @@ def __init__( "RELEASE_COOKIE": self._cookie, "RELEASE_DISTRIBUTION": "name", } - release_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "erlang", "bin", "server" - ) - self._erlang_process = subprocess.Popen([release_path, "start"], env=env) + + self._server_release_path = os.path.join(self._data_dir, "bin", "server") + self._download_elixir_boombox_release() + + self._erlang_process = subprocess.Popen([self._server_release_path, "start"], env=env) atexit.register(lambda: self._erlang_process.kill()) super().__init__(True) @@ -300,6 +315,61 @@ def _handle_termination(self) -> None: async def _await_future(self, response_future): return await response_future + def _download_elixir_boombox_release(self) -> None: + class TqdmUpTo(tqdm.tqdm): + def update_to(self, b=1, bsize=1, tsize=None): + if tsize is not None: + self.total = tsize + self.update(b * bsize - self.n) + + try: + self._version = importlib.metadata.version(PACKAGE_NAME) + except importlib.metadata.PackageNotFoundError: + self._version = "dev" + + self._data_dir = platformdirs.user_data_dir( + appname=PACKAGE_NAME, ensure_exists=True, version=self._version + ) + + if os.path.exists(self._server_release_path): + logging.info("Elixir boombox release already present.") + return + logging.info("Elixir boombox release not found, downloading...") + + if self._version == "dev": + release_url = os.path.join(RELEASES_URL, "latest/download") + else: + release_url = os.path.join(RELEASES_URL, f"download/v{self._version}") + + system = platform.system().lower() + arch = platform.machine().lower() + + if system == "linux" and arch == "x86_64": + release_tarball = "boombox-server-linux-x86.tar.gz" + elif system == "darwin" and arch == "arm64": + release_tarball = "boombox-server-macos-arm.tar.gz" + else: + raise RuntimeError(f"Unsupported platform: {system} {arch}") + + download_url = os.path.join(release_url, release_tarball) + tarball_path = os.path.join(self._data_dir, release_tarball) + + with TqdmUpTo( + unit="B", + unit_scale=True, + unit_divisor=1024, + miniters=1, + desc=f"Downloading {release_tarball}", + ) as t: + urllib.request.urlretrieve( + download_url, filename=tarball_path, reporthook=t.update_to + ) + + logging.info("Download complete. Extracting...") + with tarfile.open(tarball_path) as tar: + tar.extractall(self._data_dir) + os.remove(tarball_path) + @staticmethod def _dtype_to_sample_format(dtype: np.dtype) -> tuple[AudioSampleFormat, np.dtype]: type_mapping = {"i": "s", "u": "u", "f": "f"} diff --git a/python/src/boombox/guard.c b/python/src/boombox/guard.c new file mode 100644 index 00000000..6cb4bb55 --- /dev/null +++ b/python/src/boombox/guard.c @@ -0,0 +1,2 @@ +// This empty C file is only so that when building wheels they are built for the +// specific platform