Move FFMPEG Decoder to lib

This commit is contained in:
Florian Märkl 2020-12-25 17:58:56 +01:00
commit 673a2de9c1
No known key found for this signature in database
GPG key ID: 125BC8A5A6A1E857
24 changed files with 566 additions and 350 deletions

View file

@ -75,6 +75,12 @@ set(SOURCE_FILES
src/regist.c
src/opusdecoder.c)
if(CHIAKI_ENABLE_FFMPEG_DECODER)
list(APPEND HEADER_FILES include/chiaki/ffmpegdecoder.h)
list(APPEND SOURCE_FILES src/ffmpegdecoder.c)
endif()
set(CHIAKI_LIB_ENABLE_PI_DECODER "${CHIAKI_ENABLE_FFMPEG_DECODER}")
if(CHIAKI_ENABLE_PI_DECODER)
list(APPEND HEADER_FILES include/chiaki/pidecoder.h)
list(APPEND SOURCE_FILES src/pidecoder.c)
@ -127,6 +133,10 @@ endif()
target_link_libraries(chiaki-lib Nanopb::nanopb)
target_link_libraries(chiaki-lib Jerasure::Jerasure)
if(CHIAKI_ENABLE_FFMPEG_DECODER)
target_link_libraries(chiaki-lib FFMPEG::avcodec FFMPEG::avutil)
endif()
if(CHIAKI_ENABLE_PI_DECODER)
target_link_libraries(chiaki-lib ILClient::ILClient)
endif()

View file

@ -100,6 +100,8 @@ static inline bool chiaki_codec_is_hdr(ChiakiCodec codec)
return codec == CHIAKI_CODEC_H265_HDR;
}
CHIAKI_EXPORT const char *chiaki_codec_name(ChiakiCodec codec);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,43 @@
#ifndef CHIAKI_FFMPEG_DECODER_H
#define CHIAKI_FFMPEG_DECODER_H
#include <chiaki/config.h>
#include <chiaki/log.h>
#include <chiaki/thread.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <libavcodec/avcodec.h>
typedef struct chiaki_ffmpeg_decoder_t ChiakiFfmpegDecoder;
typedef void (*ChiakiFfmpegFrameAvailable)(ChiakiFfmpegDecoder *decover, void *user);
struct chiaki_ffmpeg_decoder_t
{
ChiakiLog *log;
ChiakiMutex mutex;
AVCodec *av_codec;
AVCodecContext *codec_context;
enum AVPixelFormat hw_pix_fmt;
AVBufferRef *hw_device_ctx;
ChiakiMutex cb_mutex;
ChiakiFfmpegFrameAvailable frame_available_cb;
void *frame_available_cb_user;
};
CHIAKI_EXPORT ChiakiErrorCode chiaki_ffmpeg_decoder_init(ChiakiFfmpegDecoder *decoder, ChiakiLog *log,
ChiakiCodec codec, const char *hw_decoder_name,
ChiakiFfmpegFrameAvailable frame_available_cb, void *frame_available_cb_user);
CHIAKI_EXPORT void chiaki_ffmpeg_decoder_fini(ChiakiFfmpegDecoder *decoder);
CHIAKI_EXPORT bool chiaki_ffmpeg_decoder_video_sample_cb(uint8_t *buf, size_t buf_size, void *user);
CHIAKI_EXPORT AVFrame *chiaki_ffmpeg_decoder_pull_frame(ChiakiFfmpegDecoder *decoder);
CHIAKI_EXPORT enum AVPixelFormat chiaki_ffmpeg_decoder_get_pixel_format(ChiakiFfmpegDecoder *decoder);
#ifdef __cplusplus
}
#endif
#endif // CHIAKI_FFMPEG_DECODER_H

View file

@ -50,6 +50,20 @@ CHIAKI_EXPORT void chiaki_log_hexdump_raw(ChiakiLog *log, ChiakiLogLevel level,
#define CHIAKI_LOGW(log, ...) do { chiaki_log((log), CHIAKI_LOG_WARNING, __VA_ARGS__); } while(0)
#define CHIAKI_LOGE(log, ...) do { chiaki_log((log), CHIAKI_LOG_ERROR, __VA_ARGS__); } while(0)
typedef struct chiaki_log_sniffer_t
{
ChiakiLog *forward_log; // The original log, where everything is forwarded
ChiakiLog sniff_log; // The log where others will log into
uint32_t sniff_level_mask;
char *buf; // always null-terminated
size_t buf_len; // strlen(buf)
} ChiakiLogSniffer;
CHIAKI_EXPORT void chiaki_log_sniffer_init(ChiakiLogSniffer *sniffer, uint32_t level_mask, ChiakiLog *forward_log);
CHIAKI_EXPORT void chiaki_log_sniffer_fini(ChiakiLogSniffer *sniffer);
static inline ChiakiLog *chiaki_log_sniffer_get_log(ChiakiLogSniffer *sniffer) { return &sniffer->sniff_log; }
static inline const char *chiaki_log_sniffer_get_buffer(ChiakiLogSniffer *sniffer) { return sniffer->buf; }
#ifdef __cplusplus
}
#endif

