encoder: See description
Features, Fixes, etc: - Added encoding 'Mode' selection which allows users to choose between Normal (Single Frame), Temporal (Single + Last Frame) and Sequential (VFW Default). - Fixed several compiler warnings with mismatched types and unused variables. - 'Keyframe Interval' and 'Bitrate' now default to 0 (Encoder decides). - 'Quality' now defaults to 100.0%. - Added default Quality and Keyframe Interval to log. - 'Bitrate', 'Quality', 'Configure' and 'About' are hidden when the encoder does not support them. - Encoder States are stored in-memory during an OBS session. ToDo: - Save/Load encoder state from disk.
This commit is contained in:
+27
-5
@@ -28,6 +28,11 @@ namespace VFW {
|
|||||||
obs_encoder_info obsInfo;
|
obs_encoder_info obsInfo;
|
||||||
|
|
||||||
std::string FourCC, FourCC2;
|
std::string FourCC, FourCC2;
|
||||||
|
std::vector<uint8_t> stateInfo;
|
||||||
|
|
||||||
|
int32_t defaultQuality;
|
||||||
|
int32_t defaultKeyframeRate;
|
||||||
|
bool hasConfigure, hasAbout;
|
||||||
};
|
};
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
bool Finalize();
|
bool Finalize();
|
||||||
@@ -65,11 +70,28 @@ namespace VFW {
|
|||||||
private:
|
private:
|
||||||
VFW::Info* myInfo;
|
VFW::Info* myInfo;
|
||||||
HIC hIC;
|
HIC hIC;
|
||||||
std::vector<char> vbiInput, vbiOutput;
|
|
||||||
BITMAPINFO *biInput, *biOutput;
|
|
||||||
std::vector<char> inBuffer, outBuffer;
|
|
||||||
COMPVARS cv;
|
COMPVARS cv;
|
||||||
uint32_t width, height, fpsNum, fpsDen;
|
std::vector<char>
|
||||||
uint32_t userKeyframeInterval, userBitrate, userQuality;
|
m_bufferInput,
|
||||||
|
m_bufferInputBitmapInfo,
|
||||||
|
m_bufferPrevInput,
|
||||||
|
m_bufferPrevInputBitmapInfo,
|
||||||
|
m_bufferOutput,
|
||||||
|
m_bufferOutputBitmapInfo;
|
||||||
|
BITMAPINFO
|
||||||
|
*m_inputBitmapInfo,
|
||||||
|
*m_prevInputBitmapInfo,
|
||||||
|
*m_outputBitmapInfo;
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
m_width, m_height,
|
||||||
|
m_fpsNum, m_fpsDen,
|
||||||
|
m_keyframeInterval,
|
||||||
|
m_bitrate, m_quality;
|
||||||
|
bool
|
||||||
|
m_useNormalCompress,
|
||||||
|
m_useTemporalFlag,
|
||||||
|
m_useBitrateFlag,
|
||||||
|
m_useQualityFlag;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#define PROP_KEYFRAME_INTERVAL "KeyframeInterval"
|
#define PROP_KEYFRAME_INTERVAL "KeyframeInterval"
|
||||||
#define PROP_MODE "Mode"
|
#define PROP_MODE "Mode"
|
||||||
#define PROP_MODE_NORMAL "Mode.Normal"
|
#define PROP_MODE_NORMAL "Mode.Normal"
|
||||||
|
#define PROP_MODE_TEMPORAL "Mode.Temporal"
|
||||||
#define PROP_MODE_SEQUENTIAL "Mode.Sequential"
|
#define PROP_MODE_SEQUENTIAL "Mode.Sequential"
|
||||||
|
|
||||||
#define PROP_CONFIGURE "Configure"
|
#define PROP_CONFIGURE "Configure"
|
||||||
|
|||||||
+248
-80
@@ -4,6 +4,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <emmintrin.h>
|
||||||
|
|
||||||
std::map<std::string, VFW::Info*> _IdToInfo;
|
std::map<std::string, VFW::Info*> _IdToInfo;
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ std::string FourCCFromInt32(DWORD& fccHandler) {
|
|||||||
return std::string(reinterpret_cast<char*>(&fccHandler), 4);
|
return std::string(reinterpret_cast<char*>(&fccHandler), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FormattedICCError(DWORD error) {
|
std::string FormattedICCError(LRESULT error) {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case ICERR_OK:
|
case ICERR_OK:
|
||||||
return "Ok";
|
return "Ok";
|
||||||
@@ -89,7 +90,7 @@ bool VFW::Initialize() {
|
|||||||
icinfo.dwSize = sizeof(icinfo);
|
icinfo.dwSize = sizeof(icinfo);
|
||||||
|
|
||||||
DWORD fccType = 0;
|
DWORD fccType = 0;
|
||||||
for (size_t i = 0; ICInfo(fccType, i, &icinfo); i++) {
|
for (size_t i = 0; ICInfo(fccType, (DWORD)i, &icinfo); i++) {
|
||||||
HIC hIC = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_QUERY);
|
HIC hIC = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_QUERY);
|
||||||
if (hIC) {
|
if (hIC) {
|
||||||
ICINFO icinfo2;
|
ICINFO icinfo2;
|
||||||
@@ -113,7 +114,7 @@ bool VFW::Initialize() {
|
|||||||
std::vector<char> pathBuf(512);
|
std::vector<char> pathBuf(512);
|
||||||
snprintf(pathBuf.data(), pathBuf.size(), "%.128ls", icinfo2.szDriver);
|
snprintf(pathBuf.data(), pathBuf.size(), "%.128ls", icinfo2.szDriver);
|
||||||
info->Path = std::string(pathBuf.data());
|
info->Path = std::string(pathBuf.data());
|
||||||
|
|
||||||
// Register
|
// Register
|
||||||
std::memset(&info->obsInfo, 0, sizeof(obs_encoder_info));
|
std::memset(&info->obsInfo, 0, sizeof(obs_encoder_info));
|
||||||
info->obsInfo.id = info->Id.data();
|
info->obsInfo.id = info->Id.data();
|
||||||
@@ -136,13 +137,20 @@ bool VFW::Initialize() {
|
|||||||
//info->obsInfo.get_sei_data = VFW::Encoder::get_sei_data;
|
//info->obsInfo.get_sei_data = VFW::Encoder::get_sei_data;
|
||||||
info->obsInfo.get_video_info = VFW::Encoder::get_video_info;
|
info->obsInfo.get_video_info = VFW::Encoder::get_video_info;
|
||||||
|
|
||||||
PLOG_INFO("Registering '%s' (Id: %s, FourCC1: %s, FourCC2: %s, Codec: %s, Driver: '%s')",
|
info->defaultQuality = ICGetDefaultQuality(hIC);
|
||||||
|
info->defaultKeyframeRate = ICGetDefaultKeyFrameRate(hIC);
|
||||||
|
info->hasConfigure = ICQueryConfigure(hIC);
|
||||||
|
info->hasAbout = ICQueryAbout(hIC);
|
||||||
|
|
||||||
|
PLOG_INFO("Registering '%s' (Id: %s, FourCC1: %s, FourCC2: %s, Codec: %s, Driver: '%s', DefQual: %ld, DefKfR: %ld)",
|
||||||
info->Name.c_str(),
|
info->Name.c_str(),
|
||||||
info->Id.c_str(),
|
info->Id.c_str(),
|
||||||
info->FourCC.c_str(),
|
info->FourCC.c_str(),
|
||||||
info->FourCC2.c_str(),
|
info->FourCC2.c_str(),
|
||||||
info->obsInfo.codec,
|
info->obsInfo.codec,
|
||||||
info->Path.c_str());
|
info->Path.c_str(),
|
||||||
|
info->defaultQuality,
|
||||||
|
info->defaultKeyframeRate);
|
||||||
|
|
||||||
obs_register_encoder(&info->obsInfo);
|
obs_register_encoder(&info->obsInfo);
|
||||||
_IdToInfo.insert(std::make_pair(info->Id, info));
|
_IdToInfo.insert(std::make_pair(info->Id, info));
|
||||||
@@ -163,23 +171,34 @@ const char* VFW::Encoder::get_name(void* type_data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VFW::Encoder::get_defaults(obs_data_t *settings) {
|
void VFW::Encoder::get_defaults(obs_data_t *settings) {
|
||||||
obs_data_set_default_int(settings, PROP_BITRATE, 2000);
|
obs_data_set_default_int(settings, PROP_BITRATE, 0);
|
||||||
obs_data_set_default_double(settings, PROP_QUALITY, 90.0);
|
obs_data_set_default_double(settings, PROP_QUALITY, 100.0);
|
||||||
obs_data_set_default_double(settings, PROP_KEYFRAME_INTERVAL, 2.0);
|
obs_data_set_default_double(settings, PROP_KEYFRAME_INTERVAL, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_properties_t* VFW::Encoder::get_properties(void *data) {
|
obs_properties_t* VFW::Encoder::get_properties(void *data) {
|
||||||
VFW::Info* info = static_cast<VFW::Info*>(data);
|
VFW::Info* info = static_cast<VFW::Info*>(data);
|
||||||
|
|
||||||
obs_properties_t* pr = obs_properties_create();
|
obs_properties_t* pr = obs_properties_create();
|
||||||
obs_properties_set_param(pr, data, nullptr);
|
obs_properties_set_param(pr, data, nullptr);
|
||||||
obs_property_t* p;
|
obs_property_t* p;
|
||||||
|
|
||||||
p = obs_properties_add_int(pr, PROP_BITRATE, "Bitrate", 1, 1000000, 1);
|
p = obs_properties_add_int_slider(pr, PROP_BITRATE, "Bitrate", 0, 300000, 1);
|
||||||
|
obs_property_set_visible(p, ((info->icInfo2.dwFlags & VIDCF_CRUNCH) != 0));
|
||||||
p = obs_properties_add_float_slider(pr, PROP_QUALITY, "Quality", 1, 100, 0.01);
|
p = obs_properties_add_float_slider(pr, PROP_QUALITY, "Quality", 1, 100, 0.01);
|
||||||
p = obs_properties_add_float(pr, PROP_KEYFRAME_INTERVAL, "Keyframe Interval", 0.1, 30.00, 0.1);
|
obs_property_set_visible(p, ((info->icInfo2.dwFlags & VIDCF_QUALITY) != 0));
|
||||||
|
p = obs_properties_add_float(pr, PROP_KEYFRAME_INTERVAL, "Keyframe Interval", 0.00, 30.00, 0.01);
|
||||||
|
|
||||||
|
p = obs_properties_add_list(pr, PROP_MODE, "Mode", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||||
|
obs_property_list_add_string(p, "Normal", PROP_MODE_NORMAL);
|
||||||
|
if ((info->icInfo2.dwFlags & VIDCF_TEMPORAL) != 0)
|
||||||
|
obs_property_list_add_string(p, "Temporal", PROP_MODE_TEMPORAL);
|
||||||
|
obs_property_list_add_string(p, "Sequential", PROP_MODE_SEQUENTIAL);
|
||||||
|
|
||||||
p = obs_properties_add_button(pr, PROP_CONFIGURE, "Configure", cb_configure);
|
p = obs_properties_add_button(pr, PROP_CONFIGURE, "Configure", cb_configure);
|
||||||
|
obs_property_set_visible(p, info->hasConfigure);
|
||||||
p = obs_properties_add_button(pr, PROP_ABOUT, "About", cb_about);
|
p = obs_properties_add_button(pr, PROP_ABOUT, "About", cb_about);
|
||||||
|
obs_property_set_visible(p, info->hasAbout);
|
||||||
|
|
||||||
return pr;
|
return pr;
|
||||||
}
|
}
|
||||||
@@ -192,7 +211,25 @@ bool VFW::Encoder::cb_configure(obs_properties_t *pr, obs_property_t *p, void *d
|
|||||||
VFW::Info* info = static_cast<VFW::Info*>(obs_properties_get_param(pr));
|
VFW::Info* info = static_cast<VFW::Info*>(obs_properties_get_param(pr));
|
||||||
|
|
||||||
HIC hIC = ICOpen(info->icInfo.fccType, info->icInfo.fccHandler, ICMODE_FASTCOMPRESS);
|
HIC hIC = ICOpen(info->icInfo.fccType, info->icInfo.fccHandler, ICMODE_FASTCOMPRESS);
|
||||||
|
if (info->stateInfo.size() > 0) {
|
||||||
|
LRESULT err = ICSetState(hIC, info->stateInfo.data(), info->stateInfo.size());
|
||||||
|
if (err != ICERR_OK) {
|
||||||
|
PLOG_ERROR("Failed to set state before Configure: %s.",
|
||||||
|
FormattedICCError(err).c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ICSetState(hIC, NULL, 0);
|
||||||
|
}
|
||||||
ICConfigure(hIC, GetDesktopWindow());
|
ICConfigure(hIC, GetDesktopWindow());
|
||||||
|
DWORD size = ICGetStateSize(hIC);
|
||||||
|
if (size > 0) {
|
||||||
|
info->stateInfo.resize(size);
|
||||||
|
LRESULT err = ICGetState(hIC, info->stateInfo.data(), size);
|
||||||
|
if (err != ICERR_OK) {
|
||||||
|
PLOG_ERROR("Failed to retrieve state after Configure: %s.",
|
||||||
|
FormattedICCError(err).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
ICClose(hIC);
|
ICClose(hIC);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -204,6 +241,9 @@ bool VFW::Encoder::cb_about(obs_properties_t *pr, obs_property_t *p, void *data)
|
|||||||
UNREFERENCED_PARAMETER(data);
|
UNREFERENCED_PARAMETER(data);
|
||||||
|
|
||||||
VFW::Info* info = static_cast<VFW::Info*>(obs_properties_get_param(pr));
|
VFW::Info* info = static_cast<VFW::Info*>(obs_properties_get_param(pr));
|
||||||
|
HIC hIC = ICOpen(info->icInfo.fccType, info->icInfo.fccHandler, ICMODE_FASTCOMPRESS);
|
||||||
|
ICAbout(hIC, GetDesktopWindow());
|
||||||
|
ICClose(hIC);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -226,12 +266,12 @@ VFW::Encoder::Encoder(obs_data_t *settings, obs_encoder_t *encoder) {
|
|||||||
// Generic information.
|
// Generic information.
|
||||||
video_t* obsVideo = obs_encoder_video(encoder);
|
video_t* obsVideo = obs_encoder_video(encoder);
|
||||||
const struct video_output_info *voi = video_output_get_info(obsVideo);
|
const struct video_output_info *voi = video_output_get_info(obsVideo);
|
||||||
width = obs_encoder_get_width(encoder); height = obs_encoder_get_height(encoder);
|
m_width = obs_encoder_get_width(encoder); m_height = obs_encoder_get_height(encoder);
|
||||||
fpsNum = voi->fps_num; fpsDen = voi->fps_den;
|
m_fpsNum = voi->fps_num; m_fpsDen = voi->fps_den;
|
||||||
double_t factor = double_t(fpsNum) / double_t(fpsDen);
|
double_t factor = double_t(m_fpsNum) / double_t(m_fpsDen);
|
||||||
userKeyframeInterval = max(uint32_t(factor * obs_data_get_double(settings, PROP_KEYFRAME_INTERVAL)), 1);
|
m_keyframeInterval = max(uint32_t(factor * obs_data_get_double(settings, PROP_KEYFRAME_INTERVAL)), 0);
|
||||||
userBitrate = obs_data_get_int(settings, PROP_BITRATE);
|
m_bitrate = uint32_t(obs_data_get_int(settings, PROP_BITRATE));
|
||||||
userQuality = uint32_t(obs_data_get_double(settings, PROP_QUALITY) * 100);
|
m_quality = uint32_t(obs_data_get_double(settings, PROP_QUALITY) * 100);
|
||||||
|
|
||||||
hIC = ICOpen(myInfo->icInfo.fccType, myInfo->icInfo.fccHandler, ICMODE_FASTCOMPRESS);
|
hIC = ICOpen(myInfo->icInfo.fccType, myInfo->icInfo.fccHandler, ICMODE_FASTCOMPRESS);
|
||||||
if (!hIC) {
|
if (!hIC) {
|
||||||
@@ -244,36 +284,68 @@ VFW::Encoder::Encoder(obs_data_t *settings, obs_encoder_t *encoder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PLOG_DEBUG("Initializing at %" PRIu32 "x%" PRIu32 " with %" PRIu32 "/%" PRIu32 " FPS",
|
PLOG_DEBUG("Initializing at %" PRIu32 "x%" PRIu32 " with %" PRIu32 "/%" PRIu32 " FPS",
|
||||||
width, height, fpsNum, fpsDen);
|
m_width, m_height, m_fpsNum, m_fpsDen);
|
||||||
PLOG_DEBUG("Using Bitrate %" PRIu32 " kbit, Quality %" PRIu32 ".%02" PRIu32 "%%, Keyframe Interval %" PRIu32,
|
PLOG_DEBUG("Using Bitrate %" PRIu32 " kbit, Quality %" PRIu32 ".%02" PRIu32 "%%, Keyframe Interval %" PRIu32,
|
||||||
userBitrate,
|
m_bitrate,
|
||||||
userQuality / 100, userQuality % 100,
|
m_quality / 100, m_quality % 100,
|
||||||
userKeyframeInterval);
|
m_keyframeInterval);
|
||||||
|
|
||||||
|
// Store temporary flags
|
||||||
|
m_useBitrateFlag = (myInfo->icInfo2.dwFlags & VIDCF_CRUNCH) != 0;
|
||||||
|
m_useQualityFlag = (myInfo->icInfo2.dwFlags & VIDCF_QUALITY) != 0;
|
||||||
|
|
||||||
|
const char* emode = obs_data_get_string(settings, PROP_MODE);
|
||||||
|
if (strcmp(emode, PROP_MODE_NORMAL)) {
|
||||||
|
m_useTemporalFlag = false;
|
||||||
|
m_useNormalCompress = true;
|
||||||
|
} else if (strcmp(emode, PROP_MODE_TEMPORAL)) {
|
||||||
|
m_useTemporalFlag = true;
|
||||||
|
m_useNormalCompress = true;
|
||||||
|
} else {
|
||||||
|
m_useTemporalFlag = false;
|
||||||
|
m_useNormalCompress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load State from memory.
|
||||||
|
if (myInfo->stateInfo.size() > 0) {
|
||||||
|
LRESULT err = ICSetState(hIC, myInfo->stateInfo.data(), myInfo->stateInfo.size());
|
||||||
|
if (err != ICERR_OK) {
|
||||||
|
PLOG_ERROR("Failed to set state before encoding: %s.",
|
||||||
|
FormattedICCError(err).c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ICSetState(hIC, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma region Get Bitmap Information
|
#pragma region Get Bitmap Information
|
||||||
vbiInput.resize(sizeof(BITMAPINFOHEADER));
|
m_bufferInputBitmapInfo.resize(sizeof(BITMAPINFOHEADER));
|
||||||
std::memset(vbiInput.data(), 0, vbiInput.size());
|
std::memset(m_bufferInputBitmapInfo.data(), 0, m_bufferInputBitmapInfo.size());
|
||||||
biInput = reinterpret_cast<BITMAPINFO*>(vbiInput.data());
|
m_inputBitmapInfo = reinterpret_cast<BITMAPINFO*>(m_bufferInputBitmapInfo.data());
|
||||||
biInput->bmiHeader.biSize = vbiInput.size();
|
m_inputBitmapInfo->bmiHeader.biSize = (DWORD)m_bufferInputBitmapInfo.size();
|
||||||
biInput->bmiHeader.biWidth = width;
|
m_inputBitmapInfo->bmiHeader.biWidth = m_width;
|
||||||
biInput->bmiHeader.biHeight = height;
|
m_inputBitmapInfo->bmiHeader.biHeight = m_height;
|
||||||
biInput->bmiHeader.biPlanes = 1;
|
m_inputBitmapInfo->bmiHeader.biPlanes = 1;
|
||||||
biInput->bmiHeader.biBitCount = 32;
|
m_inputBitmapInfo->bmiHeader.biBitCount = 32;
|
||||||
biInput->bmiHeader.biCompression = BI_RGB;
|
m_inputBitmapInfo->bmiHeader.biCompression = BI_RGB;
|
||||||
biInput->bmiHeader.biSizeImage = width * height * (biInput->bmiHeader.biBitCount / 8) * biInput->bmiHeader.biPlanes;
|
m_inputBitmapInfo->bmiHeader.biSizeImage = m_width * m_height * (m_inputBitmapInfo->bmiHeader.biBitCount / 8) * m_inputBitmapInfo->bmiHeader.biPlanes;
|
||||||
|
|
||||||
err = ICSendMessage(hIC, ICM_COMPRESS_GET_FORMAT, (DWORD_PTR)biInput, NULL);
|
err = ICSendMessage(hIC, ICM_COMPRESS_GET_FORMAT, (DWORD_PTR)m_inputBitmapInfo, NULL);
|
||||||
if (err <= 0) {
|
if (err <= 0) {
|
||||||
PLOG_ERROR("Unable to retrieve format information size: %s.",
|
PLOG_ERROR("Unable to retrieve format information size: %s.",
|
||||||
FormattedICCError(err).c_str());
|
FormattedICCError(err).c_str());
|
||||||
throw std::exception();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
vbiOutput.resize(err);
|
if (m_useTemporalFlag) {
|
||||||
std::memset(vbiOutput.data(), 0, vbiOutput.size());
|
m_bufferPrevInputBitmapInfo.resize(m_bufferInputBitmapInfo.size());
|
||||||
biOutput = (BITMAPINFO*)vbiOutput.data();
|
std::memcpy(m_bufferPrevInputBitmapInfo.data(), m_bufferInputBitmapInfo.data(), m_bufferInputBitmapInfo.size());
|
||||||
biOutput->bmiHeader.biSize = vbiOutput.size();
|
}
|
||||||
err = ICSendMessage(hIC, ICM_COMPRESS_GET_FORMAT, (DWORD_PTR)biInput, (DWORD_PTR)biOutput);
|
|
||||||
|
m_bufferOutputBitmapInfo.resize(err);
|
||||||
|
std::memset(m_bufferOutputBitmapInfo.data(), 0, m_bufferOutputBitmapInfo.size());
|
||||||
|
m_outputBitmapInfo = (BITMAPINFO*)m_bufferOutputBitmapInfo.data();
|
||||||
|
m_outputBitmapInfo->bmiHeader.biSize = (DWORD)m_bufferOutputBitmapInfo.size();
|
||||||
|
err = ICSendMessage(hIC, ICM_COMPRESS_GET_FORMAT, (DWORD_PTR)m_inputBitmapInfo, (DWORD_PTR)m_outputBitmapInfo);
|
||||||
if (err != ICERR_OK) {
|
if (err != ICERR_OK) {
|
||||||
PLOG_ERROR("Unable to retrieve format information: %s.",
|
PLOG_ERROR("Unable to retrieve format information: %s.",
|
||||||
FormattedICCError(err).c_str());
|
FormattedICCError(err).c_str());
|
||||||
@@ -281,24 +353,40 @@ VFW::Encoder::Encoder(obs_data_t *settings, obs_encoder_t *encoder) {
|
|||||||
}
|
}
|
||||||
#pragma endregion Get Bitmap Information
|
#pragma endregion Get Bitmap Information
|
||||||
|
|
||||||
std::memset(&cv, 0, sizeof(COMPVARS));
|
// Prepare Input Buffers
|
||||||
cv.cbSize = sizeof(COMPVARS);
|
size_t alignedWidth = (m_width / 16) * 16;
|
||||||
cv.dwFlags = ICMF_COMPVARS_VALID;
|
const size_t bufferSize = alignedWidth * m_height * 4;
|
||||||
cv.hic = hIC;
|
m_bufferInput.resize(bufferSize);
|
||||||
cv.fccType = myInfo->icInfo2.fccType;
|
m_bufferPrevInput.resize(bufferSize);
|
||||||
cv.fccHandler = myInfo->icInfo2.fccHandler;
|
|
||||||
cv.lpbiOut = biOutput;
|
|
||||||
cv.lKey = userKeyframeInterval;
|
|
||||||
cv.lDataRate = userBitrate;
|
|
||||||
cv.lQ = userQuality;
|
|
||||||
|
|
||||||
if (!ICSeqCompressFrameStart(&cv, biInput)) {
|
// Begin Compression
|
||||||
PLOG_ERROR("Unable to begin encoding.");
|
if (m_useNormalCompress) {
|
||||||
throw std::exception();
|
LRESULT err = ICCompressBegin(hIC, m_inputBitmapInfo, m_outputBitmapInfo);
|
||||||
|
if (err != ICERR_OK) {
|
||||||
|
PLOG_ERROR("Unable to begin encoding: %s.", FormattedICCError(err).c_str());
|
||||||
|
throw std::runtime_error(FormattedICCError(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD size = ICCompressGetSize(hIC, m_inputBitmapInfo, m_outputBitmapInfo);
|
||||||
|
m_bufferOutput.resize(size);
|
||||||
|
} else {
|
||||||
|
std::memset(&cv, 0, sizeof(COMPVARS));
|
||||||
|
cv.cbSize = sizeof(COMPVARS);
|
||||||
|
cv.dwFlags = ICMF_COMPVARS_VALID;
|
||||||
|
cv.hic = hIC;
|
||||||
|
cv.fccType = myInfo->icInfo2.fccType;
|
||||||
|
cv.fccHandler = myInfo->icInfo2.fccHandler;
|
||||||
|
cv.lpbiOut = m_outputBitmapInfo;
|
||||||
|
cv.lKey = m_keyframeInterval;
|
||||||
|
cv.lDataRate = m_bitrate;
|
||||||
|
cv.lQ = m_quality;
|
||||||
|
|
||||||
|
if (!ICSeqCompressFrameStart(&cv, m_inputBitmapInfo)) {
|
||||||
|
PLOG_ERROR("Unable to begin encoding.");
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inBuffer.resize(width * height * 4);
|
|
||||||
outBuffer.resize(width * height * 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VFW::Encoder::destroy(void* data) {
|
void VFW::Encoder::destroy(void* data) {
|
||||||
@@ -306,7 +394,13 @@ void VFW::Encoder::destroy(void* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VFW::Encoder::~Encoder() {
|
VFW::Encoder::~Encoder() {
|
||||||
ICSeqCompressFrameEnd(&cv);
|
if (m_useNormalCompress) {
|
||||||
|
ICCompressEnd(hIC);
|
||||||
|
} else {
|
||||||
|
ICSeqCompressFrameEnd(&cv);
|
||||||
|
//ICCompressorFree(&cv);
|
||||||
|
}
|
||||||
|
|
||||||
ICClose(hIC);
|
ICClose(hIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,38 +409,110 @@ 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) {
|
bool VFW::Encoder::encode(struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet) {
|
||||||
DWORD dwCkID, cwCompFlags;
|
|
||||||
|
|
||||||
// Vertically invert Image for some reason.
|
// Vertically invert Image for some reason.
|
||||||
const size_t ysize = height;
|
|
||||||
for (size_t y = 0; y < height; y++) {
|
|
||||||
uint8_t offset = frame->linesize[0] * y;
|
|
||||||
uint8_t target = width * 4 * (ysize - y - 1);
|
|
||||||
|
|
||||||
uint8_t* in = reinterpret_cast<uint8_t*>(frame->data[0]) + (frame->linesize[0] * y);
|
size_t maxX = (m_width * 4) / 16;
|
||||||
uint8_t* out = reinterpret_cast<uint8_t*>(inBuffer.data()) + (width * 4 * (ysize - y - 1));
|
if (maxX <= 0)
|
||||||
std::memcpy(out, in, width * 4);
|
maxX = 1;
|
||||||
|
|
||||||
|
// xF xE xD xC xB xA x9 x8 x7 x6 x5 x4 x3 x2 x1 x0
|
||||||
|
// A B G R A B G R A B G R A B G R
|
||||||
|
|
||||||
|
// Swizzle Mask (128-Bit swapping, could do higher but AMD decided to say fuck it)
|
||||||
|
/*
|
||||||
|
0xF 0xE 0xD 0xC
|
||||||
|
0xB 0xA 0x9 0x8
|
||||||
|
0x7 0x6 0x5 0x4
|
||||||
|
0x3 0x2 0x1 0x0
|
||||||
|
*/
|
||||||
|
__m128i swizzle = _mm_set_epi8(
|
||||||
|
0xF, 0xC, 0xD, 0xE,
|
||||||
|
0xB, 0x8, 0x9, 0xA,
|
||||||
|
0x7, 0x4, 0x5, 0x6,
|
||||||
|
0x3, 0x0, 0x1, 0x2);
|
||||||
|
|
||||||
|
const size_t lineSize = m_width * 4;
|
||||||
|
for (size_t y = 0; y < m_height; ++y) {
|
||||||
|
__m128i* pIn = reinterpret_cast<__m128i*>(frame->data[0] + (y * lineSize));
|
||||||
|
__m128i* pOut = reinterpret_cast<__m128i*>(m_bufferInput.data() + ((m_height - 1 - y) * lineSize));
|
||||||
|
|
||||||
|
for (size_t x = 0; x < maxX; ++x) {
|
||||||
|
__m128i in = _mm_loadu_si128(pIn + x);
|
||||||
|
__m128i out = _mm_shuffle_epi8(in, swizzle);
|
||||||
|
_mm_storeu_si128(pOut + x, out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL keyframe; LONG plSize = outBuffer.size();
|
//for (size_t y = 0; y < m_height; ++y) {
|
||||||
LPVOID fptr = ICSeqCompressFrame(
|
|
||||||
&cv,
|
// uint8_t* lineOut = reinterpret_cast<uint8_t*>(m_bufferInput.data()) + (lineOutSize * (m_height - y - 1));
|
||||||
0,
|
|
||||||
reinterpret_cast<LPVOID>(inBuffer.data()),
|
// for (size_t x = 0; x < m_width; ++x) {
|
||||||
&keyframe,
|
// lineOut[x * 4] = lineIn[x * 4 + 2];
|
||||||
&plSize);
|
// lineOut[x * 4 + 1] = lineIn[x * 4 + 1];
|
||||||
if (fptr != NULL) {
|
// lineOut[x * 4 + 2] = lineIn[x * 4];
|
||||||
*received_packet = true;
|
// lineOut[x * 4 + 3] = lineIn[x * 4 + 3];
|
||||||
packet->keyframe = keyframe;
|
// }
|
||||||
packet->dts = packet->pts = frame->pts;
|
|
||||||
packet->size = plSize;
|
//}
|
||||||
packet->data = reinterpret_cast<uint8_t*>(fptr);
|
|
||||||
packet->type = OBS_ENCODER_VIDEO;
|
bool isKeyframe = false;
|
||||||
return true;
|
|
||||||
|
*received_packet = false;
|
||||||
|
if (m_useNormalCompress) {
|
||||||
|
DWORD dwFlags, cwCompFlags;
|
||||||
|
bool makeKeyframe = (m_keyframeInterval > 0) && ((frame->pts % m_keyframeInterval) == 0);
|
||||||
|
|
||||||
|
LRESULT err = ICCompress(hIC,
|
||||||
|
makeKeyframe ? ICCOMPRESS_KEYFRAME : 0,
|
||||||
|
&(m_outputBitmapInfo->bmiHeader), m_bufferOutput.data(),
|
||||||
|
&(m_inputBitmapInfo->bmiHeader), m_bufferInput.data(),
|
||||||
|
&dwFlags, &cwCompFlags,
|
||||||
|
(LONG)frame->pts,
|
||||||
|
m_useBitrateFlag ? m_bitrate : 0,
|
||||||
|
m_useQualityFlag ? m_quality : 0,
|
||||||
|
!makeKeyframe && m_useTemporalFlag ? &(m_prevInputBitmapInfo->bmiHeader) : NULL,
|
||||||
|
!makeKeyframe && m_useTemporalFlag ? m_bufferPrevInput.data() : NULL);
|
||||||
|
if (err != ICERR_OK) {
|
||||||
|
PLOG_ERROR("Unable to encode: %s.", FormattedICCError(err).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap Buffers
|
||||||
|
m_bufferPrevInput.swap(m_bufferInput);
|
||||||
|
|
||||||
|
// Store some information we need right now.
|
||||||
|
packet->size = m_outputBitmapInfo->bmiHeader.biSizeImage;
|
||||||
|
isKeyframe = (cwCompFlags & AVIIF_KEYFRAME) != 0;
|
||||||
} else {
|
} else {
|
||||||
PLOG_ERROR("Unable to encode.");
|
BOOL keyframe; LONG plSize = (LONG)m_bufferInput.size();
|
||||||
return false;
|
LPVOID fptr = ICSeqCompressFrame(
|
||||||
|
&cv,
|
||||||
|
0,
|
||||||
|
reinterpret_cast<LPVOID>(m_bufferInput.data()),
|
||||||
|
&keyframe,
|
||||||
|
&plSize);
|
||||||
|
if (fptr == NULL) {
|
||||||
|
PLOG_ERROR("Unable to encode.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plSize > m_bufferOutput.size())
|
||||||
|
m_bufferOutput.resize(plSize);
|
||||||
|
std::memcpy(m_bufferOutput.data(), fptr, plSize);
|
||||||
|
packet->size = plSize;
|
||||||
|
isKeyframe = keyframe != 0;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*received_packet = true;
|
||||||
|
packet->type = OBS_ENCODER_VIDEO;
|
||||||
|
packet->data = reinterpret_cast<uint8_t*>(m_bufferOutput.data());
|
||||||
|
packet->keyframe = isKeyframe;
|
||||||
|
packet->pts = frame->pts;
|
||||||
|
packet->dts = frame->pts - 2;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VFW::Encoder::update(void *data, obs_data_t *settings) {
|
bool VFW::Encoder::update(void *data, obs_data_t *settings) {
|
||||||
@@ -383,5 +549,7 @@ void VFW::Encoder::get_video_info(void *data, struct video_scale_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;
|
info->format = VIDEO_FORMAT_RGBA;
|
||||||
|
info->range = VIDEO_RANGE_FULL;
|
||||||
|
info->colorspace = VIDEO_CS_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user