Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create encoder #2

Merged
merged 24 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions bundlex.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@ defmodule Membrane.VPx.BundlexProject do
[
vpx_decoder: [
interface: :nif,
sources: ["vpx_decoder.c"],
sources: ["vpx_decoder.c", "vpx_common.c"],
os_deps: [
libvpx: [
{:precompiled, Membrane.PrecompiledDependencyProvider.get_dependency_url(:libvpx)},
{:pkg_config, "vpx"}
]
],
preprocessor: Unifex
],
vpx_encoder: [
interface: :nif,
sources: ["vpx_encoder.c", "vpx_common.c"],
os_deps: [
libvpx: [
{:precompiled, Membrane.PrecompiledDependencyProvider.get_dependency_url(:libvpx)},
Expand All @@ -20,14 +31,6 @@ defmodule Membrane.VPx.BundlexProject do
],
preprocessor: Unifex
]
# vpx_encoder: [
# interface: :nif,
# sources: ["vpx_encoder.c"],
# os_deps: [
# libvpx: [{:pkg_config, "vpx"}]
# ],
# preprocessor: Unifex
# ]
]
end
end
85 changes: 85 additions & 0 deletions c_src/membrane_vpx_plugin/vpx_common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include "vpx_common.h"

UNIFEX_TERM result_error(
UnifexEnv *env,
const char *reason,
UNIFEX_TERM (*result_error_fun)(UnifexEnv *, const char *),
vpx_codec_ctx_t *codec_context,
void *state
) {
char *full_reason;
if (codec_context) {
const char *error = vpx_codec_error(codec_context);
const char *detail = vpx_codec_error_detail(codec_context);
if (detail) {
full_reason = unifex_alloc(strlen(reason) + strlen(error) + strlen(detail) + 5);
sprintf(full_reason, "%s: %s: %s", reason, error, detail);
} else {
full_reason = unifex_alloc(strlen(reason) + strlen(error) + 3);
sprintf(full_reason, "%s: %s", reason, error);
}
} else {
full_reason = unifex_alloc(strlen(reason) + 1);
sprintf(full_reason, "%s", reason);
}

if (state) unifex_release_resource(state);

UNIFEX_TERM result = result_error_fun(env, full_reason);
unifex_free(full_reason);
return result;
}

Dimensions get_plane_dimensions(const vpx_image_t *img, int plane) {
const int height =
(plane > 0 && img->y_chroma_shift > 0) ? (img->d_h + 1) >> img->y_chroma_shift : img->d_h;

int width =
(plane > 0 && img->x_chroma_shift > 0) ? (img->d_w + 1) >> img->x_chroma_shift : img->d_w;

// Fixing NV12 chroma width if it is odd
if (img->fmt == VPX_IMG_FMT_NV12 && plane == 1) width = (width + 1) & ~1;

return (Dimensions){width, height};
}

void convert_between_image_and_raw_frame(
vpx_image_t *img, UnifexPayload *raw_frame, ConversionType conversion_type
) {
const int bytes_per_pixel = (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;

// Assuming that for nv12 we write all chroma data at once
const int number_of_planes = (img->fmt == VPX_IMG_FMT_NV12) ? 2 : 3;
unsigned char *frame_data = raw_frame->data;

for (int plane = 0; plane < number_of_planes; ++plane) {
unsigned char *image_buf = img->planes[plane];
const int stride = img->stride[plane];
Dimensions plane_dimensions = get_plane_dimensions(img, plane);

for (unsigned int y = 0; y < plane_dimensions.height; ++y) {
size_t bytes_to_write = bytes_per_pixel * plane_dimensions.width;
switch (conversion_type) {
case RAW_FRAME_TO_IMAGE:
memcpy(image_buf, frame_data, bytes_to_write);
break;

case IMAGE_TO_RAW_FRAME:
memcpy(frame_data, image_buf, bytes_to_write);
break;
}
image_buf += stride;
frame_data += bytes_to_write;
varsill marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

void free_payloads(UnifexPayload **payloads, unsigned int payloads_cnt) {
for (unsigned int i = 0; i < payloads_cnt; i++) {
if (payloads[i] != NULL) {
unifex_payload_release(payloads[i]);
unifex_free(payloads[i]);
}
}
unifex_free(payloads);
}
28 changes: 28 additions & 0 deletions c_src/membrane_vpx_plugin/vpx_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once
#include "vpx/vpx_codec.h"
#include "vpx/vpx_image.h"
#include <unifex/payload.h>
#include <unifex/unifex.h>

typedef struct Dimensions {
unsigned int width;
unsigned int height;
} Dimensions;

UNIFEX_TERM result_error(
UnifexEnv *env,
const char *reason,
UNIFEX_TERM (*result_error_fun)(UnifexEnv *, const char *),
vpx_codec_ctx_t *codec_context,
void *state
);

typedef enum ConversionType { IMAGE_TO_RAW_FRAME, RAW_FRAME_TO_IMAGE } ConversionType;

Dimensions get_plane_dimensions(const vpx_image_t *img, int plane);

void free_payloads(UnifexPayload **payloads, unsigned int payloads_cnt);

void convert_between_image_and_raw_frame(
vpx_image_t *img, UnifexPayload *raw_frame, ConversionType conversion_type
);
65 changes: 14 additions & 51 deletions c_src/membrane_vpx_plugin/vpx_decoder.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "vpx_decoder.h"

// The following code is based on the simple_decoder example provided by libvpx
// (https://github.com/webmproject/libvpx/blob/main/examples/simple_decoder.c)

void handle_destroy_state(UnifexEnv *env, State *state) {
UNIFEX_UNUSED(env);

Expand All @@ -20,28 +23,13 @@ UNIFEX_TERM create(UnifexEnv *env, Codec codec) {
}

if (vpx_codec_dec_init(&state->codec_context, state->codec_interface, NULL, 0)) {
result = create_result_error(env, "Failed to initialize decoder");
unifex_release_state(env, state);
return result;
return result_error(env, "Failed to initialize decoder", create_result_error, NULL, state);
}
result = create_result_ok(env, state);
unifex_release_state(env, state);
return result;
}

Dimensions get_plane_dimensions(const vpx_image_t *img, int plane) {
const int height =
(plane > 0 && img->y_chroma_shift > 0) ? (img->d_h + 1) >> img->y_chroma_shift : img->d_h;

int width =
(plane > 0 && img->x_chroma_shift > 0) ? (img->d_w + 1) >> img->x_chroma_shift : img->d_w;

// Fixing NV12 chroma width if it is odd
if (img->fmt == VPX_IMG_FMT_NV12 && plane == 1)
width = (width + 1) & ~1;

return (Dimensions){width, height};
}
size_t get_image_byte_size(const vpx_image_t *img) {
const int bytes_per_pixel = (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
const int number_of_planes = (img->fmt == VPX_IMG_FMT_NV12) ? 2 : 3;
Expand All @@ -55,25 +43,8 @@ size_t get_image_byte_size(const vpx_image_t *img) {
return image_size;
}

void get_output_frame_from_image(const vpx_image_t *img, UnifexPayload *output_frame) {
const int bytes_per_pixel = (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;

// Assuming that for nv12 we write all chroma data at once
const int number_of_planes = (img->fmt == VPX_IMG_FMT_NV12) ? 2 : 3;
unsigned char *frame_data = output_frame->data;

for (int plane = 0; plane < number_of_planes; ++plane) {
const unsigned char *buf = img->planes[plane];
const int stride = img->stride[plane];
Dimensions plane_dimensions = get_plane_dimensions(img, plane);

for (unsigned int y = 0; y < plane_dimensions.height; ++y) {
size_t bytes_to_write = bytes_per_pixel * plane_dimensions.width;
memcpy(frame_data, buf, bytes_to_write);
buf += stride;
frame_data += bytes_to_write;
}
}
void get_raw_frame_from_image(vpx_image_t *img, UnifexPayload *raw_frame) {
convert_between_image_and_raw_frame(img, raw_frame, IMAGE_TO_RAW_FRAME);
}

void alloc_output_frame(UnifexEnv *env, const vpx_image_t *img, UnifexPayload **output_frame) {
Expand All @@ -85,19 +56,14 @@ PixelFormat get_pixel_format_from_image(vpx_image_t *img) {
switch (img->fmt) {
case VPX_IMG_FMT_I422:
return PIXEL_FORMAT_I422;

case VPX_IMG_FMT_I420:
return PIXEL_FORMAT_I420;

case VPX_IMG_FMT_I444:
return PIXEL_FORMAT_I444;

case VPX_IMG_FMT_YV12:
return PIXEL_FORMAT_YV12;

case VPX_IMG_FMT_NV12:
return PIXEL_FORMAT_NV12;

default:
return PIXEL_FORMAT_I420;
}
Expand All @@ -107,11 +73,13 @@ UNIFEX_TERM decode_frame(UnifexEnv *env, UnifexPayload *frame, State *state) {
vpx_codec_iter_t iter = NULL;
vpx_image_t *img = NULL;
PixelFormat pixel_format = PIXEL_FORMAT_I420;
unsigned int frames_cnt = 0, allocated_frames = 2;
UnifexPayload **output_frames = unifex_alloc(allocated_frames * sizeof(*output_frames));
unsigned int frames_cnt = 0, allocated_frames = 1;
UnifexPayload **output_frames = unifex_alloc(allocated_frames * sizeof(UnifexPayload*));

if (vpx_codec_decode(&state->codec_context, frame->data, frame->size, NULL, 0)) {
return decode_frame_result_error(env, "Decoding frame failed");
return result_error(
env, "Decoding frame failed", decode_frame_result_error, &state->codec_context, NULL
);
}

while ((img = vpx_codec_get_frame(&state->codec_context, &iter)) != NULL) {
Expand All @@ -121,19 +89,14 @@ UNIFEX_TERM decode_frame(UnifexEnv *env, UnifexPayload *frame, State *state) {
}

alloc_output_frame(env, img, &output_frames[frames_cnt]);
get_output_frame_from_image(img, output_frames[frames_cnt]);
get_raw_frame_from_image(img, output_frames[frames_cnt]);
pixel_format = get_pixel_format_from_image(img);
frames_cnt++;
}

UNIFEX_TERM result = decode_frame_result_ok(env, output_frames, frames_cnt, pixel_format);
for (unsigned int i = 0; i < frames_cnt; i++) {
if (output_frames[i] != NULL) {
unifex_payload_release(output_frames[i]);
unifex_free(output_frames[i]);
}
}
unifex_free(output_frames);

free_payloads(output_frames, frames_cnt);

return result;
}
6 changes: 1 addition & 5 deletions c_src/membrane_vpx_plugin/vpx_decoder.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
#pragma once
#include "vpx/vp8dx.h"
#include "vpx/vpx_decoder.h"
#include "vpx_common.h"
#include <erl_nif.h>

typedef struct State {
vpx_codec_ctx_t codec_context;
vpx_codec_iface_t *codec_interface;
} State;

typedef struct Dimensions {
unsigned int width;
unsigned int height;
} Dimensions;

#include "_generated/vpx_decoder.h"
2 changes: 1 addition & 1 deletion c_src/membrane_vpx_plugin/vpx_decoder.spec.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ spec decode_frame(payload, state) ::
{:ok :: label, frames :: [payload], pixel_format :: pixel_format}
| {:error :: label, reason :: atom}

dirty :cpu, create: 1, decode_frame: 2
dirty :cpu, [:create, :decode_frame]
Loading