mirror of
https://git.sr.ht/~thestr4ng3r/chiaki
synced 2025-08-21 14:03:11 -07:00
Move FFMPEG Decoder to lib
This commit is contained in:
parent
aba17d48a3
commit
673a2de9c1
24 changed files with 566 additions and 350 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
43
lib/include/chiaki/ffmpegdecoder.h
Normal file
43
lib/include/chiaki/ffmpegdecoder.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
216
lib/src/ffmpegdecoder.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue