mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
video: implement PS5 Vdecsw using a shared queued AVC decoder
Co-authored-by: Ali Almohaya <AliAlmohaya@gmail.com>
This commit is contained in:
@@ -0,0 +1,459 @@
|
||||
#include "common/abi.h"
|
||||
#include "libs/libs.h"
|
||||
#include "libs/videoDec2Decoder.h"
|
||||
#include "loader/symbolDatabase.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Libs::Vdecsw {
|
||||
|
||||
LIB_VERSION("Vdecsw", 1, "Vdecsw", 1, 1);
|
||||
|
||||
namespace Decoder = VideoDec2::Decoder;
|
||||
|
||||
enum class Error : uint32_t {
|
||||
ApiFail = 0x0100,
|
||||
StructSize,
|
||||
ArgumentPointer,
|
||||
DecoderInstance,
|
||||
MemorySize,
|
||||
MemoryPointer,
|
||||
FrameBufferSize,
|
||||
FrameBufferPointer,
|
||||
FrameBufferAlignment,
|
||||
AccessUnitSize = 0x010d,
|
||||
AccessUnitPointer,
|
||||
OutputInfo,
|
||||
ComputeQueue,
|
||||
InputQueueFull = 0x0113,
|
||||
OutputBufferFull,
|
||||
InputQueueEmpty = 0x0116,
|
||||
DecodePending,
|
||||
OutputBufferEmpty,
|
||||
ConfigInfo = 0x0200,
|
||||
ComputePipeId,
|
||||
ComputeQueueId,
|
||||
ResourceType,
|
||||
CodecType,
|
||||
InputQueueDepth = 0x0206,
|
||||
DpbFrameCount = 0x0209,
|
||||
FrameWidthHeight,
|
||||
ExtraConfigInfo,
|
||||
AccessUnit = 0x0301,
|
||||
OversizeDecode,
|
||||
};
|
||||
|
||||
constexpr int32_t Fail(Error error) {
|
||||
return static_cast<int32_t>(0x81510000u | static_cast<uint32_t>(error));
|
||||
}
|
||||
|
||||
struct ComputeMemoryInfo {
|
||||
size_t this_size;
|
||||
size_t cpu_gpu_memory_size;
|
||||
void* cpu_gpu_memory;
|
||||
};
|
||||
|
||||
struct ComputeConfigInfo {
|
||||
size_t this_size;
|
||||
uint16_t pipe_id;
|
||||
uint16_t queue_id;
|
||||
bool check_memory_type;
|
||||
uint8_t reserved0;
|
||||
uint16_t reserved1;
|
||||
};
|
||||
|
||||
struct ComputeQueue {
|
||||
uint16_t pipe_id;
|
||||
uint16_t queue_id;
|
||||
uint32_t decoders = 0;
|
||||
};
|
||||
|
||||
struct ConfigInfo {
|
||||
size_t this_size;
|
||||
uint32_t resource_type;
|
||||
uint32_t codec_type;
|
||||
uint32_t profile;
|
||||
uint32_t max_level;
|
||||
int32_t max_width;
|
||||
int32_t max_height;
|
||||
int32_t max_dpb_frames;
|
||||
uint32_t input_queue_depth;
|
||||
ComputeQueue* compute_queue;
|
||||
uint64_t cpu_affinity_mask;
|
||||
int32_t cpu_thread_priority;
|
||||
bool optimize_progressive;
|
||||
bool check_memory_type;
|
||||
uint8_t reserved0;
|
||||
int8_t extra_dpb_frames;
|
||||
void* extra_config;
|
||||
bool disable_sync_input;
|
||||
uint32_t max_pending_sync;
|
||||
};
|
||||
|
||||
struct MemoryInfo {
|
||||
size_t this_size;
|
||||
size_t cpu_memory_size;
|
||||
void* cpu_memory;
|
||||
size_t gpu_memory_size;
|
||||
void* gpu_memory;
|
||||
size_t cpu_gpu_memory_size;
|
||||
void* cpu_gpu_memory;
|
||||
size_t max_frame_buffer_size;
|
||||
uint32_t frame_buffer_alignment;
|
||||
uint32_t reserved0;
|
||||
};
|
||||
|
||||
struct InputData {
|
||||
size_t this_size;
|
||||
void* au_data;
|
||||
size_t au_size;
|
||||
uint64_t pts;
|
||||
uint64_t dts;
|
||||
uint64_t attached_data;
|
||||
};
|
||||
|
||||
struct InputResult {
|
||||
size_t this_size;
|
||||
void* decoded_au;
|
||||
uint32_t output_frame_count;
|
||||
uint32_t reserved0;
|
||||
};
|
||||
|
||||
struct FrameBuffer {
|
||||
size_t this_size;
|
||||
void* data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct OutputInfo {
|
||||
size_t this_size;
|
||||
bool valid;
|
||||
bool last_frame;
|
||||
bool error_frame;
|
||||
uint8_t picture_count;
|
||||
uint32_t codec_type;
|
||||
uint32_t width;
|
||||
uint32_t pitch;
|
||||
uint32_t height;
|
||||
bool discarded_frame;
|
||||
void* frame_buffer;
|
||||
size_t frame_buffer_size;
|
||||
uint32_t format;
|
||||
uint32_t pitch_in_bytes;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ComputeMemoryInfo) == 24);
|
||||
static_assert(sizeof(ComputeConfigInfo) == 16);
|
||||
static_assert(sizeof(ConfigInfo) == 80);
|
||||
static_assert(offsetof(ConfigInfo, extra_dpb_frames) == 63);
|
||||
static_assert(offsetof(ConfigInfo, disable_sync_input) == 72);
|
||||
static_assert(sizeof(MemoryInfo) == 72);
|
||||
static_assert(sizeof(InputData) == 48);
|
||||
static_assert(sizeof(InputResult) == 24);
|
||||
static_assert(sizeof(FrameBuffer) == 24);
|
||||
static_assert(sizeof(OutputInfo) == 56);
|
||||
static_assert(offsetof(OutputInfo, discarded_frame) == 28);
|
||||
static_assert(offsetof(OutputInfo, frame_buffer) == 32);
|
||||
|
||||
struct State;
|
||||
static std::shared_mutex g_registry_mutex;
|
||||
static std::unordered_set<ComputeQueue*> g_queues;
|
||||
static std::unordered_set<State*> g_decoders;
|
||||
|
||||
struct State {
|
||||
ConfigInfo config;
|
||||
Decoder::Instance* decoder;
|
||||
std::mutex mutex;
|
||||
std::deque<InputResult> completed_inputs;
|
||||
std::optional<FrameBuffer> output_buffer;
|
||||
|
||||
~State() {
|
||||
Decoder::Destroy(decoder);
|
||||
--config.compute_queue->decoders;
|
||||
}
|
||||
};
|
||||
|
||||
// The shared registry lock keeps guest-backed state alive until each API call returns.
|
||||
// Deletion takes the exclusive lock before releasing that memory to the guest.
|
||||
struct StateAccess {
|
||||
std::shared_lock<std::shared_mutex> lock {g_registry_mutex};
|
||||
State* value;
|
||||
explicit StateAccess(State* handle): value(g_decoders.contains(handle) ? handle : nullptr) {}
|
||||
explicit operator bool() const { return value != nullptr; }
|
||||
State* operator->() const { return value; }
|
||||
};
|
||||
|
||||
static int32_t Result(Decoder::Result result) {
|
||||
switch (result) {
|
||||
case Decoder::Result::Ok: return 0;
|
||||
case Decoder::Result::ApiFail: return Fail(Error::ApiFail);
|
||||
case Decoder::Result::AccessUnit: return Fail(Error::AccessUnit);
|
||||
case Decoder::Result::FrameBufferSize: return Fail(Error::FrameBufferSize);
|
||||
case Decoder::Result::OversizeDecode: return Fail(Error::OversizeDecode);
|
||||
case Decoder::Result::InputFull: return Fail(Error::OutputBufferFull);
|
||||
case Decoder::Result::NoFrame: return Fail(Error::DecodePending);
|
||||
}
|
||||
return Fail(Error::ApiFail);
|
||||
}
|
||||
|
||||
static int32_t ValidateConfig(const ConfigInfo* config) {
|
||||
if (config == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (config->this_size != sizeof(ConfigInfo)) return Fail(Error::StructSize);
|
||||
if (config->resource_type != 1) return Fail(Error::ResourceType);
|
||||
if (config->codec_type != 1) return Fail(Error::CodecType);
|
||||
if (config->reserved0 != 0) return Fail(Error::ConfigInfo);
|
||||
if (config->extra_config != nullptr) return Fail(Error::ExtraConfigInfo);
|
||||
if (config->input_queue_depth < 1 || config->input_queue_depth > 8)
|
||||
return Fail(Error::InputQueueDepth);
|
||||
if (config->max_dpb_frames < 1 || config->extra_dpb_frames < 0)
|
||||
return Fail(Error::DpbFrameCount);
|
||||
if (config->max_width <= 0 || config->max_height <= 0) return Fail(Error::FrameWidthHeight);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t FrameSize(const ConfigInfo& config) {
|
||||
// AVC output: 256-byte pitch, 16-line maximum height, and 4096 bytes of metadata.
|
||||
const auto pitch = (static_cast<size_t>(config.max_width) + 255u) & ~size_t {255};
|
||||
const auto height = (static_cast<size_t>(config.max_height) + 15u) & ~size_t {15};
|
||||
return pitch * height * 3 / 2 + 4096;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI QueryComputeMemoryInfo(ComputeMemoryInfo* memory) {
|
||||
PRINT_NAME();
|
||||
if (memory == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (memory->this_size != sizeof(ComputeMemoryInfo)) return Fail(Error::StructSize);
|
||||
memory->cpu_gpu_memory_size = sizeof(ComputeQueue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI AllocateComputeQueue(const ComputeConfigInfo* config,
|
||||
const ComputeMemoryInfo* memory,
|
||||
ComputeQueue** output) {
|
||||
PRINT_NAME();
|
||||
if (config == nullptr || memory == nullptr || output == nullptr)
|
||||
return Fail(Error::ArgumentPointer);
|
||||
if (config->this_size != sizeof(ComputeConfigInfo) ||
|
||||
memory->this_size != sizeof(ComputeMemoryInfo))
|
||||
return Fail(Error::StructSize);
|
||||
if (config->reserved0 != 0 || config->reserved1 != 0) return Fail(Error::ConfigInfo);
|
||||
if (config->pipe_id > 4) return Fail(Error::ComputePipeId);
|
||||
if (config->queue_id > 7) return Fail(Error::ComputeQueueId);
|
||||
if (memory->cpu_gpu_memory_size < sizeof(ComputeQueue)) return Fail(Error::MemorySize);
|
||||
if (memory->cpu_gpu_memory == nullptr ||
|
||||
reinterpret_cast<uintptr_t>(memory->cpu_gpu_memory) % alignof(ComputeQueue) != 0)
|
||||
return Fail(Error::MemoryPointer);
|
||||
std::scoped_lock lock(g_registry_mutex);
|
||||
for (const auto* queue: g_queues)
|
||||
if ((queue->pipe_id == config->pipe_id && queue->queue_id == config->queue_id) ||
|
||||
queue == memory->cpu_gpu_memory)
|
||||
return Fail(Error::ComputeQueue);
|
||||
auto* queue = new (memory->cpu_gpu_memory) ComputeQueue {config->pipe_id, config->queue_id};
|
||||
g_queues.insert(queue);
|
||||
*output = queue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI ReleaseComputeQueue(ComputeQueue* queue) {
|
||||
PRINT_NAME();
|
||||
std::scoped_lock lock(g_registry_mutex);
|
||||
if (!g_queues.contains(queue) || queue->decoders != 0) return Fail(Error::ComputeQueue);
|
||||
g_queues.erase(queue);
|
||||
queue->~ComputeQueue();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI QueryDecoderMemoryInfo(const ConfigInfo* config, MemoryInfo* memory) {
|
||||
PRINT_NAME();
|
||||
if (const auto error = ValidateConfig(config); error != 0) return error;
|
||||
if (memory == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (memory->this_size != sizeof(MemoryInfo)) return Fail(Error::StructSize);
|
||||
memory->cpu_memory_size = sizeof(State);
|
||||
// The FFmpeg backend needs no guest GPU workspace.
|
||||
memory->gpu_memory_size = 0;
|
||||
memory->cpu_gpu_memory_size = 0;
|
||||
memory->max_frame_buffer_size = FrameSize(*config);
|
||||
memory->frame_buffer_alignment = 256;
|
||||
memory->reserved0 = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI CreateDecoder(const ConfigInfo* config, const MemoryInfo* memory,
|
||||
State** output) {
|
||||
PRINT_NAME();
|
||||
if (const auto error = ValidateConfig(config); error != 0) return error;
|
||||
if (memory == nullptr || output == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (memory->this_size != sizeof(MemoryInfo)) return Fail(Error::StructSize);
|
||||
if (memory->cpu_memory_size < sizeof(State)) return Fail(Error::MemorySize);
|
||||
if (memory->cpu_memory == nullptr ||
|
||||
reinterpret_cast<uintptr_t>(memory->cpu_memory) % alignof(State) != 0)
|
||||
return Fail(Error::MemoryPointer);
|
||||
std::scoped_lock lock(g_registry_mutex);
|
||||
if (!g_queues.contains(config->compute_queue)) return Fail(Error::ComputeQueue);
|
||||
if (g_decoders.contains(static_cast<State*>(memory->cpu_memory)))
|
||||
return Fail(Error::MemoryPointer);
|
||||
auto* decoder = Decoder::Create(
|
||||
{config->codec_type, config->max_width, config->max_height,
|
||||
std::max(2u, static_cast<uint32_t>(config->max_dpb_frames) + config->extra_dpb_frames)});
|
||||
if (decoder == nullptr) return Fail(Error::ApiFail);
|
||||
auto* state = new (memory->cpu_memory) State {*config, decoder};
|
||||
++config->compute_queue->decoders;
|
||||
g_decoders.insert(state);
|
||||
*output = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI DeleteDecoder(State* handle) {
|
||||
PRINT_NAME();
|
||||
std::scoped_lock lock(g_registry_mutex);
|
||||
if (g_decoders.erase(handle) == 0) return Fail(Error::DecoderInstance);
|
||||
handle->~State();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI SetDecodeInput(State* handle, const InputData* input) {
|
||||
PRINT_NAME();
|
||||
const StateAccess state(handle);
|
||||
if (!state) return Fail(Error::DecoderInstance);
|
||||
if (input == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (input->this_size != sizeof(InputData)) return Fail(Error::StructSize);
|
||||
if (input->au_data == nullptr) return Fail(Error::AccessUnitPointer);
|
||||
if (input->au_size == 0) return Fail(Error::AccessUnitSize);
|
||||
std::scoped_lock lock(state->mutex);
|
||||
const auto& config = state->config;
|
||||
if (!config.disable_sync_input &&
|
||||
state->completed_inputs.size() >=
|
||||
uint64_t {config.input_queue_depth} + config.max_pending_sync)
|
||||
return Fail(Error::InputQueueFull);
|
||||
uint32_t produced = 0;
|
||||
const auto result = Decoder::Submit(
|
||||
state->decoder,
|
||||
{input->au_data, input->au_size, input->pts, input->dts, input->attached_data}, &produced);
|
||||
if (result != Decoder::Result::Ok) return Result(result);
|
||||
if (!config.disable_sync_input)
|
||||
state->completed_inputs.push_back({sizeof(InputResult), input->au_data, produced, 0});
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI TrySyncDecodeInput(State* handle, InputResult* output) {
|
||||
PRINT_NAME();
|
||||
const StateAccess state(handle);
|
||||
if (!state) return Fail(Error::DecoderInstance);
|
||||
if (output == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (output->this_size != sizeof(InputResult)) return Fail(Error::StructSize);
|
||||
std::scoped_lock lock(state->mutex);
|
||||
if (state->config.disable_sync_input) return Fail(Error::ApiFail);
|
||||
if (state->completed_inputs.empty()) return Fail(Error::InputQueueEmpty);
|
||||
*output = state->completed_inputs.front();
|
||||
state->completed_inputs.pop_front();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI SetDecodeOutput(State* handle, FrameBuffer* buffer) {
|
||||
PRINT_NAME();
|
||||
const StateAccess state(handle);
|
||||
if (!state) return Fail(Error::DecoderInstance);
|
||||
if (buffer == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (buffer->this_size != sizeof(FrameBuffer)) return Fail(Error::StructSize);
|
||||
if (buffer->data == nullptr) return Fail(Error::FrameBufferPointer);
|
||||
if (reinterpret_cast<uintptr_t>(buffer->data) % 256 != 0)
|
||||
return Fail(Error::FrameBufferAlignment);
|
||||
if (buffer->size < FrameSize(state->config)) return Fail(Error::FrameBufferSize);
|
||||
std::scoped_lock lock(state->mutex);
|
||||
if (state->output_buffer) return Fail(Error::OutputBufferFull);
|
||||
state->output_buffer = *buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI TrySyncDecodeOutput(State* handle, OutputInfo* output) {
|
||||
PRINT_NAME();
|
||||
const StateAccess state(handle);
|
||||
if (!state) return Fail(Error::DecoderInstance);
|
||||
if (output == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (output->this_size != sizeof(OutputInfo)) return Fail(Error::StructSize);
|
||||
std::scoped_lock lock(state->mutex);
|
||||
if (!state->output_buffer) return Fail(Error::OutputBufferEmpty);
|
||||
const auto& buffer = *state->output_buffer;
|
||||
Decoder::Output decoded {};
|
||||
const auto result = Decoder::Receive(state->decoder, {buffer.data, buffer.size}, &decoded);
|
||||
if (result != Decoder::Result::Ok) return Result(result);
|
||||
*output = {sizeof(OutputInfo),
|
||||
true,
|
||||
decoded.last_frame,
|
||||
decoded.error_frame,
|
||||
1,
|
||||
decoded.codec_type,
|
||||
decoded.width,
|
||||
decoded.pitch,
|
||||
decoded.height,
|
||||
false,
|
||||
decoded.buffer,
|
||||
decoded.buffer_size,
|
||||
0,
|
||||
decoded.pitch};
|
||||
state->output_buffer.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI FinalizeDecodeSequence(State* handle) {
|
||||
PRINT_NAME();
|
||||
const StateAccess state(handle);
|
||||
if (!state) return Fail(Error::DecoderInstance);
|
||||
std::scoped_lock lock(state->mutex);
|
||||
return Result(Decoder::Finalize(state->decoder));
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI ResetDecoder(State* handle) {
|
||||
PRINT_NAME();
|
||||
const StateAccess state(handle);
|
||||
if (!state) return Fail(Error::DecoderInstance);
|
||||
std::scoped_lock lock(state->mutex);
|
||||
Decoder::Reset(state->decoder);
|
||||
state->completed_inputs.clear();
|
||||
state->output_buffer.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t KYTY_SYSV_ABI GetAvcPictureInfo(const OutputInfo* output,
|
||||
Decoder::AvcPictureInfo* first,
|
||||
Decoder::AvcPictureInfo* second) {
|
||||
PRINT_NAME();
|
||||
if (output == nullptr || first == nullptr) return Fail(Error::ArgumentPointer);
|
||||
if (output->this_size != sizeof(OutputInfo) || first->this_size != sizeof(*first) ||
|
||||
(second != nullptr && second->this_size != sizeof(*second)))
|
||||
return Fail(Error::StructSize);
|
||||
if (!output->valid || output->codec_type != 1 || output->picture_count == 0)
|
||||
return Fail(Error::OutputInfo);
|
||||
Decoder::PictureInfo decoded {};
|
||||
if (!Decoder::GetPictureInfo(output->frame_buffer, &decoded) || decoded.codec_type != 1)
|
||||
return Fail(Error::OutputInfo);
|
||||
*first = Decoder::MakeAvcPictureInfo(decoded, sizeof(*first));
|
||||
if (second != nullptr) *second = {.this_size = sizeof(*second)};
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitVdecsw_1) {
|
||||
PRINT_NAME_ENABLE(true);
|
||||
LIB_FUNC("0moTubWCsTM", QueryComputeMemoryInfo);
|
||||
LIB_FUNC("hIgrg5h4V6s", AllocateComputeQueue);
|
||||
LIB_FUNC("A+2M7EivuOU", QueryDecoderMemoryInfo);
|
||||
LIB_FUNC("+L5ArV1tPGA", CreateDecoder);
|
||||
LIB_FUNC("ecUtPX+dBYk", DeleteDecoder);
|
||||
LIB_FUNC("fX-zOOefbbs", ReleaseComputeQueue);
|
||||
LIB_FUNC("aqMiF0AgUYI", SetDecodeInput);
|
||||
LIB_FUNC("veb-YBrOqo0", ResetDecoder);
|
||||
LIB_FUNC("kMBw37oH8nI", TrySyncDecodeOutput);
|
||||
LIB_FUNC("rgtMCOpyBSc", SetDecodeOutput);
|
||||
LIB_FUNC("ihNT-uuEAr4", GetAvcPictureInfo);
|
||||
LIB_FUNC("5Y6nZqIZvBg", FinalizeDecodeSequence);
|
||||
LIB_FUNC("l4sQYy5wPkc", TrySyncDecodeInput);
|
||||
}
|
||||
|
||||
} // namespace Libs::Vdecsw
|
||||
@@ -111,69 +111,7 @@ struct Videodec2FrameBuffer {
|
||||
bool is_accepted;
|
||||
};
|
||||
|
||||
struct Videodec2AvcPictureInfo {
|
||||
size_t this_size;
|
||||
bool is_valid;
|
||||
|
||||
uint64_t pts_data;
|
||||
uint64_t dts_data;
|
||||
uint64_t attached_data;
|
||||
|
||||
uint8_t idr_picture_flag;
|
||||
uint8_t profile_idc;
|
||||
uint8_t level_idc;
|
||||
uint32_t pic_width_in_mbs_minus1;
|
||||
uint32_t pic_height_in_map_units_minus1;
|
||||
uint8_t frame_mbs_only_flag;
|
||||
|
||||
uint8_t frame_cropping_flag;
|
||||
uint32_t frame_crop_left_offset;
|
||||
uint32_t frame_crop_right_offset;
|
||||
uint32_t frame_crop_top_offset;
|
||||
uint32_t frame_crop_bottom_offset;
|
||||
|
||||
uint8_t aspect_ratio_info_present_flag;
|
||||
uint8_t aspect_ratio_idc;
|
||||
uint16_t sar_width;
|
||||
uint16_t sar_height;
|
||||
|
||||
uint8_t video_signal_type_present_flag;
|
||||
uint8_t video_format;
|
||||
uint8_t video_full_range_flag;
|
||||
uint8_t colour_description_present_flag;
|
||||
uint8_t colour_primaries;
|
||||
uint8_t transfer_characteristics;
|
||||
uint8_t matrix_coefficients;
|
||||
|
||||
uint8_t timing_info_present_flag;
|
||||
uint32_t num_units_in_tick;
|
||||
uint32_t time_scale;
|
||||
uint8_t fixed_frame_rate_flag;
|
||||
|
||||
uint8_t bitstream_restriction_flag;
|
||||
uint8_t max_dec_frame_buffering;
|
||||
|
||||
uint8_t pic_struct_present_flag;
|
||||
uint8_t pic_struct;
|
||||
uint8_t field_pic_flag;
|
||||
uint8_t bottom_field_flag;
|
||||
|
||||
uint8_t sequence_parameter_set_present_flag;
|
||||
uint8_t picture_parameter_set_present_flag;
|
||||
uint8_t au_delimiter_present_flag;
|
||||
uint8_t end_of_sequence_present_flag;
|
||||
uint8_t end_of_stream_present_flag;
|
||||
uint8_t filler_data_present_flag;
|
||||
uint8_t picture_timing_sei_present_flag;
|
||||
uint8_t buffering_period_sei_present_flag;
|
||||
|
||||
uint8_t constraint_set0_flag;
|
||||
uint8_t constraint_set1_flag;
|
||||
uint8_t constraint_set2_flag;
|
||||
uint8_t constraint_set3_flag;
|
||||
uint8_t constraint_set4_flag;
|
||||
uint8_t constraint_set5_flag;
|
||||
};
|
||||
using Videodec2AvcPictureInfo = Decoder::AvcPictureInfo;
|
||||
|
||||
struct Videodec2ComputeMemoryInfo {
|
||||
size_t this_size;
|
||||
@@ -235,6 +173,8 @@ static void FillNoPictureOutput(const Videodec2FrameBuffer* frame_buffer,
|
||||
static int32_t MapDecoderResult(Decoder::Result result) {
|
||||
switch (result) {
|
||||
case Decoder::Result::Ok: return OK;
|
||||
case Decoder::Result::InputFull:
|
||||
case Decoder::Result::NoFrame:
|
||||
case Decoder::Result::ApiFail: return VIDEODEC2_ERROR_API_FAIL;
|
||||
case Decoder::Result::AccessUnit: return VIDEODEC2_ERROR_ACCESS_UNIT;
|
||||
case Decoder::Result::FrameBufferSize: return VIDEODEC2_ERROR_FRAME_BUFFER_SIZE;
|
||||
@@ -599,40 +539,7 @@ static int32_t KYTY_SYSV_ABI GetPictureInfo(const Videodec2OutputInfo* output_in
|
||||
return VIDEODEC2_ERROR_STRUCT_SIZE;
|
||||
}
|
||||
|
||||
Videodec2AvcPictureInfo picture {};
|
||||
picture.this_size = requested_size;
|
||||
picture.is_valid = true;
|
||||
picture.pts_data = decoded.pts;
|
||||
picture.dts_data = decoded.dts;
|
||||
picture.attached_data = decoded.attached_data;
|
||||
picture.idr_picture_flag = decoded.key_frame ? 1 : 0;
|
||||
picture.profile_idc = static_cast<uint8_t>(decoded.profile);
|
||||
picture.level_idc = static_cast<uint8_t>(decoded.level);
|
||||
picture.pic_width_in_mbs_minus1 = (decoded.width + 15u) / 16u - 1u;
|
||||
picture.pic_height_in_map_units_minus1 = (decoded.height + 15u) / 16u - 1u;
|
||||
picture.frame_mbs_only_flag = 1;
|
||||
picture.frame_cropping_flag = decoded.crop_left != 0 || decoded.crop_right != 0 ||
|
||||
decoded.crop_top != 0 || decoded.crop_bottom != 0
|
||||
? 1
|
||||
: 0;
|
||||
picture.frame_crop_left_offset = decoded.crop_left;
|
||||
picture.frame_crop_right_offset = decoded.crop_right;
|
||||
picture.frame_crop_top_offset = decoded.crop_top;
|
||||
picture.frame_crop_bottom_offset = decoded.crop_bottom;
|
||||
picture.aspect_ratio_info_present_flag =
|
||||
decoded.sar_width != 0 && decoded.sar_height != 0 ? 1 : 0;
|
||||
picture.aspect_ratio_idc = picture.aspect_ratio_info_present_flag ? 255 : 0;
|
||||
picture.sar_width = decoded.sar_width;
|
||||
picture.sar_height = decoded.sar_height;
|
||||
picture.video_signal_type_present_flag = 1;
|
||||
picture.video_format = 5;
|
||||
picture.video_full_range_flag = decoded.color_range == 2 ? 1 : 0;
|
||||
picture.colour_description_present_flag =
|
||||
decoded.color_primaries != 0 || decoded.color_trc != 0 || decoded.color_space != 0 ? 1
|
||||
: 0;
|
||||
picture.colour_primaries = decoded.color_primaries;
|
||||
picture.transfer_characteristics = decoded.color_trc;
|
||||
picture.matrix_coefficients = decoded.color_space;
|
||||
const auto picture = Decoder::MakeAvcPictureInfo(decoded, requested_size);
|
||||
std::memcpy(first_picture_info, &picture, requested_size);
|
||||
} else {
|
||||
const auto result = fill_common(first_picture_info, true);
|
||||
|
||||
@@ -22,6 +22,10 @@ namespace VideoDec2 {
|
||||
LIB_DEFINE(InitVideoDec2_1);
|
||||
} // namespace VideoDec2
|
||||
|
||||
namespace Vdecsw {
|
||||
LIB_DEFINE(InitVdecsw_1);
|
||||
} // namespace Vdecsw
|
||||
|
||||
namespace LibMouse {
|
||||
LIB_DEFINE(InitMouse_1);
|
||||
} // namespace LibMouse
|
||||
@@ -148,6 +152,7 @@ void InitAll(Loader::SymbolDatabase* s) {
|
||||
LibUlt::InitUlt_1(s);
|
||||
InitUserService_1(s);
|
||||
VideoDec2::InitVideoDec2_1(s);
|
||||
Vdecsw::InitVdecsw_1(s);
|
||||
LibGen5::InitVideoOut_1(s);
|
||||
LibGen5::VrrStatus::InitVideoOutVrrStatus_1(s);
|
||||
InitWebBrowserDialog_1(s);
|
||||
|
||||
+173
-96
@@ -6,6 +6,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
explicit Instance(const Config& config): m_config(config) {}
|
||||
|
||||
~Instance() {
|
||||
ClearFrames();
|
||||
ClearPictureMetadata();
|
||||
if (m_sws != nullptr) {
|
||||
sws_freeContext(m_sws);
|
||||
@@ -119,116 +121,138 @@ public:
|
||||
|
||||
[[nodiscard]] uint32_t CodecType() const { return m_config.codec_type; }
|
||||
|
||||
[[nodiscard]] Result Submit(const Input& input, uint32_t* produced) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
return SubmitInput(input, produced);
|
||||
}
|
||||
|
||||
[[nodiscard]] Result Receive(const FrameBuffer& frame_buffer, Output* output) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
return ReceiveOutput(frame_buffer, output);
|
||||
}
|
||||
|
||||
[[nodiscard]] Result Finalize() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
if (m_finalized) return Result::Ok;
|
||||
const auto drained = DrainFrames();
|
||||
if (drained != Result::Ok) return drained;
|
||||
const int result = avcodec_send_packet(m_codec, nullptr);
|
||||
if (result == AVERROR(EAGAIN)) return Result::InputFull;
|
||||
if (result < 0 && result != AVERROR_EOF) return Result::ApiFail;
|
||||
m_finalized = true;
|
||||
return DrainFrames();
|
||||
}
|
||||
|
||||
[[nodiscard]] Result DecodeInput(const Input& input, const FrameBuffer& frame_buffer,
|
||||
Output* output) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
*output = {};
|
||||
|
||||
AVPacket* packet = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
if (packet == nullptr || frame == nullptr ||
|
||||
input.size > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
return Result::ApiFail;
|
||||
uint32_t produced = 0;
|
||||
auto result = SubmitInput(input, &produced);
|
||||
if (result == Result::InputFull) {
|
||||
// The combined API supplies an output buffer together with each AU.
|
||||
result = ReceiveOutput(frame_buffer, output);
|
||||
if (result != Result::Ok) return result;
|
||||
return SubmitInput(input, &produced);
|
||||
}
|
||||
|
||||
int result = av_new_packet(packet, static_cast<int>(input.size));
|
||||
if (result < 0) {
|
||||
LOGF("Videodec2: av_new_packet failed: %s (%d)\n", AvErrorString(result), result);
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
return Result::ApiFail;
|
||||
}
|
||||
std::memcpy(packet->data, input.data, input.size);
|
||||
packet->pts = ToAvTimestamp(input.pts);
|
||||
packet->dts = ToAvTimestamp(input.dts);
|
||||
|
||||
packet->opaque_ref = av_buffer_alloc(sizeof(PacketMetadata));
|
||||
if (packet->opaque_ref == nullptr) {
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
return Result::ApiFail;
|
||||
}
|
||||
const PacketMetadata metadata {input.pts, input.dts, input.attached_data};
|
||||
std::memcpy(packet->opaque_ref->data, &metadata, sizeof(metadata));
|
||||
|
||||
bool have_pending_frame = false;
|
||||
result = avcodec_send_packet(m_codec, packet);
|
||||
if (result == AVERROR(EAGAIN)) {
|
||||
result = avcodec_receive_frame(m_codec, frame);
|
||||
if (result < 0) {
|
||||
LOGF("Videodec2: decoder rejected an AU while no output was available: %s (%d)\n",
|
||||
AvErrorString(result), result);
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
return Result::AccessUnit;
|
||||
}
|
||||
have_pending_frame = true;
|
||||
result = avcodec_send_packet(m_codec, packet);
|
||||
}
|
||||
if (result < 0) {
|
||||
LOGF("Videodec2: avcodec_send_packet failed: %s (%d)\n", AvErrorString(result), result);
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
return Result::AccessUnit;
|
||||
}
|
||||
|
||||
Result decode_result = Result::Ok;
|
||||
if (!have_pending_frame) {
|
||||
result = avcodec_receive_frame(m_codec, frame);
|
||||
if (result != AVERROR(EAGAIN) && result != AVERROR_EOF) {
|
||||
if (result < 0) {
|
||||
LOGF("Videodec2: avcodec_receive_frame failed: %s (%d)\n",
|
||||
AvErrorString(result), result);
|
||||
decode_result = Result::AccessUnit;
|
||||
} else {
|
||||
decode_result = CopyFrame(frame, frame_buffer, output);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
decode_result = CopyFrame(frame, frame_buffer, output);
|
||||
}
|
||||
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
return decode_result;
|
||||
if (result != Result::Ok) return result;
|
||||
result = ReceiveOutput(frame_buffer, output);
|
||||
return result == Result::NoFrame ? Result::Ok : result;
|
||||
}
|
||||
|
||||
[[nodiscard]] Result FlushOutput(const FrameBuffer& frame_buffer, Output* output) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
*output = {};
|
||||
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
if (frame == nullptr) {
|
||||
return Result::ApiFail;
|
||||
}
|
||||
|
||||
// Guest Flush collects available pictures between AUs while retaining reference frames.
|
||||
const int receive_result = avcodec_receive_frame(m_codec, frame);
|
||||
if (receive_result == AVERROR(EAGAIN) || receive_result == AVERROR_EOF) {
|
||||
av_frame_free(&frame);
|
||||
return Result::Ok;
|
||||
}
|
||||
if (receive_result < 0) {
|
||||
LOGF("Videodec2: receiving a flushed frame failed: %s (%d)\n",
|
||||
AvErrorString(receive_result), receive_result);
|
||||
av_frame_free(&frame);
|
||||
return Result::ApiFail;
|
||||
}
|
||||
|
||||
const auto result = CopyFrame(frame, frame_buffer, output);
|
||||
av_frame_free(&frame);
|
||||
return result;
|
||||
const auto result = ReceiveOutput(frame_buffer, output);
|
||||
return result == Result::NoFrame ? Result::Ok : result;
|
||||
}
|
||||
|
||||
void ResetDecoder() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
avcodec_flush_buffers(m_codec);
|
||||
ClearFrames();
|
||||
ClearPictureMetadata();
|
||||
m_finalized = m_eof = false;
|
||||
}
|
||||
|
||||
private:
|
||||
struct PendingFrame {
|
||||
AVFrame* frame;
|
||||
PictureInfo picture;
|
||||
};
|
||||
|
||||
void ClearFrames() {
|
||||
for (auto& pending: m_frames) av_frame_free(&pending.frame);
|
||||
m_frames.clear();
|
||||
}
|
||||
|
||||
[[nodiscard]] Result DrainFrames(uint32_t* produced = nullptr) {
|
||||
while (!m_eof && m_frames.size() < m_config.max_pending_frames) {
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
if (frame == nullptr) return Result::ApiFail;
|
||||
const int result = avcodec_receive_frame(m_codec, frame);
|
||||
if (result < 0) {
|
||||
av_frame_free(&frame);
|
||||
if (result == AVERROR_EOF) m_eof = true;
|
||||
if (result == AVERROR(EAGAIN) || m_eof) return Result::Ok;
|
||||
LOGF("Video decoder: avcodec_receive_frame failed: %s (%d)\n",
|
||||
AvErrorString(result), result);
|
||||
return Result::AccessUnit;
|
||||
}
|
||||
m_frames.push_back({frame, MakePictureInfo(frame)});
|
||||
if (produced != nullptr) ++*produced;
|
||||
}
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
[[nodiscard]] Result SubmitInput(const Input& input, uint32_t* produced) {
|
||||
*produced = 0;
|
||||
if (m_finalized) return Result::ApiFail;
|
||||
const auto drained = DrainFrames();
|
||||
if (drained != Result::Ok) return drained;
|
||||
if (m_frames.size() >= m_config.max_pending_frames) return Result::InputFull;
|
||||
if (input.size > static_cast<size_t>(std::numeric_limits<int>::max())) return Result::ApiFail;
|
||||
AVPacket* packet = av_packet_alloc();
|
||||
if (packet == nullptr) return Result::ApiFail;
|
||||
int result = av_new_packet(packet, static_cast<int>(input.size));
|
||||
if (result < 0) {
|
||||
av_packet_free(&packet);
|
||||
return Result::ApiFail;
|
||||
}
|
||||
std::memcpy(packet->data, input.data, input.size);
|
||||
packet->pts = ToAvTimestamp(input.pts);
|
||||
packet->dts = ToAvTimestamp(input.dts);
|
||||
packet->opaque_ref = av_buffer_alloc(sizeof(PacketMetadata));
|
||||
if (packet->opaque_ref == nullptr) {
|
||||
av_packet_free(&packet);
|
||||
return Result::ApiFail;
|
||||
}
|
||||
const PacketMetadata metadata {input.pts, input.dts, input.attached_data};
|
||||
std::memcpy(packet->opaque_ref->data, &metadata, sizeof(metadata));
|
||||
result = avcodec_send_packet(m_codec, packet);
|
||||
av_packet_free(&packet);
|
||||
if (result == AVERROR(EAGAIN)) return Result::InputFull; // The AU was not accepted.
|
||||
if (result < 0) {
|
||||
LOGF("Video decoder: submitting an AU failed: %s (%d)\n", AvErrorString(result), result);
|
||||
return Result::AccessUnit;
|
||||
}
|
||||
// Once accepted, a full output queue cannot turn this AU into a retry.
|
||||
return DrainFrames(produced);
|
||||
}
|
||||
|
||||
[[nodiscard]] Result ReceiveOutput(const FrameBuffer& frame_buffer, Output* output) {
|
||||
*output = {};
|
||||
const auto drained = DrainFrames();
|
||||
if (drained != Result::Ok) return drained;
|
||||
if (m_frames.empty()) return Result::NoFrame;
|
||||
auto& pending = m_frames.front();
|
||||
const auto result = CopyFrame(pending.frame, pending.picture, frame_buffer, output);
|
||||
if (result != Result::Ok) return result;
|
||||
output->last_frame = m_finalized && m_eof && m_frames.size() == 1u;
|
||||
av_frame_free(&pending.frame);
|
||||
m_frames.pop_front();
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
[[nodiscard]] PictureInfo MakePictureInfo(const AVFrame* frame) const {
|
||||
PictureInfo result {};
|
||||
if (frame->opaque_ref != nullptr && frame->opaque_ref->size >= sizeof(PacketMetadata)) {
|
||||
@@ -268,8 +292,8 @@ private:
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] Result CopyFrame(const AVFrame* frame, const FrameBuffer& frame_buffer,
|
||||
Output* output) {
|
||||
[[nodiscard]] Result CopyFrame(const AVFrame* frame, const PictureInfo& picture,
|
||||
const FrameBuffer& frame_buffer, Output* output) {
|
||||
if (frame->width <= 0 || frame->height <= 0) {
|
||||
return Result::ApiFail;
|
||||
}
|
||||
@@ -329,7 +353,7 @@ private:
|
||||
|
||||
{
|
||||
std::scoped_lock lock(g_picture_mutex);
|
||||
g_picture_infos[frame_buffer.data] = {this, MakePictureInfo(frame)};
|
||||
g_picture_infos[frame_buffer.data] = {this, picture};
|
||||
m_picture_buffers.insert(frame_buffer.data);
|
||||
}
|
||||
return Result::Ok;
|
||||
@@ -346,6 +370,9 @@ private:
|
||||
m_picture_buffers.clear();
|
||||
}
|
||||
|
||||
std::deque<PendingFrame> m_frames;
|
||||
bool m_finalized = false;
|
||||
bool m_eof = false;
|
||||
Config m_config;
|
||||
AVCodecContext* m_codec = nullptr;
|
||||
SwsContext* m_sws = nullptr;
|
||||
@@ -358,7 +385,7 @@ bool IsCodecSupported(uint32_t codec_type) {
|
||||
}
|
||||
|
||||
Instance* Create(const Config& config) {
|
||||
if (!IsCodecSupported(config.codec_type)) {
|
||||
if (!IsCodecSupported(config.codec_type) || config.max_pending_frames < 2u) {
|
||||
return nullptr;
|
||||
}
|
||||
auto* instance = new Instance(config);
|
||||
@@ -377,6 +404,18 @@ uint32_t GetCodecType(const Instance* instance) {
|
||||
return instance->CodecType();
|
||||
}
|
||||
|
||||
Result Submit(Instance* instance, const Input& input, uint32_t* produced) {
|
||||
return instance->Submit(input, produced);
|
||||
}
|
||||
|
||||
Result Receive(Instance* instance, const FrameBuffer& frame_buffer, Output* output) {
|
||||
return instance->Receive(frame_buffer, output);
|
||||
}
|
||||
|
||||
Result Finalize(Instance* instance) {
|
||||
return instance->Finalize();
|
||||
}
|
||||
|
||||
Result Decode(Instance* instance, const Input& input, const FrameBuffer& frame_buffer,
|
||||
Output* output) {
|
||||
return instance->DecodeInput(input, frame_buffer, output);
|
||||
@@ -390,6 +429,44 @@ void Reset(Instance* instance) {
|
||||
instance->ResetDecoder();
|
||||
}
|
||||
|
||||
AvcPictureInfo MakeAvcPictureInfo(const PictureInfo& decoded, size_t size) {
|
||||
AvcPictureInfo picture {};
|
||||
picture.this_size = size;
|
||||
picture.is_valid = true;
|
||||
picture.pts_data = decoded.pts;
|
||||
picture.dts_data = decoded.dts;
|
||||
picture.attached_data = decoded.attached_data;
|
||||
picture.idr_picture_flag = decoded.key_frame ? 1 : 0;
|
||||
picture.profile_idc = static_cast<uint8_t>(decoded.profile);
|
||||
picture.level_idc = static_cast<uint8_t>(decoded.level);
|
||||
picture.pic_width_in_mbs_minus1 = (decoded.width + 15u) / 16u - 1u;
|
||||
picture.pic_height_in_map_units_minus1 = (decoded.height + 15u) / 16u - 1u;
|
||||
picture.frame_mbs_only_flag = 1;
|
||||
picture.frame_cropping_flag = decoded.crop_left != 0 || decoded.crop_right != 0 ||
|
||||
decoded.crop_top != 0 || decoded.crop_bottom != 0
|
||||
? 1
|
||||
: 0;
|
||||
picture.frame_crop_left_offset = decoded.crop_left;
|
||||
picture.frame_crop_right_offset = decoded.crop_right;
|
||||
picture.frame_crop_top_offset = decoded.crop_top;
|
||||
picture.frame_crop_bottom_offset = decoded.crop_bottom;
|
||||
picture.aspect_ratio_info_present_flag =
|
||||
decoded.sar_width != 0 && decoded.sar_height != 0 ? 1 : 0;
|
||||
picture.aspect_ratio_idc = picture.aspect_ratio_info_present_flag ? 255 : 0;
|
||||
picture.sar_width = decoded.sar_width;
|
||||
picture.sar_height = decoded.sar_height;
|
||||
picture.video_signal_type_present_flag = 1;
|
||||
picture.video_format = 5;
|
||||
picture.video_full_range_flag = decoded.color_range == 2 ? 1 : 0;
|
||||
picture.colour_description_present_flag =
|
||||
decoded.color_primaries != 0 || decoded.color_trc != 0 || decoded.color_space != 0 ? 1
|
||||
: 0;
|
||||
picture.colour_primaries = decoded.color_primaries;
|
||||
picture.transfer_characteristics = decoded.color_trc;
|
||||
picture.matrix_coefficients = decoded.color_space;
|
||||
return picture;
|
||||
}
|
||||
|
||||
bool GetPictureInfo(void* frame_buffer, PictureInfo* picture_info) {
|
||||
std::scoped_lock lock(g_picture_mutex);
|
||||
const auto it = g_picture_infos.find(frame_buffer);
|
||||
|
||||
@@ -14,12 +14,15 @@ enum class Result {
|
||||
AccessUnit,
|
||||
FrameBufferSize,
|
||||
OversizeDecode,
|
||||
InputFull,
|
||||
NoFrame,
|
||||
};
|
||||
|
||||
struct Config {
|
||||
uint32_t codec_type = 0;
|
||||
int32_t max_width = -1;
|
||||
int32_t max_height = -1;
|
||||
uint32_t codec_type = 0;
|
||||
int32_t max_width = -1;
|
||||
int32_t max_height = -1;
|
||||
uint32_t max_pending_frames = 2;
|
||||
};
|
||||
|
||||
struct Input {
|
||||
@@ -37,6 +40,7 @@ struct FrameBuffer {
|
||||
|
||||
struct Output {
|
||||
bool valid = false;
|
||||
bool last_frame = false;
|
||||
bool error_frame = false;
|
||||
bool buffer_accepted = false;
|
||||
uint32_t codec_type = 0;
|
||||
@@ -69,12 +73,83 @@ struct PictureInfo {
|
||||
bool key_frame = false;
|
||||
};
|
||||
|
||||
struct AvcPictureInfo {
|
||||
size_t this_size;
|
||||
bool is_valid;
|
||||
|
||||
uint64_t pts_data;
|
||||
uint64_t dts_data;
|
||||
uint64_t attached_data;
|
||||
|
||||
uint8_t idr_picture_flag;
|
||||
uint8_t profile_idc;
|
||||
uint8_t level_idc;
|
||||
uint32_t pic_width_in_mbs_minus1;
|
||||
uint32_t pic_height_in_map_units_minus1;
|
||||
uint8_t frame_mbs_only_flag;
|
||||
|
||||
uint8_t frame_cropping_flag;
|
||||
uint32_t frame_crop_left_offset;
|
||||
uint32_t frame_crop_right_offset;
|
||||
uint32_t frame_crop_top_offset;
|
||||
uint32_t frame_crop_bottom_offset;
|
||||
|
||||
uint8_t aspect_ratio_info_present_flag;
|
||||
uint8_t aspect_ratio_idc;
|
||||
uint16_t sar_width;
|
||||
uint16_t sar_height;
|
||||
|
||||
uint8_t video_signal_type_present_flag;
|
||||
uint8_t video_format;
|
||||
uint8_t video_full_range_flag;
|
||||
uint8_t colour_description_present_flag;
|
||||
uint8_t colour_primaries;
|
||||
uint8_t transfer_characteristics;
|
||||
uint8_t matrix_coefficients;
|
||||
|
||||
uint8_t timing_info_present_flag;
|
||||
uint32_t num_units_in_tick;
|
||||
uint32_t time_scale;
|
||||
uint8_t fixed_frame_rate_flag;
|
||||
|
||||
uint8_t bitstream_restriction_flag;
|
||||
uint8_t max_dec_frame_buffering;
|
||||
|
||||
uint8_t pic_struct_present_flag;
|
||||
uint8_t pic_struct;
|
||||
uint8_t field_pic_flag;
|
||||
uint8_t bottom_field_flag;
|
||||
|
||||
uint8_t sequence_parameter_set_present_flag;
|
||||
uint8_t picture_parameter_set_present_flag;
|
||||
uint8_t au_delimiter_present_flag;
|
||||
uint8_t end_of_sequence_present_flag;
|
||||
uint8_t end_of_stream_present_flag;
|
||||
uint8_t filler_data_present_flag;
|
||||
uint8_t picture_timing_sei_present_flag;
|
||||
uint8_t buffering_period_sei_present_flag;
|
||||
|
||||
uint8_t constraint_set0_flag;
|
||||
uint8_t constraint_set1_flag;
|
||||
uint8_t constraint_set2_flag;
|
||||
uint8_t constraint_set3_flag;
|
||||
uint8_t constraint_set4_flag;
|
||||
uint8_t constraint_set5_flag;
|
||||
};
|
||||
|
||||
static_assert(sizeof(AvcPictureInfo) == 120);
|
||||
|
||||
[[nodiscard]] AvcPictureInfo MakeAvcPictureInfo(const PictureInfo& decoded, size_t size);
|
||||
|
||||
class Instance;
|
||||
|
||||
[[nodiscard]] bool IsCodecSupported(uint32_t codec_type);
|
||||
[[nodiscard]] Instance* Create(const Config& config);
|
||||
void Destroy(Instance* instance);
|
||||
[[nodiscard]] uint32_t GetCodecType(const Instance* instance);
|
||||
[[nodiscard]] Result Submit(Instance* instance, const Input& input, uint32_t* produced);
|
||||
[[nodiscard]] Result Receive(Instance* instance, const FrameBuffer& frame_buffer, Output* output);
|
||||
[[nodiscard]] Result Finalize(Instance* instance);
|
||||
[[nodiscard]] Result Decode(Instance* instance, const Input& input, const FrameBuffer& frame_buffer,
|
||||
Output* output);
|
||||
[[nodiscard]] Result Flush(Instance* instance, const FrameBuffer& frame_buffer, Output* output);
|
||||
|
||||
Reference in New Issue
Block a user