Skip to content

Commit

Permalink
Decode single frame
Browse files Browse the repository at this point in the history
  • Loading branch information
mickel8 committed Jan 19, 2024
1 parent bc94cde commit e06fba4
Show file tree
Hide file tree
Showing 10 changed files with 392 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ FFMPEG_INCLUDE_DIR = ffmpeg_build/include
FFMPEG_LIB_DIR = ffmpeg_build/lib

# uncomment to compile with debug logs
# XAV_DEBUG_LOGS = -DXAV_DEBUG=1
XAV_DEBUG_LOGS = -DXAV_DEBUG=1

ifeq ($(USE_BUNDLED_FFMPEG), true)
CFLAGS = -fPIC -I$(ERTS_INCLUDE_DIR) -I${FFMPEG_INCLUDE_DIR} -I${XAV_DIR} -shared $(XAV_DEBUG_LOGS)
Expand Down
Empty file added c_src/xav/decoder.c
Empty file.
6 changes: 6 additions & 0 deletions c_src/xav/decoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include <libavcodec/avcodec.h>

struct Decoder {
const AVCodec *codec;
AVCodecContext *c;
};
17 changes: 2 additions & 15 deletions c_src/xav/reader.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "reader.h"

void convert_to_rgb(AVFrame *src_frame, uint8_t *dst_data[], int dst_linesize[]);
#include "utils.h"

int reader_init(struct Reader *reader, char *path, size_t path_size, int device_flag,
enum AVMediaType media_type) {
Expand Down Expand Up @@ -251,16 +250,4 @@ void reader_free(struct Reader *reader) {
av_frame_free(&reader->frame);
avformat_close_input(&reader->fmt_ctx);
XAV_FREE(reader->path);
}

void convert_to_rgb(AVFrame *src_frame, uint8_t *dst_data[], int dst_linesize[]) {
struct SwsContext *sws_ctx =
sws_getContext(src_frame->width, src_frame->height, src_frame->format, src_frame->width,
src_frame->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL);

av_image_alloc(dst_data, dst_linesize, src_frame->width, src_frame->height, AV_PIX_FMT_RGB24, 1);

// is this (const uint8_t * const*) cast really correct?
sws_scale(sws_ctx, (const uint8_t *const *)src_frame->data, src_frame->linesize, 0,
src_frame->height, dst_data, dst_linesize);
}
}
12 changes: 12 additions & 0 deletions c_src/xav/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ void print_supported_pix_fmts(AVCodec *codec) {
}
}

void convert_to_rgb(AVFrame *src_frame, uint8_t *dst_data[], int dst_linesize[]) {
struct SwsContext *sws_ctx =
sws_getContext(src_frame->width, src_frame->height, src_frame->format, src_frame->width,
src_frame->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL);

av_image_alloc(dst_data, dst_linesize, src_frame->width, src_frame->height, AV_PIX_FMT_RGB24, 1);

// is this (const uint8_t * const*) cast really correct?
sws_scale(sws_ctx, (const uint8_t *const *)src_frame->data, src_frame->linesize, 0,
src_frame->height, dst_data, dst_linesize);
}

