From b55a847420b19ea09783838a03e2298d0d79d428 Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Wed, 3 May 2017 08:12:00 +0200 Subject: [PATCH] Add basic compression stuff --- Include/enc-vfw.h | 17 +++- Include/plugin.h | 1 + Source/enc-vfw.cpp | 198 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 198 insertions(+), 18 deletions(-) diff --git a/Include/enc-vfw.h b/Include/enc-vfw.h index c3fb5df..524b419 100644 --- a/Include/enc-vfw.h +++ b/Include/enc-vfw.h @@ -3,6 +3,7 @@ #include "libobs/obs-encoder.h" #include +#include // VFW #define COMPMAN @@ -19,8 +20,13 @@ namespace VFW { struct Info { std::string Id; std::string Name; + std::string Path; ICINFO icInfo; + ICINFO icInfo2; + size_t index; obs_encoder_info obsInfo; + + std::string FourCC; }; bool Initialize(); bool Finalize(); @@ -30,7 +36,7 @@ namespace VFW { static const char* get_name(void* type_data); static void get_defaults(obs_data_t *settings); - static obs_properties_t* get_properties(void *type_data, void *data); + static obs_properties_t* get_properties(void *data); static void* create(obs_data_t *settings, obs_encoder_t *encoder); Encoder(obs_data_t *settings, obs_encoder_t *encoder); @@ -58,6 +64,15 @@ namespace VFW { private: + VFW::Info* myInfo; + ICINFO icinfo; + HIC hIC; + std::vector vbiInput, vbiOutput, vbiOutputOld; + BITMAPINFO *biInput, *biOutput, *biOutputOld = nullptr; + std::vector frameBuffer, oldFrameBuffer; + + + uint32_t width, height, fpsNum, fpsDen, kfinterval; }; }; diff --git a/Include/plugin.h b/Include/plugin.h index cfe3af6..e77fbf1 100644 --- a/Include/plugin.h +++ b/Include/plugin.h @@ -1,6 +1,7 @@ #pragma once #include "libobs/obs-module.h" #include +#include ////////////////////////////////////////////////////////////////////////// // Macro Definitions // diff --git a/Source/enc-vfw.cpp b/Source/enc-vfw.cpp index 404f32f..934cd1e 100644 --- a/Source/enc-vfw.cpp +++ b/Source/enc-vfw.cpp @@ -9,6 +9,46 @@ std::map _IdToInfo; #define snprintf sprintf_s +std::string FourCCFromInt32(DWORD& fccHandler) { + return std::string(reinterpret_cast(&fccHandler), 4); +} + +std::string FormattedICCError(DWORD error) { + switch (error) { + case ICERR_OK: + return "Ok"; + case ICERR_UNSUPPORTED: + return "Unsupported"; + case ICERR_BADFORMAT: + return "Bad Format"; + case ICERR_MEMORY: + return "Memory"; + case ICERR_INTERNAL: + return "Internal"; + case ICERR_BADFLAGS: + return "Bad Flags"; + case ICERR_BADPARAM: + return "Bad Parameter"; + case ICERR_BADSIZE: + return "Bad Size"; + case ICERR_BADHANDLE: + return "Bad Handle"; + case ICERR_CANTUPDATE: + return "Can't Update"; + case ICERR_ABORT: + return "Abort"; + case ICERR_ERROR: + return "Generic Error"; + case ICERR_BADBITDEPTH: + return "Bad Bit Depth"; + case ICERR_BADIMAGESIZE: + return "Bad Image Size"; + case ICERR_CUSTOM: + default: + return "Custom Error"; + } +} + bool VFW::Initialize() { // Initialize all VFW Encoders (we can only use one anyway) ICINFO icinfo; @@ -17,25 +57,32 @@ bool VFW::Initialize() { DWORD fccType = 0; for (size_t i = 0; ICInfo(fccType, i, &icinfo); i++) { - HIC hIC = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_QUERY); + HIC hIC = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_FASTCOMPRESS); if (hIC) { - if (ICGetInfo(hIC, &icinfo, sizeof(icinfo))) { + ICINFO icinfo2; + if (ICGetInfo(hIC, &icinfo2, sizeof(icinfo2))) { std::vector idBuf(64); - snprintf(idBuf.data(), idBuf.size(), "%ls", icinfo.szName); + snprintf(idBuf.data(), idBuf.size(), "%ls", icinfo2.szName); std::vector nameBuf(1024); - snprintf(nameBuf.data(), nameBuf.size(), "%ls (" PLUGIN_NAME ")", icinfo.szDescription); + snprintf(nameBuf.data(), nameBuf.size(), "%ls (" PLUGIN_NAME ")", icinfo2.szDescription); + std::vector pathBuf(1024); + snprintf(pathBuf.data(), pathBuf.size(), "%ls (" PLUGIN_NAME ")", icinfo2.szDriver); // Track VFW::Info* info = new VFW::Info(); info->Id = std::string(idBuf.data()); info->Name = std::string(nameBuf.data()); - info->icInfo = icinfo; + info->Path = std::string(pathBuf.data()); + std::memcpy(&info->icInfo, &icinfo, sizeof(ICINFO)); + std::memcpy(&info->icInfo2, &icinfo2, sizeof(ICINFO)); + info->index = i; + info->FourCC = FourCCFromInt32(info->icInfo2.fccHandler); // Register std::memset(&info->obsInfo, 0, sizeof(obs_encoder_info)); info->obsInfo.id = info->Id.data(); info->obsInfo.type = OBS_ENCODER_VIDEO; - info->obsInfo.codec = "vidc"; + info->obsInfo.codec = info->FourCC.c_str(); info->obsInfo.type_data = info; // circular reference but whatever, it's not reference counted info->obsInfo.get_name = VFW::Encoder::get_name; info->obsInfo.create = VFW::Encoder::create; @@ -48,8 +95,8 @@ bool VFW::Initialize() { info->obsInfo.get_video_info = VFW::Encoder::get_video_info; PLOG_INFO("Registering: %s %s", - info->Id.data(), - info->Name.data()); + info->Id.c_str(), + info->Name.c_str()); obs_register_encoder(&info->obsInfo); _IdToInfo.insert(std::make_pair(info->Id, info)); @@ -70,15 +117,95 @@ const char* VFW::Encoder::get_name(void* type_data) { } void VFW::Encoder::get_defaults(obs_data_t *settings) { - + obs_data_set_default_int(settings, PROP_BITRATE, 2000); + obs_data_set_default_double(settings, PROP_QUALITY, 90.0); + obs_data_set_default_double(settings, PROP_KEYFRAME_INTERVAL, 2.0); } void* VFW::Encoder::create(obs_data_t *settings, obs_encoder_t *encoder) { - return new VFW::Encoder(settings, encoder); + try { + return new VFW::Encoder(settings, encoder); + } catch (...) { + return nullptr; + } } VFW::Encoder::Encoder(obs_data_t *settings, obs_encoder_t *encoder) { + PLOG_DEBUG(__FUNCTION_NAME__); + myInfo = static_cast(obs_encoder_get_type_data(encoder)); + + LRESULT err = ICERR_OK; + + // Generic information. + video_t* obsVideo = obs_encoder_video(encoder); + const struct video_output_info *voi = video_output_get_info(obsVideo); + fpsNum = voi->fps_num; fpsDen = voi->fps_den; + kfinterval = uint32_t(double_t(fpsDen) / double_t(fpsNum) * obs_data_get_double(settings, PROP_KEYFRAME_INTERVAL)); + width = obs_encoder_get_width(encoder); height = obs_encoder_get_height(encoder); + + PLOG_DEBUG("Initializing at %" PRIu32 "x%" PRIu32 " with %" PRIu32 "/%" PRIu32 " FPS.", + width, height, fpsNum, fpsDen); + + hIC = ICOpen(myInfo->icInfo.fccType, myInfo->icInfo.fccHandler, ICMODE_FASTCOMPRESS); + if (!hIC) { + PLOG_ERROR("Failed to create '%s' VFW encoder.", + myInfo->Name.c_str()); + throw std::exception(); + } else { + PLOG_INFO("Created '%s' VFW encoder.", + myInfo->Name.c_str()); + } + + #pragma region Get Bitmap Information + vbiInput.resize(sizeof(BITMAPINFO)); + biInput = reinterpret_cast(vbiInput.data()); + biInput->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + biInput->bmiHeader.biWidth = width; + biInput->bmiHeader.biHeight = height; + biInput->bmiHeader.biPlanes = 1; + biInput->bmiHeader.biBitCount = 32; + biInput->bmiHeader.biCompression = BI_RGB; + biInput->bmiHeader.biSizeImage = width * height * 4; + biInput->bmiHeader.biXPelsPerMeter = 1; + biInput->bmiHeader.biYPelsPerMeter = 1; + biInput->bmiHeader.biClrUsed = 0; + biInput->bmiHeader.biClrImportant = 0; + + err = ICSendMessage(hIC, ICM_COMPRESS_GET_FORMAT, reinterpret_cast(biInput), NULL); + if (err <= 0) { + PLOG_ERROR("Unable to retrieve format information size: %s.", + FormattedICCError(err).c_str()); + throw std::exception(); + } + + vbiOutput.resize(err); + biOutput = reinterpret_cast(vbiOutput.data()); + biOutput->bmiHeader.biSize = err; + err = ICSendMessage(hIC, ICM_COMPRESS_GET_FORMAT, reinterpret_cast(biInput), reinterpret_cast(biOutput)); + if (err != ICERR_OK) { + PLOG_ERROR("Unable to retrieve format information: %s.", + FormattedICCError(err).c_str()); + throw std::exception(); + } + #pragma endregion Get Bitmap Information + + err = ICSendMessage(hIC, ICM_COMPRESS_GET_SIZE, reinterpret_cast(biInput), reinterpret_cast(biOutput)); + if (err <= 0) { + PLOG_ERROR("Unable to retrieve expected buffer size: %s.", + FormattedICCError(err).c_str()); + throw std::exception(); + } + + frameBuffer.resize(err); + oldFrameBuffer.resize(err); + + err = ICCompressBegin(hIC, &biInput, &biOutput); + if (err != ICERR_OK) { + PLOG_ERROR("Unable to begin encoding: %s.", + FormattedICCError(err).c_str()); + throw std::exception(); + } } void VFW::Encoder::destroy(void* data) { @@ -86,7 +213,8 @@ void VFW::Encoder::destroy(void* data) { } VFW::Encoder::~Encoder() { - + ICCompressEnd(hIC); + ICClose(hIC); } bool VFW::Encoder::encode(void *data, struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet) { @@ -94,29 +222,64 @@ bool VFW::Encoder::encode(void *data, struct encoder_frame *frame, struct encode } bool VFW::Encoder::encode(struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet) { - return false; + bool keyframe = (frame->pts % kfinterval) == 0; + LRESULT err = ICCompress(hIC, + keyframe ? ICCOMPRESS_KEYFRAME : 0, + &(biOutput->bmiHeader), + reinterpret_cast(frameBuffer.data()), + &(biInput->bmiHeader), + frame->data[0], + 0, + 0, + frame->pts, + frameBuffer.size(), + 1000, + &(biOutputOld != nullptr ? biOutputOld->bmiHeader : biOutput->bmiHeader), + keyframe ? NULL : reinterpret_cast(oldFrameBuffer.data()) + ); + if (err == ICERR_OK) { + // Store old information. + std::memcpy(oldFrameBuffer.data(), frameBuffer.data(), oldFrameBuffer.size()); + + *received_packet = true; + packet->keyframe = keyframe; + packet->dts = packet->pts = frame->pts; + packet->size = oldFrameBuffer.size(); + packet->data = reinterpret_cast(oldFrameBuffer.data()); + return true; + } else { + PLOG_ERROR("Unable to encode: %s.", + FormattedICCError(err).c_str()); + return false; + } } -obs_properties_t* VFW::Encoder::get_properties(void *type_data, void *data) { - VFW::Info* info = static_cast(type_data); +obs_properties_t* VFW::Encoder::get_properties(void *data) { + VFW::Info* info = static_cast(data); obs_properties_t* pr = obs_properties_create(); + obs_properties_set_param(pr, data, nullptr); obs_property_t* p; p = obs_properties_add_int(pr, PROP_BITRATE, "Bitrate", 1, 1000000, 1); p = obs_properties_add_float(pr, PROP_QUALITY, "Quality", 1, 100, 0.01); p = obs_properties_add_float(pr, PROP_KEYFRAME_INTERVAL, "Keyframe Interval", 0.01, 30.00, 0.01); p = obs_properties_add_button(pr, PROP_CONFIGURE, "Configure", cb_configure); - p = obs_properties_add_button(pr, PROP_ABOUT, "Configure", cb_about); + p = obs_properties_add_button(pr, PROP_ABOUT, "About", cb_about); return pr; } bool VFW::Encoder::cb_configure(obs_properties_t *pr, obs_property_t *p, void *data) { + VFW::Info* info = static_cast(obs_properties_get_param(pr)); + return false; } bool VFW::Encoder::cb_about(obs_properties_t *pr, obs_property_t *p, void *data) { + VFW::Info* info = static_cast(obs_properties_get_param(pr)); + + return false; } @@ -142,11 +305,12 @@ bool VFW::Encoder::get_sei_data(void *data, uint8_t **sei_data, size_t *size) { bool VFW::Encoder::get_sei_data(uint8_t** sei_data, size_t* size) { return false; - } void VFW::Encoder::get_video_info(void *data, struct video_scale_info *info) { return static_cast(data)->get_video_info(info); } -void VFW::Encoder::get_video_info(struct video_scale_info *info) {} +void VFW::Encoder::get_video_info(struct video_scale_info *info) { + info->format = VIDEO_FORMAT_BGRX; +}