Packages
membrane_h264_ffmpeg_plugin
0.22.1
0.32.7
0.32.6
0.32.5
0.32.4
0.32.3
0.32.2
0.32.1
0.32.0
0.31.8
0.31.7
0.31.6
0.31.5
0.31.4
0.31.3
0.31.2
0.31.1
0.31.0
0.30.1
0.30.0
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.2
0.26.1
0.26.0
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.0
0.23.0
0.22.1
0.22.0
0.21.6
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.3
0.16.2
0.16.1
0.16.0
0.15.0
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
Membrane H264 decoder and encoder based on FFmpeg and x264
Current section
Files
Jump to
Current section
Files
c_src/membrane_h264_ffmpeg_plugin/encoder.c
#include "encoder.h"
void handle_destroy_state(UnifexEnv *env, State *state) {
UNIFEX_UNUSED(env);
if (state->codec_ctx != NULL) {
avcodec_free_context(&state->codec_ctx);
}
}
UNIFEX_TERM create(UnifexEnv *env, int width, int height, char *pix_fmt,
char *preset, char *profile, int max_b_frames,
int framerate_num, int framerate_denom, int crf) {
UNIFEX_TERM res;
AVDictionary *params = NULL;
State *state = unifex_alloc_state(env);
state->codec_ctx = NULL;
state->last_pts = -1;
// TODO: Consider using av_log_set_callback to pass messages to membrane
// logger
av_log_set_level(AV_LOG_QUIET);
#if (LIBAVCODEC_VERSION_MAJOR < 58)
avcodec_register_all();
#endif
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec) {
res = create_result_error(env, "nocodec");
goto exit_create;
}
state->codec_ctx = avcodec_alloc_context3(codec);
if (!state->codec_ctx) {
res = create_result_error(env, "codec_alloc");
goto exit_create;
}
state->codec_ctx->width = width;
state->codec_ctx->height = height;
if (strcmp(pix_fmt, "I420") == 0) {
state->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
} else if (strcmp(pix_fmt, "I422") == 0) {
state->codec_ctx->pix_fmt = AV_PIX_FMT_YUV422P;
} else {
res = create_result_error(env, "pix_fmt");
goto exit_create;
}
state->codec_ctx->framerate.num = framerate_num;
state->codec_ctx->framerate.den = framerate_denom;
if (framerate_num == 0) {
state->codec_ctx->time_base.num = 1;
state->codec_ctx->time_base.den = 30;
} else {
state->codec_ctx->time_base.num = framerate_denom;
state->codec_ctx->time_base.den = framerate_num;
}
if (max_b_frames > -1) {
state->codec_ctx->max_b_frames = max_b_frames;
}
av_dict_set(¶ms, "preset", preset, 0);
if (strcmp("nil", profile) != 0) {
av_dict_set(¶ms, "profile", profile, 0);
}
av_dict_set_int(¶ms, "crf", crf, 0);
if (avcodec_open2(state->codec_ctx, codec, ¶ms) < 0) {
res = create_result_error(env, "codec_open");
goto exit_create;
}
res = create_result_ok(env, state);
exit_create:
unifex_release_state(env, state);
return res;
}
UNIFEX_TERM get_frame_size(UnifexEnv *env, State *state) {
int frame_size = av_image_get_buffer_size(state->codec_ctx->pix_fmt,
state->codec_ctx->width,
state->codec_ctx->height, 1);
if (frame_size < 0) {
return get_frame_size_result_error(env);
}
return get_frame_size_result_ok(env, frame_size);
}
static int get_frames(UnifexEnv *env, AVFrame *frame,
UnifexPayload ***ret_frames, int64_t **dts_list,
int *max_frames, int *frame_cnt, int use_shm,
State *state) {
AVPacket *pkt = av_packet_alloc();
UnifexPayload **frames = unifex_alloc((*max_frames) * sizeof(*frames));
int64_t *timestamps = unifex_alloc((*max_frames) * sizeof(*timestamps));
int ret = avcodec_send_frame(state->codec_ctx, frame);
if (ret < 0) {
ret = ENCODER_SEND_FRAME_ERROR;
goto exit_get_frames;
}
ret = avcodec_receive_packet(state->codec_ctx, pkt);
while (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
if (ret < 0) {
ret = ENCODER_ENCODE_ERROR;
goto exit_get_frames;
}
if (*frame_cnt >= (*max_frames)) {
*max_frames *= 2;
frames = unifex_realloc(frames, (*max_frames) * sizeof(*frames));
timestamps =
unifex_realloc(timestamps, (*max_frames) * sizeof(*timestamps));
}
timestamps[*frame_cnt] = pkt->dts;
frames[*frame_cnt] = unifex_alloc(sizeof(UnifexPayload));
UnifexPayloadType payload_type;
if (use_shm) {
payload_type = UNIFEX_PAYLOAD_SHM;
} else {
payload_type = UNIFEX_PAYLOAD_BINARY;
}
unifex_payload_alloc(env, payload_type, pkt->size, frames[*frame_cnt]);
memcpy(frames[*frame_cnt]->data, pkt->data, pkt->size);
(*frame_cnt)++;
ret = avcodec_receive_packet(state->codec_ctx, pkt);
}
ret = 0;
exit_get_frames:
*ret_frames = frames;
*dts_list = timestamps;
av_packet_free(&pkt);
return ret;
}
UNIFEX_TERM encode(UnifexEnv *env, UnifexPayload *payload, int64_t pts,
int use_shm, State *state) {
UNIFEX_TERM res_term;
int res = 0;
int max_frames = 16, frame_cnt = 0;
UnifexPayload **out_frames = NULL;
int64_t *dts_list = NULL;
AVFrame *frame = av_frame_alloc();
frame->format = state->codec_ctx->pix_fmt;
frame->width = state->codec_ctx->width;
frame->height = state->codec_ctx->height;
av_image_fill_arrays(frame->data, frame->linesize, payload->data,
frame->format, frame->width, frame->height, 1);
if (pts == AV_NOPTS_VALUE) {
frame->pts = state->last_pts + 1;
} else {
frame->pts = pts;
}
state->last_pts = frame->pts;
res = get_frames(env, frame, &out_frames, &dts_list, &max_frames, &frame_cnt,
use_shm, state);
switch (res) {
case ENCODER_SEND_FRAME_ERROR:
res_term = encode_result_error(env, "send_frame");
break;
case ENCODER_ENCODE_ERROR:
res_term = encode_result_error(env, "encode");
break;
default:
res_term =
encode_result_ok(env, dts_list, frame_cnt, out_frames, frame_cnt);
}
if (out_frames != NULL) {
for (int i = 0; i < frame_cnt; i++) {
if (out_frames[i] != NULL) {
unifex_payload_release(out_frames[i]);
unifex_free(out_frames[i]);
}
}
unifex_free(out_frames);
}
if (dts_list != NULL) {
unifex_free(dts_list);
}
av_frame_free(&frame);
return res_term;
}
UNIFEX_TERM flush(UnifexEnv *env, int use_shm, State *state) {
UNIFEX_TERM res_term;
int max_frames = 16, frame_cnt = 0;
UnifexPayload **out_frames = NULL;
int64_t *dts_list = NULL;
int res = get_frames(env, NULL, &out_frames, &dts_list, &max_frames,
&frame_cnt, use_shm, state);
switch (res) {
case ENCODER_SEND_FRAME_ERROR:
res_term = encode_result_error(env, "send_frame");
break;
case ENCODER_ENCODE_ERROR:
res_term = encode_result_error(env, "encode");
break;
default:
res_term =
encode_result_ok(env, dts_list, frame_cnt, out_frames, frame_cnt);
}
if (out_frames != NULL) {
for (int i = 0; i < frame_cnt; i++) {
if (out_frames[i] != NULL) {
unifex_payload_release(out_frames[i]);
unifex_free(out_frames[i]);
}
}
unifex_free(out_frames);
}
if (dts_list != NULL) {
unifex_free(dts_list);
}
return res_term;
}