From a617100e9c4fdb74f90e91d1e6234d4e5360471f Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Tue, 11 Jul 2017 11:29:13 +0200 Subject: [PATCH] enc-aomedia-av1: Initial version Properly configures, builds, registers and attempts to encode. Gets stuck on an unusual long stall when finally having enough frames to encode. --- .editorconfig | 19 ++ .gitignore | 13 ++ CMakeLists.txt | 156 ++++++++++++++ LICENSE | 278 ++++++++++++++++++++++++ data/locale/en-US.ini | 35 +++ source/av1-encoder.cpp | 474 +++++++++++++++++++++++++++++++++++++++++ source/av1-encoder.h | 79 +++++++ source/plugin.cpp | 72 +++++++ source/plugin.h | 49 +++++ source/strings.h | 70 ++++++ source/version.h.in | 28 +++ 11 files changed, 1273 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 data/locale/en-US.ini create mode 100644 source/av1-encoder.cpp create mode 100644 source/av1-encoder.h create mode 100644 source/plugin.cpp create mode 100644 source/plugin.h create mode 100644 source/strings.h create mode 100644 source/version.h.in diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a1dbec5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig is awesome: http://EditorConfig.org +# Since OBS follows the Linux kernel coding style I have started this file to +# help with automatically setting various text editors to those requirements. + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file. +[*] +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 +indent_style = tab +indent_size = 8 + +# As per notr1ch, for 3rd party code that's a part of obs-outputs. +[plugins/obs-outputs/librtmp/*.{cpp,c,h}] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a610c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +## Ignore file for OBS-AMD-Media-Framework. + +# CMAKE +/build +/build32 +/build64 +/deps +/deps32 +/deps64 + +# Visual Studio +/.vs +/#Build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..224246c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,156 @@ +cmake_minimum_required(VERSION 2.8.12) +PROJECT(enc-aomedia-av1) + +################################################################################ +# Options +################################################################################ +SET(AOMEDIA_AV1_BUILD_DIR "" CACHE PATH "Path to the directory containing the CMake build files.") +SET(AOMEDIA_AV1_INCLUDE_DIR "" CACHE PATH "Include directory for AOMedia1 containing aom.h") + +if(NOT TARGET obs-studio) + SET(OBSSTUDIO_DIR "" CACHE PATH "OBS Studio Source Code Directory") +endif() + +# Validate +if(AOMEDIA_AV1_BUILD_DIR STREQUAL "") + message(FATAL_ERROR "AV1: Build directory not set!") + return() +elseif(NOT EXISTS "${AOMEDIA_AV1_BUILD_DIR}/RelWithDebInfo/aom.lib") + message(FATAL_ERROR "AV1: Build directory set, but could not find RelWithDebInfo/aom.lib!") + return() +elseif(NOT EXISTS "${AOMEDIA_AV1_BUILD_DIR}/Debug/aom.lib") + message(FATAL_ERROR "AV1: Build directory set, but could not find Debug/aom.lib!") + return() +endif() + +if(AOMEDIA_AV1_INCLUDE_DIR STREQUAL "") + message(FATAL_ERROR "AV1: Include directory not set!") + return() +elseif(NOT EXISTS "${AOMEDIA_AV1_INCLUDE_DIR}/aom/aom.h") + message(FATAL_ERROR "AV1: Include directory set, but could not find aom.h!") + return() +endif() + +if(NOT TARGET obs-studio) + if(OBSSTUDIO_DIR STREQUAL "") + message(FATAL_ERROR "OBSSTUDIO_DIR not set!") + return() + endif() + if(NOT EXISTS "${OBSSTUDIO_DIR}/libobs/obs-module.h") + message(FATAL_ERROR "OBSSTUDIO_DIR invalid!") + return() + endif() +endif() + +################################################################################ +# Code +################################################################################ + +# Versioning +SET(VERSION_MAJOR 0) +SET(VERSION_MINOR 0) +SET(VERSION_PATCH 0) +configure_file( + "${PROJECT_SOURCE_DIR}/source/version.h.in" + "${PROJECT_BINARY_DIR}/source/version.h" +) + +# Headers +SET(enc-aomedia-av1_HEADERS + "${PROJECT_SOURCE_DIR}/source/av1-encoder.h" + "${PROJECT_SOURCE_DIR}/source/plugin.h" + "${PROJECT_BINARY_DIR}/source/version.h" + "${PROJECT_SOURCE_DIR}/source/strings.h" +) + +# Sources +SET(enc-aomedia-av1_SOURCES + "${PROJECT_SOURCE_DIR}/source/av1-encoder.cpp" + "${PROJECT_SOURCE_DIR}/source/plugin.cpp" + "${PROJECT_SOURCE_DIR}/source/version.h.in" +) + +# Libraries +SET(enc-aomedia-av1_LIBRARIES + debug ${AOMEDIA_AV1_BUILD_DIR}/Debug/aom.lib + optimized ${AOMEDIA_AV1_BUILD_DIR}/RelWithDebInfo/aom.lib +) + +# Include Directories +INCLUDE_DIRECTORIES( + "${AOMEDIA_AV1_BUILD_DIR}/dist/include" + "${AOMEDIA_AV1_BUILD_DIR}" + "${PROJECT_BINARY_DIR}" + "${PROJECT_BINARY_DIR}/source" + "${PROJECT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/source" +) + +################################################################################ +# Standalone and OBS Studio Build Data +################################################################################ +if(TARGET obs-studio) + # Directories + INCLUDE_DIRECTORIES( + "${CMAKE_SOURCE_DIR}" + ) + SET(LIBOBS_LIBRARIES libobs) +else() + # Standlone Specific + + # Find OBS Libraries + SET(obsPath "${OBSSTUDIO_DIR}") + INCLUDE("${obsPath}/cmake/external/Findlibobs.cmake") + + # Compiling + INCLUDE_DIRECTORIES( + "${OBSSTUDIO_DIR}" + ) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +################################################################################ +# Build +################################################################################ +ADD_LIBRARY(enc-aomedia-av1 MODULE + ${enc-aomedia-av1_HEADERS} + ${enc-aomedia-av1_SOURCES} +) +TARGET_LINK_LIBRARIES(enc-aomedia-av1 + ${LIBOBS_LIBRARIES} + ${enc-aomedia-av1_LIBRARIES} +) + +# All Warnings, Extra Warnings, Pedantic +if(MSVC) + # Force to always compile with W4 + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() +elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + # Update if necessary + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic") +endif() + +if(TARGET obs-studio) + install_obs_plugin_with_data(enc-aomedia-av1 data) +else() + math(EXPR BITS "8*${CMAKE_SIZEOF_VOID_P}") + add_custom_command(TARGET enc-aomedia-av1 POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${PROJECT_SOURCE_DIR}/data" + "${PROJECT_SOURCE_DIR}/#Build/data/obs-plugins/enc-aomedia-av1" + ) + add_custom_command(TARGET enc-aomedia-av1 POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "$" + "${PROJECT_SOURCE_DIR}/#Build/obs-plugins/${BITS}bit/$" + ) + add_custom_command(TARGET enc-aomedia-av1 POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "$/enc-aomedia-av1.pdb" + "${PROJECT_SOURCE_DIR}/#Build/obs-plugins/${BITS}bit/enc-aomedia-av1.pdb" + ) +endif() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..181f05e --- /dev/null +++ b/LICENSE @@ -0,0 +1,278 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. \ No newline at end of file diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini new file mode 100644 index 0000000..e8424e5 --- /dev/null +++ b/data/locale/en-US.ini @@ -0,0 +1,35 @@ +Name="AOMedia Video 1 (Alliance for Open Media)" +Common.Default="Default" +Common.Disabled="Disabled" +Common.Enabled="Enabled" +Common.Fixed="Fixed" +Common.Dynamic="Dynamic" +# Encoder +Usage="Usage" +Threads="Threads" +Profile="Profile" +ErrorResilient="Error Resilience Mode" +ErrorResilient.Partition="Partition" +LagInFrames="Lag (In Frames)" +RateControl.DropFrameThreshold="Drop-Frame Threshold (%)" +RateControl.Resize.Mode="Resize Mode" +RateControl.Resize.Numerator="Resize Numerator" +RateControl.Resize.KeyframeNumerator="Resize Keyframe Numerator" +RateControl.SuperRes.Mode="SuperRes Mode" +RateControl.SuperRes.Numerator="SuperRes Numerator" +RateControl.SuperRes.KeyframeNumerator="SuperRes Keyframe Numerator" +RateControl.Mode="Rate Control Mode" +RateControl.Mode.VBR="Variable Bitrate (VBR)" +RateControl.Mode.CBR="Constant Bitrate (CBR)" +RateControl.Mode.CQ="Constrained Quality (CQ)" +RateControl.Mode.Q="Constant Quality (Q)" +RateControl.Bitrate="Bitrate (kbit)" +RateControl.Quantizer.Min="Quantizer Minimum" +RateControl.Quantizer.Max="Quantizer Maximum" +RateControl.Undershoot="Undershoot (%)" +RateControl.Overshoot="Overshoot (%)" +RateControl.Buffer.Size="Buffer Size (kbit)" +RateControl.Buffer.InitialSize="Buffer Initial Size (kbit)" +RateControl.Buffer.OptimalSize="Buffer Optimal Size (kbit)" +Keyframe.Interval.Min="Keyframe Interval Minimum (Frames)" +Keyframe.Interval.Max="Keyframe Interval Maximum (Frames)" \ No newline at end of file diff --git a/source/av1-encoder.cpp b/source/av1-encoder.cpp new file mode 100644 index 0000000..2f24d16 --- /dev/null +++ b/source/av1-encoder.cpp @@ -0,0 +1,474 @@ +/* + * AV1 Encoder for Open Broadcaster Software Studio + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "av1-encoder.h" +#include "strings.h" +#include +#include +#include +#include + +const char * AV1Encoder::get_name(void *) { + return P_TRANSLATE(P_NAME); +} + +void * AV1Encoder::create(obs_data_t *data, obs_encoder_t *encoder) { + try { + return new AV1Encoder(data, encoder); + } catch (std::runtime_error ex) { + PLOG_ERROR("Exception: %s", ex.what()); + return NULL; + } +} + +AV1Encoder::AV1Encoder(obs_data_t *data, obs_encoder_t *encoder) : m_self(encoder) { + aom_codec_err_t res; + + #pragma region OBS Video Data + // OBS Video Data + uint32_t obsWidth = obs_encoder_get_width(encoder); + uint32_t obsHeight = obs_encoder_get_height(encoder); + video_t *obsVideoInfo = obs_encoder_video(encoder); + const struct video_output_info *voi = video_output_get_info(obsVideoInfo); + uint32_t obsFPSnum = voi->fps_num; + uint32_t obsFPSden = voi->fps_den; + + /// Color Format correction. + switch (voi->format) { + case VIDEO_FORMAT_RGBA: + PLOG_WARNING("Color Format RGBA not supported, using BGRA instead."); + m_imageFormat = AOM_IMG_FMT_ARGB_LE; + break; + case VIDEO_FORMAT_BGRX: + PLOG_WARNING("Color Format BGRX not supported, using BGRA instead."); + m_imageFormat = AOM_IMG_FMT_ARGB_LE; + break; + case VIDEO_FORMAT_BGRA: + m_imageFormat = AOM_IMG_FMT_ARGB_LE; + break; + case VIDEO_FORMAT_YVYU: + m_imageFormat = AOM_IMG_FMT_YVYU; + break; + case VIDEO_FORMAT_YUY2: + m_imageFormat = AOM_IMG_FMT_YUY2; + break; + case VIDEO_FORMAT_UYVY: + m_imageFormat = AOM_IMG_FMT_UYVY; + break; + case VIDEO_FORMAT_Y800: + PLOG_WARNING("Color Format Y800 not supported, using I420 instead."); + m_imageFormat = AOM_IMG_FMT_I420; + break; + case VIDEO_FORMAT_NV12: + PLOG_WARNING("Color Format NV12 not supported, using I420 instead."); + m_imageFormat = AOM_IMG_FMT_I420; + break; + case VIDEO_FORMAT_I420: + m_imageFormat = AOM_IMG_FMT_I420; + break; + case VIDEO_FORMAT_I444: + m_imageFormat = AOM_IMG_FMT_I444; + break; + } + #pragma endregion OBS Video Data + + // Ensure correct resolution. + if ((obsWidth % 2) != 0 || (obsHeight % 2) != 0) { + throw std::runtime_error("Resolution (Width & Height) must be a multiple of 2."); + } + + // Test if Codec is available (again) + const AvxInterface* av1enc = get_aom_encoder_by_name("av1"); + if (!av1enc) { + throw std::runtime_error("Encoder is not available."); + } + + // Get default configuration. + res = aom_codec_enc_config_default(av1enc->codec_interface(), &m_configuration, 0); + if (res != AOM_CODEC_OK) { + throw std::runtime_error("Failed to get default encoder configuration."); + } + + update(data); + m_configuration.g_timebase.den = obsFPSnum; + m_configuration.g_timebase.num = obsFPSden; + m_configuration.g_w = obsWidth; + m_configuration.g_h = obsHeight; + m_configuration.g_input_bit_depth = AOM_BITS_8; + m_configuration.g_bit_depth = AOM_BITS_8; + m_configuration.g_pass = AOM_RC_ONE_PASS; + + maxencodetime = uint32_t((double_t(obsFPSden) / double_t(obsFPSnum)) * 1000000); + + // Create frame buffer. + if (!aom_img_alloc(&m_image, m_imageFormat, obsWidth, obsHeight, 1)) { + throw std::runtime_error("Failed to create frame buffer."); + } else { + switch (voi->range) { + case VIDEO_RANGE_PARTIAL: + m_image.range = aom_color_range_t::AOM_CR_STUDIO_RANGE; + break; + default: + m_image.range = aom_color_range_t::AOM_CR_FULL_RANGE; + break; + } + switch (voi->colorspace) { + case VIDEO_CS_601: + m_image.cs = aom_color_space_t::AOM_CS_BT_601; + break; + case VIDEO_CS_DEFAULT: + case VIDEO_CS_709: + m_image.cs = aom_color_space_t::AOM_CS_BT_709; + break; + } + } + + // Initialize + res = aom_codec_enc_init(&m_codec, av1enc->codec_interface(), &m_configuration, 0); + if (res != AOM_CODEC_OK) { + std::vector buf(1024); + sprintf(buf.data(), "Failed to initialize encoder, code %d.", res); + throw std::runtime_error(std::string(buf.data())); + } + + PLOG_INFO("Encoder initialized."); +} + +void AV1Encoder::destroy(void *ptr) { + delete reinterpret_cast(ptr); +} + +AV1Encoder::~AV1Encoder() { + aom_img_free(&m_image); +} + +void AV1Encoder::get_defaults(obs_data_t *data) { + PLOG_DEBUG("%s", __FUNCTION_NAME__); + + // default settings from actual encoder. + const AvxInterface* av1enc = get_aom_encoder_by_name("av1"); + if (!av1enc) { + throw std::runtime_error("Encoder is not available."); + } + + // Get default configuration. + aom_codec_enc_cfg_t cfg; + aom_codec_err_t res = aom_codec_enc_config_default(av1enc->codec_interface(), &cfg, 0); + if (res != AOM_CODEC_OK) { + throw std::runtime_error("Failed to get default encoder configuration."); + } + + obs_data_set_default_int(data, P_USAGE, cfg.g_usage); + obs_data_set_default_int(data, P_THREADS, cfg.g_threads); + obs_data_set_default_int(data, P_PROFILE, cfg.g_profile); + obs_data_set_default_int(data, P_ERRORRESILIENT, cfg.g_error_resilient); + obs_data_set_default_int(data, P_LAGINFRAMES, cfg.g_lag_in_frames); + obs_data_set_default_int(data, P_RC_DROPFRAMETHRESHOLD, cfg.rc_dropframe_thresh); + obs_data_set_default_int(data, P_RC_RESIZE_MODE, cfg.rc_resize_mode); + obs_data_set_default_int(data, P_RC_RESIZE_NUMERATOR, cfg.rc_resize_numerator); + obs_data_set_default_int(data, P_RC_RESIZE_KEYFRAMENUMERATOR, cfg.rc_resize_kf_numerator); + obs_data_set_default_int(data, P_RC_SUPERRES_MODE, cfg.rc_superres_mode); + obs_data_set_default_int(data, P_RC_SUPERRES_NUMERATOR, cfg.rc_superres_numerator); + obs_data_set_default_int(data, P_RC_SUPERRES_KEYFRAMENUMERATOR, cfg.rc_superres_kf_numerator); + obs_data_set_default_int(data, P_RC_MODE, cfg.rc_end_usage); + obs_data_set_default_int(data, P_RC_BITRATE, cfg.rc_target_bitrate); + obs_data_set_default_int(data, P_RC_QUANTIZER_MIN, cfg.rc_min_quantizer); + obs_data_set_default_int(data, P_RC_QUANTIZER_MAX, cfg.rc_max_quantizer); + obs_data_set_default_int(data, P_RC_UNDERSHOOT, cfg.rc_undershoot_pct); + obs_data_set_default_int(data, P_RC_OVERSHOOT, cfg.rc_overshoot_pct); + obs_data_set_default_int(data, P_RC_BUFFER_SIZE, cfg.rc_buf_sz); + obs_data_set_default_int(data, P_RC_BUFFER_INITIALSIZE, cfg.rc_buf_initial_sz); + obs_data_set_default_int(data, P_RC_BUFFER_OPTIMALSIZE, cfg.rc_buf_optimal_sz); + obs_data_set_default_int(data, P_KF_INTERVAL_MIN, cfg.kf_min_dist); + obs_data_set_default_int(data, P_KF_INTERVAL_MAX, cfg.kf_max_dist); +} + +obs_properties_t * AV1Encoder::get_properties(void *ptr) { + obs_properties_t* pr = obs_properties_create(); + obs_property_t* p = nullptr; + + // g_usage + p = obs_properties_add_list(pr, P_USAGE, P_TRANSLATE(P_USAGE), + obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(p, P_TRANSLATE(P_COMMON_DEFAULT), 0); + + // g_threads + p = obs_properties_add_int_slider(pr, P_THREADS, P_TRANSLATE(P_THREADS), + 0, 16, 1); + + // g_profile + p = obs_properties_add_list(pr, P_PROFILE, P_TRANSLATE(P_PROFILE), + obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(p, "4:2:0 8-bit", 0); + obs_property_list_add_int(p, "4:4:4, 4:2:2, 4:4:0 8-bit", 1); + + // g_error_resilient + p = obs_properties_add_list(pr, P_ERRORRESILIENT, P_TRANSLATE(P_ERRORRESILIENT), + obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(p, P_TRANSLATE(P_COMMON_DEFAULT), AOM_ERROR_RESILIENT_DEFAULT); + obs_property_list_add_int(p, P_TRANSLATE(P_ERRORRESILIENT_PARTITION), AOM_ERROR_RESILIENT_PARTITIONS); + + // g_lag_in_frames + p = obs_properties_add_int(pr, P_LAGINFRAMES, P_TRANSLATE(P_LAGINFRAMES), + 0, 1000, 1); + + // rc_dropframe_thresh + p = obs_properties_add_int_slider(pr, P_RC_DROPFRAMETHRESHOLD, P_TRANSLATE(P_RC_DROPFRAMETHRESHOLD), + 0, 1000, 1); + + // rc_resize_mode + p = obs_properties_add_list(pr, P_RC_RESIZE_MODE, P_TRANSLATE(P_RC_RESIZE_MODE), + obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(p, P_TRANSLATE(P_COMMON_DISABLED), 0); + obs_property_list_add_int(p, P_TRANSLATE(P_COMMON_FIXED), 1); + obs_property_list_add_int(p, P_TRANSLATE(P_COMMON_DYNAMIC), 2); + + // rc_resize_numerator + p = obs_properties_add_int_slider(pr, P_RC_RESIZE_NUMERATOR, P_TRANSLATE(P_RC_RESIZE_NUMERATOR), + 8, 16, 1); + + // rc_resize_kf_numerator + p = obs_properties_add_int_slider(pr, P_RC_RESIZE_KEYFRAMENUMERATOR, P_TRANSLATE(P_RC_RESIZE_KEYFRAMENUMERATOR), + 8, 16, 1); + + // rc_superres_mode + p = obs_properties_add_list(pr, P_RC_SUPERRES_MODE, P_TRANSLATE(P_RC_SUPERRES_MODE), + obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(p, P_TRANSLATE(P_COMMON_DISABLED), 0); + obs_property_list_add_int(p, P_TRANSLATE(P_COMMON_FIXED), 1); + obs_property_list_add_int(p, P_TRANSLATE(P_COMMON_DYNAMIC), 2); + + // rc_superres_numerator + p = obs_properties_add_int_slider(pr, P_RC_SUPERRES_NUMERATOR, P_TRANSLATE(P_RC_SUPERRES_NUMERATOR), + 8, 16, 1); + + // rc_superres_kf_numerator + p = obs_properties_add_int_slider(pr, P_RC_SUPERRES_KEYFRAMENUMERATOR, P_TRANSLATE(P_RC_SUPERRES_KEYFRAMENUMERATOR), + 8, 16, 1); + + // rc_end_usage + p = obs_properties_add_list(pr, P_RC_MODE, P_TRANSLATE(P_RC_MODE), + obs_combo_type::OBS_COMBO_TYPE_LIST, obs_combo_format::OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(p, P_TRANSLATE(P_RC_MODE_VBR), AOM_VBR); + obs_property_list_add_int(p, P_TRANSLATE(P_RC_MODE_CBR), AOM_CBR); + obs_property_list_add_int(p, P_TRANSLATE(P_RC_MODE_CQ), AOM_CQ); + obs_property_list_add_int(p, P_TRANSLATE(P_RC_MODE_Q), AOM_Q); + + // rc_target_bitrate + p = obs_properties_add_int_slider(pr, P_RC_BITRATE, P_TRANSLATE(P_RC_BITRATE), + 1, INT32_MAX, 1); + + // rc_min_quantizer + p = obs_properties_add_int_slider(pr, P_RC_QUANTIZER_MIN, P_TRANSLATE(P_RC_QUANTIZER_MIN), + 0, 63, 1); + + // rc_max_quantizer + p = obs_properties_add_int_slider(pr, P_RC_QUANTIZER_MAX, P_TRANSLATE(P_RC_QUANTIZER_MAX), + 0, 63, 1); + + // rc_undershoot_pct + p = obs_properties_add_int_slider(pr, P_RC_UNDERSHOOT, P_TRANSLATE(P_RC_UNDERSHOOT), + 0, 1000, 1); + + // rc_overshoot_pct + p = obs_properties_add_int_slider(pr, P_RC_OVERSHOOT, P_TRANSLATE(P_RC_OVERSHOOT), + 0, 1000, 1); + + // rc_max_buffer_size + p = obs_properties_add_int_slider(pr, P_RC_BUFFER_SIZE, P_TRANSLATE(P_RC_BUFFER_SIZE), + 1, INT32_MAX, 1); + + // rc_buffer_initial_size + p = obs_properties_add_int_slider(pr, P_RC_BUFFER_INITIALSIZE, P_TRANSLATE(P_RC_BUFFER_INITIALSIZE), + 1, INT32_MAX, 1); + + // rc_buffer_optimal_size + p = obs_properties_add_int_slider(pr, P_RC_BUFFER_OPTIMALSIZE, P_TRANSLATE(P_RC_BUFFER_OPTIMALSIZE), + 1, INT32_MAX, 1); + + // rc_min_quantizer + p = obs_properties_add_int_slider(pr, P_KF_INTERVAL_MIN, P_TRANSLATE(P_KF_INTERVAL_MIN), + 0, 9999, 1); + + // rc_min_quantizer + p = obs_properties_add_int_slider(pr, P_KF_INTERVAL_MAX, P_TRANSLATE(P_KF_INTERVAL_MAX), + 0, 9999, 1); + + // Instance specific settings. + if (ptr != nullptr) + reinterpret_cast(ptr)->get_properties(pr); + + return pr; +} + +void AV1Encoder::get_properties(obs_properties_t *) {} + +bool AV1Encoder::update(void *ptr, obs_data_t *data) { + return reinterpret_cast(ptr)->update(data); +} + +bool AV1Encoder::update(obs_data_t *data) { + m_configuration.g_usage = (unsigned int)obs_data_get_int(data, P_USAGE); + m_configuration.g_threads = (unsigned int)obs_data_get_int(data, P_THREADS); + m_configuration.g_profile = (unsigned int)obs_data_get_int(data, P_PROFILE); + m_configuration.g_error_resilient = (unsigned int)obs_data_get_int(data, P_ERRORRESILIENT); + m_configuration.g_lag_in_frames = (unsigned int)obs_data_get_int(data, P_LAGINFRAMES); + m_configuration.rc_dropframe_thresh = (unsigned int)obs_data_get_int(data, P_RC_DROPFRAMETHRESHOLD); + m_configuration.rc_resize_mode = (unsigned int)obs_data_get_int(data, P_RC_RESIZE_MODE); + m_configuration.rc_resize_numerator = (unsigned int)obs_data_get_int(data, P_RC_RESIZE_NUMERATOR); + m_configuration.rc_resize_kf_numerator = (unsigned int)obs_data_get_int(data, P_RC_RESIZE_KEYFRAMENUMERATOR); + m_configuration.rc_superres_mode = (unsigned int)obs_data_get_int(data, P_RC_SUPERRES_MODE); + m_configuration.rc_superres_numerator = (unsigned int)obs_data_get_int(data, P_RC_SUPERRES_NUMERATOR); + m_configuration.rc_superres_kf_numerator = (unsigned int)obs_data_get_int(data, P_RC_SUPERRES_KEYFRAMENUMERATOR); + m_configuration.rc_end_usage = (aom_rc_mode)obs_data_get_int(data, P_RC_MODE); + m_configuration.rc_target_bitrate = (unsigned int)obs_data_get_int(data, P_RC_BITRATE); + m_configuration.rc_min_quantizer = (unsigned int)obs_data_get_int(data, P_RC_QUANTIZER_MIN); + m_configuration.rc_max_quantizer = (unsigned int)obs_data_get_int(data, P_RC_QUANTIZER_MAX); + m_configuration.rc_undershoot_pct = (unsigned int)obs_data_get_int(data, P_RC_UNDERSHOOT); + m_configuration.rc_overshoot_pct = (unsigned int)obs_data_get_int(data, P_RC_OVERSHOOT); + m_configuration.rc_buf_sz = (unsigned int)obs_data_get_int(data, P_RC_BUFFER_SIZE); + m_configuration.rc_buf_initial_sz = (unsigned int)obs_data_get_int(data, P_RC_BUFFER_INITIALSIZE); + m_configuration.rc_buf_optimal_sz = (unsigned int)obs_data_get_int(data, P_RC_BUFFER_OPTIMALSIZE); + m_configuration.kf_min_dist = (unsigned int)obs_data_get_int(data, P_KF_INTERVAL_MIN); + m_configuration.kf_max_dist = (unsigned int)obs_data_get_int(data, P_KF_INTERVAL_MAX); + + return false; +} + +bool AV1Encoder::encode(void *ptr, struct encoder_frame *frame, struct encoder_packet *packet, bool *recframe) { + return reinterpret_cast(ptr)->encode(frame, packet, recframe); +} + +bool AV1Encoder::encode(struct encoder_frame *frame, struct encoder_packet *packet, bool *received_frame) { + if (m_imageFormat == AOM_IMG_FMT_ARGB_LE) { + std::memcpy(m_image.planes[AOM_PLANE_PACKED], frame->data[0], frame->linesize[0] * m_image.h); + } else if (m_imageFormat == AOM_IMG_FMT_I444) { + std::memcpy(m_image.planes[AOM_PLANE_Y], frame->data[0], frame->linesize[0] * m_image.h); + std::memcpy(m_image.planes[AOM_PLANE_U], frame->data[1], frame->linesize[1] * m_image.h); + std::memcpy(m_image.planes[AOM_PLANE_V], frame->data[2], frame->linesize[2] * m_image.h); + } else if (m_imageFormat == AOM_IMG_FMT_I420) { + std::memcpy(m_image.planes[AOM_PLANE_Y], frame->data[0], frame->linesize[0] * m_image.h); + std::memcpy(m_image.planes[AOM_PLANE_U], frame->data[1], frame->linesize[1] * m_image.h / 2); + std::memcpy(m_image.planes[AOM_PLANE_V], frame->data[2], frame->linesize[2] * m_image.h / 2); + } + + // Encode + aom_codec_err_t res = aom_codec_encode(&m_codec, &m_image, frame->pts, 1, 0, maxencodetime); + if (res != AOM_CODEC_OK) { + PLOG_ERROR("Encoding packet failed, code: %lld", res); + return false; + } + + // Get Packet + aom_codec_iter_t iter = NULL; + for (const aom_codec_cx_pkt_t *pkt = aom_codec_get_cx_data(&m_codec, &iter); pkt != NULL; pkt = aom_codec_get_cx_data(&m_codec, &iter)) { + if (*received_frame == true) + break; + + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + packet->pts = pkt->data.frame.pts; + packet->size = pkt->data.frame.sz; + packet->keyframe = pkt->data.frame.flags & AOM_FRAME_IS_KEY; + packet->data = (uint8_t*)pkt->data.frame.buf; + packet->dts = packet->pts - pkt->data.frame.duration; + *received_frame = true; + } // ToDo: determine live two-pass encoding, technically possible. + } + + if (!*received_frame) { + PLOG_WARNING("No frame for encode call."); + } else { + PLOG_DEBUG("Packet (PTS: %lld, Size: %lld, Keyframe: %s)", + packet->pts, + packet->size, + packet->keyframe ? "y" : "n"); + } + + return true; +} + +bool AV1Encoder::get_extra_data(void *ptr, uint8_t **data, size_t *size) { + return reinterpret_cast(ptr)->get_extra_data(data, size); +} + +bool AV1Encoder::get_extra_data(uint8_t **data, size_t *size) { + aom_fixed_buf_t* buf = aom_codec_get_global_headers(&m_codec); + if (!buf) { + return false; + } + + *data = (uint8_t*)buf->buf; + *size = buf->sz; + + return true; +} + +void AV1Encoder::get_video_info(void *ptr, struct video_scale_info *vsi) { + return reinterpret_cast(ptr)->get_video_info(vsi); +} + +void AV1Encoder::get_video_info(struct video_scale_info *vsi) { + switch (m_imageFormat) { + case AOM_IMG_FMT_ARGB_LE: + vsi->format = VIDEO_FORMAT_BGRA; + break; + case AOM_IMG_FMT_YVYU: + vsi->format = VIDEO_FORMAT_YVYU; + break; + case AOM_IMG_FMT_YUY2: + vsi->format = VIDEO_FORMAT_YUY2; + break; + case AOM_IMG_FMT_UYVY: + vsi->format = VIDEO_FORMAT_UYVY; + break; + case AOM_IMG_FMT_I420: + vsi->format = VIDEO_FORMAT_I420; + break; + case AOM_IMG_FMT_I444: + vsi->format = VIDEO_FORMAT_I444; + break; + } +} + +// Taken from tools_common.c +#include "aom/aomcx.h" +#define AV1_FOURCC 0x31305641 + +static const AvxInterface aom_encoders[] = { + { "av1", AV1_FOURCC, &aom_codec_av1_cx }, +}; + +int get_aom_encoder_count(void) { + return sizeof(aom_encoders) / sizeof(aom_encoders[0]); +} + +const AvxInterface *get_aom_encoder_by_index(int i) { + return &aom_encoders[i]; +} + +const AvxInterface *get_aom_encoder_by_name(const char *name) { + int i; + + for (i = 0; i < get_aom_encoder_count(); ++i) { + const AvxInterface *encoder = get_aom_encoder_by_index(i); + if (strcmp(encoder->name, name) == 0) return encoder; + } + + return NULL; +} diff --git a/source/av1-encoder.h b/source/av1-encoder.h new file mode 100644 index 0000000..f7ede75 --- /dev/null +++ b/source/av1-encoder.h @@ -0,0 +1,79 @@ +/* + * AV1 Encoder for Open Broadcaster Software Studio + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma once +#include "libobs/obs-module.h" + + //AOM +#include +#include + +class AV1Encoder { + public: + static const char *get_name(void *); + + static void *create(obs_data_t *, obs_encoder_t *); + AV1Encoder(obs_data_t *, obs_encoder_t *); + + static void destroy(void *); + ~AV1Encoder(); + + static void get_defaults(obs_data_t *); + + static obs_properties_t *get_properties(void *); + void get_properties(obs_properties_t *); + + static bool update(void *, obs_data_t *); + bool update(obs_data_t *); + + /// Called after encoder create, but before encode. + static void get_video_info(void *, struct video_scale_info *); + void get_video_info(struct video_scale_info *); + + /// Encode a frame. + static bool encode(void *, struct encoder_frame *, struct encoder_packet *, bool *); + bool encode(struct encoder_frame *, struct encoder_packet *, bool *); + + /// Retrieve encoder extra data. + static bool get_extra_data(void *, uint8_t **, size_t *); + bool get_extra_data(uint8_t **, size_t *); + + private: + obs_encoder_t* m_self; + + //AV1 + aom_img_fmt m_imageFormat; + aom_image_t m_image; + aom_codec_enc_cfg_t m_configuration; + aom_codec_ctx_t m_codec; + + uint32_t width, height; + uint32_t maxencodetime; +}; + +// Taken from tools_common.h +typedef struct AvxInterface { + const char *const name; + const uint32_t fourcc; + aom_codec_iface_t *(*const codec_interface)(); +} AvxInterface; + +int get_aom_encoder_count(void); +const AvxInterface *get_aom_encoder_by_index(int i); +const AvxInterface *get_aom_encoder_by_name(const char *name); diff --git a/source/plugin.cpp b/source/plugin.cpp new file mode 100644 index 0000000..d2096b4 --- /dev/null +++ b/source/plugin.cpp @@ -0,0 +1,72 @@ +/* + * AV1 Encoder for Open Broadcaster Software Studio + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "plugin.h" +#include "av1-encoder.h" + +#include "libobs/obs-module.h" + +OBS_DECLARE_MODULE(); +OBS_MODULE_AUTHOR("Michael Fabian Dirks"); +OBS_MODULE_USE_DEFAULT_LOCALE("enc-aomedia-av1", "en-US"); + +obs_encoder_info g_av1encoder; + +MODULE_EXPORT bool obs_module_load(void) { + // Only register if it is actually available. + const AvxInterface* encoder = get_aom_encoder_by_name("av1"); + if (encoder) { + memset(&g_av1encoder, 0, sizeof(g_av1encoder)); + g_av1encoder.id = "enc-aomedia-av1"; + g_av1encoder.type = obs_encoder_type::OBS_ENCODER_VIDEO; + g_av1encoder.codec = "av01"; + g_av1encoder.get_name = AV1Encoder::get_name; + g_av1encoder.create = AV1Encoder::create; + g_av1encoder.destroy = AV1Encoder::destroy; + g_av1encoder.encode = AV1Encoder::encode; + g_av1encoder.get_defaults = AV1Encoder::get_defaults; + g_av1encoder.get_properties = AV1Encoder::get_properties; + g_av1encoder.update = AV1Encoder::update; + g_av1encoder.get_extra_data = AV1Encoder::get_extra_data; + g_av1encoder.get_video_info = AV1Encoder::get_video_info; + + obs_register_encoder(&g_av1encoder); + } + return true; +} + +MODULE_EXPORT void obs_module_unload(void) {} + +MODULE_EXPORT const char* obs_module_name() { + return PLUGIN_NAME; +} + +MODULE_EXPORT const char* obs_module_description() { + return PLUGIN_NAME; +} + +#ifdef _WIN32 +#define NOMINMAX +#define NOINOUT +#include + +BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { + return TRUE; +} +#endif diff --git a/source/plugin.h b/source/plugin.h new file mode 100644 index 0000000..a8d4a1a --- /dev/null +++ b/source/plugin.h @@ -0,0 +1,49 @@ +/* + * AV1 Encoder for Open Broadcaster Software Studio + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma once +#include + +#pragma warning (push) +#pragma warning (disable: 4201) +#include "libobs/obs-module.h" +#include "libobs/util/platform.h" +#pragma warning (pop) + +// Plugin +#define PLUGIN_NAME "AV1" +#include "version.h" + +#define PLOG(level, ...) blog(level, "[" PLUGIN_NAME "] " __VA_ARGS__); +#define PLOG_ERROR(...) PLOG(LOG_ERROR, __VA_ARGS__) +#define PLOG_WARNING(...) PLOG(LOG_WARNING, __VA_ARGS__) +#define PLOG_INFO(...) PLOG(LOG_INFO, __VA_ARGS__) +#define PLOG_DEBUG(...) PLOG(LOG_DEBUG, __VA_ARGS__) + +// Utility +#define vstr(s) dstr(s) +#define dstr(s) #s + +#ifndef __FUNCTION_NAME__ +#if defined(_WIN32) || defined(_WIN64) //WINDOWS +#define __FUNCTION_NAME__ __FUNCTION__ +#else //*NIX +#define __FUNCTION_NAME__ __func__ +#endif +#endif diff --git a/source/strings.h b/source/strings.h new file mode 100644 index 0000000..3af00eb --- /dev/null +++ b/source/strings.h @@ -0,0 +1,70 @@ +/* + * AV1 Encoder for Open Broadcaster Software Studio + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma once +#include "plugin.h" + +#define P_DESCRIPTION(x) x ".Description" +#define P_TRANSLATE(x) obs_module_text(x) +#define P_TRANSLATE_DESCRIPTION(x) P_TRANSLATE(P_DESCRIPTION(x)) + +#define P_NAME "Name" +#define P_COMMON_DEFAULT "Common.Default" +#define P_COMMON_ENABLED "Common.Enabled" +#define P_COMMON_DISABLED "Common.Disabled" +#define P_COMMON_FIXED "Common.Fixed" +#define P_COMMON_DYNAMIC "Common.Dynamic" + +#define P_USAGE "Usage" +#define P_THREADS "Threads" +#define P_PROFILE "Profile" +#define P_ERRORRESILIENT "ErrorResilient" +#define P_ERRORRESILIENT_PARTITION "ErrorResilient.Partition" +#define P_LAGINFRAMES "LagInFrames" + +// Rate Control +#define P_RC_DROPFRAMETHRESHOLD "RateControl.DropFrameThreshold" +/// Super & Subresolution +#define P_RC_RESIZE_MODE "RateControl.Resize.Mode" +#define P_RC_RESIZE_NUMERATOR "RateControl.Resize.Numerator" +#define P_RC_RESIZE_KEYFRAMENUMERATOR "RateControl.Resize.KeyframeNumerator" +#define P_RC_SUPERRES_MODE "RateControl.SuperRes.Mode" +#define P_RC_SUPERRES_NUMERATOR "RateControl.SuperRes.Numerator" +#define P_RC_SUPERRES_KEYFRAMENUMERATOR "RateControl.SuperRes.KeyframeNumerator" +/// Mode +#define P_RC_MODE "RateControl.Mode" +#define P_RC_MODE_VBR "RateControl.Mode.VBR" +#define P_RC_MODE_CBR "RateControl.Mode.CBR" +#define P_RC_MODE_CQ "RateControl.Mode.CQ" +#define P_RC_MODE_Q "RateControl.Mode.Q" +/// VBR, CBR +#define P_RC_BITRATE "RateControl.Bitrate" +/// Quantizer +#define P_RC_QUANTIZER_MIN "RateControl.Quantizer.Min" +#define P_RC_QUANTIZER_MAX "RateControl.Quantizer.Max" +/// Bitrate Tolerance +#define P_RC_UNDERSHOOT "RateControl.Undershoot" +#define P_RC_OVERSHOOT "RateControl.Overshoot" +/// Decoder Buffer Model +#define P_RC_BUFFER_SIZE "RateControl.Buffer.Size" +#define P_RC_BUFFER_INITIALSIZE "RateControl.Buffer.InitialSize" +#define P_RC_BUFFER_OPTIMALSIZE "RateControl.Buffer.OptimalSize" +/// Keyframe Mode +#define P_KF_INTERVAL_MIN "Keyframe.Interval.Min" +#define P_KF_INTERVAL_MAX "Keyframe.Interval.Max" diff --git a/source/version.h.in b/source/version.h.in new file mode 100644 index 0000000..ce9c7a7 --- /dev/null +++ b/source/version.h.in @@ -0,0 +1,28 @@ +/* + * AV1 Encoder for Open Broadcaster Software Studio + * Copyright (C) 2017 Michael Fabian Dirks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +// AUTOGENERATED FILE, DO NOT MODIFY + +#pragma once +#include + +const uint16_t PLUGIN_VERSION_MAJOR = @VERSION_MAJOR@; +const uint16_t PLUGIN_VERSION_MINOR = @VERSION_MINOR@; +const uint32_t PLUGIN_VERSION_PATCH = @VERSION_PATCH@; +const uint64_t PLUGIN_VERSION_FULL = (((uint64_t)(PLUGIN_VERSION_MAJOR & 0xFFFF) << 48ull) | ((uint64_t)(PLUGIN_VERSION_MINOR & 0xFFFF) << 32ull) | ((uint64_t)(PLUGIN_VERSION_PATCH) & 0xFFFFFFFF));