View file

@ -105,3 +105,18 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_lib_init()
return CHIAKI_ERR_SUCCESS;
}
CHIAKI_EXPORT const char *chiaki_codec_name(ChiakiCodec codec)
{
switch(codec)
{
case CHIAKI_CODEC_H264:
return "H264";
case CHIAKI_CODEC_H265:
return "H265";
case CHIAKI_CODEC_H265_HDR:
return "H265/HDR";
default:
return "unknown";
}
}

216
lib/src/ffmpegdecoder.c Normal file
View file

@ -0,0 +1,216 @@
#include <chiaki/ffmpegdecoder.h>
#include <libavcodec/avcodec.h>
static enum AVCodecID chiaki_codec_av_codec_id(ChiakiCodec codec)
{
switch(codec)
{
case CHIAKI_CODEC_H265:
case CHIAKI_CODEC_H265_HDR:
return AV_CODEC_ID_H265;
default:
return AV_CODEC_ID_H264;
}
}
CHIAKI_EXPORT ChiakiErrorCode chiaki_ffmpeg_decoder_init(ChiakiFfmpegDecoder *decoder, ChiakiLog *log,
ChiakiCodec codec, const char *hw_decoder_name,
ChiakiFfmpegFrameAvailable frame_available_cb, void *frame_available_cb_user)
{
decoder->log = log;
decoder->frame_available_cb = frame_available_cb;
decoder->frame_available_cb_user = frame_available_cb_user;
ChiakiErrorCode err = chiaki_mutex_init(&decoder->mutex, false);
if(err != CHIAKI_ERR_SUCCESS)
return err;
decoder->hw_device_ctx = NULL;
decoder->hw_pix_fmt = AV_PIX_FMT_NONE;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100)
avcodec_register_all();
#endif
enum AVCodecID av_codec = chiaki_codec_av_codec_id(codec);
decoder->av_codec = avcodec_find_decoder(av_codec);
if(!decoder->av_codec)
{
CHIAKI_LOGE(log, "%s Codec not available", chiaki_codec_name(codec));
goto error_mutex;
}
decoder->codec_context = avcodec_alloc_context3(decoder->av_codec);
if(!decoder->codec_context)
{
CHIAKI_LOGE(log, "Failed to alloc codec context");
goto error_mutex;
}
if(hw_decoder_name)
{
CHIAKI_LOGI(log, "Using hardware decoder \"%s\"", hw_decoder_name);
enum AVHWDeviceType type = av_hwdevice_find_type_by_name(hw_decoder_name);
if(type == AV_HWDEVICE_TYPE_NONE)
{
CHIAKI_LOGE(log, "Hardware decoder \"%s\" not found", hw_decoder_name);
goto error_codec_context;
}
for(int i = 0;; i++)
{
const AVCodecHWConfig *config = avcodec_get_hw_config(decoder->av_codec, i);
if(!config)
{
CHIAKI_LOGE(log, "avcodec_get_hw_config failed");
goto error_codec_context;
}
if(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type)
{
decoder->hw_pix_fmt = config->pix_fmt;
break;
}
}
if(av_hwdevice_ctx_create(&decoder->hw_device_ctx, type, NULL, NULL, 0) < 0)
{
CHIAKI_LOGE(log, "Failed to create hwdevice context");
goto error_codec_context;
}
decoder->codec_context->hw_device_ctx = av_buffer_ref(decoder->hw_device_ctx);
}
if(avcodec_open2(decoder->codec_context, decoder->av_codec, NULL) < 0)
{
CHIAKI_LOGE(log, "Failed to open codec context");
goto error_codec_context;
}
return CHIAKI_ERR_SUCCESS;
error_codec_context:
if(decoder->hw_device_ctx)
av_buffer_unref(&decoder->hw_device_ctx);
avcodec_free_context(&decoder->codec_context);
error_mutex:
chiaki_mutex_fini(&decoder->mutex);
return CHIAKI_ERR_UNKNOWN;
}
CHIAKI_EXPORT void chiaki_ffmpeg_decoder_fini(ChiakiFfmpegDecoder *decoder)
{
avcodec_close(decoder->codec_context);
avcodec_free_context(&decoder->codec_context);
if(decoder->hw_device_ctx)
av_buffer_unref(&decoder->hw_device_ctx);
}
CHIAKI_EXPORT bool chiaki_ffmpeg_decoder_video_sample_cb(uint8_t *buf, size_t buf_size, void *user)
{
ChiakiFfmpegDecoder *decoder = user;
chiaki_mutex_lock(&decoder->mutex);
AVPacket packet;
av_init_packet(&packet);
packet.data = buf;
packet.size = buf_size;
int r;
send_packet:
r = avcodec_send_packet(decoder->codec_context, &packet);
if(r != 0)
{
if(r == AVERROR(EAGAIN))
{
CHIAKI_LOGE(decoder->log, "AVCodec internal buffer is full removing frames before pushing");
AVFrame *frame = av_frame_alloc();
if(!frame)
{
CHIAKI_LOGE(decoder->log, "Failed to alloc AVFrame");
goto hell;
}
r = avcodec_receive_frame(decoder->codec_context, frame);
av_frame_free(&frame);
if(r != 0)
{
CHIAKI_LOGE(decoder->log, "Failed to pull frame");
goto hell;
}
goto send_packet;
}
else
{
char errbuf[128];
av_make_error_string(errbuf, sizeof(errbuf), r);
CHIAKI_LOGE(decoder->log, "Failed to push frame: %s", errbuf);
goto hell;
}
}
chiaki_mutex_unlock(&decoder->mutex);
decoder->frame_available_cb(decoder, decoder->frame_available_cb_user);
return true;
hell:
chiaki_mutex_unlock(&decoder->mutex);
return false;
}
static AVFrame *pull_from_hw(ChiakiFfmpegDecoder *decoder, AVFrame *hw_frame)
{
AVFrame *sw_frame = av_frame_alloc();
if(av_hwframe_transfer_data(sw_frame, hw_frame, 0) < 0)
{
CHIAKI_LOGE(decoder->log, "Failed to transfer frame from hardware");
av_frame_unref(sw_frame);
sw_frame = NULL;
}
av_frame_unref(hw_frame);
return sw_frame;
}
CHIAKI_EXPORT AVFrame *chiaki_ffmpeg_decoder_pull_frame(ChiakiFfmpegDecoder *decoder)
{
chiaki_mutex_lock(&decoder->mutex);
// always try to pull as much as possible and return only the very last frame
AVFrame *frame_last = NULL;
AVFrame *frame = NULL;
while(true)
{
AVFrame *next_frame;
if(frame_last)
{
av_frame_unref(frame_last);
next_frame = frame_last;
}
else
{
next_frame = av_frame_alloc();
if(!next_frame)
break;
}
frame_last = frame;
frame = next_frame;
int r = avcodec_receive_frame(decoder->codec_context, frame);
if(!r)
frame = decoder->hw_device_ctx ? pull_from_hw(decoder, frame) : frame;
else
{
if(r != AVERROR(EAGAIN))
CHIAKI_LOGE(decoder->log, "Decoding with FFMPEG failed");
av_frame_free(&frame);
frame = frame_last;
break;
}
}
chiaki_mutex_unlock(&decoder->mutex);
return frame;
}
CHIAKI_EXPORT enum AVPixelFormat chiaki_ffmpeg_decoder_get_pixel_format(ChiakiFfmpegDecoder *decoder)
{
// TODO: this is probably very wrong, especially for hdr
return decoder->hw_device_ctx
? AV_PIX_FMT_NV12
: AV_PIX_FMT_YUV420P;
}

View file

@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
CHIAKI_EXPORT char chiaki_log_level_char(ChiakiLogLevel level)
{
@ -176,4 +177,51 @@ CHIAKI_EXPORT void chiaki_log_hexdump_raw(ChiakiLog *log, ChiakiLogLevel level,
str[buf_size*2] = 0;
chiaki_log(log, level, "%s", str);
free(str);
}
}
static void log_sniffer_cb(ChiakiLogLevel level, const char *msg, void *user);
CHIAKI_EXPORT void chiaki_log_sniffer_init(ChiakiLogSniffer *sniffer, uint32_t level_mask, ChiakiLog *forward_log)
{
sniffer->forward_log = forward_log;
// level_mask is applied later and everything is forwarded unmasked, so use ALL here:
chiaki_log_init(&sniffer->sniff_log, CHIAKI_LOG_ALL, log_sniffer_cb, sniffer);
sniffer->sniff_level_mask = level_mask;
sniffer->buf = calloc(1, 1);
sniffer->buf_len = '\0';
}
CHIAKI_EXPORT void chiaki_log_sniffer_fini(ChiakiLogSniffer *sniffer)
{
free(sniffer->buf);
}
static void log_sniffer_push(ChiakiLogSniffer *sniffer, ChiakiLogLevel level, const char *msg)
{
size_t len = strlen(msg);
if(!len)
return;
bool nl = sniffer->buf_len != 0;
char *new_buf = realloc(sniffer->buf, sniffer->buf_len + (nl ? 1 : 0) + 4 + len + 1);
if(!new_buf)
return;
sniffer->buf = new_buf;
if(nl)
sniffer->buf[sniffer->buf_len++] = '\n';
sniffer->buf[sniffer->buf_len++] = '[';
sniffer->buf[sniffer->buf_len++] = chiaki_log_level_char(level);
sniffer->buf[sniffer->buf_len++] = ']';
sniffer->buf[sniffer->buf_len++] = ' ';
memcpy(sniffer->buf + sniffer->buf_len, msg, len);
sniffer->buf_len += len;
sniffer->buf[sniffer->buf_len] = '\0';
}
static void log_sniffer_cb(ChiakiLogLevel level, const char *msg, void *user)
{
ChiakiLogSniffer *sniffer = user;
if(level & sniffer->sniff_level_mask)
log_sniffer_push(sniffer, level, msg);
if(sniffer->forward_log)
chiaki_log(sniffer->forward_log, level, "%s", msg);
}