216 lines
5.0 KiB
C++
216 lines
5.0 KiB
C++
//
|
|
// Core FFmpeg-style codec registry and decoding helpers.
|
|
//
|
|
|
|
#include "cpdecoder.hh"
|
|
|
|
#include <algorithm>
|
|
#include <mutex>
|
|
|
|
namespace ffmsep {
|
|
|
|
namespace {
|
|
|
|
std::vector<const CPCodec*>& codec_registry() {
|
|
static std::vector<const CPCodec*> registry;
|
|
return registry;
|
|
}
|
|
|
|
std::mutex& registry_mutex() {
|
|
static std::mutex m;
|
|
return m;
|
|
}
|
|
|
|
void attach_codec(CPCodecContext* ctx, const CPCodec* codec) {
|
|
if (!ctx) {
|
|
return;
|
|
}
|
|
|
|
ctx->codec = codec;
|
|
if (!codec) {
|
|
ctx->codec_type = CPMediaType::Unknown;
|
|
ctx->priv_data = nullptr;
|
|
ctx->release_priv_storage();
|
|
return;
|
|
}
|
|
|
|
ctx->codec_type = codec->type;
|
|
ctx->priv_data = ctx->ensure_priv_storage(codec->priv_data_size);
|
|
}
|
|
|
|
bool codec_name_equals(const CPCodec* codec, std::string_view name) {
|
|
if (!codec || !codec->name) {
|
|
return false;
|
|
}
|
|
return std::string_view(codec->name) == name;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void* CPCodecContext::ensure_priv_storage(std::size_t size) {
|
|
if (size == 0U) {
|
|
priv_storage.clear();
|
|
priv_data = nullptr;
|
|
return nullptr;
|
|
}
|
|
if (priv_storage.size() != size) {
|
|
priv_storage.assign(size, static_cast<std::uint8_t>(0));
|
|
}
|
|
priv_data = priv_storage.data();
|
|
return priv_data;
|
|
}
|
|
|
|
void CPCodecContext::release_priv_storage() noexcept {
|
|
priv_storage.clear();
|
|
priv_data = nullptr;
|
|
}
|
|
|
|
void cpcodec_register(const CPCodec* codec) {
|
|
if (!codec || !codec->name) {
|
|
return;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(registry_mutex());
|
|
auto& reg = codec_registry();
|
|
auto already = std::find(reg.begin(), reg.end(), codec);
|
|
if (already != reg.end()) {
|
|
return;
|
|
}
|
|
|
|
auto same_id = std::find_if(reg.begin(), reg.end(), [codec](const CPCodec* entry) {
|
|
return entry && codec && entry->id == codec->id && codec->id != CPCodecID::Unknown;
|
|
});
|
|
if (same_id != reg.end()) {
|
|
*same_id = codec;
|
|
return;
|
|
}
|
|
|
|
reg.push_back(codec);
|
|
}
|
|
|
|
void cpcodec_register_many(std::initializer_list<const CPCodec*> codecs) {
|
|
for (const CPCodec* codec : codecs) {
|
|
cpcodec_register(codec);
|
|
}
|
|
}
|
|
|
|
const CPCodec* cpcodec_find_decoder(CPCodecID id) {
|
|
std::lock_guard<std::mutex> lock(registry_mutex());
|
|
const auto& reg = codec_registry();
|
|
auto it = std::find_if(reg.begin(), reg.end(), [id](const CPCodec* codec) {
|
|
return codec && codec->id == id;
|
|
});
|
|
return it == reg.end() ? nullptr : *it;
|
|
}
|
|
|
|
const CPCodec* cpcodec_find_decoder_by_name(std::string_view name) {
|
|
std::lock_guard<std::mutex> lock(registry_mutex());
|
|
const auto& reg = codec_registry();
|
|
auto it = std::find_if(reg.begin(), reg.end(), [name](const CPCodec* codec) {
|
|
return codec_name_equals(codec, name);
|
|
});
|
|
return it == reg.end() ? nullptr : *it;
|
|
}
|
|
|
|
std::vector<const CPCodec*> cpcodec_list_codecs() {
|
|
std::lock_guard<std::mutex> lock(registry_mutex());
|
|
return codec_registry();
|
|
}
|
|
|
|
CPCodecContext* cpcodec_alloc_context3(const CPCodec* codec) {
|
|
auto* ctx = new CPCodecContext();
|
|
if (codec) {
|
|
attach_codec(ctx, codec);
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
int cpcodec_open2(CPCodecContext* ctx, const CPCodec* codec) {
|
|
if (!ctx) {
|
|
return CP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (ctx->is_open) {
|
|
return CP_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
if (codec) {
|
|
attach_codec(ctx, codec);
|
|
}
|
|
|
|
if (!ctx->codec) {
|
|
return CP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
ctx->is_open = true;
|
|
if (ctx->codec->init) {
|
|
int rc = ctx->codec->init(ctx);
|
|
if (rc < 0) {
|
|
ctx->is_open = false;
|
|
if (ctx->codec->close) {
|
|
ctx->codec->close(ctx);
|
|
}
|
|
return rc;
|
|
}
|
|
}
|
|
return CP_SUCCESS;
|
|
}
|
|
|
|
int cpcodec_close(CPCodecContext* ctx) {
|
|
if (!ctx) {
|
|
return CP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!ctx->is_open) {
|
|
return CP_SUCCESS;
|
|
}
|
|
|
|
if (ctx->codec && ctx->codec->close) {
|
|
ctx->codec->close(ctx);
|
|
}
|
|
|
|
ctx->is_open = false;
|
|
ctx->release_priv_storage();
|
|
ctx->codec_type = CPMediaType::Unknown;
|
|
ctx->codec = nullptr;
|
|
ctx->priv_data = nullptr;
|
|
return CP_SUCCESS;
|
|
}
|
|
|
|
void cpcodec_free_context(CPCodecContext** ctx) {
|
|
if (!ctx || !*ctx) {
|
|
return;
|
|
}
|
|
cpcodec_close(*ctx);
|
|
delete *ctx;
|
|
*ctx = nullptr;
|
|
}
|
|
|
|
int cpcodec_send_packet(CPCodecContext* ctx, const CPPacket* packet) {
|
|
if (!ctx || !packet) {
|
|
return CP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!ctx->is_open || !ctx->codec) {
|
|
return CP_ERROR_NOT_OPEN;
|
|
}
|
|
if (!ctx->codec->send_packet) {
|
|
return CP_ERROR_INVALID_STATE;
|
|
}
|
|
return ctx->codec->send_packet(ctx, *packet);
|
|
}
|
|
|
|
int cpcodec_receive_frame(CPCodecContext* ctx, CPFrame* frame) {
|
|
if (!ctx || !frame) {
|
|
return CP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (!ctx->is_open || !ctx->codec) {
|
|
return CP_ERROR_NOT_OPEN;
|
|
}
|
|
if (!ctx->codec->receive_frame) {
|
|
return CP_ERROR_INVALID_STATE;
|
|
}
|
|
return ctx->codec->receive_frame(ctx, *frame);
|
|
}
|
|
|
|
} // namespace ffmsep
|