ERL_NIF_TERM xav_nif_ok(ErlNifEnv *env, ERL_NIF_TERM data_term) {
ERL_NIF_TERM ok_term = enif_make_atom(env, "ok");
return enif_make_tuple(env, 2, ok_term, data_term);
Expand Down
8 changes: 8 additions & 0 deletions c_src/xav/utils.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#include <erl_nif.h>
// TODO revisit imports
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>

#ifdef XAV_DEBUG
#define XAV_LOG_DEBUG(X, ...) \
Expand All @@ -12,6 +19,7 @@
#define XAV_FREE(X) enif_free(X)

void print_supported_pix_fmts(AVCodec *codec);
void convert_to_rgb(AVFrame *src_frame, uint8_t *dst_data[], int dst_linesize[]);

ERL_NIF_TERM xav_nif_ok(ErlNifEnv *env, ERL_NIF_TERM data_term);
ERL_NIF_TERM xav_nif_error(ErlNifEnv *env, char *reason);
Expand Down
125 changes: 124 additions & 1 deletion c_src/xav/xav_nif.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "reader.h"
#include "decoder.h"

ErlNifResourceType *reader_resource_type;
ErlNifResourceType *decoder_resource_type;

ERL_NIF_TERM new_reader(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {

Expand Down Expand Up @@ -111,17 +113,138 @@ ERL_NIF_TERM next_frame(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
return xav_nif_ok(env, frame_term);
}

ERL_NIF_TERM new_decoder(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
struct Decoder *decoder = enif_alloc_resource(decoder_resource_type, sizeof(struct Decoder));

decoder->codec = avcodec_find_decoder(AV_CODEC_ID_VP8);
if (!decoder->codec){
return xav_nif_raise(env, "decoder_not_found");
}

decoder->c = avcodec_alloc_context3(decoder->codec);
if (!decoder->c) {
return xav_nif_raise(env, "failed_to_alloc_context");
}

if (avcodec_open2(decoder->c, decoder->codec, NULL) <0){
return xav_nif_raise(env, "failed_to_open_codec");
}

ERL_NIF_TERM decoder_term = enif_make_resource(env, decoder);
enif_release_resource(decoder);

return xav_nif_ok(env, decoder_term);
}

ERL_NIF_TERM decode(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
if (argc != 4) {
return xav_nif_raise(env, "invalid_arg_count");
}

struct Decoder *decoder;
if (!enif_get_resource(env, argv[0], decoder_resource_type, (void **)&decoder)) {
return xav_nif_raise(env, "couldnt_get_decoder_resource");
}

ErlNifBinary data;
if (!enif_inspect_binary(env, argv[1], &data)) {
return xav_nif_raise(env, "couldnt_inspect_binary");
}

int pts;
if (!enif_get_int(env, argv[2], &pts)) {
return xav_nif_raise(env, "couldnt_get_int");
}

int dts;
if (!enif_get_int(env, argv[3], &pts)) {
return xav_nif_raise(env, "couldnt_get_int");
}

AVPacket *pkt = NULL;
pkt = av_packet_alloc();

if (!pkt) {
return xav_nif_raise(env, "couldnt_alloc_av_packet");
}

pkt->data = data.data;
pkt->size = data.size;
pkt->pts = pts;
pkt->dts = dts;

printf("size: %d\n", data.size);

int ret;
ret = avcodec_send_packet(decoder->c, pkt);
if (ret != 0) {
return xav_nif_raise(env, "couldnt_send_packet");
}

printf("send ret: %d\n", ret);

AVFrame *frame = av_frame_alloc();

if(!frame) {
return xav_nif_raise(env, "couldnt_alloc_frame");
}

ret = avcodec_receive_frame(decoder->c, frame);
if (ret != 0) {
return xav_nif_raise(env, "failed_to_decode1");
}

printf("%d %d %d %d %d\n", frame->width, frame->height, frame->format, frame->key_frame, frame->linesize);
// ret = avcodec_receive_frame(decoder->c, frame);


// if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
// return xav_nif_raise(env, "failed_to_decode2");
// }

uint8_t **frame_data;
int *frame_linesize;
uint8_t *rgb_dst_data[4];
int rgb_dst_linesize[4];
if (frame->format != AV_PIX_FMT_RGB24) {
printf("Converting to rgb\n");
convert_to_rgb(frame, rgb_dst_data, rgb_dst_linesize);
frame_data=rgb_dst_data;
frame_linesize=rgb_dst_linesize;
} else {
frame_data = frame->data;
frame_linesize = frame->linesize;
}

ERL_NIF_TERM frame_term = xav_nif_frame_to_term(env, frame_data, frame_linesize, "vp8", frame->width, frame->height, frame->pts);

return xav_nif_ok(env, frame_term);
}

void free_reader(ErlNifEnv *env, void *obj) {
struct Reader *reader = (struct Reader *)obj;
reader_free(reader);
}

void free_decoder(ErlNifEnv *env, void *obj) {
XAV_LOG_DEBUG("Freeing Decoder object");
struct Decoder *decoder = (struct Decoder *)obj;
if (decoder->c) {
avcodec_free_context(&decoder->c);
}
}

static ErlNifFunc xav_funcs[] = {{"new_reader", 3, new_reader},
{"next_frame", 1, next_frame, ERL_NIF_DIRTY_JOB_CPU_BOUND}};
{"next_frame", 1, next_frame, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"new_decoder", 0, new_decoder},
{"decode", 4, decode, ERL_NIF_DIRTY_JOB_CPU_BOUND}};

static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) {
reader_resource_type =
enif_open_resource_type(env, NULL, "Reader", free_reader, ERL_NIF_RT_CREATE, NULL);

decoder_resource_type =
enif_open_resource_type(env, NULL, "Decoder", free_decoder, ERL_NIF_RT_CREATE, NULL);
return 0;
}

Expand Down
9 changes: 9 additions & 0 deletions lib/decoder.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Xav.Decoder do

Check warning on line 1 in lib/decoder.ex

View workflow job for this annotation

GitHub Actions / lint OTP 25 / Elixir 1.14

Modules should have a @moduledoc tag.
def new() do
Xav.NIF.new_decoder()
end

def decode(decoder, data, pts, dts) do
Xav.NIF.decode(decoder, data, pts, dts)
end
end
6 changes: 5 additions & 1 deletion lib/nif.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ defmodule Xav.NIF do
@on_load :__on_load__

def __on_load__ do
path = :filename.join(:code.priv_dir(:xav), 'libxav')
path = :filename.join(:code.priv_dir(:xav), ~c"libxav")
:ok = :erlang.load_nif(path, 0)
end

def new_reader(_path, _device, _video), do: :erlang.nif_error(:undef)

def next_frame(_reader), do: :erlang.nif_error(:undef)

def new_decoder(), do: :erlang.nif_error(:undef)

def decode(_decoder, _data, _pts, _dts), do: :erlang.nif_error(:undef)
end
Loading

0 comments on commit e06fba4

Please sign in to comment.