Skip to content

Commit

Permalink
Use precompiled ffmpeg (#49)
Browse files Browse the repository at this point in the history
* Use precompiled ffmpeg

* Change releases to latests

* Ignore gcc warnings

* Prepare for releasing

* Unify get_ffmpeg_url

* Cleanup pragmas

* Replace depracated av_get_channel_layout_nb_channels with AVChannelLayout

* Remove unecessary warning ignoring
  • Loading branch information
DominikWolek authored Oct 12, 2023
1 parent 32051e1 commit a4be345
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 47 deletions.
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,30 @@ It is a part of [Membrane Multimedia Framework](https://membrane.stream).
Add the following line to your `deps` in `mix.exs`. Run `mix deps.get`.

```elixir
{:membrane_ffmpeg_swresample_plugin, "~> 0.17.3"}
{:membrane_ffmpeg_swresample_plugin, "~> 0.18.0"}
```

You also need to have [FFmpeg](https://www.ffmpeg.org/) library installed.
The precompiled builds of the [ffmpeg](https://www.ffmpeg.org) will be pulled and linked automatically. However, should there be any problems, consider installing it manually.

### Manual instalation of dependencies

#### macOS

```shell
brew install ffmpeg
```

#### Ubuntu

```shell
sudo apt-get install ffmpeg
```

#### Arch / Manjaro

```shell
pacman -S ffmpeg
```

## Usage

Expand Down
22 changes: 21 additions & 1 deletion bundlex.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
defmodule Membrane.FFmpeg.SWResample.BundlexProject do
use Bundlex.Project

defp get_ffmpeg_url() do
membrane_precompiled_url_prefix =
"https://github.com/membraneframework-precompiled/precompiled_ffmpeg/releases/latest/download/ffmpeg"

case Bundlex.get_target() do
%{os: "linux"} ->
{:precompiled,
"https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n6.0-latest-linux64-gpl-shared-6.0.tar.xz"}

%{architecture: "x86_64", os: "darwin" <> _rest_of_os_name} ->
{:precompiled, "#{membrane_precompiled_url_prefix}_macos_intel.tar.gz"}

%{architecture: "aarch64", os: "darwin" <> _rest_of_os_name} ->
{:precompiled, "#{membrane_precompiled_url_prefix}_macos_arm.tar.gz"}

_other ->
nil
end
end

def project do
[
natives: natives()
Expand All @@ -13,7 +33,7 @@ defmodule Membrane.FFmpeg.SWResample.BundlexProject do
interface: :nif,
sources: ["converter.c", "converter_lib.c"],
deps: [membrane_common_c: :membrane, unifex: :unifex],
pkg_configs: ["libavutil", "libswresample"],
os_deps: [{[get_ffmpeg_url(), :pkg_config], ["libswresample", "libavutil"]}],
preprocessor: Unifex
]
]
Expand Down
3 changes: 2 additions & 1 deletion c_src/membrane_ffmpeg_swresample_plugin/converter.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#pragma once

#include <erl_nif.h>
#include <membrane/membrane.h>

#include <erl_nif.h>
#include <stdio.h>

#include "converter_lib.h"
Expand Down
99 changes: 68 additions & 31 deletions c_src/membrane_ffmpeg_swresample_plugin/converter_lib.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@

#include "converter_lib.h"

char *lib_init(ConverterState *state, char from_s24le,
enum AVSampleFormat src_sample_fmt, int src_rate,
int64_t src_ch_layout, enum AVSampleFormat dst_sample_fmt,
int dst_rate, int64_t dst_ch_layout) {
int dst_rate, int64_t dst_ch_layout)
{
state->swr_ctx = NULL;

struct SwrContext *swr_ctx = swr_alloc();
if (!swr_ctx) {
if (!swr_ctx)
{
return "swr_alloc";
}

Expand All @@ -23,35 +26,46 @@ char *lib_init(ConverterState *state, char from_s24le,
if (swr_init(swr_ctx) < 0)
return "swr_init";

AVChannelLayout src_ch_av_layout;
AVChannelLayout dst_ch_av_layout;
av_channel_layout_from_mask(&src_ch_av_layout, src_ch_layout);
av_channel_layout_from_mask(&dst_ch_av_layout, dst_ch_layout);

*state = (ConverterState){
.swr_ctx = swr_ctx,
.src_rate = src_rate,
.dst_rate = dst_rate,
.src_sample_fmt = src_sample_fmt,
.dst_sample_fmt = dst_sample_fmt,
.src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout),
.dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout),
.src_nb_channels = src_ch_av_layout.nb_channels,
.dst_nb_channels = dst_ch_av_layout.nb_channels,
.from_s24le = from_s24le};

return NULL;
}

static char *free_conversion_data(char *error, ConverterState *state,
uint8_t *input, uint8_t **src_data,
uint8_t **dst_data) {
if (state->from_s24le && input) {
uint8_t **dst_data)
{
if (state->from_s24le && input)
{
unifex_free(input);
}

if (src_data) {
if (src_data[0]) {
if (src_data)
{
if (src_data[0])
{
av_freep(&src_data[0]);
}
av_freep(&src_data);
}

if (dst_data) {
if (error && dst_data[0]) {
if (dst_data)
{
if (error && dst_data[0])
{
av_freep(&dst_data[0]);
}
av_freep(&dst_data);
Expand All @@ -60,18 +74,21 @@ static char *free_conversion_data(char *error, ConverterState *state,
return error;
}

static char *convert_s24le_to_s32le(uint8_t **data, int *data_size) {
static char *convert_s24le_to_s32le(uint8_t **data, int *data_size)
{
uint8_t *input = *data;
int input_size = *data_size;

if (input_size % 3 != 0) {
if (input_size % 3 != 0)
{
return "invalid_input_size";
}

int output_size = input_size * 4 / 3;
uint8_t *output = unifex_alloc(output_size);

for (int i = 0; i < input_size / 3; i++) {
for (int i = 0; i < input_size / 3; i++)
{
uint8_t b0 = input[3 * i];
uint8_t b1 = input[3 * i + 1];
uint8_t b2 = input[3 * i + 2];
Expand All @@ -87,10 +104,13 @@ static char *convert_s24le_to_s32le(uint8_t **data, int *data_size) {
}

char *lib_convert(ConverterState *state, uint8_t *input, int input_size,
uint8_t **output, int *output_size) {
if (state->from_s24le) {
uint8_t **output, int *output_size)
{
if (state->from_s24le)
{
char *res = convert_s24le_to_s32le(&input, &input_size);
if (res) {
if (res)
{
return res;
}
}
Expand All @@ -103,7 +123,8 @@ char *lib_convert(ConverterState *state, uint8_t *input, int input_size,

if (0 > av_samples_alloc_array_and_samples(
&src_data, &src_linesize, state->src_nb_channels, src_nb_samples,
state->src_sample_fmt, 1)) {
state->src_sample_fmt, 1))
{
return free_conversion_data("alloc_source_samples", state, input, src_data,
dst_data);
}
Expand All @@ -114,26 +135,32 @@ char *lib_convert(ConverterState *state, uint8_t *input, int input_size,

if (0 > av_samples_alloc_array_and_samples(
&dst_data, &dst_linesize, state->dst_nb_channels,
max_dst_nb_samples, state->dst_sample_fmt, 1)) {
max_dst_nb_samples, state->dst_sample_fmt, 1))
{
return free_conversion_data("alloc_destination_samples", state, input,
src_data, dst_data);
}

int dst_nb_samples = swr_convert(state->swr_ctx, dst_data, max_dst_nb_samples,
(const uint8_t **)src_data, src_nb_samples);

if (dst_nb_samples < 0) {
if (dst_nb_samples < 0)
{
return free_conversion_data("convert", state, input, src_data, dst_data);
}

if (dst_nb_samples == 0) {
if (dst_nb_samples == 0)
{
*output_size = 0;
} else {
}
else
{
*output_size =
av_samples_get_buffer_size(&dst_linesize, state->dst_nb_channels,
dst_nb_samples, state->dst_sample_fmt, 1);

if (*output_size < 0) {
if (*output_size < 0)
{
return free_conversion_data("calculate_output_size", state, input,
src_data, dst_data);
}
Expand All @@ -144,13 +171,15 @@ char *lib_convert(ConverterState *state, uint8_t *input, int input_size,
return NULL;
}

char *lib_flush(ConverterState *state, uint8_t **output, int *output_size) {
char *lib_flush(ConverterState *state, uint8_t **output, int *output_size)
{
uint8_t **dst_data = NULL;
int dst_linesize;

int max_dst_nb_samples = swr_get_out_samples(state->swr_ctx, 0);

if (max_dst_nb_samples == 0) {
if (max_dst_nb_samples == 0)
{
// nothing was buffered, converter does not need flush
*output = NULL;
*output_size = 0;
Expand All @@ -159,27 +188,33 @@ char *lib_flush(ConverterState *state, uint8_t **output, int *output_size) {

if (0 > av_samples_alloc_array_and_samples(
&dst_data, &dst_linesize, state->dst_nb_channels,
max_dst_nb_samples, state->dst_sample_fmt, 0)) {
max_dst_nb_samples, state->dst_sample_fmt, 0))
{
return free_conversion_data("alloc_destination_samples", state, NULL, NULL,
dst_data);
}

int dst_nb_samples =
swr_convert(state->swr_ctx, dst_data, max_dst_nb_samples, NULL, 0);

if (dst_nb_samples < 0) {
if (dst_nb_samples < 0)
{
return free_conversion_data("convert", state, NULL, NULL, dst_data);
}

if (dst_nb_samples == 0) {
if (dst_nb_samples == 0)
{
*output = NULL;
*output_size = 0;
} else {
}
else
{
*output = dst_data[0];
*output_size =
av_samples_get_buffer_size(&dst_linesize, state->dst_nb_channels,
dst_nb_samples, state->dst_sample_fmt, 1);
if (*output_size < 0) {
if (*output_size < 0)
{
return free_conversion_data("calculate_output_size", state, NULL, NULL,
dst_data);
}
Expand All @@ -191,8 +226,10 @@ char *lib_flush(ConverterState *state, uint8_t **output, int *output_size) {

void lib_free_output(uint8_t **output) { av_freep(output); }

void lib_destroy(ConverterState *state) {
if (state->swr_ctx) {
void lib_destroy(ConverterState *state)
{
if (state->swr_ctx)
{
swr_free(&(state->swr_ctx));
}
}
8 changes: 4 additions & 4 deletions c_src/membrane_ffmpeg_swresample_plugin/converter_lib.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <unifex/unifex.h>

typedef struct ConverterState {
typedef struct ConverterState
{
struct SwrContext *swr_ctx;
enum AVSampleFormat src_sample_fmt, dst_sample_fmt;
int src_rate, dst_rate;
Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Membrane.FFmpeg.SWResample.Mixfile do
use Mix.Project

@github_url "https://github.com/membraneframework/membrane_ffmpeg_swresample_plugin"
@version "0.17.3"
@version "0.18.0"

def project do
[
Expand Down Expand Up @@ -45,7 +45,7 @@ defmodule Membrane.FFmpeg.SWResample.Mixfile do
{:bunch, "~> 1.6"},
{:unifex, "~> 1.1"},
{:membrane_common_c, "~> 0.15.0"},
{:bundlex, "~> 1.0"},
{:bundlex, "~> 1.2"},
# Testing
{:mockery, "~> 2.1", runtime: false},
{:membrane_file_plugin, "~> 0.15.0", only: :test},
Expand Down
Loading

0 comments on commit a4be345

Please sign in to comment.