Here’s how the main NihAV header looks and it should remain the same (maybe I’ll add error codes there as well but that’s it):
[sourcecode language=”c”]
#ifndef NA_COMMON_H
#define NA_COMMON_H
#include
#include
struct NASet;
enum NAOptionType {
NA_OPT_NULL = 0,
NA_OPT_FLAGS,
NA_OPT_INT,
NA_OPT_DOUBLE,
NA_OPT_STRING,
NA_OPT_BINARY,
NA_OPT_POINTER,
};
typedef union NAOptionValue {
int64_t i64;
uint64_t u64;
double dbl;
const char *str;
struct bin {
const char *ptr;
size_t size;
} bin;
const void *ptr;
} NAOptionValue;
typedef struct NAOption {
const char *name;
enum NAOptionType type;
NAOptionValue value;
} NAOption;
enum NAOptionInterfaceType {
NA_OPT_IF_ANY,
NA_OPT_IF_MINMAX,
NA_OPT_IF_ENUMERATED,
};
typedef struct NAOptionInterface {
const char *name;
const char *explanation;
enum NAOptionType type;
enum NAOptionInterfaceType if_type;
NAOptionValue min_val, max_val;
NAOptionValue *enums;
} NAOptionInterface;
typedef struct NALibrary {
void* (*malloc)(size_t size);
void* (*realloc)(void *ptr, size_t new_size);
void (*free)(void *ptr);
struct NASet *components;
} NALibrary;
#define NA_CLASS_MAGIC 0x11AC1A55
typedef struct NAClass {
uint32_t magic;
const char *name;
const NAOptionInterface *opt_if;
struct NASet *options;
NALibrary *library;
void (*cleanup)(NAClass *c);
} NAClass;
void na_init_library(NALibrary *lib);
void na_init_library_custom_alloc(NALibrary *lib,
void* (*new_malloc)(size_t size),
void* (*new_realloc)(void *ptr, size_t new_size),
void (*new_free)(void *ptr));
int na_lib_add_component(NALibrary *lib, const char *cname, void *component);
void *na_lib_query_component(NALibrary *lib, const char *cname);
void na_clean_library(NALibrary *lib);
int na_class_set_option(NAClass *c, NAOption *opt);
const NAOption* na_class_query_option(NAClass *c, const char *name);
void na_class_unset_option(NAClass *c, const char *name);
void na_class_destroy(NAClass *c);
#endif
[/sourcecode]
So what we have here is essentially three main entities NihAV will use for everything: NALibrary
, NAClass
and NAOption
.
NALibrary
is the core that manages the rest. As you can see it has a collection of components that, as discussed in the previous post, will contain the set of instances implementing tasks (e.g. codecs, de/compressors, hashes, de/muxers etc.) and this library object also contains allocator for memory management. This way it can be all pinned to the needed instance, e.g. once I’ve seen a code that had used libavcodec
in two separate modules — for video and audio of course — and those two modules didn’t know a thing about each other (and were dynamically loaded too). Note to self: implement filtered loading for components, e.g. when initialising libnacodec
only audio decoders will be registered or when initialising libnacompr
only decoders are registered etc. etc.
The second component is NAClass
. Every public component of NihAV beside NALibrary
will be an instance of NAClass
. Users are not supposed to construct one themselves, there will be utility functions for doing that behind the scenes (after all, you don’t need this object directly, you need a component in NALibrary
doing what you want).
And the third component is what makes it more extensible without adding many public fields — NAOption
for storing parameters in a class and NAOptionInterface
for defining what options that class accepts.
Expected flow is like this:
NALibrary
instance is created;- needed compontents are registered there (by creating copies inside the library tied to it — see the next to last field in
NAClass
); - when an instance is queried, a copy is created for that operation (the definition is quite small and you should not do it often so it should not be a complete murder);
- user sets the options on the obtained instance;
- user uses aforementioned instance to do work (coding/decoding, muxing, whatever);
- user invokes destructor for the instance;
NALibrary
instance is destroyed.
There will be some exceptions, i.e. probing should be done stateless by simply walking over the set of probe objects and invoking probe()
there without creating a new instances. And something similar for decoder detection too — current libavcodec
way with registering and initialising all decoders is an overkill.
This is how it should be. Volunteers to implement? None? Not even myself?! Yes, I thought so.
Magic number is certainly wrong.
What the magic is supposed to do?
It’s for ensuring that the object is really a class instance. I remember having mysterious crashes just because AVClass* was missing from the structure (not possible in NihAV by design). So here I can at least check that the input is a proper instance and not just some garbage.
I like it so far.
Can you maybe explain the need of custom memory allocations?