diff options
| author | 2024-10-21 19:22:50 -0400 | |
|---|---|---|
| committer | 2024-10-21 19:22:50 -0400 | |
| commit | 60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2 (patch) | |
| tree | 08ff2ce7975f523112e7ad2fe4f797b4fc7db5de /src/codec | |
| parent | 2f9a0945bfeee3296cec3d38d094e4c49f9cb65f (diff) | |
| download | camu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.tar.gz camu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.tar.bz2 camu-60b4ebfbf3be78dba9dc7c65ab2bdaa0b218c0c2.zip | |
Everything before initial synced list
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/codec')
| -rw-r--r-- | src/codec/codec.h | 260 | ||||
| -rw-r--r-- | src/codec/common.h | 12 | ||||
| -rw-r--r-- | src/codec/ffmpeg/common.c | 32 | ||||
| -rw-r--r-- | src/codec/ffmpeg/common.h | 6 | ||||
| -rw-r--r-- | src/codec/ffmpeg/decoder.c | 60 | ||||
| -rw-r--r-- | src/codec/ffmpeg/decoder.h | 8 | ||||
| -rw-r--r-- | src/codec/ffmpeg/demuxer.c | 57 | ||||
| -rw-r--r-- | src/codec/ffmpeg/demuxer.h | 1 | ||||
| -rw-r--r-- | src/codec/ffmpeg/encoder.c | 126 | ||||
| -rw-r--r-- | src/codec/ffmpeg/encoder.h | 7 | ||||
| -rw-r--r-- | src/codec/ffmpeg/packet_ext.c | 38 | ||||
| -rw-r--r-- | src/codec/ffmpeg/packet_ext.h | 2 | ||||
| -rw-r--r-- | src/codec/ffmpeg/resampler.c | 173 | ||||
| -rw-r--r-- | src/codec/ffmpeg/resampler.h | 34 | ||||
| -rw-r--r-- | src/codec/meson.build | 126 | ||||
| -rw-r--r-- | src/codec/spng/decoder.h | 3 | ||||
| -rw-r--r-- | src/codec/spng/impl.c | 59 | ||||
| -rw-r--r-- | src/codec/stb_image/decoder.h | 3 | ||||
| -rw-r--r-- | src/codec/stb_image/impl.c | 75 | ||||
| -rw-r--r-- | src/codec/wuffs/decoder.h | 3 | ||||
| -rw-r--r-- | src/codec/wuffs/impl.c | 110 |
21 files changed, 620 insertions, 575 deletions
diff --git a/src/codec/codec.h b/src/codec/codec.h index fd847b8..9528522 100644 --- a/src/codec/codec.h +++ b/src/codec/codec.h @@ -3,38 +3,94 @@ #include <al/types.h> #include <al/array.h> #include <aki/common.h> - -#include "../cache/handle.h" - #ifdef CAMU_HAVE_FFMPEG #include <libavutil/pixdesc.h> #include <libavformat/avformat.h> #endif +#include "../cache/handle.h" + +#ifndef CAMU_SINK_NO_VIDEO +#include "../render/renderer.h" +#else +struct camu_renderer; +#endif + #ifdef CAMU_HAVE_FFMPEG enum { CAMU_OK = 0, + CAMU_ERR_ERROR = 1, CAMU_ERR_EOF = AVERROR_EOF, CAMU_ERR_AGAIN = AVERROR(EAGAIN) }; enum { CAMU_SEEK_SIZE = AVSEEK_SIZE }; +enum { + CAMU_STREAM_UNKNOWN = AVMEDIA_TYPE_UNKNOWN, + CAMU_STREAM_AUDIO = AVMEDIA_TYPE_AUDIO, + CAMU_STREAM_VIDEO = AVMEDIA_TYPE_VIDEO, + CAMU_STREAM_SUBTITLE = AVMEDIA_TYPE_SUBTITLE +}; +enum { + CAMU_SAMPLE_FORMAT_NONE = AV_SAMPLE_FMT_NONE, + CAMU_SAMPLE_FORMAT_U8 = AV_SAMPLE_FMT_U8, + CAMU_SAMPLE_FORMAT_S16 = AV_SAMPLE_FMT_S16, + CAMU_SAMPLE_FORMAT_S32 = AV_SAMPLE_FMT_S32, + CAMU_SAMPLE_FORMAT_FLT = AV_SAMPLE_FMT_FLT, + CAMU_SAMPLE_FORMAT_DBL = AV_SAMPLE_FMT_DBL, + CAMU_SAMPLE_FORMAT_U8P = AV_SAMPLE_FMT_U8P, + CAMU_SAMPLE_FORMAT_S16P = AV_SAMPLE_FMT_S16P, + CAMU_SAMPLE_FORMAT_S32P = AV_SAMPLE_FMT_S32P, + CAMU_SAMPLE_FORMAT_FLTP = AV_SAMPLE_FMT_FLTP, + CAMU_SAMPLE_FORMAT_DBLP = AV_SAMPLE_FMT_DBLP, + CAMU_SAMPLE_FORMAT_S64 = AV_SAMPLE_FMT_S64, + CAMU_SAMPLE_FORMAT_S64P = AV_SAMPLE_FMT_S64P +}; +enum { + CAMU_PIXEL_FORMAT_RGBA = AV_PIX_FMT_RGBA, + CAMU_PIXEL_FORMAT_RGB = AV_PIX_FMT_RGB8, + CAMU_PIXEL_FORMAT_GREYA = AV_PIX_FMT_GRAY8A, + CAMU_PIXEL_FORMAT_GREY = AV_PIX_FMT_GRAY8 +}; #else enum { CAMU_OK = 0, + CAMU_ERR_ERROR = 1, CAMU_ERR_EOF = -1, CAMU_ERR_AGAIN = -2 }; enum { CAMU_SEEK_SIZE = 0x10000 }; -#endif - enum { - CAMU_PIXEL_FMT_RGB = 0, - CAMU_PIXEL_FMT_RGBA + CAMU_STREAM_UNKNOWN = 0, + CAMU_STREAM_AUDIO, + CAMU_STREAM_VIDEO, + CAMU_STREAM_SUBTITLE }; +enum { + CAMU_SAMPLE_FORMAT_NONE = -1, + CAMU_SAMPLE_FORMAT_U8, + CAMU_SAMPLE_FORMAT_S16, + CAMU_SAMPLE_FORMAT_S32, + CAMU_SAMPLE_FORMAT_FLT, + CAMU_SAMPLE_FORMAT_DBL, + CAMU_SAMPLE_FORMAT_U8P, + CAMU_SAMPLE_FORMAT_S16P, + CAMU_SAMPLE_FORMAT_S32P, + CAMU_SAMPLE_FORMAT_FLTP, + CAMU_SAMPLE_FORMAT_DBLP, + CAMU_SAMPLE_FORMAT_S64, + CAMU_SAMPLE_FORMAT_S64P +}; +enum { + CAMU_PIXEL_FORMAT_RGBA = 0, + CAMU_PIXEL_FORMAT_RGB, + CAMU_PIXEL_FORMAT_GREYA, + CAMU_PIXEL_FORMAT_GREY +}; +#endif enum { CAMU_FRAME_DEVICE_ALLOCATED = 1 @@ -47,21 +103,27 @@ enum { #endif }; -struct camu_icc_profile { - u8 *data; - size_t size; +struct camu_audio_format { + s32 format; + s32 sample_rate; +#ifdef CAMU_HAVE_FFMPEG + AVChannelLayout channel_layout; +#endif + s32 channel_count; }; -struct camu_stream { +struct camu_codec_stream { + u8 mode; u8 type; - s64 duration; + u64 duration; struct { - u8 fmt; s32 width; s32 height; - struct camu_icc_profile icc; + s32 format; } video; - //struct { } audio; + struct { + struct camu_audio_format fmt; + } audio; #ifdef CAMU_HAVE_FFMPEG struct { AVStream *stream; @@ -70,56 +132,182 @@ struct camu_stream { #endif }; -struct camu_packet { - u8 type; +struct camu_codec_packet { + u8 mode; struct aki_buffer *buffer; #ifdef CAMU_HAVE_FFMPEG struct { AVPacket *pkt; } av; #endif }; -struct camu_frame { - u8 type; - u8 *data; +struct camu_codec_frame { + u8 mode; u8 flags; - u8 fmt; - s32 width; - s32 height; + u8 *data; + s32 format; f64 pts; + struct { + s32 width; + s32 height; + } video; + struct { + u32 sample_count; + } audio; #ifdef CAMU_HAVE_FFMPEG struct { AVFrame *frame; } av; #endif - void *opaque; }; struct camu_demuxer { - u8 type; + u8 mode; bool (*init)(struct camu_demuxer *, struct cch_handle *); - s32 (*get_packet)(struct camu_demuxer *, struct camu_packet *); + s32 (*get_packet)(struct camu_demuxer *, struct camu_codec_packet *); u64 (*get_duration)(struct camu_demuxer *); bool (*seek)(struct camu_demuxer *, u64); void (*free)(struct camu_demuxer **); - array(struct camu_stream) streams; - array(s32) subscribed; + array(struct camu_codec_stream) streams; + s32 subscribed; }; -struct camu_renderer; struct camu_decoder { - bool (*init)(struct camu_decoder *, struct camu_renderer *, struct camu_stream *); - void (*set_callback)(struct camu_decoder *, void (*callback)(void *, struct camu_frame *), void *); - s32 (*push)(struct camu_decoder *, struct camu_packet *); + bool (*init)(struct camu_decoder *, struct camu_renderer *, struct camu_codec_stream *, + void (*callback)(void *, struct camu_codec_frame *), void *); + s32 (*push)(struct camu_decoder *, struct camu_codec_packet *); s32 (*process)(struct camu_decoder *); void (*flush)(struct camu_decoder *); void (*free)(struct camu_decoder **); - struct camu_stream *stream; }; -static inline void camu_frame_discard(struct camu_frame *frame) +struct camu_resampler_format { + bool resampler_needed; + struct camu_audio_format in; + struct camu_audio_format req; +}; + +struct camu_resampler { + bool (*init)(struct camu_resampler *, struct camu_resampler_format *); + s32 (*convert)(struct camu_resampler *, const u8 **, s32); + s32 (*flush)(struct camu_resampler *); + u8 **(*get_data)(struct camu_resampler *); + void (*free)(struct camu_resampler **); +}; + +AL_UNUSED_FUNCTION_PUSH + +static inline bool camu_resampler_format_matches(struct camu_resampler_format *fmt) { + return fmt->in.format == fmt->req.format + && fmt->in.sample_rate == fmt->req.sample_rate #ifdef CAMU_HAVE_FFMPEG - if (frame->type == CAMU_FFMPEG_COMPAT) { - av_frame_free(&frame->av.frame); + && av_channel_layout_compare(&fmt->in.channel_layout, &fmt->req.channel_layout) == 0 +#endif + && fmt->in.channel_count == fmt->req.channel_count; +} + +static inline void camu_audio_format_copy(struct camu_audio_format *dest, struct camu_audio_format *src) +{ + dest->format = src->format; + dest->sample_rate = src->sample_rate; +#ifdef CAMU_HAVE_FFMPEG + av_channel_layout_copy(&dest->channel_layout, &src->channel_layout); +#endif + dest->channel_count = src->channel_count; +} + +static const char *camu_audio_format_name(s32 format) +{ +#ifdef CAMU_HAVE_FFMPEG + return av_get_sample_fmt_name(format); +#else + (void)format; + return "(undefined)"; +#endif +} + +static inline s32 camu_audio_format_bytes_per_sample(struct camu_audio_format *fmt) +{ +#if CAMU_HAVE_FFMPEG + return av_get_bytes_per_sample(fmt->format); +#else + switch (fmt->format) { + case CAMU_SAMPLE_FORMAT_U8: + case CAMU_SAMPLE_FORMAT_U8P: return 1; + case CAMU_SAMPLE_FORMAT_S16: + case CAMU_SAMPLE_FORMAT_S16P: return 2; + case CAMU_SAMPLE_FORMAT_S32: + case CAMU_SAMPLE_FORMAT_S32P: + case CAMU_SAMPLE_FORMAT_FLT: + case CAMU_SAMPLE_FORMAT_FLTP: return 4; + case CAMU_SAMPLE_FORMAT_S64: + case CAMU_SAMPLE_FORMAT_S64P: + case CAMU_SAMPLE_FORMAT_DBL: + case CAMU_SAMPLE_FORMAT_DBLP: return 8; + default: + al_assert(false); + return -1; } #endif +} + +static inline size_t camu_audio_format_samples_to_bytes(struct camu_audio_format *fmt, size_t samples) +{ + s32 bytes_per_sample = camu_audio_format_bytes_per_sample(fmt); + return samples * bytes_per_sample * fmt->channel_count; +} + +static inline f64 camu_audio_format_samples_to_sec(struct camu_audio_format *fmt, size_t samples) +{ + return samples / (f64)fmt->sample_rate; +} + +static inline size_t camu_audio_format_usec_to_bytes(struct camu_audio_format *fmt, size_t usec) +{ + // Keep order of operations for precision. + size_t samples = usec * (fmt->sample_rate / 1000000.0); + return samples * camu_audio_format_bytes_per_sample(fmt) * fmt->channel_count; +} + +static inline size_t camu_audio_format_sec_to_bytes(struct camu_audio_format *fmt, f64 sec) +{ + size_t samples = sec * fmt->sample_rate; + return samples * camu_audio_format_bytes_per_sample(fmt) * fmt->channel_count; +} + +static inline f64 camu_audio_format_bytes_to_sec(struct camu_audio_format *fmt, size_t bytes) +{ + size_t samples = bytes / (camu_audio_format_bytes_per_sample(fmt) * fmt->channel_count); + return samples / (f64)fmt->sample_rate; +} + +static const char *camu_pixel_format_name(s32 format) +{ + switch (format) { + case CAMU_PIXEL_FORMAT_RGBA: return "rgba"; + case CAMU_PIXEL_FORMAT_RGB: return "rgb"; + case CAMU_PIXEL_FORMAT_GREYA: return "grey + alpha"; + case CAMU_PIXEL_FORMAT_GREY: return "grey"; + } + return "(unknown)"; +} + +static inline s32 camu_pixel_format_from_channels(s32 channels) +{ + switch (channels) { + case 4: return CAMU_PIXEL_FORMAT_RGBA; + case 3: return CAMU_PIXEL_FORMAT_RGB; + case 2: return CAMU_PIXEL_FORMAT_GREYA; + case 1: return CAMU_PIXEL_FORMAT_GREY; + default: al_assert(false); return CAMU_PIXEL_FORMAT_GREY; + } +} + +static inline void camu_codec_frame_discard(struct camu_codec_frame *frame) +{ +#ifdef CAMU_HAVE_FFMPEG + if (frame->mode == CAMU_FFMPEG_COMPAT) av_frame_free(&frame->av.frame); +#endif + if (frame->mode == CAMU_NORMAL && frame->data) al_free(frame->data); al_free(frame); } + +AL_UNUSED_FUNCTION_POP diff --git a/src/codec/common.h b/src/codec/common.h index adf503d..98665d7 100644 --- a/src/codec/common.h +++ b/src/codec/common.h @@ -13,17 +13,7 @@ static inline void *camu_page_alloc(size_t size) #endif } -static inline void *camu_page_realloc(void *ptr, size_t old_size, size_t size) +static inline void *camu_page_realloc(void *ptr, size_t size) { -#ifdef AL_HAVE_POSIX_MEMALIGN - void *old_ptr = ptr; - al_posix_memalign(&ptr, al_page_size, size); - if (old_ptr) { - al_memcpy(ptr, old_ptr, old_size); - al_free(old_ptr); - } - return ptr; -#else return al_realloc(ptr, size); -#endif } diff --git a/src/codec/ffmpeg/common.c b/src/codec/ffmpeg/common.c index 57caff7..b7a232e 100644 --- a/src/codec/ffmpeg/common.c +++ b/src/codec/ffmpeg/common.c @@ -1,6 +1,38 @@ +#include <al/lib.h> +#include <al/log.h> +#include <libavutil/log.h> + #include "common.h" void camu_ff_set_log_callback(void (*callback)(void *, int, const char *, va_list)) { av_log_set_callback(callback); } + +static char *av_log_buf = NULL; +static s32 av_log_pos = 0; + +static void av_log_callback(void *userdata, int level, const char *fmt, va_list args) +{ + (void)userdata; + al_assert(av_log_buf); + if (level < AV_LOG_DEBUG) { + av_log_pos += al_vsnprintf(&av_log_buf[av_log_pos], 512, fmt, args); + if (av_log_buf[av_log_pos - 1] == '\n' || av_log_pos >= 512) { + al_log_info("ff_log", av_log_buf); + av_log_pos = 0; + } + } +} + +void camu_ff_set_default_log_callback() +{ + av_log_buf = (char *)al_malloc(1024); + camu_ff_set_log_callback(av_log_callback); +} + + +void camu_ff_free_default_log_callback() +{ + al_free(av_log_buf); +} diff --git a/src/codec/ffmpeg/common.h b/src/codec/ffmpeg/common.h index ded7b35..229ffb0 100644 --- a/src/codec/ffmpeg/common.h +++ b/src/codec/ffmpeg/common.h @@ -1,6 +1,10 @@ #pragma once #include <al/types.h> -#include <libavutil/log.h> +#include <al/log.h> +// userdata, level, fmt, args void camu_ff_set_log_callback(void (*callback)(void *, int, const char *, va_list)); + +void camu_ff_set_default_log_callback(); +void camu_ff_free_default_log_callback(); diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c index 7071bea..d231b2f 100644 --- a/src/codec/ffmpeg/decoder.c +++ b/src/codec/ffmpeg/decoder.c @@ -1,8 +1,6 @@ -#include <libavutil/cpu.h> #include <al/lib.h> #include <al/log.h> - -//#include "../../render/renderer.h" +#include <libavutil/cpu.h> #include "decoder.h" @@ -11,7 +9,8 @@ static void close_internal(struct camu_ff_decoder *av) if (av->codec_context) avcodec_free_context(&av->codec_context); } -static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_stream *stream) +static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_codec_stream *stream, + void (*callback)(void *, struct camu_codec_frame *), void *userdata) { struct camu_ff_decoder *av = (struct camu_ff_decoder *)dec; @@ -37,49 +36,45 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend /* s32 cpus = av_cpu_count(); - cpus = (cpus > 16) ? 4 : cpus / 4; - if (cpus == 0) cpus = 1; + if (cpus > 4) cpus = 2; av->codec_context->thread_count = cpus; + av->codec_context->thread_type = FF_THREAD_SLICE; al_log_debug("ff_decoder", "Using %i threads for decoder.", cpus); */ - (void)renderer; - /* - if (renderer && renderer->get_buffer2 && codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { +#ifndef CAMU_SINK_NO_VIDEO + if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO && renderer && renderer->get_buffer2) { av->codec_context->get_buffer2 = renderer->get_buffer2; av->codec_context->opaque = renderer->opaque; } - */ +#else + (void)renderer; +#endif if (avcodec_open2(av->codec_context, codec, NULL) < 0) { al_log_error("ff_decoder", "Failed to open codec (%s).", codec->name); goto err; } - if (codec->long_name) { - al_log_info("ff_decoder", "Codec: %s (%s) %ldkbps.", codec->name, codec->long_name, codecpar->bit_rate / 1000); - } else { - al_log_info("ff_decoder", "Codec: %s %ldkbps.", codec->name, codecpar->bit_rate / 1000); - } + const char *long_name = codec->long_name ? codec->long_name : codec->name; + s64 kbps = codecpar->bit_rate > 0 ? codecpar->bit_rate / 1000L : 0L; + al_log_info("ff_decoder", "Codec: %s (%s) %ldkbps.", codec->name, long_name, kbps); av->time_base = stream->av.stream->time_base; - av->last_pts = 0; - av->last_duration = 0; + av->duration = stream->av.stream->duration; + //av->last_pts = 0; + //av->last_duration = 0; //av->seek_pos = -1; + av->callback = callback; + av->userdata = userdata; + return true; err: close_internal(av); return false; } -static void ff_decoder_set_callback(struct camu_decoder *dec, void (*callback)(void *, struct camu_frame *), void *userdata) -{ - struct camu_ff_decoder *av = (struct camu_ff_decoder *)dec; - av->callback = callback; - av->userdata = userdata; -} - static s32 send_packet(struct camu_ff_decoder *av, AVPacket *pkt) { /* @@ -92,16 +87,16 @@ static s32 send_packet(struct camu_ff_decoder *av, AVPacket *pkt) s32 ret = avcodec_send_packet(av->codec_context, pkt); if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) { - al_log_error("ff_decoder", "Error sending packet to decoder: (%s).", av_err2str(ret)); + al_log_error("ff_decoder", "Error sending packet to the decoder: (%s).", av_err2str(ret)); } return ret; } -static s32 ff_decoder_push(struct camu_decoder *dec, struct camu_packet *packet) +static s32 ff_decoder_push(struct camu_decoder *dec, struct camu_codec_packet *packet) { struct camu_ff_decoder *av = (struct camu_ff_decoder *)dec; - return send_packet(av, (packet) ? packet->av.pkt : NULL); + return send_packet(av, packet ? packet->av.pkt : NULL); } static void ff_decoder_flush(struct camu_decoder *dec) @@ -114,19 +109,19 @@ static s32 receive_frames(struct camu_ff_decoder *av) { s32 ret; do { - struct camu_frame *frame = al_alloc_object(struct camu_frame); - frame->type = CAMU_FFMPEG_COMPAT; + struct camu_codec_frame *frame = al_alloc_object(struct camu_codec_frame); + frame->mode = CAMU_FFMPEG_COMPAT; frame->av.frame = av_frame_alloc(); ret = avcodec_receive_frame(av->codec_context, frame->av.frame); if (ret < 0) { av_frame_free(&frame->av.frame); al_free(frame); + // Checking for EAGAIN should prevent an infinite loop. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; - al_log_error("ff_decoder", "Error receiving packet from decoder: (%s).", av_err2str(ret)); + al_log_error("ff_decoder", "Error receiving packet from the decoder: (%s).", av_err2str(ret)); continue; } - // Track pts and duration of the previous frame so we can handle multiple frames - // in a single packet. + // Track pts and duration of the previous frame so we can handle multiple frames in a single packet. //if (av->codec_context->codec_type == AVMEDIA_TYPE_AUDIO) { // if (frame->av.frame->best_effort_timestamp < 0) { // frame->av.frame->best_effort_timestamp = av->last_pts + av->last_duration; @@ -158,7 +153,6 @@ struct camu_decoder *camu_ff_decoder_create(void) { struct camu_ff_decoder *av = al_alloc_object(struct camu_ff_decoder); av->dec.init = ff_decoder_init; - av->dec.set_callback = ff_decoder_set_callback; av->dec.push = ff_decoder_push; av->dec.process = ff_decoder_process; av->dec.flush = ff_decoder_flush; diff --git a/src/codec/ffmpeg/decoder.h b/src/codec/ffmpeg/decoder.h index 45cca00..88bbd4c 100644 --- a/src/codec/ffmpeg/decoder.h +++ b/src/codec/ffmpeg/decoder.h @@ -1,7 +1,6 @@ #pragma once #include <al/types.h> - #include <libavcodec/avcodec.h> #include "../codec.h" @@ -10,10 +9,11 @@ struct camu_ff_decoder { struct camu_decoder dec; AVCodecContext *codec_context; AVRational time_base; - s64 last_pts; - s64 last_duration; + u64 duration; + //s64 last_pts; + //s64 last_duration; //s64 seek_pos; - void (*callback)(void *, struct camu_frame *); + void (*callback)(void *, struct camu_codec_frame *); void *userdata; }; diff --git a/src/codec/ffmpeg/demuxer.c b/src/codec/ffmpeg/demuxer.c index 9b26596..7ff0dca 100644 --- a/src/codec/ffmpeg/demuxer.c +++ b/src/codec/ffmpeg/demuxer.c @@ -3,7 +3,7 @@ #include "demuxer.h" #include "avio.h" -#define BUF_SIZE 16384 +#define DEMUX_BUF_SIZE (4096 * 4) static void close_internal(struct camu_ff_demuxer *av) { @@ -28,16 +28,16 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl goto err; } - u8 *buf = (u8 *)av_malloc(BUF_SIZE); - av->io_context = avio_alloc_context(buf, BUF_SIZE, 0, handle, camu_avio_read, NULL, camu_avio_seek); + u8 *buf = (u8 *)av_malloc(DEMUX_BUF_SIZE); + av->io_context = avio_alloc_context(buf, DEMUX_BUF_SIZE, 0, handle, camu_avio_read, NULL, camu_avio_seek); if (!av->io_context) { al_log_error("ff_demuxer", "Failed to create custom io context."); goto err; } if (!cch_handle_can_seek(handle)) { - // This should always be unset if we know we cannot seek. - // But, should also never be unset if we *do* want to seek because it + // AVIO_SEEKABLE_NORMAL should always be unset if we know we cannot seek. + // However, it should also never be unset if we *do* want to seek because it // disables long seeks (Not tested in a long time). av->io_context->seekable &= ~AVIO_SEEKABLE_NORMAL; } else { @@ -47,12 +47,19 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl av->format_context->pb = av->io_context; av->format_context->flags |= AVFMT_FLAG_CUSTOM_IO; - if (avformat_open_input(&av->format_context, "", 0, 0) < 0) { + AVDictionary *opts = NULL; + av_dict_set(&opts, "analyzeduration", "10000000", 0); + av_dict_set(&opts, "probesize", "20M", 0); + + if (avformat_open_input(&av->format_context, "file:", NULL, &opts) < 0) { al_log_error("ff_demuxer", "Failed to open input."); + av_dict_free(&opts); goto err; } - if (avformat_find_stream_info(av->format_context, 0) < 0) { + av_dict_free(&opts); + + if (avformat_find_stream_info(av->format_context, NULL) < 0) { al_log_error("ff_demuxer", "Failed to find stream info."); goto err; } @@ -70,6 +77,7 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl for (u32 i = 0; i < av->format_context->nb_streams; i++) { AVStream *stream = av->format_context->streams[i]; enum AVMediaType type = stream->codecpar->codec_type; + if (type != AVMEDIA_TYPE_AUDIO && type != AVMEDIA_TYPE_VIDEO) type = AVMEDIA_TYPE_UNKNOWN; // Try to detect attached images. if (type == AVMEDIA_TYPE_VIDEO && GUESS_STREAM_IS_IMAGE(stream)) { al_log_info("ff_demuxer", "Guessing that stream #%u is an image.", i); @@ -83,12 +91,14 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl } al_log_info("ff_demuxer", "Setting stream #%u to a duration of %.3fs.", i, stream->duration * av_q2d(stream->time_base)); } - if (stream->start_time < 0) stream->start_time = 0; + if (stream->start_time == AV_NOPTS_VALUE) stream->start_time = 0; s64 duration = av_rescale_q(stream->duration, stream->time_base, AV_TIME_BASE_Q); al_assert(duration >= 0); if ((u64)duration > av->duration) av->duration = (u64)duration; - al_array_push(av->demux.streams, ((struct camu_stream){ - .type = CAMU_FFMPEG_COMPAT, + al_array_push(av->demux.streams, ((struct camu_codec_stream){ + .mode = CAMU_FFMPEG_COMPAT, + .type = type, + .duration = (u64)duration, .av.stream = stream })); } @@ -106,30 +116,15 @@ err: return false; } -/* -static AVStream *av_stream_at_index(struct camu_ff_demuxer *av, s32 index) -{ - struct camu_stream *stream; - al_array_foreach_ptr(av->demux.streams, i, stream) { - if (stream->av.stream->index == index) return stream->av.stream; - } - return NULL; -} -*/ - static bool subscribed_to_index(struct camu_demuxer *demux, s32 index) { - s32 subscribed; - al_array_foreach(demux->subscribed, i, subscribed) { - if (subscribed == index) return true; - } - return false; + return ((demux->subscribed >> index) & 1); } -static s32 ff_demuxer_get_packet(struct camu_demuxer *demux, struct camu_packet *packet) +static s32 ff_demuxer_get_packet(struct camu_demuxer *demux, struct camu_codec_packet *packet) { struct camu_ff_demuxer *av = (struct camu_ff_demuxer *)demux; - packet->type = CAMU_FFMPEG_COMPAT; + packet->mode = CAMU_FFMPEG_COMPAT; s32 ret = 0; do { ret = av_read_frame(av->format_context, packet->av.pkt); @@ -160,12 +155,13 @@ static bool ff_demuxer_seek(struct camu_demuxer *demux, u64 pos) { struct camu_ff_demuxer *av = (struct camu_ff_demuxer *)demux; if (pos > av->duration) pos = av->duration; + al_assert(pos <= INT64_MAX); s64 ts = av_rescale_q((s64)pos, (AVRational){ 1, 1000000 }, AV_TIME_BASE_Q); if (av->eof) { avformat_flush(av->format_context); av->eof = false; } - if (avformat_seek_file(av->format_context, -1, INT64_MIN, ts, ts, AVSEEK_FLAG_BACKWARD) < 0) { + if (avformat_seek_file(av->format_context, -1, INT64_MIN, ts, ts, 0) < 0) { return false; } av->seeked = true; @@ -184,9 +180,8 @@ static void ff_demuxer_free(struct camu_demuxer **demux) struct camu_demuxer *camu_ff_demuxer_create(void) { struct camu_ff_demuxer *av = al_alloc_object(struct camu_ff_demuxer); - av->demux.type = CAMU_FFMPEG_COMPAT; + av->demux.mode = CAMU_FFMPEG_COMPAT; al_array_init(av->demux.streams); - al_array_init(av->demux.subscribed); av->demux.init = ff_demuxer_init; av->demux.get_packet = ff_demuxer_get_packet; av->demux.get_duration = ff_demuxer_get_duration; diff --git a/src/codec/ffmpeg/demuxer.h b/src/codec/ffmpeg/demuxer.h index 8327534..68ad03e 100644 --- a/src/codec/ffmpeg/demuxer.h +++ b/src/codec/ffmpeg/demuxer.h @@ -3,7 +3,6 @@ #include <al/types.h> #include <al/lib.h> #include <al/array.h> - #include <libavformat/avformat.h> #include "../codec.h" diff --git a/src/codec/ffmpeg/encoder.c b/src/codec/ffmpeg/encoder.c index 4617baf..83a8b55 100644 --- a/src/codec/ffmpeg/encoder.c +++ b/src/codec/ffmpeg/encoder.c @@ -1,3 +1,5 @@ +#include <al/lib.h> +#include <al/log.h> #include <libavcodec/avcodec.h> #include <libavcodec/codec.h> #include <libavformat/avformat.h> @@ -6,13 +8,12 @@ #include <libavutil/error.h> #include <libavutil/frame.h> #include <libavutil/samplefmt.h> -#include <al/lib.h> -#include <al/log.h> #include "encoder.h" #define BITRATE 256000 -#define FRAME_SIZE 20 +#define FRAME_DURATION 20 +#define SAMPLES_PER_FRAME (48000 / 1000.0 * FRAME_DURATION) #define CHANNELS 2 static void free_frame_data(struct camu_ff_encoder *enc) @@ -20,15 +21,6 @@ static void free_frame_data(struct camu_ff_encoder *enc) if (enc->frame && enc->frame->nb_samples > 0) av_freep(&enc->frame->data[0]); } -static void alloc_frame_data(struct camu_ff_encoder *enc, s32 frame_size) -{ - if (enc->frame->nb_samples >= frame_size) return; - free_frame_data(enc); - s32 nb_channels = enc->frame->ch_layout.nb_channels; - av_samples_alloc(enc->frame->data, NULL, nb_channels, frame_size, enc->frame->format, 0); - enc->frame->nb_samples = frame_size; -} - bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext) { al_memset(enc, 0, sizeof(struct camu_ff_encoder)); @@ -51,7 +43,7 @@ bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char av_channel_layout_default(&enc->codec_context->ch_layout, CHANNELS); enc->codec_context->sample_fmt = AV_SAMPLE_FMT_FLT; enc->codec_context->bit_rate = BITRATE; - enc->codec_context->frame_size = FRAME_SIZE; + enc->codec_context->frame_size = SAMPLES_PER_FRAME; enc->codec_context->time_base = (AVRational){ 1, enc->codec_context->sample_rate }; enc->codec_context->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; @@ -77,23 +69,17 @@ bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char AVCodecContext *out_codec = enc->codec_context; - av_channel_layout_copy(&enc->fmt.req_channel_layout, &out_codec->ch_layout); - enc->fmt.req_sample_rate = out_codec->sample_rate; - enc->fmt.req_format = out_codec->sample_fmt; - - enc->audio_fifo = av_audio_fifo_alloc(enc->fmt.req_format, - enc->fmt.req_channel_layout.nb_channels, 1); - - if (!enc->audio_fifo) { - al_log_error("ff_encoder", "Failed to alloc fifo."); - goto err; - } + enc->fmt.req.format = out_codec->sample_fmt; + enc->fmt.req.sample_rate = out_codec->sample_rate; + av_channel_layout_copy(&enc->fmt.req.channel_layout, &out_codec->ch_layout); + enc->fmt.req.channel_count = out_codec->ch_layout.nb_channels; enc->frame = av_frame_alloc(); - enc->frame->nb_samples = 0; - enc->frame->sample_rate = enc->fmt.req_sample_rate; - av_channel_layout_copy(&enc->frame->ch_layout, &enc->fmt.req_channel_layout); - enc->frame->format = enc->fmt.req_format; + enc->frame->format = enc->fmt.req.format; + enc->frame->sample_rate = enc->fmt.req.sample_rate; + av_channel_layout_copy(&enc->frame->ch_layout, &enc->fmt.req.channel_layout); + av_samples_alloc(enc->frame->data, NULL, enc->frame->ch_layout.nb_channels, SAMPLES_PER_FRAME, enc->frame->format, 1); + enc->frame->nb_samples = SAMPLES_PER_FRAME; enc->frame->pts = 0; return true; @@ -104,93 +90,31 @@ err: void camu_ff_encoder_push(struct camu_ff_encoder *enc, u8 **data, s32 sample_count) { - s32 alloc_size = av_audio_fifo_size(enc->audio_fifo) + sample_count; - if (av_audio_fifo_space(enc->audio_fifo) < alloc_size) { - if (av_audio_fifo_realloc(enc->audio_fifo, alloc_size) < 0) { - al_assert(false); - } + al_assert(sample_count == SAMPLES_PER_FRAME * CHANNELS); + enc->frame->data[0] = data[0]; + s32 ret = avcodec_send_frame(enc->codec_context, enc->frame); + if (ret < 0 && ret != AVERROR_EOF) { + al_log_error("ff_encoder", "Error sending frame to the encoder: (%s).", av_err2str(ret)); + return; } - av_audio_fifo_write(enc->audio_fifo, (void **)data, sample_count); -} - -static bool receive_packets(struct camu_ff_encoder *enc) -{ - s32 ret; AVPacket pkt = { 0 }; - - do { - ret = avcodec_receive_packet(enc->codec_context, &pkt); - if (ret < 0) break; - enc->callback(enc->userdata, &pkt); - } while (1); - - if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) { - al_log_error("ff_encoder", "Error receiving packet from encoder: (%s).", av_err2str(ret)); - return false; - } - - return true; -} - -static s32 send_frame(struct camu_ff_encoder *enc, AVFrame *in_frame) -{ - s32 ret = avcodec_send_frame(enc->codec_context, in_frame); + ret = avcodec_receive_packet(enc->codec_context, &pkt); if (ret < 0 && ret != AVERROR_EOF) { - al_log_error("ff_encoder", "Error sending frame to encoder: (%s).", av_err2str(ret)); + al_log_error("ff_encoder", "Error receiving packet from the encoder: (%s).", av_err2str(ret)); + return; } - return ret; -} - -static s32 read_frame(struct camu_ff_encoder *enc, s32 frame_size) -{ - alloc_frame_data(enc, frame_size); - return av_audio_fifo_read(enc->audio_fifo, (void **)enc->frame->data, frame_size); -} - -bool camu_ff_encoder_process(struct camu_ff_encoder *enc, bool flush) -{ - s32 samples_available = av_audio_fifo_size(enc->audio_fifo); - if (samples_available == 0 && flush) { - // Flush encoder. - send_frame(enc, NULL); - receive_packets(enc); - // This should guarantee that there are not packets left. - return false; - } - - s32 frame_size = enc->codec_context->frame_size; - if (samples_available == 0 || (samples_available < frame_size && !flush)) { - return false; - } - - s32 ret; - do { - if (samples_available < frame_size) frame_size = samples_available; - read_frame(enc, frame_size); - samples_available -= frame_size; - do { - ret = send_frame(enc, enc->frame); - if (ret < 0) break; - if (!receive_packets(enc)) return false; - } while (ret == AVERROR(EAGAIN)); - if (ret < 0) break; - enc->frame->pts += frame_size; - } while (samples_available > 0); - - // ret < 0 means encoder errored while sending frame. - return ret == 0; + enc->callback(enc->userdata, &pkt); + enc->frame->pts += SAMPLES_PER_FRAME; } void camu_ff_encoder_reset(struct camu_ff_encoder *enc) { avcodec_flush_buffers(enc->codec_context); - av_audio_fifo_reset(enc->audio_fifo); } void camu_ff_encoder_close(struct camu_ff_encoder *enc) { if (enc->codec_context) avcodec_free_context(&enc->codec_context); - if (enc->audio_fifo) av_audio_fifo_free(enc->audio_fifo); free_frame_data(enc); if (enc->frame) av_frame_free(&enc->frame); } diff --git a/src/codec/ffmpeg/encoder.h b/src/codec/ffmpeg/encoder.h index 9bfe8ea..c0d953c 100644 --- a/src/codec/ffmpeg/encoder.h +++ b/src/codec/ffmpeg/encoder.h @@ -5,16 +5,14 @@ #include <libavutil/audio_fifo.h> #include <libavutil/rational.h> -#include "resampler.h" +#include "../codec.h" struct camu_ff_encoder { AVCodecContext *codec_context; const AVOutputFormat *output_format; - struct camu_ff_resample_fmt fmt; - + struct camu_resampler_format fmt; AVFrame *frame; - AVAudioFifo *audio_fifo; void (*callback)(void *, AVPacket *); void *userdata; @@ -22,6 +20,5 @@ struct camu_ff_encoder { bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char *ext); void camu_ff_encoder_push(struct camu_ff_encoder *enc, u8 **data, s32 sample_count); -bool camu_ff_encoder_process(struct camu_ff_encoder *enc, bool flush); void camu_ff_encoder_reset(struct camu_ff_encoder *enc); void camu_ff_encoder_close(struct camu_ff_encoder *enc); diff --git a/src/codec/ffmpeg/packet_ext.c b/src/codec/ffmpeg/packet_ext.c index 8fe4c39..c0e400d 100644 --- a/src/codec/ffmpeg/packet_ext.c +++ b/src/codec/ffmpeg/packet_ext.c @@ -16,6 +16,7 @@ void aki_packet_write_av_codec_parameters(struct aki_packet *packet, AVCodecPara AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->width); AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->height); AKI_PACKET_WRITE_TYPE(packet, AVRational, codecpar->sample_aspect_ratio); + AKI_PACKET_WRITE_TYPE(packet, AVRational, codecpar->framerate); AKI_PACKET_WRITE_TYPE(packet, enum AVFieldOrder, codecpar->field_order); AKI_PACKET_WRITE_TYPE(packet, enum AVColorRange, codecpar->color_range); AKI_PACKET_WRITE_TYPE(packet, enum AVColorPrimaries, codecpar->color_primaries); @@ -23,18 +24,13 @@ void aki_packet_write_av_codec_parameters(struct aki_packet *packet, AVCodecPara AKI_PACKET_WRITE_TYPE(packet, enum AVColorSpace, codecpar->color_space); AKI_PACKET_WRITE_TYPE(packet, enum AVChromaLocation, codecpar->chroma_location); AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->video_delay); + AKI_PACKET_WRITE_TYPE(packet, AVChannelLayout, codecpar->ch_layout); AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->sample_rate); AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->block_align); AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->frame_size); AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->initial_padding); AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->trailing_padding); AKI_PACKET_WRITE_TYPE(packet, s32, codecpar->seek_preroll); - AKI_PACKET_WRITE_TYPE(packet, AVChannelLayout, codecpar->ch_layout); -} - -void aki_packet_write_av_rational(struct aki_packet *packet, AVRational rational) -{ - AKI_PACKET_WRITE_TYPE(packet, AVRational, rational); } void aki_packet_write_av_codec_id(struct aki_packet *packet, enum AVCodecID codec_id) @@ -46,15 +42,15 @@ void aki_packet_write_av_stream(struct aki_packet *packet, AVStream *stream) { AKI_PACKET_WRITE_TYPE(packet, s32, stream->index); aki_packet_write_av_codec_parameters(packet, stream->codecpar); - aki_packet_write_av_rational(packet, stream->time_base); + AKI_PACKET_WRITE_TYPE(packet, AVRational, stream->time_base); AKI_PACKET_WRITE_TYPE(packet, s64, stream->duration); AKI_PACKET_WRITE_TYPE(packet, s64, stream->start_time); AKI_PACKET_WRITE_TYPE(packet, s64, stream->nb_frames); if (stream->avg_frame_rate.den == 0) { // r_frame_rate.den == 0 handled on the client. - aki_packet_write_av_rational(packet, stream->r_frame_rate); + AKI_PACKET_WRITE_TYPE(packet, AVRational, stream->r_frame_rate); } else { - aki_packet_write_av_rational(packet, stream->avg_frame_rate); + AKI_PACKET_WRITE_TYPE(packet, AVRational, stream->avg_frame_rate); } } @@ -68,7 +64,9 @@ void aki_packet_write_av_packet(struct aki_packet *packet, AVPacket *pkt) AKI_PACKET_WRITE_TYPE(packet, s32, pkt->flags); AKI_PACKET_WRITE_TYPE(packet, s32, pkt->side_data_elems); for (s32 i = 0; i < pkt->side_data_elems; i++) { - AKI_PACKET_WRITE_TYPE(packet, size_t, pkt->side_data[i].size); + // Possibly upcast size_t. + s64 size = (s64)pkt->side_data[i].size; + AKI_PACKET_WRITE_TYPE(packet, s64, size); AKI_PACKET_WRITE_DATA(packet, pkt->side_data[i].data, pkt->side_data[i].size); AKI_PACKET_WRITE_TYPE(packet, enum AVPacketSideDataType, pkt->side_data[i].type); } @@ -96,6 +94,7 @@ void aki_packet_read_av_codec_parameters(struct aki_packet *packet, AVCodecParam AKI_PACKET_READ_TYPE(packet, s32, codecpar->width); AKI_PACKET_READ_TYPE(packet, s32, codecpar->height); AKI_PACKET_READ_TYPE(packet, AVRational, codecpar->sample_aspect_ratio); + AKI_PACKET_READ_TYPE(packet, AVRational, codecpar->framerate); AKI_PACKET_READ_TYPE(packet, enum AVFieldOrder, codecpar->field_order); AKI_PACKET_READ_TYPE(packet, enum AVColorRange, codecpar->color_range); AKI_PACKET_READ_TYPE(packet, enum AVColorPrimaries, codecpar->color_primaries); @@ -103,20 +102,13 @@ void aki_packet_read_av_codec_parameters(struct aki_packet *packet, AVCodecParam AKI_PACKET_READ_TYPE(packet, enum AVColorSpace, codecpar->color_space); AKI_PACKET_READ_TYPE(packet, enum AVChromaLocation, codecpar->chroma_location); AKI_PACKET_READ_TYPE(packet, s32, codecpar->video_delay); + AKI_PACKET_READ_TYPE(packet, AVChannelLayout, codecpar->ch_layout); AKI_PACKET_READ_TYPE(packet, s32, codecpar->sample_rate); AKI_PACKET_READ_TYPE(packet, s32, codecpar->block_align); AKI_PACKET_READ_TYPE(packet, s32, codecpar->frame_size); AKI_PACKET_READ_TYPE(packet, s32, codecpar->initial_padding); AKI_PACKET_READ_TYPE(packet, s32, codecpar->trailing_padding); AKI_PACKET_READ_TYPE(packet, s32, codecpar->seek_preroll); - AKI_PACKET_READ_TYPE(packet, AVChannelLayout, codecpar->ch_layout); -} - -AVRational aki_packet_read_av_rational(struct aki_packet *packet) -{ - AVRational r; - AKI_PACKET_READ_TYPE(packet, AVRational, r); - return r; } enum AVCodecID aki_packet_read_av_codec_id(struct aki_packet *packet) @@ -131,11 +123,11 @@ AVStream *aki_packet_read_av_stream(AVFormatContext *format_context, const AVCod AVStream *stream = avformat_new_stream(format_context, codec); AKI_PACKET_READ_TYPE(packet, s32, stream->index); aki_packet_read_av_codec_parameters(packet, stream->codecpar); - stream->time_base = aki_packet_read_av_rational(packet); + AKI_PACKET_READ_TYPE(packet, AVRational, stream->time_base); AKI_PACKET_READ_TYPE(packet, s64, stream->duration); AKI_PACKET_READ_TYPE(packet, s64, stream->start_time); AKI_PACKET_READ_TYPE(packet, s64, stream->nb_frames); - stream->avg_frame_rate = aki_packet_read_av_rational(packet); + AKI_PACKET_READ_TYPE(packet, AVRational, stream->avg_frame_rate); return stream; } @@ -152,10 +144,12 @@ AVPacket *aki_packet_read_av_packet(struct aki_packet *packet) AKI_PACKET_READ_TYPE(packet, s32, side_data_elems); pkt->side_data_elems = 0; for (s32 i = 0; i < side_data_elems; i++) { - size_t size; u8 *data; enum AVPacketSideDataType type; - AKI_PACKET_READ_TYPE(packet, s32, size); + s64 storage_for_size; + AKI_PACKET_READ_TYPE(packet, s64, storage_for_size); + al_assert((sizeof(size_t) > 4 || storage_for_size <= INT32_MAX)); + size_t size = (size_t)storage_for_size; AKI_PACKET_READ_DATA(packet, size, data); AKI_PACKET_READ_TYPE(packet, enum AVPacketSideDataType, type); u8 *side_data = av_packet_new_side_data(pkt, type, size); diff --git a/src/codec/ffmpeg/packet_ext.h b/src/codec/ffmpeg/packet_ext.h index ff6e337..36b6ed5 100644 --- a/src/codec/ffmpeg/packet_ext.h +++ b/src/codec/ffmpeg/packet_ext.h @@ -8,13 +8,11 @@ #include <libavformat/avformat.h> void aki_packet_write_av_codec_parameters(struct aki_packet *packet, AVCodecParameters *codecpar); -void aki_packet_write_av_rational(struct aki_packet *packet, AVRational rational); void aki_packet_write_av_codec_id(struct aki_packet *packet, enum AVCodecID codec_id); void aki_packet_write_av_stream(struct aki_packet *packet, AVStream *stream); void aki_packet_write_av_packet(struct aki_packet *packet, AVPacket *pkt); void aki_packet_read_av_codec_parameters(struct aki_packet *packet, AVCodecParameters *codecpar); -AVRational aki_packet_read_av_rational(struct aki_packet *packet); enum AVCodecID aki_packet_read_av_codec_id(struct aki_packet *packet); AVStream *aki_packet_read_av_stream(AVFormatContext *format_context, const AVCodec *codec, struct aki_packet *packet); AVPacket *aki_packet_read_av_packet(struct aki_packet *packet); diff --git a/src/codec/ffmpeg/resampler.c b/src/codec/ffmpeg/resampler.c index d4b281a..00418e6 100644 --- a/src/codec/ffmpeg/resampler.c +++ b/src/codec/ffmpeg/resampler.c @@ -1,161 +1,108 @@ +#include <al/lib.h> +#include <al/log.h> #include <libavutil/channel_layout.h> #include <libavutil/frame.h> -#include <libavutil/opt.h> #include <libavutil/samplefmt.h> +#include <libavutil/opt.h> #include <libswresample/swresample.h> -#include <al/lib.h> -#include <al/log.h> #include "resampler.h" -static bool in_req_matches(struct camu_ff_resample_fmt *fmt) +static bool ff_resampler_init(struct camu_resampler *resamp, struct camu_resampler_format *fmt) { - return (fmt->in_format == fmt->req_format) && - (av_channel_layout_compare(&fmt->in_channel_layout, &fmt->req_channel_layout) == 0) && - (fmt->in_sample_rate == fmt->req_sample_rate); -} - -bool camu_ff_resampler_init(struct camu_ff_resampler *resamp, struct camu_ff_resample_fmt *fmt) -{ - al_memset(resamp, 0, sizeof(struct camu_ff_resampler)); - - fmt->resampler_needed = !in_req_matches(fmt); - if (!fmt->resampler_needed) return true; + struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp; + al_assert(fmt->resampler_needed); - camu_ff_resample_fmt_copy(&resamp->fmt, fmt); - - SwrContext *ctx = NULL; - s32 ret = swr_alloc_set_opts2(&ctx, - &fmt->req_channel_layout, - fmt->req_format, - fmt->req_sample_rate, - &fmt->in_channel_layout, - fmt->in_format, - fmt->in_sample_rate, - 0, - NULL); + camu_audio_format_copy(&av->fmt, &fmt->req); + s32 ret = swr_alloc_set_opts2(&av->resample_context, + &fmt->req.channel_layout, fmt->req.format, fmt->req.sample_rate, + &fmt->in.channel_layout, fmt->in.format, fmt->in.sample_rate, 0, NULL); if (ret != 0) { al_log_error("lav_resampler", "Failed to configure resampler context."); return false; } +#ifndef _WIN32 // Slower but more accurate resampler. - //av_opt_set_int(ctx, "resampler", SWR_ENGINE_SOXR, 0); + av_opt_set_int(av->resample_context, "resampler", SWR_ENGINE_SOXR, 0); +#endif - if (swr_init(ctx) != 0) { - swr_free(&ctx); - al_log_error("lav_resampler", "Failed to open resampler context."); + if (swr_init(av->resample_context) != 0) { + swr_free(&av->resample_context); + al_log_error("lav_resampler", "Failed to init resampler context."); return false; } - resamp->resample_context = ctx; - return true; } -s32 camu_ff_resampler_sample_count(struct camu_ff_resampler *resamp, s32 in_samples) -{ - return swr_get_out_samples(resamp->resample_context, in_samples); - //return av_rescale_rnd(swr_get_delay(resamp->resample_context, resamp->fmt.req_sample_rate) + - // in_samples, resamp->fmt.in_sample_rate, resamp->fmt.req_sample_rate, AV_ROUND_UP); -} - -static inline void free_sample_data(struct camu_ff_resampler *resamp) +static inline void free_sample_data(struct camu_ff_resampler *av) { - if (resamp->data[0]) av_freep(&resamp->data[0]); + if (av->data[0]) av_freep(&av->data[0]); } -static inline void alloc_sample_data(struct camu_ff_resampler *resamp, s32 sample_count) +static inline void alloc_sample_data(struct camu_ff_resampler *av, s32 sample_count) { - if (resamp->sample_count >= sample_count) return; - free_sample_data(resamp); - s32 nb_channels = resamp->fmt.req_channel_layout.nb_channels; - av_samples_alloc(resamp->data, NULL, nb_channels, sample_count, resamp->fmt.req_format, 32); - resamp->sample_count = sample_count; + if (av->sample_count >= sample_count) return; + free_sample_data(av); + s32 nb_channels = av->fmt.channel_layout.nb_channels; + av_samples_alloc(av->data, NULL, nb_channels, sample_count, av->fmt.format, 32); + av->sample_count = sample_count; } -s32 camu_ff_resampler_convert(struct camu_ff_resampler *resamp, const u8 **in_data, s32 in_samples) +static s32 ff_resampler_convert(struct camu_resampler *resamp, const u8 **in_data, s32 in_samples) { + struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp; al_assert(in_samples > 0); - s32 max_resample_count = camu_ff_resampler_sample_count(resamp, in_samples); - alloc_sample_data(resamp, max_resample_count); - s32 resample_count = swr_convert(resamp->resample_context, resamp->data, - max_resample_count, in_data, in_samples); + s32 out_resample_count = swr_get_out_samples(av->resample_context, in_samples); + alloc_sample_data(av, out_resample_count); + s32 resample_count = swr_convert(av->resample_context, av->data, + out_resample_count, in_data, in_samples); if (resample_count < 0) { - al_log_error("lav_resampler", "Failed to convert samples (%i).", resample_count); + al_log_error("lav_resampler", "Failed to convert samples (%d).", resample_count); } return resample_count; } -u8 **camu_ff_resampler_get_data(struct camu_ff_resampler *resamp) +static s32 ff_resampler_flush(struct camu_resampler *resamp) { - return (u8 **)resamp->data; -} - -void camu_ff_resampler_close(struct camu_ff_resampler *resamp) -{ - if (resamp->resample_context) { - swr_close(resamp->resample_context); - swr_free(&resamp->resample_context); + struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp; + s32 resample_count = swr_convert(av->resample_context, av->data, + av->sample_count, NULL, 0); + if (swr_init(av->resample_context) != 0) { + swr_free(&av->resample_context); + al_log_error("lav_resampler", "Failed to re-init resampler context after flush."); + return -1; } - free_sample_data(resamp); -} - -void camu_ff_resample_fmt_copy(struct camu_ff_resample_fmt *dest, struct camu_ff_resample_fmt *src) -{ - dest->resampler_needed = src->resampler_needed; - dest->in_format = src->in_format; - av_channel_layout_copy(&dest->in_channel_layout, &src->in_channel_layout); - dest->in_sample_rate = src->in_sample_rate; - dest->req_format = src->req_format; - av_channel_layout_copy(&dest->req_channel_layout, &src->req_channel_layout); - dest->req_sample_rate = src->req_sample_rate; -} - -size_t camu_ff_resample_fmt_bytes_per_sample(struct camu_ff_resample_fmt *fmt) -{ - return (size_t)av_get_bytes_per_sample(fmt->req_format); -} - -size_t camu_ff_resample_fmt_samples_to_bytes(struct camu_ff_resample_fmt *fmt, size_t samples) -{ - s32 size; - s32 ret = av_samples_get_buffer_size(&size, fmt->req_channel_layout.nb_channels, (s32)samples, fmt->req_format, 1); - al_assert(ret >= 0); - return (size_t)size; -} - -size_t camu_ff_resample_fmt_samples_to_usec(struct camu_ff_resample_fmt *fmt, size_t samples) -{ - return samples / (fmt->req_sample_rate / 1000000.0); -} - -f64 camu_ff_resample_fmt_samples_to_sec(struct camu_ff_resample_fmt *fmt, size_t samples) -{ - return samples / (f64)fmt->req_sample_rate; -} - -size_t camu_ff_resample_fmt_usec_to_bytes(struct camu_ff_resample_fmt *fmt, size_t usec) -{ - size_t samples = usec * (fmt->req_sample_rate / 1000000.0); - return samples * (camu_ff_resample_fmt_bytes_per_sample(fmt) * fmt->req_channel_layout.nb_channels); + return resample_count; } -size_t camu_ff_resample_fmt_sec_to_bytes(struct camu_ff_resample_fmt *fmt, f64 sec) +static u8 **ff_resampler_get_data(struct camu_resampler *resamp) { - size_t samples = sec * fmt->req_sample_rate; - return samples * (camu_ff_resample_fmt_bytes_per_sample(fmt) * fmt->req_channel_layout.nb_channels); + struct camu_ff_resampler *av = (struct camu_ff_resampler *)resamp; + return (u8 **)av->data; } -size_t camu_ff_resample_fmt_bytes_to_usec(struct camu_ff_resample_fmt *fmt, size_t bytes) +static void ff_resampler_free(struct camu_resampler **resamp) { - size_t samples = bytes / (camu_ff_resample_fmt_bytes_per_sample(fmt) * fmt->req_channel_layout.nb_channels); - return samples / (fmt->req_sample_rate / 1000000.0); + struct camu_ff_resampler *av = (struct camu_ff_resampler *)*resamp; + if (av->resample_context) { + swr_close(av->resample_context); + swr_free(&av->resample_context); + } + free_sample_data(av); + al_free(av); + *resamp = NULL; } -f64 camu_ff_resample_fmt_bytes_to_sec(struct camu_ff_resample_fmt *fmt, size_t bytes) +struct camu_resampler *camu_ff_resampler_create(void) { - size_t samples = bytes / (camu_ff_resample_fmt_bytes_per_sample(fmt) * fmt->req_channel_layout.nb_channels); - return samples / ((f64)fmt->req_sample_rate); + struct camu_ff_resampler *av = al_alloc_object(struct camu_ff_resampler); + av->resamp.init = ff_resampler_init; + av->resamp.convert = ff_resampler_convert; + av->resamp.flush = ff_resampler_flush; + av->resamp.get_data = ff_resampler_get_data; + av->resamp.free = ff_resampler_free; + return (struct camu_resampler *)av; } diff --git a/src/codec/ffmpeg/resampler.h b/src/codec/ffmpeg/resampler.h index 4f7b07d..35e1158 100644 --- a/src/codec/ffmpeg/resampler.h +++ b/src/codec/ffmpeg/resampler.h @@ -1,40 +1,16 @@ #pragma once -#include <libswresample/swresample.h> -#include <libavutil/opt.h> #include <al/types.h> +#include <libswresample/swresample.h> -struct camu_ff_resample_fmt { - bool resampler_needed; - enum AVSampleFormat in_format; - enum AVSampleFormat req_format; - s32 in_channel_count; - s32 req_channel_count; - AVChannelLayout in_channel_layout; - AVChannelLayout req_channel_layout; - s32 in_sample_rate; - s32 req_sample_rate; -}; +#include "../codec.h" struct camu_ff_resampler { + struct camu_resampler resamp; SwrContext *resample_context; - struct camu_ff_resample_fmt fmt; + struct camu_audio_format fmt; u8 *data[AV_NUM_DATA_POINTERS]; s32 sample_count; }; -bool camu_ff_resampler_init(struct camu_ff_resampler *resamp, struct camu_ff_resample_fmt *fmt); -s32 camu_ff_resampler_sample_count(struct camu_ff_resampler *resamp, s32 in_samples); -s32 camu_ff_resampler_convert(struct camu_ff_resampler *resamp, const u8 **in_data, s32 in_samples); -u8 **camu_ff_resampler_get_data(struct camu_ff_resampler *resamp); -void camu_ff_resampler_close(struct camu_ff_resampler *resamp); - -void camu_ff_resample_fmt_copy(struct camu_ff_resample_fmt *dest, struct camu_ff_resample_fmt *src); -size_t camu_ff_resample_fmt_bytes_per_sample(struct camu_ff_resample_fmt *fmt); -size_t camu_ff_resample_fmt_samples_to_bytes(struct camu_ff_resample_fmt *fmt, size_t samples); -size_t camu_ff_resample_fmt_samples_to_usec(struct camu_ff_resample_fmt *fmt, size_t samples); -f64 camu_ff_resample_fmt_samples_to_sec(struct camu_ff_resample_fmt *fmt, size_t samples); -size_t camu_ff_resample_fmt_usec_to_bytes(struct camu_ff_resample_fmt *fmt, size_t usec); -size_t camu_ff_resample_fmt_sec_to_bytes(struct camu_ff_resample_fmt *fmt, f64 sec); -size_t camu_ff_resample_fmt_bytes_to_usec(struct camu_ff_resample_fmt *fmt, size_t bytes); -f64 camu_ff_resample_fmt_bytes_to_sec(struct camu_ff_resample_fmt *fmt, size_t bytes); +struct camu_resampler *camu_ff_resampler_create(void); diff --git a/src/codec/meson.build b/src/codec/meson.build index f3b604f..7a01040 100644 --- a/src/codec/meson.build +++ b/src/codec/meson.build @@ -1,71 +1,77 @@ -ffmpeg_src = [ - 'ffmpeg/common.c', - 'ffmpeg/packet_ext.c' -] -ffmpeg_server_src = [ - ffmpeg_src, - 'ffmpeg/demuxer.c', - 'ffmpeg/encoder.c', - 'ffmpeg/avio.c' -] -ffmpeg_client_src = [ - ffmpeg_src, - 'ffmpeg/decoder.c', - 'ffmpeg/resampler.c' -] -ffmpeg_inc = [] -ffmpeg_args = [] -ffmpeg_server_deps = [] -ffmpeg_client_deps = [] -ffmpeg_client_use_scaler = false +codec_deps = [] -if ffmpeg_client_use_scaler - ffmpeg_client_src += ['ffmpeg/scaler.c'] +if get_option('codecs').contains('ffmpeg') + ffmpeg_src = [ + 'ffmpeg/common.c', + 'ffmpeg/packet_ext.c', + 'ffmpeg/decoder.c', + 'ffmpeg/resampler.c', + 'ffmpeg/scaler.c', + 'ffmpeg/encoder.c', + ] + ffmpeg_inc = [] + ffmpeg_deps = [] + ffmpeg_args = [] +if not get_option('sink-only') + ffmpeg_src += [ + 'ffmpeg/demuxer.c', + 'ffmpeg/avio.c' + ] endif -libavutil = dependency('libavutil', required: false) -libavformat = dependency('libavformat', required: false) -libavcodec = dependency('libavcodec', required: false) -libswresample = dependency('libswresample', required: false) -libswscale = dependency('libswscale', required: false) -if not (libavutil.found() and libavformat.found() and libavcodec.found() and libswresample.found() and libswscale.found()) - ffmpeg_proj = subproject('ffmpeg', required: false) - if ffmpeg_proj.found() - libavutil = ffmpeg_proj.get_variable('avutil') - libavformat = ffmpeg_proj.get_variable('avformat') - libavcodec = ffmpeg_proj.get_variable('avcodec') - libswresample = ffmpeg_proj.get_variable('swresample') - libswscale = ffmpeg_proj.get_variable('swscale') - # Only used to set include dirs for ffmpeg. - ffmpeg_inc_dep = ffmpeg_proj.get_variable('ffmpeg_inc_dep') - ffmpeg_server_deps += [ffmpeg_inc_dep] - ffmpeg_client_deps += [ffmpeg_inc_dep, dependency('zlib')] - soxr = compiler.find_library('soxr', required: false) - if soxr.found() - ffmpeg_client_deps += [soxr] + libavutil = dependency('libavutil', required: false) + libavformat = dependency('libavformat', required: false) + libavcodec = dependency('libavcodec', required: false) + libswresample = dependency('libswresample', required: false) + libswscale = dependency('libswscale', required: false) + if not (libavutil.found() and libavformat.found() and libavcodec.found() and libswresample.found() and libswscale.found()) + ffmpeg_proj = subproject('ffmpeg', required: false) + if ffmpeg_proj.found() + libavutil = ffmpeg_proj.get_variable('avutil') + libavformat = ffmpeg_proj.get_variable('avformat') + libavcodec = ffmpeg_proj.get_variable('avcodec') + libswresample = ffmpeg_proj.get_variable('swresample') + libswscale = ffmpeg_proj.get_variable('swscale') + # Only used to set include dirs for ffmpeg. + ffmpeg_inc_dep = ffmpeg_proj.get_variable('ffmpeg_inc_dep') + # Extra static dependencies. + zlib = dependency('zlib') + bcrypt = compiler.find_library('bcrypt') + ffmpeg_deps += [ffmpeg_inc_dep, zlib, bcrypt] + soxr = compiler.find_library('soxr', required: false) + if soxr.found() + ffmpeg_deps += [soxr] + endif endif - ffmpeg_client_deps += [compiler.find_library('bcrypt')] endif -endif -if libavutil.found() and libavformat.found() and libavcodec.found() and libswresample.found() and libswscale.found() - ffmpeg_args += ['-DCAMU_HAVE_FFMPEG'] - ffmpeg_server_deps += [libavutil, libavformat, libavcodec] - ffmpeg_client_deps += [libavutil, libavformat, libavcodec, libswresample] - if ffmpeg_client_use_scaler - ffmpeg_client_deps += [libswscale] + if libavutil.found() and libavformat.found() and libavcodec.found() and libswresample.found() and libswscale.found() + ffmpeg_deps += [libavutil, libavformat, libavcodec, libswresample, libswscale] + ffmpeg_args += ['-DCAMU_HAVE_FFMPEG'] + ffmpeg_codec = declare_dependency(sources: ffmpeg_src, dependencies: ffmpeg_deps, + include_directories: ffmpeg_inc, compile_args: ffmpeg_args) + codec_deps += [ffmpeg_codec] endif - ffmpeg_server = declare_dependency(sources: ffmpeg_server_src, - dependencies: ffmpeg_server_deps, include_directories: ffmpeg_inc, compile_args: ffmpeg_args) - ffmpeg_client = declare_dependency(sources: ffmpeg_client_src, - dependencies: ffmpeg_client_deps, include_directories: ffmpeg_inc, compile_args: ffmpeg_args) endif -#stb_image = declare_dependency(sources: ['stb_image/impl.c'], -# dependencies: subproject('stb').get_variable('stb')) +if get_option('codecs').contains('stb_image') + stb = subproject('stb').get_variable('stb') + stb_image_codec = declare_dependency(sources: ['stb_image/impl.c'], dependencies: stb) + codec_deps += [stb_image_codec] +endif + +if get_option('codecs').contains('spng') + spng = dependency('spng', required: false) + if spng.found() + spng_codec = declare_dependency(sources: ['spng/impl.c'], dependencies: spng) + codec_deps += [spng_codec] + endif +endif -#wuffs = declare_dependency(sources: ['wuffs/impl.c'], -# dependencies: subproject('wuffs').get_variable('wuffs')) +if get_option('codecs').contains('wuffs') + wuffs = subproject('wuffs').get_variable('wuffs') + wuffs_codec = declare_dependency(sources: ['wuffs/impl.c'], dependencies: wuffs) + codec_deps += [wuffs_codec] +endif -#spng = declare_dependency(sources: ['spng/impl.c'], -# dependencies: dependency('spng')) +codecs = declare_dependency(dependencies: [stela, codec_deps]) diff --git a/src/codec/spng/decoder.h b/src/codec/spng/decoder.h index fee30c6..c027fce 100644 --- a/src/codec/spng/decoder.h +++ b/src/codec/spng/decoder.h @@ -4,7 +4,8 @@ struct camu_spng_decoder { struct camu_decoder dec; - void (*callback)(void *, struct camu_frame *); + struct camu_codec_stream *stream; + void (*callback)(void *, struct camu_codec_frame *); void (*userdata); }; diff --git a/src/codec/spng/impl.c b/src/codec/spng/impl.c index 9089e99..198d514 100644 --- a/src/codec/spng/impl.c +++ b/src/codec/spng/impl.c @@ -31,10 +31,12 @@ static bool spng_demuxer_init(struct camu_demuxer *demux, struct cch_handle *han struct spng_ihdr ihdr; spng_get_ihdr(ctx, &ihdr); - struct camu_stream stream; - stream.type = CAMU_NORMAL; + struct camu_codec_stream stream; + stream.mode = CAMU_NORMAL; + stream.type = CAMU_STREAM_VIDEO; stream.video.width = ihdr.width; stream.video.height = ihdr.height; + stream.video.format = CAMU_PIXEL_FORMAT_RGBA; al_array_push(spng->demux.streams, stream); @@ -43,14 +45,16 @@ static bool spng_demuxer_init(struct camu_demuxer *demux, struct cch_handle *han return true; } -static s32 spng_demuxer_get_packet(struct camu_demuxer *demux, struct camu_packet *packet) +static s32 spng_demuxer_get_packet(struct camu_demuxer *demux, struct camu_codec_packet *packet) { struct camu_spng_demuxer *spng = (struct camu_spng_demuxer *)demux; - if (spng->eof) return CAMU_ERR_EOF; - packet->type = CAMU_NORMAL; - packet->buffer = &spng->buffer; - spng->eof = true; - return CAMU_OK; + if (!spng->eof) { + packet->mode = CAMU_NORMAL; + packet->buffer = &spng->buffer; + spng->eof = true; + return CAMU_OK; + } + return CAMU_ERR_EOF; } static u64 spng_demuxer_get_duration(struct camu_demuxer *demux) @@ -69,31 +73,27 @@ static bool spng_demuxer_seek(struct camu_demuxer *demux, u64 pos) static void spng_demuxer_free(struct camu_demuxer **demux) { struct camu_spng_demuxer *spng = (struct camu_spng_demuxer *)*demux; + aki_buffer_free(&spng->buffer); + al_array_free(spng->demux.streams); al_free(spng); *demux = NULL; } -static bool spng_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_stream *stream) +static bool spng_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_codec_stream *stream, + void (*callback)(void *, struct camu_codec_frame *), void *userdata) { struct camu_spng_decoder *spng = (struct camu_spng_decoder *)dec; - (void)spng; - (void)stream; (void)renderer; - return true; -} - -static void spng_decoder_set_callback(struct camu_decoder *dec, void (*callback)(void *, struct camu_frame *), void *userdata) -{ - struct camu_spng_decoder *spng = (struct camu_spng_decoder *)dec; + spng->stream = stream; spng->callback = callback; spng->userdata = userdata; + return true; } -static s32 spng_decoder_push(struct camu_decoder *dec, struct camu_packet *packet) +static s32 spng_decoder_push(struct camu_decoder *dec, struct camu_codec_packet *packet) { - if (!packet) return CAMU_OK; - struct camu_spng_decoder *spng = (struct camu_spng_decoder *)dec; + if (!packet) return CAMU_OK; spng_ctx *ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32); spng_set_png_buffer(ctx, aki_buffer_get_ptr(packet->buffer, 0), packet->buffer->size); @@ -103,19 +103,17 @@ static s32 spng_decoder_push(struct camu_decoder *dec, struct camu_packet *packe size_t out_size; spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &out_size); - struct camu_frame *frame = al_alloc_object(struct camu_frame); - frame->type = CAMU_NORMAL; - frame->width = spng->dec.stream->video.width; - frame->height = spng->dec.stream->video.height; - frame->fmt = CAMU_PIXEL_FMT_RGBA; - frame->pts = 0.0; - frame->flags = 0; - + struct camu_codec_frame *frame = al_alloc_object(struct camu_codec_frame); + frame->mode = CAMU_NORMAL; frame->data = camu_page_alloc(out_size); spng_decode_chunks(ctx); spng_decode_image(ctx, frame->data, out_size, SPNG_FMT_RGBA8, 0); - spng_ctx_free(ctx); + frame->flags = 0; + frame->format = CAMU_PIXEL_FORMAT_RGBA; + frame->width = spng->stream->video.width; + frame->height = spng->stream->video.height; + frame->pts = 0.0; spng->callback(spng->userdata, frame); @@ -143,7 +141,7 @@ static void spng_decoder_free(struct camu_decoder **dec) struct camu_demuxer *camu_spng_demuxer_create(void) { struct camu_spng_demuxer *spng = al_alloc_object(struct camu_spng_demuxer); - spng->demux.type = CAMU_NORMAL; + spng->demux.mode = CAMU_NORMAL; al_array_init(spng->demux.streams); spng->demux.init = spng_demuxer_init; spng->demux.get_packet = spng_demuxer_get_packet; @@ -157,7 +155,6 @@ struct camu_decoder *camu_spng_decoder_create(void) { struct camu_spng_decoder *spng = al_alloc_object(struct camu_spng_decoder); spng->dec.init = spng_decoder_init; - spng->dec.set_callback = spng_decoder_set_callback; spng->dec.push = spng_decoder_push; spng->dec.process = spng_decoder_process; spng->dec.flush = spng_decoder_flush; diff --git a/src/codec/stb_image/decoder.h b/src/codec/stb_image/decoder.h index 7083a09..af50799 100644 --- a/src/codec/stb_image/decoder.h +++ b/src/codec/stb_image/decoder.h @@ -4,7 +4,8 @@ struct camu_stbi_decoder { struct camu_decoder dec; - void (*callback)(void *, struct camu_frame *); + struct camu_codec_stream *stream; + void (*callback)(void *, struct camu_codec_frame *); void (*userdata); }; diff --git a/src/codec/stb_image/impl.c b/src/codec/stb_image/impl.c index 6058ff2..98d9e2c 100644 --- a/src/codec/stb_image/impl.c +++ b/src/codec/stb_image/impl.c @@ -7,8 +7,10 @@ #define STBI_NO_FAILURE_STRINGS #define STBI_MALLOC camu_page_alloc #define STBI_FREE al_free -#define STBI_REALLOC al_realloc +#define STBI_REALLOC camu_page_realloc +AL_UNUSED_FUNCTION_PUSH #include <stb_image.h> +AL_UNUSED_FUNCTION_POP #include "../../cache/entry.h" @@ -28,27 +30,37 @@ static bool stbi_demuxer_init(struct camu_demuxer *demux, struct cch_handle *han aki_buffer_ensure_space(&stb->buffer, size); cch_handle_read(stb->handle, aki_buffer_get_ptr(&stb->buffer, 0), size); stb->buffer.size = size; + s32 w, h, channels; - stbi_info_from_memory(aki_buffer_get_ptr(&stb->buffer, 0), stb->buffer.size, &w, &h, &channels); + u8 *data = aki_buffer_get_ptr(&stb->buffer, 0); + if (!stbi_info_from_memory(data, stb->buffer.size, &w, &h, &channels)) { + goto err; + } - struct camu_stream stream = { 0 }; - stream.type = CAMU_NORMAL; + struct camu_codec_stream stream = { 0 }; + stream.mode = CAMU_NORMAL; + stream.type = CAMU_STREAM_VIDEO; stream.video.width = w; stream.video.height = h; + stream.video.format = camu_pixel_format_from_channels(channels); al_array_push(stb->demux.streams, stream); return true; +err: + return false; } -static s32 stbi_demuxer_get_packet(struct camu_demuxer *demux, struct camu_packet *packet) +static s32 stbi_demuxer_get_packet(struct camu_demuxer *demux, struct camu_codec_packet *packet) { struct camu_stbi_demuxer *stb = (struct camu_stbi_demuxer *)demux; - if (stb->eof) return CAMU_ERR_EOF; - packet->type = CAMU_NORMAL; - packet->buffer = &stb->buffer; - stb->eof = true; - return CAMU_OK; + if (!stb->eof) { + packet->mode = CAMU_NORMAL; + packet->buffer = &stb->buffer; + stb->eof = true; + return CAMU_OK; + } + return CAMU_ERR_EOF; } static u64 stbi_demuxer_get_duration(struct camu_demuxer *demux) @@ -67,44 +79,44 @@ static bool stbi_demuxer_seek(struct camu_demuxer *demux, u64 pos) static void stbi_demuxer_free(struct camu_demuxer **demux) { struct camu_stbi_demuxer *stb = (struct camu_stbi_demuxer *)*demux; + aki_buffer_free(&stb->buffer); + al_array_free(stb->demux.streams); al_free(stb); *demux = NULL; } -static bool stbi_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_stream *stream) -{ - struct camu_stbi_decoder *stb = (struct camu_stbi_decoder *)dec; - (void)stb; - (void)stream; - (void)renderer; - return true; -} -static void stbi_decoder_set_callback(struct camu_decoder *dec, void (*callback)(void *, struct camu_frame *), void *userdata) +static bool stbi_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_codec_stream *stream, + void (*callback)(void *, struct camu_codec_frame *), void *userdata) { struct camu_stbi_decoder *stb = (struct camu_stbi_decoder *)dec; + (void)renderer; + stb->stream = stream; stb->callback = callback; stb->userdata = userdata; + return true; } -static s32 stbi_decoder_push(struct camu_decoder *dec, struct camu_packet *packet) +static s32 stbi_decoder_push(struct camu_decoder *dec, struct camu_codec_packet *packet) { - if (!packet) return CAMU_OK; - struct camu_stbi_decoder *stb = (struct camu_stbi_decoder *)dec; + if (!packet) return CAMU_OK; s32 w, h, channels; - u8 *pixels = stbi_load_from_memory(aki_buffer_get_ptr(packet->buffer, 0), - packet->buffer->size, &w, &h, &channels, 4); + u8 *data = aki_buffer_get_ptr(packet->buffer, 0); + u8 *pixels = stbi_load_from_memory(data, packet->buffer->size, &w, &h, &channels, 0); + if (!pixels) return CAMU_ERR_ERROR; + al_assert(w == stb->stream->video.width && h == stb->stream->video.height); - struct camu_frame *frame = al_alloc_object(struct camu_frame); - frame->type = CAMU_NORMAL; + struct camu_codec_frame *frame = al_alloc_object(struct camu_codec_frame); + frame->mode = CAMU_NORMAL; + frame->flags = 0; frame->data = pixels; - frame->width = stb->dec.stream->video.width; - frame->height = stb->dec.stream->video.height; - frame->fmt = CAMU_PIXEL_FMT_RGBA; + frame->video.width = w; + frame->video.height = h; + frame->format = camu_pixel_format_from_channels(channels); + al_assert(frame->format == stb->stream->video.format); frame->pts = 0.0; - frame->flags = 0; stb->callback(stb->userdata, frame); @@ -132,7 +144,7 @@ static void stbi_decoder_free(struct camu_decoder **dec) struct camu_demuxer *camu_stbi_demuxer_create(void) { struct camu_stbi_demuxer *stb = al_alloc_object(struct camu_stbi_demuxer); - stb->demux.type = CAMU_NORMAL; + stb->demux.mode = CAMU_NORMAL; al_array_init(stb->demux.streams); stb->demux.init = stbi_demuxer_init; stb->demux.get_packet = stbi_demuxer_get_packet; @@ -146,7 +158,6 @@ struct camu_decoder *camu_stbi_decoder_create(void) { struct camu_stbi_decoder *stb = al_alloc_object(struct camu_stbi_decoder); stb->dec.init = stbi_decoder_init; - stb->dec.set_callback = stbi_decoder_set_callback; stb->dec.push = stbi_decoder_push; stb->dec.process = stbi_decoder_process; stb->dec.flush = stbi_decoder_flush; diff --git a/src/codec/wuffs/decoder.h b/src/codec/wuffs/decoder.h index 71f0290..bd79236 100644 --- a/src/codec/wuffs/decoder.h +++ b/src/codec/wuffs/decoder.h @@ -5,8 +5,9 @@ struct camu_wuffs_decoder { struct camu_decoder dec; + struct camu_codec_stream *stream; u8 file_type; - void (*callback)(void *, struct camu_frame *); + void (*callback)(void *, struct camu_codec_frame *); void (*userdata); }; diff --git a/src/codec/wuffs/impl.c b/src/codec/wuffs/impl.c index 26e9011..4d9ea7e 100644 --- a/src/codec/wuffs/impl.c +++ b/src/codec/wuffs/impl.c @@ -1,3 +1,4 @@ +#include <aki/common.h> #define WUFFS_IMPLEMENTATION #define WUFFS_CONFIG__STATIC_FUNCTIONS #define WUFFS_CONFIG__MODULES @@ -11,8 +12,6 @@ #define WUFFS_CONFIG__MODULE__IMAGE #include <wuffs-unsupported-snapshot.c> -#include <aki/common.h> - #include "../../cache/entry.h" #include "demuxer.h" @@ -32,44 +31,47 @@ static bool wuffs_demuxer_init(struct camu_demuxer *demux, struct cch_handle *ha cch_handle_read(wfs->handle, aki_buffer_get_ptr(&wfs->buffer, 0), size); wfs->buffer.size = size; - wuffs_png__decoder wf_dec = {0}; + wuffs_png__decoder wf_dec = { 0 }; wuffs_base__status status = wuffs_png__decoder__initialize(&wf_dec, sizeof(wf_dec), WUFFS_VERSION, 0); - (void)status; - //if (!wuffs_base__status__is_ok(&status)) goto err; + if (!wuffs_base__status__is_ok(&status)) goto err; wuffs_png__decoder__set_quirk(&wf_dec, WUFFS_BASE__QUIRK_IGNORE_CHECKSUM, true); - //wuffs_png__decoder__set_report_metadata(&wf_dec, WUFFS_BASE__FOURCC__ICCP, true); - - wuffs_base__io_buffer src = {0}; + wuffs_base__io_buffer src = { 0 }; src.data.ptr = aki_buffer_get_ptr(&wfs->buffer, 0); src.data.len = wfs->buffer.size; src.meta.wi = wfs->buffer.size; src.meta.closed = true; - wuffs_base__image_config ic = {0}; + wuffs_base__image_config ic = { 0 }; status = wuffs_png__decoder__decode_image_config(&wf_dec, &ic, &src); - //if (!wuffs_base__status__is_ok(&status)) goto err; - //if (!wuffs_base__image_config__is_valid(&ic)) goto err; + if (!wuffs_base__status__is_ok(&status)) goto err; + if (!wuffs_base__image_config__is_valid(&ic)) goto err; - struct camu_stream stream; - stream.type = CAMU_NORMAL; + struct camu_codec_stream stream; + stream.mode = CAMU_NORMAL; + stream.type = CAMU_STREAM_VIDEO; stream.video.width = wuffs_base__pixel_config__width(&ic.pixcfg); stream.video.height = wuffs_base__pixel_config__height(&ic.pixcfg); + stream.video.format = CAMU_PIXEL_FORMAT_RGBA; al_array_push(wfs->demux.streams, stream); return true; +err: + return false; } -static s32 wuffs_demuxer_get_packet(struct camu_demuxer *demux, struct camu_packet *packet) +static s32 wuffs_demuxer_get_packet(struct camu_demuxer *demux, struct camu_codec_packet *packet) { struct camu_wuffs_demuxer *wfs = (struct camu_wuffs_demuxer *)demux; - if (wfs->eof) return CAMU_ERR_EOF; - packet->type = CAMU_NORMAL; - packet->buffer = &wfs->buffer; - wfs->eof = true; - return CAMU_OK; + if (!wfs->eof) { + packet->mode = CAMU_NORMAL; + packet->buffer = &wfs->buffer; + wfs->eof = true; + return CAMU_OK; + } + return CAMU_ERR_EOF; } static u64 wuffs_demuxer_get_duration(struct camu_demuxer *demux) @@ -88,85 +90,74 @@ static bool wuffs_demuxer_seek(struct camu_demuxer *demux, u64 pos) static void wuffs_demuxer_free(struct camu_demuxer **demux) { struct camu_wuffs_demuxer *wfs = (struct camu_wuffs_demuxer *)*demux; + aki_buffer_free(&wfs->buffer); + al_array_free(wfs->demux.streams); al_free(wfs); *demux = NULL; } -static bool wuffs_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_stream *stream) +static bool wuffs_decoder_init(struct camu_decoder *dec, struct camu_renderer *renderer, struct camu_codec_stream *stream, + void (*callback)(void *, struct camu_codec_frame *), void *userdata) { struct camu_wuffs_decoder *wfs = (struct camu_wuffs_decoder *)dec; - (void)wfs; - (void)stream; (void)renderer; - return true; -} - -static void wuffs_decoder_set_callback(struct camu_decoder *dec, void (*callback)(void *, struct camu_frame *), void *userdata) -{ - struct camu_wuffs_decoder *wfs = (struct camu_wuffs_decoder *)dec; + wfs->stream = stream; wfs->callback = callback; wfs->userdata = userdata; + return true; } -static s32 wuffs_decoder_push(struct camu_decoder *dec, struct camu_packet *packet) +static s32 wuffs_decoder_push(struct camu_decoder *dec, struct camu_codec_packet *packet) { + struct camu_wuffs_decoder *wfs = (struct camu_wuffs_decoder *)dec; if (!packet) return CAMU_OK; - struct camu_wuffs_decoder *wfs = (struct camu_wuffs_decoder *)dec; + struct camu_codec_frame *frame = al_alloc_object(struct camu_codec_frame); + frame->mode = CAMU_NORMAL; + frame->data = NULL; + frame->flags = 0; + frame->width = wfs->stream->video.width; + frame->height = wfs->stream->video.height; + frame->format = CAMU_PIXEL_FMT_RGBA; - wuffs_png__decoder wf_dec = {0}; + wuffs_png__decoder wf_dec = { 0 }; wuffs_base__status status = wuffs_png__decoder__initialize(&wf_dec, sizeof(wf_dec), WUFFS_VERSION, 0); - //if (!wuffs_base__status__is_ok(&status)) goto err; + if (!wuffs_base__status__is_ok(&status)) goto err; wuffs_png__decoder__set_quirk(&wf_dec, WUFFS_BASE__QUIRK_IGNORE_CHECKSUM, true); - //wuffs_png__decoder__set_report_metadata(&wf_dec, WUFFS_BASE__FOURCC__ICCP, true); - - wuffs_base__io_buffer src = {0}; + wuffs_base__io_buffer src = { 0 }; src.data.ptr = aki_buffer_get_ptr(packet->buffer, 0); src.data.len = packet->buffer->size; src.meta.wi = packet->buffer->size; src.meta.closed = true; - //wf_dec.private_impl.p_decode_iccp[0] = 1; - - wuffs_base__image_config ic = {0}; + wuffs_base__image_config ic = { 0 }; status = wuffs_png__decoder__decode_image_config(&wf_dec, &ic, &src); - //if (!wuffs_base__status__is_ok(&status)) goto err; - //if (!wuffs_base__image_config__is_valid(&ic)) goto err; - - struct camu_frame *frame = al_alloc_object(struct camu_frame); - frame->type = CAMU_NORMAL; - frame->width = wfs->dec.stream->video.width; - frame->height = wfs->dec.stream->video.height; - frame->fmt = CAMU_PIXEL_FMT_RGBA; + if (!wuffs_base__status__is_ok(&status)) goto err; + if (!wuffs_base__image_config__is_valid(&ic)) goto err; u32 pixsub = WUFFS_BASE__PIXEL_SUBSAMPLING__NONE; u32 pixfmt = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL; wuffs_base__pixel_config__set(&ic.pixcfg, pixfmt, pixsub, frame->width, frame->height); - wuffs_base__pixel_buffer pb = {0}; + wuffs_base__pixel_buffer pb = { 0 }; size_t dst_size = frame->width * frame->height * 4; size_t wrk_size = wuffs_png__decoder__workbuf_len(&wf_dec).max_incl; - /* - void *device_ptr = dec->renderer->get_device_ptr(dec->renderer, dst_size + wrk_size); - wuffs_base__slice_u8 wrk = wuffs_base__make_slice_u8(device_ptr + dst_size, wrk_size); - status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, - wuffs_base__make_slice_u8(device_ptr, dst_size)); - */ frame->data = camu_page_alloc(dst_size + wrk_size); - status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, - wuffs_base__make_slice_u8(frame->data, dst_size)); - //if (!wuffs_base__status__is_ok(&status)) goto err; + status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, wuffs_base__make_slice_u8(frame->data, dst_size)); + if (!wuffs_base__status__is_ok(&status)) goto err; wuffs_base__slice_u8 wrk = wuffs_base__make_slice_u8(frame->data + dst_size, wrk_size); status = wuffs_png__decoder__decode_frame(&wf_dec, &pb, &src, 0, wrk, NULL); - (void)status; - //if (!wuffs_base__status__is_ok(&status)) goto err; + if (!wuffs_base__status__is_ok(&status)) goto err; wfs->callback(wfs->userdata, frame); return CAMU_OK; +err: + camu_codec_frame_discard(frame); + return CAMU_ERR_ERROR; } static void wuffs_decoder_flush(struct camu_decoder *dec) @@ -190,7 +181,7 @@ static void wuffs_decoder_free(struct camu_decoder **dec) struct camu_demuxer *camu_wuffs_demuxer_create(void) { struct camu_wuffs_demuxer *wfs = al_alloc_object(struct camu_wuffs_demuxer); - wfs->demux.type = CAMU_NORMAL; + wfs->demux.mode = CAMU_NORMAL; al_array_init(wfs->demux.streams); wfs->demux.init = wuffs_demuxer_init; wfs->demux.get_packet = wuffs_demuxer_get_packet; @@ -204,7 +195,6 @@ struct camu_decoder *camu_wuffs_decoder_create(void) { struct camu_wuffs_decoder *wfs = al_alloc_object(struct camu_wuffs_decoder); wfs->dec.init = wuffs_decoder_init; - wfs->dec.set_callback = wuffs_decoder_set_callback; wfs->dec.push = wuffs_decoder_push; wfs->dec.process = wuffs_decoder_process; wfs->dec.flush = wuffs_decoder_flush; |